I have two tables:
users contains (id, name, email) id is the primary key.
users_details contains (user_id, address, city, postcode) user_id is the foreign key to user table.
The users.id=users_details.user_id.
I wrote user model like this in User.php
class User extends AppModel {
public $name = 'User';
public $displayField = 'name';
public $primaryKey = 'id';
var $belongsTo = 'UsersDetail';
public $hasone = array(
'UsersDetail' => array(
'className' => 'UsersDetail',
'conditions' => '',
'fields' => '',
'foreignKey' => 'id',
'order' => '',
'dependent' => true
)
);
}
I wrote the usersdetail model in UsersDetail.php
class UsersDetail extends AppModel {
public $name = 'UsersDetail';
public $displayField = 'name';
public $belongsTo = array( 'User' =>
array( 'className' => 'User','foreignKey' => 'user_id') );
public $hasone = array(
'User' => array(
'className' => 'User',
'conditions' => '',
'fields' => '',
'foreignKey' => 'id',
'order' => '',
'dependent' => true
));
}
I want to get the data from table using the join with condition. The users.id=users_details.user_id.
You have declared two relationships for each model association, when you should only have one going each way.
Like so:
In the userDetail Model:
public $hasMany = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
And consequently, in the User Model:
public $belongsTo = array(
'UserDetail' => array(
'className' => 'UserDetail',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Check out the documentation for more details.
user has (id, name, email) id is the primary key.
users_details has(user_id, address, city, postcode) user_id is the foreign key to user table. Means.
Point to remember:
one to one relation - hasOne - A user table has one users_detail.
belongto - A userdetails table belongs to User table.
So, the relation between the two tables is hasone and belongsto.
find the right code below.
User model:
public $hasOne = array('UsersDetail' => array(
'className' => 'UsersDetail',
'foreignKey' => 'user_id')
);
UsersDetail model
public $belongsTo = array('User' => array(
'className' => 'Users',
'foreignKey' => 'id',
'conditions' => '',
'fields' => '',
'order' => '')
);
which provide the join precisely. You can add the conditions, order in collections.
The output query
SELECT `User`.`id`, `User`.`first_name`, `User`.`last_name`, `User`.`email`, `User`.`mobile`, `User`.`pswd`, `UsersDetail`.`user_id`, `UsersDetail`.`address`, `UsersDetail`.`city`, `UsersDetail`.`state`, `UsersDetail`.`postcode`, `UsersDetail`.`dob`, `UsersDetail`.`photo` FROM `cakedemo`.`users` AS `User` LEFT JOIN `cakedemo`.`users_details` AS `UsersDetail` ON (`UsersDetail`.`user_id` = `User`.`id`) WHERE 1 = 1
Thank you,
Related
I have jobs and countries table
Table - Jobs
Column name = pickup_region1
Table - Countries
Column name = id
I am using find query on job table..
How to associate country table on job model so that when I run select query on job it automatically bind this relation
jobs.country_id = Countries.id
class Job extends AppModel {
public $belongsTo = array(
'country' => array(
'className' => 'country',
'primaryKey' => 'id',
'foreignKey' => 'country_id',
'dependent' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
) );
}
Model for countries should be: Country.php for table countries,
when you stick to convention, this statment is enought:
public $belongsTo = array('Country');
If you have $recursive set to -1, you need to tell cake which association should use when, like that:
(more about $recursive)
$this->Job->contain(array('Country'));
And next query on Job will arrive with Country
You can also contain inside find query:
$this->Job->find('all', array(
...
'contain' => array('Country')
));
more info
hello I have three tables in my database. Country, State,and City. In state table there is a country_id as foreign key and in city table there is state_id as foreign key. The problem is I want to get the country name when I am querying in the city table. I'll share the code so you can fully understand
Country.php
class Country extends AppModel{
public $useTable = 'countries';
}
State.php
class City extends AppModel{
public $useTable = 'cities';
public $belongsTo = array(
'State' => array(
'className' => 'State',
'foreignKey' => 'state_id',
'fields' => array('state.id','state.name')
)
);
}
City
class State extends AppModel{
public $useTable = 'states';
public $belongsTo = array(
'Country' => array(
'className' => 'Country',
'foreignKey' => 'country_id',
'fields' => array('Country.id','Country.name','Country.short_name')
)
);
okay here is the code. If I use this query
$this->State->find('all', array(
'conditions' => array(
'state.country_id' => $country_id
),
I can get the country name from country table. same is the case If I do this in city table like this
$this->City->find('all', array(
'conditions' => array(
'city.state_id' => $state_id
),
I can get all the state names city names here. So Now in this same table in one query how I can get the country name as well ?
Try using the recursive property.
Setting $this->City->recursive = 2 gets associated data for the table and associated data on the associated tables.
$this->City->recursive = 2;
$this->City->find('all', array(
'conditions' => array(
'city.state_id' => $state_id
),
//other stuff you use in this find
));
You can also using the "joins" flag.
$this->City->find('all', array(
'conditions' => array(
'City.state_id' => $state_id
),
'joins' => array(
array(
'table' => 'states',
'alias' => 'State',
'type' => 'left',
'conditions' => 'State.id = City.state_id'
),
array(
'table' => 'countries',
'alias' => 'Country',
'type' => 'left',
'conditions' => 'Country.id = State.country_id'
)
),
'fields' => array('City.id', 'State.name', 'Country.name',
//include all the fields you need
)
));
See: http://book.cakephp.org/2.0/en/models/model-attributes.html#recursive
In addition to the first answer, make sure to contain the foreign key for the Country.
class City extends AppModel{
public $belongsTo = array(
'State' => array(
'className' => 'State',
'foreignKey' => 'state_id',
'fields' => array('State.id','State.name', 'State.country_id'), // Make sure to contain the foreign key for the `Country`.
)
);
}
For your information, you can also use ContainableBehavior instead of recursive.
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
I am trying to display a field from a 3rd table in a relationship, and after checking posts here and the docs, I am still stuck.I have 3 models all related in some way. I have found similar posts here but I am still not getting it to work. I am learning so sorry if I have missed this somewhere in the docs but I have read a fair bit and have tried a lot. I am guessing too much now on this so I need help.
1)Tutorsession - belongsto teachers,
2) Teacher -has 1 user,hasmany tutorsessions
3) User- has 1 teacher, //////I want to display a field from this table given I display tutorsessions
In controller
$this->set('tutor',
$this->Tutorsession->find('first',
array(
'conditions' => array('Teacher.user_id' => $id),
'contain' => 'User.username'
)
)
); //////////no error but no results
from view echo '<td>'. $item['User']['username'].'</td>'; ///////error user undefined
The tutorsession automatically gets rows from teacher table but not user table witht he model setup on a findall.
I want to display a username from the user table. I display the tutorsession table , I then can display the teacher table with the model association but I cant go from the teacher table to the user table to get a user name from the user id as the common field. I have checked the docs below and I am not sure why my code isnt ble to display a username from users table.
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html
update: here are the models
class User extends AppModel {
public $hasOne = array(
'Teacher' => array(
'className' => 'Teacher',
'dependent' => true
)
class Tutorsession extends AppModel
{
public $name='Tutorsession';
public $belongsTo = array(
'Teacher' => array(
'className' => 'Teacher',
'foreignKey' => 'teacher_id'
)
class Teacher extends AppModel
{
public $name='Teacher';
public $hasMany = array('Tutorsession');
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
Add containable behavior in your Model
public $actsAs = array('Containable');
Execute following query In controller
$contain = array('Teacher' => array('User'));
$this->set('tutor', $this->Tutorsession->find('first',array(
'conditions' => array('Teacher.user_id' => $id),
'contain' => $contain
)));
Try this:
$this->Tutorsession->recursive = 2;
$this->Tutorsession->Teacher->contain('User');
$this->set('tutor',
$this->Tutorsession->find('first',
array(
'conditions' => array('Teacher.user_id' => $id)
)
)
);
$tutor=$this->Teacher->find('first',
array(
'conditions' => array('Teacher.user_id' => $id)
)
);
debug($tutor); exit();
Your result similar:
'Teacher' => array(
'id' => '1',
'name' => 'abc',
'created' => '1397040385',
'updated' => '1397725860'
),
'User' => array(
'id' => '4',
'name' => 'administrators',
'created' => '1397032953',
'modified' => '1397032953'
),
'Tutorsession' => array(
0=>array(
'id' => '3',
'name'=>'abc',
'created' => '1400729137'
),
1=>array(
'id' => '4',
'name'=>'abc',
'created' => '1400729137'
)
)
Ensure your models are correct
I have three tables: Users, Profiles, and Friends.
They are connected like so:
public $name = 'User';
public $hasOne = 'Profile';
public $hasMany = array(
'Post',
'Answer'
);
public $hasAndBelongsToMany = array(
'User'=>array(
'className' => 'User',
'joinTable' => 'friends',
'foreignKey' => 'user_from',
'associationForeignKey' => 'user_to'
)
);
and the profile model is just a belongsTo user so I have not shown that, and the friends model is virtual by doing the association in the user model.
So to get a list of friends for a user. I pass the username like so in the friends controller:
public function index( $username )
{
$friends = $this->User->find('first',
array(
'conditions'=>array(
'User.username'=>$username
),
'contain'=>array(
'Profile',
'Friend'=>array(
'conditions'=>array(
'Friend.status'=>1
),
'contain'=>'Profile'
)
)
)
);
$this->set('friends', $friends);
}
Which should pull the data for the friends and there associated Profile info!
However I get this error: Model "Friend" is not associated with model "Profile" [APP/Cake/Model/Behavior/ContainableBehavior.php, line 339] so something isn't working correctly...
Can anyone help?
I tried changing the name of he hasAndBelongsToMany to Friends in the model but that throw an error that the table wasn't unqiue... so that's not the solution either.
Tables:
**users**
id
username
password
**profiles**
id
name
user_id
**friends**
id
user_from
user_to
status
If I change the Model to become:
public $hasAndBelongsToMany = array(
'Friend'=>array(
'className' => 'Friend',
'joinTable' => 'friends',
'foreignKey' => 'user_from',
'associationForeignKey' => 'user_to'
)
);
then I get this error:
Database Error
Error: SQLSTATE[42000]: Syntax error or access violation: 1066 Not unique table/alias: 'Friend'
SQL Query: SELECT `Friend`.`id`, `Friend`.`user_from`, `Friend`.`user_to`, `Friend`.`created`, `Friend`.`status`, `Friend`.`id`, `Friend`.`user_from`, `Friend`.`user_to`, `Friend`.`created`, `Friend`.`status` FROM `db52704_favorr`.`friends` AS `Friend` JOIN `db52704_favorr`.`friends` AS `Friend` ON (`Friend`.`user_from` = 6 AND `Friend`.`user_to` = `Friend`.`id`)
Models and aliases of related models should be unique. In your case the problem is:
public $name = 'User';
...
public $hasAndBelongsToMany = array(
'User'=>array(
...
If friends is the join table name, than you could use Friend as the alias of the friend User.
Second thing is you're trying to contain both User => Profile and User => Friend => Profile which is probably also problematic.
In my experience, deep contains can get quite inefficient. You might consider dropping the Friend's Profile from the contain, and then extracting the friend user ids and getting the profiles separately.
Your Friend model has no user_id and thats why User model can't make contact with Friend. Try with the following:
public function index( $username )
{
$this->User->Behaviors->attach('Containable');
$friends = $this->User->find('first',
array(
'conditions'=>array(
'User.username'=>$username
),
'contain'=>array(
'Profile',
'Friend'=>array(
'conditions'=>array(
'Friend.status'=>1
)
)
)
)
);
$this->set('friends', $friends);
}
I think you are looking for something like this:
User.php
public $hasAndBelongsToMany = array(
'Friend'=>array(
'className' => 'User',
'joinTable' => 'friends',
'foreignKey' => 'user_from',
'associationForeignKey' => 'user_to'
)
);
This will alias the User table as a Friend to itself in a habtm relationship. To keep with convention, the join table should be called friends_users with user_id and friend_id keys.
Here's the result I get with a test app I made (just add your conditions):
$this->User->contain(array(
'Profile',
'Friend' => array(
'Profile',
),
));
debug($this->User->find('all', array('conditions' => array('User.id' => 1))));
array(
(int) 0 => array(
'User' => array(
'id' => '1',
'name' => 'user1'
),
'Profile' => array(
'id' => '1',
'user_id' => '1'
),
'Friend' => array(
(int) 0 => array(
'id' => '2',
'name' => 'user2',
'FriendsUser' => array(
'id' => '1',
'user_id' => '1',
'friend_id' => '2'
),
'Profile' => array(
'id' => '2',
'user_id' => '2'
)
)
)
)
)
In a model, is it possible to do multiple iterations of belongsTo?
Assuming 3 tables, alerts, schedules, tasks and the model is for alerts. I want to get at a field from tasks but I have to join through schedules.
alerts.schedule_id -> schedules.tasks_id -> tasks.name
I tried this syntax:
var $belongsTo = array(
'Schedule' => array(
'className' => 'Schedule',
'foreignKey' => 'schedule_id'
),
'Task' => array(
'className' => 'Task',
'foreignKey' => 'task_id'
));
But that just joins both Schedules and Tasks directly to Alerts (here's the sql that was generated:
LEFT JOIN `schedules` AS `Schedule` ON (`Alert`.`schedule_id` = `Schedule`.`id`) LEFT JOIN `tasks` AS `Task` ON (`Alert`.`task_id` = `Task`.`id`)
)
I ended up setting this property:
$this->Alert->recursive = 2;
In the index() function of the alerts controller.
Then I didn't have to do a separate find and was able to reference the name field in the Tasks table in index.ctp like this:
<td><?php echo $alert['Schedule']['Task']['name']; ?> </td>
Try this.
in alert.php
CLass Alert extends AppModel{
var $name = 'Alert';
var $belongsTo = array(
'Schedule' => array(
'className' => 'Schedule',
'foreignKey' => 'schedule_id'
)
);
}
in schedule.php
CLass Schedule extends AppModel{
var $name = 'Schedule';
var $belongsTo = array(
'Task' => array(
'className' => 'Task',
'foreignKey' => 'task_id'
)
);
}
then find with recursive=>2