I have 2 Models that are joined but not thrue the id's but with another field.
class Post extends AppModel{
public $useDbConfig = 'test';
public $actsAs = array('Containable');
public $belongsTo= array(
'User' => array(
'className' => 'User',
'foreignKey' => false,
'type' => 'inner',
'conditions' => array('User.field = Post.field')
));
}
But the cakephp is not connecting the 2 models correctly. To every Post it just gives the the first user (User with id 1). When I check the SQL-Statements the User statement is following:
SELECT `User`.`id`, `User`.`group_id`... FROM `users` AS `User` WHERE 1 = 1
Any ideas why this is not working?
I also tried joining the models manually:
$this->paginate = array(
'limit' => '15',
'joins' => array(
'INNER JOIN users User ON (User.field = Post.field)'
)
);
$posts= $this->paginate('Post');
Any ideas?
EDIT
I totally forgot to mention that these 2 tables are in different databases. Maybe there is the problem. But I had no problem joining tables over different databases so far.
EDIT
I was wrong about the join so I changed it from "belongsTo" to "hasOne" since a post can only be written by one user. But still no changes.
You can use the foreignKey parameter to any field, not necessarily the id:
Post model:
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'some_user_field'
)
);
User model:
public $hasMany = array (
'Post' => array (
'className' => 'Post',
'foreignKey' => 'some_user_field',
'dependent' => true
)
);
According to the book:
It is not mandatory to follow CakePHP conventions. You can easily
override the use of any foreignKey in your associations definitions.
Nevertheless, sticking to conventions will make your code less
repetitive and easier to read and maintain.
Otherwise, make joins manually:
$this->paginate = array(
'limit' => '15',
'joins' => array(
array(
'table' => 'users',
'alias' => 'User',
'type' => 'inner',
'conditions' => array('User.field = Post.field')
)
)
);
I found a solution although I don't understand why the others won't work. I put
$this->Post->primaryKey = 'field';
right before
$posts= $this->paginate('Post');
and now it works. Can anyone tell me why the other ways won't work?
Related
i have this model structure Client->Project->Landlord->Property
Property belongsto Landlord
Landlord belongsto Project
Project belongsto Client
inside the properties controller i want all the properties that belong to a specific client
How do i do this i have tried it with containable but it always gives me all Properties and not the ones that are related with their foreign keys.
$properties = $this->Property->find('all', array(
'contain' => array(
'Landlord' => array(
'Project' => array(
'conditions' => array(
'client_id' => $user['client_id'] // Passed parameter
)
),
'conditions' => array(
'Landlord.project_id' => 'Project.Id'
)
)
)
));
This isn't doing anything, it evens seems to ignore any typo's i made. what am i doing wrong here?
Oke i figured it out with joins this is what i came up with.
$joins = array(
array('table'=>'projects',
'alias' => 'Project',
'type'=>'inner',
'conditions'=> array(
'Project.client_id = '.$user['client_id']
)),
array('table'=>'landlords',
'alias' => 'Landlord',
'type'=>'inner',
'conditions'=> array(
'Project.id = Landlord.project_id','Landlord.id = Property.landlord_id'
)),
);
$properties = $this->Property->find('all',array('joins'=>$joins,'recursive'=>-1));
In a controller which is completely unrelated - a DoctorsController, I need to get some info from my ChildrenMedicine model and also the name of the medicine which is in it's own related Medicine model. So in my (unrelated) DoctorsController controller I use:
$this->loadModel('ChildrenMedicine');
$this->ChildrenMedicine->recursive = 2;
$childsMeds = $this->ChildrenMedicine->find('all', array('conditions'=>array('child_id'=>$child)));
I expected this to get me an array with all the medicines which the child currently uses, plus the info from the medicines table so I can get the name. However it only gives me:
$childsMeds[0]['ChildrenMedicine']
$childsMeds[1]['ChildrenMedicine']
whereas I was hoping for
$childsMeds[0]['ChildrenMedicine']
$childsMeds[0]['Medicine']
$childsMeds[0]['Child']
$childsMeds[1]['ChildrenMedicine']
$childsMeds[1]['Medicine']
$childsMeds[1]['Child']
Can anyone enlighten me?
EDIT - my ChildrenMedicine model associations:
public $belongsTo = array(
'Child' => array(
'className' => 'Child',
'foreignKey' => 'child_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Medicine' => array(
'className' => 'Medicine',
'foreignKey' => 'medicine_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Use Containable Behavior.
$this->loadModel('ChildrenMedicine');
$this->ChildrenMedicine->Behaviors->load('Containable');
$childsMeds = $this->ChildrenMedicine->find('all',
array(
'conditions' => array('child_id'=>$child),
'contain' => array('Child', 'Medicine')
));
A alternative method would be to use joins to solve this issue. It would be a better option that relying on setting recursive = 2.
$data = $this->ChildrenMedicine->find('all', array('conditions' => array('child_id'=>$child), 'joins' => array(
array(
'table' => 'medicines',
'alias' => 'Medicine',
'type' => 'inner', // could also do outter
'foreignKey' => false,
'conditions'=> array('ChildrenMedicine.medicine_id= Medicine.medicine_id')
))));
You can read more on joins here
For setting up HABTM relationships this article seems helpful. Look at "The Model" code
As I understood from the cakephp-documentation, one of the advantages of the 'containable'-Behavior is being able to fetch fewer data if you need fewer data...
But that doesn't seem to work in my case of a connection between users and usergroups.
My associations look like:
Group
hasMany: Membership
User
hasMany: Membership
Membership
belongsTo: User, Group
(I'm not using HABTM, instead use the Model 'Membership' in between to join users and groups).
All Models implement the 'Containable'-Behavior.
Now I want to get all the members of a group with a certain id, only their ids and mail-addresses. My query is built like that:
$members = $this->Membership->find('all', array(
'conditions' => array(
'group_id' => $id
),
'contain' => array(
'User' => array(
'fields' => array('id', 'fullName')
),
)
));
But the resulting array looks like:
array(
(int) 0 => array(
'Membership' => array(
'id' => '1',
'group_id' => '1',
'user_id' => '1',
'role' => 'member'
),
'Group' => array(
'id' => '1',
'name' => 'Foo Group'
),
'User' => array(
'password' => '*****',
'id' => '1',
'name' => 'Dr. Foo'
'mail' => 'foo#bar.baz',
'role' => 'admin'
)
)
)
So there are definietely more fields fetched than I wanted to... (it's the same thing btw wenn I set the 'contain'-key to:
'contain' => array(
'User.fullName', 'User.id'
)
Am I using the containable-behavior wrong?
Your models don't seem to be acting containabl-y at all. Have you set your models to act as containable?
class Post extends AppModel {
public $actsAs = array('Containable');
}
If so, maybe the problem is with the recursion (to avoid getting the Group array with the query). Containable behavior should handle the recursion level on its own, but try setting it on the AppModel just to be sure
class AppModel extends Model {
public $actsAs = array('Containable');
public $recursive = -1;
Your first attempt
'contain' => array(
'User' => array(
'fields' => array('id', 'fullName')
),
)
looks good in terms of syntax, so it probably the actAs thing.
Also, for debugging also, try
$this->Membership->contain('User');
$this->Membership->find('all', array(
'conditions' => array(
'group_id' => $id
));
and see if you get the expected results that way.
I can't seem to figure out how the containable behavior works, or how to retrieve the data from (and based) on associated models.
I need to put the following MySQL query in the cakephp find('all') function, I know i can use the query builder for the sql syntax but I want to use find function and want to figure out how it works.
SELECT events.id, events.name FROM events, user_roles
WHERE user_roles.event_id = events.id
AND user_roles.user_id = 1
The idea is simple, in the user_roles the user_id, role_id and event_id are stored. Now I want to retrieve all events for a certain user (here user_id = 1). I tried some variations on the following:
$data = $this->Event->find('all',array(
'contain' => array(
'UserRole' => array(
'fields' => array('id'),
'conditions' => array(
'UserRole.user_id' => $this->Auth->user('id')
)
)
)
));
But this keeps returning all events. What I want is an array like (or any other result with the same associated data):
array(
[0] => array(
[Event] => array(
'id' => 1,
'name' => 'Event Name'
),
[UserRole] => array(
'role_id' => 2,
'event_id'=> 1
)
[1] => array(
.... etc ...
)
The following models and associations are defined
class Event extends AppModel {
public $hasMany = array(
'UserRole' => array(
'className' => 'UserRole',
'foreignKey' => 'event_id',
'dependent' => false,
)
)
)
class UserRole extends AppModel {
public $belongsTo = array(
'Event' => array(
'className' => 'Event',
'foreignKey' => 'event_id'
),
)
)
So how does it work ;).
Edit:
I forgot to mention that I've set in AppModel.php:
public $recursive = -1;
Containable finds the associated records for the original query. What you are doing is a find('all') on your Event model with absolutely no conditions, so it will return all Events.
Then Containable will retrieve all the UserRole records that are for the given user_id and are also associated to the Event by the event_id
If you want to only retrieve events for the given user, you could do it one of two ways
Via the Event->UserRole association and chained models:
$data = $this->Event->UserRole->find('first', array(
'conditions' => array('UserRole.user_id' => $this->Auth->user('id'),
'contain' => array('Event')
));
$events = $data['Event'];
or
Via your current code and then using the Hash::extract method to get the Events that match. (Hash is a CakePHP core class)
$events = Hash::extract('/UserRole[user_id='.$this->Auth->user('id').']/..', $data);
I'm having this problem with relationships with Codeigniter's DataMapper.
I have an Interview model that has an author_id and an interviewee_id. They both relate to the user id in the user model.
I've been trying several approaches and none work; this is what I have right now:
class Interview extends DataMapper
{
var $has_one = array(
'interviewee' => array(
'class' => 'user',
'other_field' => 'done_interview'),
'author' => array(
'class' => 'user',
'other_field' => 'written_interview')
);
}
class User extends DataMapper
{
var $has_many = array(
'done_interview' => array(
'class' => 'interview',
'other_field' => 'interviewee'),
'written_interview' => array(
'class' => 'interview',
'other_field' => 'author')
);
}
How do I let DataMapper know that one relationship will go through author_id, and the other through interviewee_id?
Quoting Nelo who answered his question in a comment:
It seems there needs to be a user_id field necessarily and then for the other relationships I can name the fields however I want. So Changing interviewee_id to user_id seems to do the drill. Just in case someone has the same problem out there