CakePHP linking a hasMany relationship with a different index - php

I have a table of securities like so:
CREATE TABLE IF NOT EXISTS `securities` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ticker` varchar(36) NOT NULL,
`name` varchar(180) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ticker` (`ticker`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=585 ;
I.e. the primary key is id whilst there is another unique index ticker.
The ticker index refers to my other table, secuity_prices which has this
CREATE TABLE IF NOT EXISTS `security_prices` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`price_date` date NOT NULL,
`ticker` varchar(36) NOT NULL,
`price` decimal(10,6) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=88340 ;
I want to define a hasMany relationship between them such that security hasMany securityPrice [securityPrice belongsTo security].
The problem I am having is that Cake is using the primary key of security to link to the security_prices table instead of the ticker field. How can I get the join to be made via the ticker?
Here are my relationships:
//Security
public $hasMany = array(
'SecurityPrice' => array(
'className' => 'SecurityPrice',
'foreignKey' => 'ticker',
)
);
//SecurityPrice
public $belongsTo = array(
'Security' =>
array(
'className' => 'Security',
'foreignKey' => 'ticker',
)
);

You can't use $hasMany to do this, because those associations require that you follow Cake's naming conventions for the primary key. You are trying to join two tables via non-primary key columns. That can be done, but not via Cake's automatic associations.
You need to add the join conditions when performing a find operation or pagination operation.
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables
$options['joins'] = array(
array('table' => 'security_prices',
'alias' => 'SecurityPrice',
'type' => 'LEFT',
'conditions' => array(
'Security.ticker = SecurityPrice.ticker',
)
)
);
$Security->find('all', $options);
If you have to do this often, then you should create a custom find type.
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#creating-custom-find-types
class Security extends AppModel {
public $findMethods = array('ticker' => true);
protected function _findTicker($state, $query, $results = array()) {
if ($state === 'before') {
$query['joins'][] = array(
array('table' => 'security_prices',
'alias' => 'SecurityPrice',
'type' => 'LEFT',
'conditions' => array(
'Security.ticker = SecurityPrice.ticker',
)
)
);
return $query;
}
return $results;
}
}
Then later it's easy to find with the join.
$Security->find('ticker',.....);

Related

how to setup self relation with same table in yii

I am using Yii 1.x version.
I am stuck while setting up a relation with same table using two different model. my scenario is something like this.
There are two tables, user and user_friend_list
Two different model for each table User & UserFriendList.
Following are the fields for my table.
User: id, firstname, lastname.
UserFriendList: id, user_id, friend_user_id
I want to set up relation between two model so that i can fetch all friend information of any user. Kindly suggest me what would be best approach to achieve this???
Note:
The User table stores user information.
User Friend List table store friend list of user.
Try -
In User model -
'friends' => array(self::HAS_MANY, 'UserFriendList', 'user_id'),
In UserFriendList model -
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
Two tables - user & friends
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`password` varchar(50) NOT NULL,
`name` varchar(100) NOT NULL,
`email` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
CREATE TABLE IF NOT EXISTS `friend` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`friend_user_id` int(11) NOT NULL,
`status` varchar(1) NOT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `friend_user_id` (`friend_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
--
ALTER TABLE `friend`
ADD CONSTRAINT `friend_ibfk_2` FOREIGN KEY (`friend_user_id`) REFERENCES `user` (`id`),
ADD CONSTRAINT `friend_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`);
Relations I have in my user model
return array(
'friends' => array(self::HAS_MANY, 'Friend', 'friend_user_id'),
'friends1' => array(self::HAS_MANY, 'Friend', 'user_id'),
);
And friend model
return array(
'friendUser' => array(self::BELONGS_TO, 'User', 'friend_user_id'),
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
);
I hope this helps. I found it on yiiforum

yii migration return 1215 mysql error

After searching and checking my code over & over, now I want to ask.
I have 2 table umessage and ureply, when I want to add foreign key from ureply refrence to umessage I got 1215 mysql error.
Codes in file m140602_080318_create_table_umessage.php which create umessage table:
public function safeUp()
{
/*
* Create table umessage, this is connection way between customer & seller about specific object
* Add foreign key to table user and it's column id with sender column
* Add foreign key to table object and it's column id with objId column
*/
$this->createTable('tbl_umessage', array(
'id' => 'pk',
'time' => 'INT(15) NOT NULL',
'body' => 'TEXT NOT NULL',
'status' => 'TINYINT NOT NULL DEFAULT 0',
'visibleToS' => 'TINYINT NOT NULL DEFAULT 0',
'visibleToR' => 'TINYINT NOT NULL DEFAULT 0',
'sender' => 'INT(11)',
'objId' => 'INT(11) NOT NULL',
), 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci');
}
And codes in file m140602_080329_create_table_ureply.php which create ureply table:
public function safeUp()
{
/*
* Create table ureply which store all replies to exact message
* Add foreign key to table umessage and it's column id with msgId column
*/
$this->createTable('tbl_ureply', array(
'id' => 'pk',
'time' => 'INT(15) NOT NULL',
'body' => 'TEXT NOT NULL',
'isSender' => 'TINYINT NOT NULL DEFAULT 0',
'msgId' => 'INT(11) NOT NULL',
), 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci');
$this->addForeignKey('fk_ureply_umessage', 'tbl_ureply', 'msgId', 'umessage', 'id', 'CASCADE', 'CASCADE');
}
1215 error is for adding fk_ureply_umessage foreign key and I can't find my goofs.
Any help will be appreciated. thanks in advance.
You have a mistake in addForeignKey method:
$this->addForeignKey('fk_ureply_umessage', 'tbl_ureply', 'msgId', 'umessage', 'id', 'CASCADE', 'CASCADE');
the table that the foreign key references to should be tbl_umessage not umessage:
$this->addForeignKey('fk_ureply_umessage', 'tbl_ureply', 'msgId', 'tbl_umessage', 'id', 'CASCADE', 'CASCADE');
The error is related to foreign key references check the data type is equivalent between the foreign key and its reference

CakePHP save empty model

I have 2 types of users: employees and customers and I need to distinguish between them so I have created 2 separate tables for them. Then I chose CakePHP as my framework and then I wanted to follow Simple Authentication tutorial where is one table for Users. So I have decided to create tables like this:
CREATE TABLE IF NOT EXISTS `users` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`username` varchar(36) COLLATE utf8_czech_ci NOT NULL,
`password` varchar(36) COLLATE utf8_czech_ci NOT NULL,
`role` varchar(30) COLLATE utf8_czech_ci NOT NULL,
`name` varchar(30) COLLATE utf8_czech_ci NOT NULL,
`surname` varchar(40) COLLATE utf8_czech_ci NOT NULL,
`phone` varchar(16) COLLATE utf8_czech_ci NOT NULL,
`email` varchar(40) COLLATE utf8_czech_ci NOT NULL,
`employee_id` int(20) DEFAULT NULL,
`customer_id` int(20) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `EMAIL` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `employee` (
`id` int(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `customer` (
`id` int(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT AUTO_INCREMENT=1 ;
My models:
Employee
public $hasOne = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'employee_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
User:
public $belongsTo = array(
'Employee' => array(
'className' => 'Employee',
'foreignKey' => 'employee_id',
'conditions' => '',
'fields' => '',
'order' => ''
);
Add User function:
public function add() {
if ($this->request->is('post')) {
$this->User->create();
$roles = array('admin', 'employee');
if (in_array($this->request->data['User']['role'], $roles)) {
if ($this->User->Employee->save($this->request->data))
$this->request->data['User']['employee_id'] = $this->User->Employee->getLastInsertId();
else {
$this->Session->setFlash(__('Employee could not be saved.'));
return;
}
}
else {
$this->User->Customer->save($this->request->data);
$this->request->data['User']['customer_id'] = $this->User->Customer->getLastInsertId();
$this->User->Customer->create();
}
if (!$this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
else {
$this->Session->setFlash(__('The user has been saved.'));
}
//return $this->redirect(array('action' => 'index'));
}
$employees = $this->User->Employee->find('list');
$customers = $this->User->Customer->find('list');
$this->set(compact('employees', 'customers'));
}
I have a feeling that this conceptual model is not right because Employee and Customer tables contain only primary keys.
Also ($this->User->Employee->save($this->request->data) returns false. Is there a problem that CakePHP is not able to save empty model?
Or do you have any better idea how to model these tables?
Thanks.
If you intend to have information specific to an employee type user and a customer type user, the direction you're going in is fine, and you'd add those future fields to the customer and employee tables. If all you need to do is distinguish between an employee type user and a customer type user, then all you need is a field in your user table to distinguish type, such as
is_employee tinyint(1) default 0,

Associations not using foreign key

I have a model (AccountAgentDetail) that has 2 associations. One is a belongsTo (AccountUser) and the other is a hasOne(AccountProfile). The table for AccountAgent only has a FK relation to AccountUser. This model and the associated models are part of a plugin.
The issue I am seeing is that when the query is executed the join from AccountProfile to AccountAgentDetail is using the wrong association. It is using the id field of the AccountAgentDetail table instead of the fk field that I have defined in the AccountAgentDetail model.
This is the model that I am working with:
<?php
class AccountAgentDetail extends AccountModuleAppModel {
var $name = 'AccountAgentDetail';
var $primaryKey = 'agent_detail_id';
var $belongsTo = array(
'AccountUser' => array(
'className' => 'AccountModule.AccountUser',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
var $hasOne = array(
'AccountProfile' => array(
'className' => 'AccountModule.AccountProfile',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
public function getProspectiveAgents($count = 10)
{
return $this->find('all',
array(
'conditions'=>array('AccountAgentDetail.is_prospect'=>1),
'order'=>array('AccountAgentDetail.created_date DESC')
)
);
}
}
?>
This is the query that is executed when I call the method getProspectiveAgents. The issue I am seeing is in the second left join it is using AccountAgentDetail.agent_detail_id instead of AccountAgentDetail.user_id
SELECT
`AccountAgentDetail`.`agent_detail_id`,
`AccountAgentDetail`.`user_id`,
`AccountAgentDetail`.`is_prospect`,
`AccountAgentDetail`.`mls_id`,
`AccountAgentDetail`.`primary_office`,
`AccountAgentDetail`.`primary_board`,
`AccountAgentDetail`.`commission_plan`,
`AccountAgentDetail`.`referred_by`,
`AccountAgentDetail`.`referral_source`,
`AccountAgentDetail`.`previous_brokerage`,
`AccountAgentDetail`.`created_date`,
`AccountAgentDetail`.`last_modify_date`,
`AccountAgentDetail`.`created_by`,
`AccountAgentDetail`.`last_modifed_by`,
`AccountUser`.`user_id`,
`AccountUser`.`user_name`,
`AccountUser`.`user_pass`,
`AccountUser`.`user_status`,
`AccountUser`.`user_group`,
`AccountUser`.`instance_id`,
`AccountUser`.`is_logged_in`,
`AccountUser`.`is_visible`,
`AccountUser`.`created_by`,
`AccountUser`.`last_modified_by`,
`AccountUser`.`created_date`,
`AccountUser`.`last_modified_date`,
`AccountProfile`.`profile_id`,
`AccountProfile`.`user_id`,
`AccountProfile`.`first_name`,
`AccountProfile`.`middle_name`,
`AccountProfile`.`last_name`,
`AccountProfile`.`birth_date`,
`AccountProfile`.`ssn`,
`AccountProfile`.`employee_id`,
`AccountProfile`.`hire_date`,
`AccountProfile`.`sever_date`,
`AccountProfile`.`rehire_date`,
`AccountProfile`.`created_by`,
`AccountProfile`.`last_modified_by`,
`AccountProfile`.`created_date`,
`AccountProfile`.`last_modify_date`
FROM
`account_agent_details` AS `AccountAgentDetail`
LEFT JOIN `account_users` AS `AccountUser` ON(
`AccountAgentDetail`.`user_id` = `AccountUser`.`user_id`
)
LEFT JOIN `account_profiles` AS `AccountProfile` ON(
`AccountProfile`.`user_id` = `AccountAgentDetail`.`agent_detail_id`
)
WHERE
`AccountAgentDetail`.`is_prospect` = 1
ORDER BY
`AccountAgentDetail`.`created_date` DESC
You have decalared 'agent_detail_id' as primary key, it seems to me logical to select this as primary key for a join instead of user_id which is foreign key! Although I don't know exactly how this class works in the background I think you should work your model.
In your place I would work it in my mind out of the mvc-class-model context, thinking only under the E-Relational "frame".
Not sure about that...maybe you business requirements are such that you can share the primary key user_id, practically speaking having it both as foreign key and primary key in AccountAgentDetail if it is a ono-to-one.

Cakephp Save with a table where the primary key is not 'id'

I have an existing web application that I am converting to use CakePHP.
The problem is that the primary keys for most of the tables are in this format "${table_name}_id" (story_id) instead of the CakePHP way of 'id'
When ever I try to update some of the fields for a row in the story table, the Save() function will return false. Is there any way of getting a more detailed error report from the Save() function. ?
When I set Configure::write('debug', 2); in core.php and check the SQL statements I do not see any UPDATE command, only SELECT statements.
I tried to edit the controller adding the following line to manually set the id field for the controller but it did not help.
$this->Story->id = $this->data['Story']['story_id'] ;
I'm running out of ideas. Any suggestions?
I have included the source code that I am using below
Story controller:
function admin_edit($id = null)
{
if (!$id && empty($this->data)) {
$this->Session->setFlash(__('Invalid '. Configure::read('Site.media') , true));
$this->redirect(array('action'=>'index'));
}
$this->layout = 'admin';
if (!empty($this->data)) {
if ($this->Story->save($this->data)) {
$this->Session->setFlash(__('The '. Configure::read('Site.media') .' has been saved', true));
} else {
$this->Session->setFlash(__('The '. Configure::read('Site.media') .' could not be saved. Please, try again.', true));
}
}
$this->data = $this->Story->read(null, $id );
}
Story model:
class Story extends AppModel {
var $name = 'Story';
var $primaryKey = 'story_id';
var $validate = array(
'author_id' => array('numeric'),
'title' => array('notempty'),
'story' => array('notempty'),
'genra' => array('notempty'),
'form' => array('notempty'),
'wordcount' => array('Please enter a number between 1 and 1000' => array(
'rule' => array('range', 1, 1001),
'message' => 'Please enter a number between 1 and 1000' ),
'Required' => array( 'rule' => 'numeric', 'required' => true )
)
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'Author' => array(
'className' => 'Author',
'foreignKey' => 'author_id'
)
);
var $hasMany = array(
'UserNote' => array(
'className' => 'UserNote',
'foreignKey' => 'story_id',
'dependent' => false,
'conditions' => 'UserNote.notes != ""'
)
);
}
Story view:
echo $form->create('Story', array('action' => 'edit' ) );
echo $form->input('story_id',array('type'=>'hidden') );
echo $form->input('title');
echo $form->input('story');
echo $form->input('bio' );
echo $form->end('Update story details');?>
Story table
CREATE TABLE IF NOT EXISTS `stories` (
`story_id` int(11) NOT NULL AUTO_INCREMENT,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`closed` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`author_id` int(11) NOT NULL,
`title` varchar(255) NOT NULL,
`story` text NOT NULL,
`genra` varchar(255) NOT NULL,
`form` varchar(128) DEFAULT NULL,
`wordcount` varchar(255) NOT NULL,
`terms` varchar(255) NOT NULL DEFAULT '0',
`status` varchar(255) NOT NULL DEFAULT 'slush',
`published` date NOT NULL,
`payment` varchar(255) NOT NULL DEFAULT 'none',
`paypal_address` varchar(255) NOT NULL,
`resubmission` tinyint(1) NOT NULL DEFAULT '0',
`bio` text NOT NULL,
`password` varchar(255) NOT NULL DEFAULT 'yyggrrdd',
`comments` text NOT NULL,
PRIMARY KEY (`story_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=10905 ;
You should manually override the primary key field in the model (which is the right place to do this - the name of a primary key field is an attribute of the model, and not something that should be 'fudged' around in the controller.)
class Example extends AppModel { var $primaryKey = 'example_id'; // example_id is the field name in the database}
The above code is from http://book.cakephp.org/view/437/primaryKey
While the suggestion to turn off validation will work, it's not the right way to go about it.
Lastly, if you're setting model variables within a controller, you use $this->Model->set('attributeName',value) rather than $this->Model->attributeName
It looks like the story controller was validating the data, and the data was invalid.
Adding the following line to the controller will stop the validation of the data.
$this->Story->validate = array(); // Stop valadation on the story.
I found this solution on this page
15 essential CakePHP tips

Categories