CakePHP - How do I join a table on a joined table? - cakephp

I am in the FilesController and I'm trying to get a file based on the condition that its order belongs to the current user.
FilesController
// Check the file exists and that it belongs to the user
$this->File->find('first', array(
'conditions' => array(
'File.id' => $id,
'Order.Customer.id' => $this->Auth->user('id')
),
'recursive' => 2
));
Cake SQL Error
Unknown column 'Order.Customer.id' in 'where clause'
I'm trying to get the SQL to left join orders onto files and then left join customers onto orders, but I can't figure out how to join the customers table, although I'm sure I've done this before. I've tried everything I can think of with the conditions, and using contains.
Here's my model relationships:
Customer Model
class Customer extends AppModel {
public $hasMany = array('Order');
}
Order Model
class Order extends AppModel {
public $belongsTo = array('Customer');
public $hasMany = array('File');
}
File Model
class File extends AppModel {
public $belongsTo = array('Order');
}

Try joining the tables using the 'joins' parameter. Sometimes 'contains' doesn't work and you need to fall back on this.
$this->File->find('first', array(
'conditions' => array(
'File.id' => $id,
'Order.Customer_id' => $this->Auth->user('id')
),
'joins' => array(
array(
'table' => 'orders',
'alias' => 'Order',
'type' => 'LEFT',
'conditions' => array('File.orders_id = Order.id')
),
array(
'table' => 'customers',
'alias' => 'Customer',
'type' => 'LEFT',
'conditions' => array('Customer.orders_id = Order.id')
)
)
));

You may want to use Containable (http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html) as it is the easiest solution. You can not use Order.Customer.id as Cake does not nest conditions like that. Manual joins would also work.

$this->loadModel('Customer');
$customer = $this->Customer->findById($this->Auth->user('id'), array(
'conditions' => array(
'File.id' => $id
),
'recursive' => 2
));
Orders will be accessible as:
pr($customer['Order']);
File will be accessible as:
pr($customer['File']);

I realised there is no need to actually join the customers table in this instance, because the orders table already has the customer_id.
$this->File->find('first', array(
'conditions' => array(
'File.id' => $id,
'Order.customer_id' => $this->Auth->user('id')
)
));

Related

Cakephp 2 retrieve data where condition depends on child table

I am having a strange behavior which I do not understand with my cakephp 2.
In my Model 'Study' I have a has many relation:
class Study extends AppModel {
public $hasOne = array(
'SubjectFilter' => array(
'className' => 'Subject_filter',
'dependent' => true
)
);
public $hasMany = array(
'ExecutedStudyTable' => array(
'className' => 'ExecutedStudyTable',
'foreignKey' =>'study_id',
'dependent' => true,
),
);
The ExecutedtStudyTable Model looks like this:
class ExecutedStudyTable extends AppModel {
public $belongsTo= array(
'Study' => array(
'className' => 'Study',
'foreignKey' => 'study_id'
),
'User' => array(
'className' => 'User',
'foreignKey' => false,
'conditions' => array('ExecutedStudyTable.user_id = User.id')
)
);
}
When retrieving data everything looks good until I try to do this:
$studies = $this -> Study -> find('all',array(
'conditions' => array(
'Study.user_id' => $cId,
'OR'=>array(
array('Study.state'=>'active'),
array('Study.state'=>'rehearsal')
),
'SubjectFilter.studyCode'=>null,
'ExecutedStudyTable.user_id'=>$user
)
));
I get this error: Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'ExecutedStudyTable.user_id' in 'where clause'
Cakephp builds this query like this:
SELECT `Study`.`id`, `Study`.`user_id`, `Study`.`created`, `Study`.`modified`, `Study`.`started`, `Study`.`completed`, `Study`.`title`, `Study`.`description`, `Study`.`numberParticipants`, `Study`.`state`, `User`.`id`, `User`.`username`, `User`.`password`, `User`.`role`, `User`.`firstName`, `User`.`familyName`, `User`.`email`, `User`.`addressStreet`, `User`.`addressCity`, `User`.`addressZip`, `User`.`phone1`, `User`.`phone2`, `User`.`created`, `User`.`modified`, `User`.`passwordResetCode`, `User`.`passwordResetDate`, `User`.`sendUsernameDate`, `SubjectFilter`.`id`, `SubjectFilter`.`study_id`, `SubjectFilter`.`m`, `SubjectFilter`.`f`, `SubjectFilter`.`age18_24`, `SubjectFilter`.`age25_34`, `SubjectFilter`.`age35_44`, `SubjectFilter`.`age45_54`, `SubjectFilter`.`age55plus`, `SubjectFilter`.`studyCode` FROM `eyevido`.`studies` AS `Study` LEFT JOIN `eyevido`.`users` AS `User` ON (`Study`.`user_id` = `User`.`id`) LEFT JOIN `eyevido`.`subject_filters` AS `SubjectFilter` ON (`SubjectFilter`.`study_id` = `Study`.`id`) WHERE `Study`.`user_id` = 1402402538 AND ((`Study`.`state` = 'active') OR (`Study`.`state` = 'rehearsal')) AND `SubjectFilter`.`studyCode` IS NULL AND `ExecutedStudyTable`.`user_id` = 1130451831
The ExecutedStudyTable is not joined like the SubjectFilter and no alias is defined for the table. Why is this working correctly on the hasOne relation and not on the hasMany? Where do I make the mistake?
I appreciate your replies
If you want to filter a model by associated model's field, one way is by Joining tables.
Note: Mention proper table name for model ExecutedStudyTable. Also mention the join condition between two tables.
$studies = $this->Study->find('all',array(
'fields' => array('Study.*'),
'joins' => array(
array('table' => 'executed_study_table', // Table name
'alias' => 'ExecutedStudyTable',
'type' => 'INNER',
'conditions' => array(
'ExecutedStudyTable.<field name> = Study.<field>', // Mention join condition here
)
)
),
'conditions' => array(
'Study.user_id' => $cId,
'OR'=>array(
array('Study.state'=>'active'),
array('Study.state'=>'rehearsal')
),
'SubjectFilter.studyCode' => null,
'ExecutedStudyTable.user_id' => $user
),
'recursive' => -1
));

Select records which have atleast one hasMany relation row in Cakephp

I'm facing a problem with cakephp associations in Models.
I have to Select records which have atleast one hasMany reation row
Model
class Category extends AppModel
{
public $hasMany = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'CategoryId',
)
);
}
Query
$categories = $this->Category->find('all');
I only needed the categories which have atleast one product entry
Categories Like : Shirts, Footwear, Glasses etc
Products like :
Small, medium, large (Shirts)
With Frame, UV protected (Glass)
So, i jus want to get Shirts and Glasses Categories only because for the above example there is no products for Footwear
Use counterCache or joins
Please refer to CakePHP - Find and count how many associated records exist
The most simple way with the best performance would be using a properly indexed counter cache field as shown in the linked answer.
Sice the linked answer is not an exact duplicate with respect to the join, here's some additional info, instead of using HAVING COUNT with the join you'd use a IS NOT NULL condition. Here's an (untested) example:
$this->Category->find('all', array(
'joins' => array(
array(
'table' => 'products',
'alias' => 'Product',
'type' => 'LEFT',
'conditions' => array('Category.id = Product.CategoryId')
)
),
'conditions' => array(
'Product.CategoryId IS NOT NULL'
)
'group' => 'Category.id'
));
Depending on the used DBMS and version you might get better performance using an inner join:
$this->Category->find('all', array(
'joins' => array(
array(
'table' => 'products',
'alias' => 'Product',
'type' => 'INNER',
'conditions' => array('Category.id = Product.CategoryId')
)
),
'group' => 'Category.id'
));

Model linking in CakePHP

In this example, how do you create the "assignments-to-teachers" relationship in the models?
assignments subjects teachers
--------- --------- ------
id id id
student_id subject_name teacher_name
subject_id teacher_id
I want an SQL query that shows: assignment_id, student_id, subject_name, teacher_name. I used a "belongsTo" to create the "assignments-to-subjects" relationship, but I'm not sure how to create the "assignments-to-teachers".
AssignmentsController.php
public function index() {
$this->set('assignments', $this->Assignment->find('all'));
}
--------------------------
Assignment.php
public $belongsTo = array(
'Subject'
);
I did get this to work by adding joins to the "find('all')" in the controller, but I'm wondering how to do this in the models.
AssignmentsController.php
//(working, but looking to solve this in models, not controller)
$this->set('assignments', $this->Assignment->find('all', array(
'joins' => array(
array(
'table' => 'subjects',
'alias' => 'Subject',
'type' => 'LEFT',
'conditions' => 'Assignment.subject_id = Subject.id'
),
array(
'table' => 'teachers',
'alias' => 'Teacher',
'type' => 'LEFT',
'conditions' => 'Subject.teacher_id = Teacher.id'
)
),
'fields' => array('*','Subject.subject_name', 'Teacher.teacher_name')
)));
As far as I can tell, assignments are related to teachers through subjects, right? Then, on Subject model:
public $belongsTo = array('Teacher');
Also, on AppModel, use Containable behavior:
public $actsAs = array('Containable');
Now, you can do a find on assignment this way:
$this->Assignment->find('all', array(
'fields' => array('id', 'student_id')
'contain' => array(
'Subject' => array(
'fields' => array('id', 'subject_name')
'Teacher' => array(
'fields' => array('id', 'teacher_name')
)
)
)
));
Containable behavior will let you do a deep search through related models

Joins tables in Cakephp

i have a two tables namely; histories and users. i need to display data like:
id | Username | Lastest created Post | First created Post
the data of id and username is from users table and the last created and first created post data is from histories. i need to view all the users, their lastest created post and their first created post. please help me to make controller and view thanks
Try below.
<?php
$users = $this->User->find('all',array
(
'conditions' => array
(
//conditions goes here
),
'fields' => array
(
'User.id',
'User.username',
'History.Lastest created Post',
'History.First created Post'
)
));
?>
Assume that relation between 'User' and 'History' table is One-to-One and there's a 'user_id' column in History table, you may need to specify relation between them in History model, for example:
var $hasOne = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Then, you need to perform joins to do this. For example, somewhere in your User model, try something like this:
class User extends AppModel {
....
function getAllUsersHistory{
$allHistories = $this->find('all', array(
'joins' => array(
'table' => 'history',
'alias' => 'HistoryJoin'
'type' => 'INNER',
'conditions' => array(
// your conditions, for example: 'History.user_id' => 'User.id'
)
),
'fields' => array(
'User.id',
'User.username',
'History.lastest_created_post',
'History.first_created_post'
)
));
return $allHistories;
}
.....
}

CakePHP 2.0: display associated records

I know the solutions is simple and might have something to do with the Containable behavior but I can't get it working. Without all the tries
This is the case. I'd like to display the Event details of an Event (eg. a conference). Each event takes place in a EventVenue and each EventVenue is located in a Country.
So in the Country Model the following is present:
public $hasMany = array(
'EventVenue' => array(
'className' => 'EventVenue',
'foreignKey' => 'country_id'
))
In the EventVenue model a BelongsTo association is made
public $belongsTo = array(
'Country' => array(
'className' => 'Country',
'foreignKey' => 'country_id'
))
And in the Event model a hasOne association is made
public $hasOne = array(
'EventVenue' => array(
'className' => 'EventVenue',
'foreignKey' => 'event_id',
))
What I want is to display the country name on the page that is renderd in the EventsController. I do get all the Event and EventVenue data but the associated Country for the venue is not retrieved.
The data is retrieved in the following way
$item = $this->Event->findBySlug($slug);
How can I also get the country name (eg. Netherlands) retrieved from the database? I tried something like this but that did not work:
$item = $this->Event->findBySlug($slug,array(
'contain' => array(
'Event' => array(
'EventVenue' => array(
'Country'
)
)
)
)
Try this:
$item = $this->Event->findBySlug($slug,array(
'contain' => array(
'EventVenue' => array(
'Country'
)
)
);
Update
Turns out findBy does not support Containable. You could use this to get the desired result:
$item = $this->Event->find('first',array(
'conditions' => array(
'Event.slug' => $slug
),
'contain' => array(
'EventVenue' => array(
'Country'
)
)
);
Oh and make sure you have this in the model: public $actsAs = array('Containable');
try this method:
$this->Event->recusive = 2;
$item = $this->Event->findBySlug($slug);
You need to set the recursive to 2 before you make the find, something like this
$this->Event->recursive = 2;
with this, you'll get the Event, the EventVenue and the Country on one shot
Hope it helps

Resources