CakePHP - How to retrieve deeply associated data? - cakephp

I have the following models:
User:
hasOne Profile, Page
hasMany Comment
Comment:
belongsTo User, Page, Profile
Page:
belongsTo User
hasMany Comment
Profile:
belongsTo User
When I retrieve a page, I want to get the associated Comments, and for each comment I want the Profile.
My comments table has fields page_id and user_id.
My profile table has user_id.
So, I assume I need to do something like
Comment belongsTo
'Profile' => array(
'conditions' => array('Profile.user_id' => 'Comment.user_id')
)
but that's not working - it returns a blank profile record.
I am using CakePHP 2.0.

Use CakePHP's Containable behavior [link]. Basically, this allows you to pick and choose which related models you want to include in your find.
You can also specify which field(s) you want from each model, specify conditions on each...etc.
It should look something like this:
//PageModel
public $actsAs = array('Containable');
public function getPage($id=null) {
$this->recursive = -1; //often set to -1 in the AppModel to make -1 the default
$this->find('all', array(
'conditions' => array(
'Page.id' => $id
),
'contain' => array(
'Comment' => array(
'User' => array(
'Profile',
),
),
)
));
}

If the relations are declared correctly, all the data you need can be retrieved by searching for the desired Page. But to limit the quantity of data returned, you will probably have to use te ContainableBehavior.
PageModel:
var $actsAs = array('Containable');
PagesController:
function view($id)
{
$this->Page->contain(array('Comment' => array('User', 'Profile')));
$page = $this->Page->find('first', array('conditions' => array('Page.id' => $id)));
}
EDIT
Sharon, according to your question it seems that Comments are linked to Users and to Profiles. But if Profile means a "user profile", the link between Comment and User is enough and the contain should then be:
$this->Page->contain(array('Comment' => array('User' => array('Profile'))));

Related

how can i use two associations at once in cakephp

i'am using cakephp 2.x
i have two tables USERS and JOBS in my database,
in users table i save two types of users job candidate and company in this situation i found my self facing two relations between the tables :
(1,n)users can add (0,1)jobs. (user role here is Company)
many (1,n)users can apply to many (1,n)jobs. ( Candidate)
can i use belongsTo and HABTM(hasAndBelongsToMany) at once between the two models ?
like this :
Job model
Class Job extends AppModel{
public $belongsTo=array(
'User'=> array(
'className' => 'User',
'foreignKey' => 'user_id',
'counterCache'=>true,
'fields'=>array('id','username','role'),
)
);
public $hasAndBelongsToMany=array('User');
public $usetable = 'jobs';
User Model
Class User extends AppModel{
public $hasAndBelongsToMany=array('Job');
how to retrieve data depending on the first or the second context
1- show all jobs of company.
2- show users applies.
You can do this, but the two associations need different aliases. So you could refer to an User appling for a Job as an Applicant:-
class Job extends AppModel {
public $belongsTo = array(
'User'=> array(
'className' => 'User',
'foreignKey' => 'user_id',
'counterCache' => true,
'fields' => array('id', 'username', 'role'),
)
);
public $hasAndBelongsToMany = array(
'Applicant' => array(
'className' => 'User'
)
);
}
Note that you need to tell Cake that the Applicant uses the User model via the className property. For your belongsTo association you don't need to specify the className for the User as it is the same as the alias is the same as the model name but I have left it in for clarity.
You also don't need to specify $usetable = 'jobs as this is implied by using the correct naming conventions.

Cakephp Associations Not Working

Hello there,
thanks in adv for help.
i have two table: named as options fields are (id,option_title,created,status) and second table as option values fields as (id,option value,options_id,created,status). THe option values table has a foreign key as options_id. Now when i created association between these two table . i didn't work.. Let me explain you how i am creating association.
In the Option model i am using following code.
var $hasMany = array(
'values' => array(
'className' => 'Optionvalue',
'foreignKey' => 'options_id'
));
But this is not working and i am not able to get data from options table. Can any please advice me.. where i am wrong. I just need options table data in the optionsvalue add view file.
Assuming you have a table called options and another called optionvalues. Here is how the Option model looks like
class Option extends AppModel {
public $hasMany = array(
'Optionvalue' => array(
'className' => 'Optionvalue',
'foreignKey' => 'option_id',
'dependent' => FALSE
)
);
}
In the Optionvalue model you then go like this
class Optionvalue extends AppModel {
public $belongsTo = array(
'Option' => array(
'className' => 'Option',
'foreignKey' => 'option_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
This is how it should look like. In the Optionvalue Add method, you will need to write this in the view
echo $this->Form->create('Optionvalue');
echo $this->Form->input('option_id');
echo $this->Form->end('Save');
and the options will appear by themselves.
Very important though, you did not follow the naming conventions. options_id needs to be option_id among others.
Use
option_id in place of 'options_id'
Cakephp strongly follows naming conventions.
http://book.cakephp.org/2.0/en/getting-started/cakephp-conventions.html
Also looks like you are not using proper naming conventions for other places too.

Cakephp 2 - Most simple associations

I'm facing trouble telling cake the most simple associations.
I have two Models:
CoreUser.php
CoreRole.php
.
One User has one Role.
How to assign that in cake? (HasOne or BelongsTo? => When to choose what?)
What to put in what Model? I tried both and ever end up with a recursion-error or it is just not working.
My SQL-Tables:
(tbl) core_users [id,username,password,role_id]
(tbl) core_roles [id,name]
My Models:
class CoreUser extends AppModel {
public $hasOne = array(
'Role' => array(
'className' => 'CoreRole',
'foreignKey' => 'id'
)
);
}
class CoreRole extends AppModel {
public $belongsTo = array(
'User' => array(
'className' => 'CoreUser',
'foreignKey' => 'role_id'
)
);
}
=> Can you give me the correct code i need to insert into one of both (or both) models to tell cake about the relationship?
Thanks in advance
Theoretically, there are two things you need to think about, one of them being non-Cakephp specific.
A. Relationships: A relationship is always bidirectional. When determining the relationship between two Models/Objects/Tables, you always ask two questions:
How many instances of B are related to one instance of A?
How many instances of A are related to one instance of B?
You've said, One user has one Role. However, One Role has how many Users related to it? That will tell you the complete relationship between a User and a Role. (Apologies for the digression but this is important and I'm referencing this book.)
B. Difference between hasMany and belongsTo:
This is determined based on the direction of traversing a relationship.
Based on point A, say you've determined that:
One User has one Role but One Role has many Users.
Now when you are in the User's model trying to fetch related Role data, you need to define the following in the User model:
class CoreUser extends AppModel {
public $belongsTo = array(
'Role' => array(
'className' => 'CoreRole',
'foreignKey' => 'role_id'
)
);
}
But when you are in the Role's model and trying to fetch related User data, you will need to define the following in the Role model:
class CoreRole extends AppModel {
public $hasMany = array(
'User' => array(
'className' => 'CoreUser',
'foreignKey' => 'role_id'
)
);
}
For a full discussion refer to this answer.

cakePHP virtualField within condition of another model

I have 2 models, User and Entity. I need to on my entities page, have pagination, and a simple search function. The search function will filter the entities, but the data it filters is a virtualField within the User model.
The Entity model:
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
};
The virtual field in the User model:
public $virtualFields = array("searchFor" => "CONCAT(User.first_name, ' ',User.last_name, ' ',User.email)");
And the condition within the entity controller:
$conditions["User.searchFor LIKE"] = "%".str_replace(" ","%",$this->passedArgs["searchFor"])."%";
$this->paginate = array(
'conditions' => $conditions,
'order' => array('Re.created' => 'DESC'),
'limit' => 20
);
From what I can read this is not possible because I cannot use virtualFields within a query of an associative model, and it says I must use it in "run time", but I really do not understand how to do that, or how to do that in this instance. Any ideas or a way I can get this to work?
You could try attaching the virtualField to the Entity model like so
$this->Entity->virtualFields['searchFor'] = $this->Entity->User->virtualFields['searchFor'];
But you have to make sure that a join is done and not 2 queries.
I believe its discussed in the book.
Edit: Book page
For this purpose I needed to do a search on a concatinated string from an associated model. Normally this can be done using Virtualfields in cake, but cake does not support using a virtualField from an associated model in the search.
Seeing that the 2 models were already linked with the belongsTo, I merely changed the condition to:
"conditions" => "CONCAT(User.first_name, ' ',User.last_name, ' ',User.email) LIKE" => "%".str_replace(" ","%",$this->passedArgs["searchFor"])."%"
Probably not the most elegant solution, but it works.

Has Many relationship how to

I have this model and controller and it returns fine if it is a hasOne relationship but now DishCategory hasMany dishes. When I change the code below to hasMany it gives me an error saying Dish.id is not a known column while if it is a hasOne it works fine. How can I make it so that it returns all the Dish.id's that have id=1? (still using the join).
class DishCategory extends AppModel{
public $hasOne = array(
'Dish' => array(
'className' => 'Dish',
'foreignKey' => 'dish_category_id'
)
);
}
class DishCategoriesController extends AppController {
function get_categories($id)
{
// find category with a dish of $id
$this->set('dishes', $this->DishCategory->find('all', array(
'conditions' => array(
'Dish.id' => $id
)
)));
// set master layout
$this->layout = 'master_layout';
}
}
hasOne relations can be joined into the primary SQL query. hasMany relations cannot and need to be queried in a separate query. The conditions you specify for the query only apply to the primary query, all separate relational queries are build just based on the ids retrieved in the primary query. Set debug to 2 and have a good look at the query log to see what I mean.
To find the category of a dish, fetch the dish and look at its related category record:
$dish = $this->Dish->find('first', array('conditions' => array('Dish.id' => $id)));
echo $dish['DishCategory']['name'];
Since I take it that a dish belongsTo one category, your query "find all categories which have a dish with id x" makes little sense; there should only be one anyway.

Resources