CakePhp 2.5 $belongsTo in two models - php

Right now I have this:
Groups
Inside Groups we have Subgroups
Inside Subgroups we have Comments
Subgroups belongs to Groups 1, 2, 3 ; subgroups table have group_id field
Comments belongs to Subgroups A, B, C ; comments table have subgroup_id field
My models:
CommentsGroup.php
<?php
App::uses('AppModel', 'Model');
class CommentsGroup extends AppModel {
public $useTable = 'comment_groups';
}
CommentsSubGroup.php
<?php
App::uses('AppModel', 'Model');
class CommentsSubGroup extends AppModel {
public $useTable = 'comment_subgroups';
public $belongsTo = array(
'CommentsGroup' => array(
'className' => 'CommentsGroup',
'foreignKey' => false,
'conditions' => ['`CommentsGroup`.`id` = `CommentsSubGroup`.`group_id`']
)
);
}
Comment.php
<?php
App::uses('AppModel', 'Model');
class Comment extends AppModel {
public $belongsTo = array(
'CommentsSubGroup' => array(
'className' => 'CommentsSubGroup',
'foreignKey' => false,
'conditions' => ['`CommentsSubGroup`.`id` = `Comment`.`subgroup_id`']
)
);
}
When I try from my controller to get the subgroup_id related to the comment it's ok. When I try to get more (the group_id linked to the subgroup_id), I fail.
Query without recursive is ok, otherwise I have:
$data = $this->_Model->find('all', ['conditions' => ['subgroup_id' => $id], 'recursive' => 2, 'order' => [$this->_Modelname . '.id' => 'DESC']]);
Take in consideration $this->_Model equals to Comment .
The error I have:
Database Error Error: SQLSTATE[42S22]: Column not found: 1054 Unknown
column 'CommentsSubGroup.group_id' in 'where clause'
SQL Query: SELECT CommentsGroup.id, CommentsGroup.name FROM
botobot_comments.comment_groups AS CommentsGroup WHERE
CommentsGroup.id = CommentsSubGroup.group_id
Any guess ? Or I am wrong and I should use $hasMany relationship ?
Thanks.

You are correct in using a belongsTo relationship but you need to specify the foreignKey attribute in order for the relationships to work. You also need to take the join condition out of the conditions key (as Cake can figure that out based on the foreign key).
For example:
App::uses('AppModel', 'Model');
class Comment extends AppModel {
public $belongsTo = array(
'CommentsSubGroup' => array(
'className' => 'CommentsSubGroup',
'foreignKey' => 'subgroup_id'
)
);
}
You also need to follow Cakes naming convention with your models and tables otherwise youll need to explicitly declare your table names and primary keys in their respective models.
In your case, CommentsSubGroup should be CommentSubGroup which would assume the table called comment_sub_group and a primary key of comment_sub_group_id

Related

CakePHP recursive not working with conditions

I have a problem with querying associated data from a Model in CakePHP. I wrote an example to show the behavior:
TestController.php:
class TestController extends AppController
{
public $uses = array(
'User',
'Upload',
'Detail'
);
public function test(){
$result = $this->Upload->find('all', array(
'recursive' => 2,
'conditions' => array('Detail.id' => 1)
));
print_r($result);
}
}
Upload.php:
class Upload extends AppModel {
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
}
Detail.php:
class Detail extends AppModel {
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
}
User.php:
class User extends AppModel {
public $hasOne = 'Detail';
public $hasMany = array(
'Upload' => array(
'className' => 'Upload',
'foreignKey' => 'user_id',
)
);
}
When I remove the condition I get back an array with Details included. But with the condition I get the following error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Detail.id' in 'where clause'
Looking at the SQL Queries it seems like he is not joining the tables correctly when I add the condition. Without the condition he is joining all three tables.
Is this a bug in CakePHP or am I doing anything wrong?
No, it is not a bug with CakePHP. It's simply the way it's designed, using conditions during a find on associated models will often create an invalid query. You should be using containable behavior or manually joining to use conditions on associated models.
Also, I suspect that you will not get the results you are looking for doing this way anyways. CakePHP by default uses left joins. Therefore, your results will not be limited by those associated with the desired Detail ID, but rather, it will get all uploads, all users associated with those uploads, and then only those details associated with those users that have the correct ID. The simplest way then to get what you're probably looking for is to do the query from the opposite direction:
$result = $this->Detail->find('all', array(
'recursive' => 2,
'conditions' => array('Detail.id' => 1)
));
EDIT: If you do want to do left joins, then make your query this way:
$result = $this->Upload->find('all', array(
'contain' => array('User' => array('Detail' => array('conditions' => array('Detail.id' => 1))),
));

CakePHP return hasOne relationships within main result array

I have a model class in CakePHP defined like this:
class Programme extends AppModel {
public $hasOne = array(
'ProgrammeLikes' => array(
'className' => 'ProgrammeLikes',
'fields' => array('likes'));
}
When retrieving my models from the database they are returned as an array with an array keyed to 'Programme' and a separate array keyed to 'ProgrammeLikes' (which contains the 'likes' value correctly). In order to reduce the changes necessary to existing code I want the 'likes' value to be within the 'Programme' array.
Is this possible?
Thanks in advance
Use virtualFields here to get this thing to be done.
class Programme extends AppModel {
public $hasOne = array(
'ProgrammeLikes' => array(
'className' => 'ProgrammeLikes',
'fields' => array('likes')
);
public $virtualFields = array(
'likes' => 'SELECT likes FROM programme_likes AS ProgrammeLikes WHERE ProgrammeLikes.id = Programme.programme_likes_id'
);
// Where programme_likes_id is the foriegnkey for Programme model
}
Note: I assumed programme_likes is your table name for ProgrammeLikes Model and programme_likes_id is the foriegnkey for
Programme Model, so you can arrange the query in your own way that suits your requirement.

Retrieving data through two model relationships

I'm trying to retrieve some data through two model relationships with CakePHP. The models and their associations are as follows:
User hasOne Profile HABTM Skill
I would like the user's skills to be returned when I do a find() operation on the User model, and right now it isn't returned. Here's my find call which is being executed against the User model:
$this->paginate = array(
'conditions' => array(
'OR' => array(
'Profile.firstname LIKE' => "%$q%",
'Profile.lastname LIKE' => "%$q%"
)
)
);
It's returning user data and profile data, but not any skill data. I tried setting recursive to 2 or 3, but that doesn't help either. The only way I can get skill data is if I run find() against the Profile model, but I can't do that. For clarification here's the relevant models and their relationships:
// app/Model/User.php
class User extends AppModel {
public $hasOne = 'Profile';
}
// app/Model/Profile.php
class Profile extends AppModel {
public $belongsTo = 'User';
public $hasAndBelongsToMany = 'Skill';
// app/Model/Skill.php
class Skill extends AppModel {
public $hasAndBelongsToMany = 'Profile';
Can anyone help me get the users skills when retrieving user data? Thanks.
Use CakePHP's Containable Behavior. Your find will then look something like this:
$this->User->find('all',array(
'conditions' => array(
'OR' => array(
'Profile.firstname LIKE' => "%$q%",
'Profile.lastname LIKE' => "%$q%"
)
),
'contain' => array(
'Profile' => array(
'Skill'
)
)
));
MUCH simpler, easier to read, and voila - you get the data you want without needing to use the dreaded 'recursive'.

Cakephp : left-right joins with non-conventioned tables

I'm using an existing database (I can't change it and its table names are not like cake conventions want it), and I'd like to do some left joins but can't do it properly :/
I've already defined my tables, giving them primary keys and the relations in the models.
Here is my problem :
Table Wysipage can have 0 to n wysipage_content, and 0 to n wysipage_menu.
an element from wysipage_content corresponds to 1 and only 1 Wysipage.
an element from wysipage_menu corresponds to 0 or 1 Wysipage.
I'd like to make a request who would give me a list of all the elements from Wysipages, with their eventuals contents and menus, all that in a single table, and by only one request.
Here are my tables definitions (I'm avoiding you the entire schema, just be aware there is a wp_id and a wp_name column) :
class Wysipage extends AppModel {
var $actsAs = array('Containable');
public $useTable = 'wysipage';
public $primaryKey = 'wp_id';
public $displayField = 'wp_name';
var $hasMany = array(
'un' => array(
'Wysipage_contenu' => array(
'className' => 'Wysipage_contenu',
'foreignKey' => 'wpc_wp_id',
)),
'deux' => array(
'Wysipage_menu' => array(
'className' => 'Wysipage_menu',
'foreignKey' => 'wpm_wp_id',
))
);
class Wysipage_contenu extends AppModel {
var $actsAs = array('Containable');
public $useTable = 'wysipage_contenu';
public $primaryKey = 'wpc_id';
public $displayField = 'wpc_h1';
public $belongsTo = array(
'Wysipage' => array(
'className' => 'Wysipage',
'foreignKey' => 'wp_id'
)
);
class Wysipage_menu extends AppModel {
var $actsAs = array('Containable');
public $useTable = 'wysipage_menu';
public $primaryKey = 'wm_id';
public $displayField = 'wm_lien';
public $belongsTo = array(
'Wysipage' => array(
'className' => 'Wysipage',
'foreignKey' => 'wm_wp_id'
)
);
And here is my code to try request (but failed) :
$this->loadModel('Wysipage_contenu');
$this->loadModel('Wysipage_menu');
$this->Wysipage->contain();
$mes_wysipages = $this->Wysipage->find('all', array('joins' => array(
array(
'table' => 'wysipage_contenu',
'alias' => 'wpc',
'type' => 'LEFT',
'conditions'=> array('wpc.wpc_wp_id = Wysipage.wp_id')
),
array(
'table' => 'wysipage_menu',
'alias' => 'wpm',
'type' => 'LEFT',
'conditions'=> array('wpm.wm_wp_id = Wysipage.wp_id')
)
)));
$this->set('wysipages', $mes_wysipages);
$this->render();
What have I done wrong? Is the problem in my model declarations? Or do I use a wrong request type? :(
The request I'd like to make is simply :
SELECT wp_id, wp_name, wpc_id, wpc_name
FROM wysipage
LEFT JOIN wysipage_contenu ON wysipage.wp_id = wysipage_contenu.wpc_wp_id
Just this :(
I'm not even sure I want a LEFT join or a RIGHT join, but anyway the problem remains the same, this code gives me bad answers with multiple occurrences of the same lines :/
Thanks :/
PS : Sorry for my bad English, it's not my native language.
You can not join content and menu tables on wp_id in same query because they both have many-to-one relationship to wp_id, and there by for each row from content there are all rows from menu with same wp_id in result of such join. You need to do 2 queries: one for content and one for menu. Or if list columns you needed identical for both tables you can union both results from this two queries.

Listing data using find statement and join table

I have the following tables for my CakePHP app that allows friendships between users:
**Users**
id
username
password
**Profiles**
id
firstname
lastname
user_id
**Friends**
id
user_id_to
user_id_from
status
So basically a user has a profile and a user can be friends with another user and this is recorded in the database table called friends with a simple status of confirmed or not using either 0 or 1 (it's an int). So friends is the join between two users.
I'm trying to list the friends for a user so for example if I get a url like:
/people/cameron/friends it will list the friends for the user Cameron.
However I'm struggling with the find statement to pass the user and find them (notice I contain the profile data) and then list friends that are related to that user. Can anyone help?
These are the Friend, User and Profile models:
class Friend extends AppModel
{
public $name = 'Friend';
public $belongsTo = array('User');
public $actsAs = array('Containable');
}
User.php
class User extends AppModel
{
public $name = 'User';
public $hasOne = 'Profile';
public $hasMany = array(
'Post',
'Answer',
'Friend' => array(
'foreignKey' => 'user_id_to'
)
);
public $belongsTo = array(
'Friend' => array(
'foreignKey' => 'user_id_from'
)
);
public $actsAs = array('Containable');
public function getFriends($username)
{
return $this->find('all',
array('conditions' => array('User.username' => $username, 'Friend.status'=>1),
'contain' => array('Friend' => array('User'))
));
}
}
Profile.php
class Profile extends AppModel
{
public $name = 'Profile';
public $belongsTo = 'User';
public $actsAs = array('Containable');
}
and this is my method for showing the friend list for a user:
public function index( $username )
{
$friends = $this->User->getFriends($username);
$this->set('friends', $this->paginate());
}
I'm currently getting this error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'User.user_id_from' in 'on clause'
SQL Query: SELECT `User`.`id`, `User`.`username`, `User`.`password`, `User`.`email`, `User`.`status`, `User`.`code`, `User`.`lastlogin`, `Friend`.`id`, `Friend`.`user_id_from`, `Friend`.`user_id_to`, `Friend`.`datetime`, `Friend`.`status` FROM `db52704_favorr`.`users` AS `User` LEFT JOIN `db52704_favorr`.`friends` AS `Friend` ON (`User`.`user_id_from` = `Friend`.`id`) WHERE `User`.`username` = 'cameron' AND `Friend`.`status` = 1
It looks as though the app thinks the foreign keys are in the User table rather than the friend table even though they called within the Friend association... Any ideas what the problem is?
Any time you specify foreign keys for $belongsTo, remember that the name you specify is the name of the field in the current table, not the other table.
So, for example, your $belongsTo references to 'foreignKey' => 'user_id_to' should be in the Friends model, not the Users model.
Re-read Cake's docs, as it does get confusing (even after years of Cake apps, I still need to refresh when I start a new project): http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html
Cake appears to be getting confused by those foreignKey assignments when constructing the join query.
you could try replacing each relation with the following to force the right join statement:
public $hasMany = array(
'Friend' => array(
'foreignKey' => null,
'conditions' => array('Friend.user_id_to = User.id')
)
);
public $belongsTo = array(
'Friend' => array(
'foreignKey' => null,
'conditions' => array('Friend.user_id_from = User.id')
)
);

Categories