I have Three tables Projects, Tasks and Tags. Projects.id is the primary key of the first table, Tasks.id is the PK of the second table and Tags.id is the PK of Third table.
Projects Model Code snippet
public $primaryKey = 'id';
public $hasMany = array(
'Tasks' => array('className' => 'Tasks','foreignKey' => 'project_id')
);
Tasks Model Code snippet
public $primaryKey = 'id';
public $hasMany = array(
'Tags' => array('className' => 'Tags','foreignKey' => 'task_id')
);
The below query return some unexpected result.
$data = $this->Projects->find('all', array(
'recursive' => 2,
'contain' => array(
'Tags' => array(
'conditions' => array('Tags.tag_name =' => "Driver")
)
)
));
The response data i am getting is
[Projects] => Array
(
[id] => 1
[project_id] => 1234
[project_name] => XYZ
)
[Tasks] => Array
(
[0] => Array
(
[id] => 1
[project_id] => 1
[task_id] => 12
[task_name] => task1
[Tags] => Array
(
[0] => Array
(
[id] => 1
[task_id] => 1
[tag_id] => 3444
[tag_name] => Driver
)
)
)
[1] => Array
(
[id] => 2
[project_id] => 1
[task_id] => 343242
[task_name] => task2
[Tags] => Array
(
)
)
See i am even getting tasks without tag as Driver. How i can exclude tasks without tag Driver?
In another word task1 do have Tag as Driver, so that should be there. But task2 don't have any tag, even then the return data contain task2. How to fix it?
You cannot limit the results of the main or parent model based on conditions on child models because "contain" actually creates separate queries for each contain model.
To get the proper result you must use join.
Related
I have a HABTM relationship in both my models.
Person.php
public $hasAndBelongsToMany = array(
'Tag' =>
array(
'className' => 'Api.Tag'
));
Tag.php
public $hasAndBelongsToMany = array(
'Person' =>
array(
'className' => 'Api.Person',
));
PeopleController.php
public function edit(){
$results = $this->Person->saveAll($this->request->data);
}
_
(
[id] => 554d92e1-7920-409a-b348-080e2773a233
[firstname] => George
[lastname] => Jones
[Company] => Array
(
[id] => 554995a3-f880-4a02-b971-9ac82773a233
[title] => xxxx
)
[Tag] => Array
(
[0] => Array //this is an existing tag
(
[id] => 530e487c-44bc-4ffe-ae2b-152f4056922c
[title] => monkey
[PeopleTag] => Array
(
[id] => 554e741d-a98c-48d0-8a52-080c2773a233
[tag_id] => 530e487c-44bc-4ffe-ae2b-152f4056922c
[person_id] => 554d92e1-7920-409a-b348-080e2773a233
[created] => -62169987600
[modified] => 1431207117
)
)
[1] => Array //this is a new tag that's not being added
(
[title] => rocketship
)
[2] => Array //this is a new tag that's not being added
(
[title] => houses
)
)
)
When I manually set tags in the database I'm able to read them back out into my app correctly and when I delete existing tags, they get removed from the database. The problem is though that nothing gets added when I try to use cakephp to add them.
My data array looks a little funky because I'm actually using this plugin to transform things.
http://friendsofcake.com/crud/docs/listeners/api-transformation.html
my CakePHP app is getting this data below, but it's not adding my new tags nor is it adding my associations in the people_tags join table. Any idea on what I'm doing wrong?
I'm using CakePHP 2.3.8 and I'm trying to more efficiently join two tables. buildings and building_rental_rates
buildings
id | name | description | property_owner | building_type
1 Big Big Building 1 1
building_rental_rates
id | building_type | rate_name | rate
1 1 daily 150.00
2 1 hourly 15.00
I want to look up a building and select the different rental rates. The tables are joined on building_type. Here's the find statement I have
$buildings = $this->Building ->find('all',array(
'Building.property_owner' => '1',
'fields' => array('Building.*','BuildingRentalRate.*'),
'joins' => array(
array(
'table' => 'building_rental_rates',
'alias' => 'BuildingRentalRate',
'type' => 'inner',
'conditions' => array(
'Building.building_type = BuildingRentalRate.building_type'
)
)
)
));
Here is the result
Array
(
[0] => Array
(
[Building] => Array
(
[id] => 1
[name] => Big
[description] => Big Building
[property_owner] => 1
[building_type] => 1
)
[BuildingRentalRate] => Array
(
[id] => 1
[building_type] => 1
[rate_name] => daily
[rate] => 150.00
)
)
[1] => Array
(
[Building] => Array
(
[id] => 1
[name] => Big
[description] => Big Building
[property_owner] => 1
[building_type] => 1
)
[BuildingRentalRate] => Array
(
[id] => 2
[building_type] => 1
[rate_name] => hourly
[rate] => 15.00
)
)
)
While the data is found properly, it's a pain to traverse through it. Can I use a join statement to result in this output? Notice how the BuildingRentalRate is an array containing all entries of that table that share the same building_type
Array
(
[0] => Array
(
[Building] => Array
(
[id] => 1
[name] => Big
[description] => Big Building
[property_owner] => 1
[building_type] => 1
)
[BuildingRentalRate] => Array
(
[0] => Array
(
[id] => 1
[building_type] => 1
[rate_name] => daily
[rate] => 150.00
)
[1] => Array
(
[id] => 2
[building_type] => 1
[rate_name] => hourly
[rate] => 15.00
)
)
)
)
I know Cake can output results like this when using model associations, but I wasn't able to get my associations correct apparently because it kept on joining Building.id on BuildingRentalRate.building_type (it should be Building.building_type = BuildingRentalRate.building_type)
Even with my association like this...
//Building.php
public $hasMany = array('BuildingRentalRate' => array('foreignKey' => 'building_type'));
//BuildingRentalRate.php
public $belongsTo = array('Building' => array('foreignKey' => 'building_type'));
It would join Building.id on BuildingRentalRate.building_type despite specificying building_type in both models as the foreign key.
Can I do some kind of nested SQL query in the conditions or is there an easier way of doing this?
you should be able to avoid using joins if in BuildingRentalRate.php you set your relationship this way
$public belongsTo = array(
'Building' => array(
'foreignKey' => false,
'conditions' => array
(
'Building.building_type' => 'BuildingRentalRate.building_type'
)
)
);
I have two tables in my database Members and Memberitems. I created two models for it. one is, Member and another is Memberitem
Member model has hasMany relation to Memberitem, and Memberitem model has belongsTo relations with my Member model.
Memberitem entries has specific categorization based on color, like, Red, Pink, Green, etc.
Now I want to select all the members who has atleast one Pink color memberitem using pagination.
Currently I am using:
$this->paginate = array(
'limit' => 5,
'contain' => array(
'Memberitem' => array(
'conditions' => array('Memberitem.color' => 'Pink')
)
)
);
But its showing all the Members.
output is this:
Array
(
[0] => Array
(
[Member] => Array
(
[id] => 1
[first_name] => fh
[last_name] => g
)
[Memberitem] => Array
(
[0] => Array
(
[id] => 1
[name] => item2
[color]=> Pink
)
)
)
[1] => Array
(
[Member] => Array
(
[id] => 2
[first_name] => ad
[last_name] => vd
)
[Memberitem] => Array
(
)
)
[2] => Array
(
[Member] => Array
(
[id] => 3
[first_name] => ae
[last_name] => sdi
)
[Memberitem] => Array
(
[0] => Array
(
[id] => 3
[name] => item1
[color]=> Pink
)
)
)
)
Its showing this result. Member of empty memberitem is still there. I want only 1st and 3 record in result.
This is a classic :-) It takes a while to get it though.
The Cake manual has an excellent peace on this. You should read: http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html#containing-deeper-associations
So if you want only Members with certain MemberItems, you should query on the MemberItems and contain the Member Model. You can actually do this from Member Model:
$this->Member->Memberitem->find('all', array(
'conditions' => 'Memberitem.color = "Pink"',
'contain' => 'Member',
));
or maybe a bit more catered to you app:
$this->paginate['Memberitem'] = array(
'contain' => array('Member'),
'conditions' => array(
'Memberitem.color' => 'Pink',
),
);
$items = $this->paginate('Memberitem');
Hope that helps!
I have the following model relationships defined:
class Publication extends AppModel {
var $name = 'Publication';
var $hasAndBelongsToMany = array(
'Author'=>array(
'className'=>'Author'
)
);
}
class Author extends AppModel {
var $name = 'Author';
var $hasAndBelongsToMany = array(
'Publication'=>array(
'className'=>'Publication'
)
);
var $belongsTo = array(
'College' => array (
'className' => 'College'
)
);
}
class College extends AppModel {
var $name = 'College';
var $hasMany = array(
'Department'=>array(
'className'=>'Department'
)
);
}
class Department extends AppModel {
var $name = 'Department';
var $belongsTo = array(
'College'=>array(
'className'=>'College'
)
);
}
The database tables are set up correctly (join tables for the HABTM, etc.). I am trying to find the one DB query to rule them all. I want to create a query that will find all of the publications with the associated authors, colleges, departments, etc. After getting data from a form, I have tried to run queries like this:
$conditions = array(
"Author.id" => $this->data['authors'],
"Publication.year" => $this->data['year']
);
$publications = $this->Publication->find('all', array('conditions' => $conditions));
This throws SQL errors saying that Author.id is not a valid field. Now, this is because a join with the 'authors' db table has not been completed by the time Author.id is being searched for. BUT, if I do this:
$pubs = $this->Publication->find('all', array('conditions' => $conditions));
then I get an array that has all of the Publications with all of the associated Authors (though, not the associated Colleges for some reason).
My question is this: what do I need to do to make the tables join before Author.id is searched for? I've attempted to use bindModel, containable, subqueries, but cannot get those to work for some reason (probably a ID10T error).
Thanks for any advice!
Edit:
The result of the following call:
$this->Publication->recursive = 2;
$pubs = $this->Publication->find('all');
are as follows:
Array (
[0] => Array (
[Publication] => Array ( [id] => 1 [title] => TestArticle [year] => 2011 [type_id] => 3 )
[Type] => Array ( [id] => 3 [type_name] => Journal Articles )
[Author] => Array (
[0] => Array ( [id] => 2 [firstname] => Jeremy [lastname] => Gustine [middle] => A [faculty] => 0 [college_id] => 4 [AuthorsPublication] => Array ( [id] => 3 [author_id] => 2 [publication_id] => 1 )
[College] => Array ( [id] => 4 [college_name] => Letters, Arts, and Sciences ) )
[1] => Array ( [id] => 3 [firstname] => George [lastname] => Obama [middle] => A [faculty] => 0 [college_id] => 6 [AuthorsPublication] => Array ( [id] => 2 [author_id] => 3 [publication_id] => 1 )
[College] => Array ( [id] => 6 [college_name] => School of Public Affairs ) )
[2] => Array ( [id] => 2 [firstname] => Jeremy [lastname] => Gustine [middle] => A [faculty] => 0 [college_id] => 4 [AuthorsPublication] => Array ( [id] => 1 [author_id] => 2 [publication_id] => 1 )
[College] => Array ( [id] => 4 [college_name] => Letters, Arts, and Sciences ) ) ) )
[1] => Array (
[Publication] => Array ( [id] => 2 [title] => TestBook [year] => 2010 [type_id] => 1 )
[Type] => Array ( [id] => 1 [type_name] => Books )
[Author] => Array (
[0] => Array ( [id] => 7 [firstname] => Sony [lastname] => Stuff [middle] => L [faculty] => 0 [college_id] => 5 [AuthorsPublication] => Array ( [id] => 4 [author_id] => 7 [publication_id] => 2 )
[College] => Array ( [id] => 5 [college_name] => Nursing and Health Science ) ) ) ) )
Hopefully that is somewhat readable...
Your second find is correct
i.e. $pubs = $this->Publication->find('all', array('conditions' => $conditions));
just above that try using recursive 2 (below code) to get related College too.
$this->Publication->recursive = 2;
I have used join query which joins three tables employee, places and employee_places.The join query gives me all the results as I have used find('all').But in the view page I just want to show employee name from employee table - place name from place table in a dropdown.
e.g:- emp1-place1,emp2-place2 and so on. Where should I give the fields name in find.
Look at the following code :
$options['joins'] = array(
array('table' => 'employees',
'alias' => 'emp',
'type' => 'inner',
'conditions' => array(
'emp.id = EmployeePlace.employee_id '
)
),
array('table' => 'places',
'alias' => 'pl',
'type' => 'inner',
'conditions' => array(
'pl.id = EmployeePlace.place_id'
)
)
);
$empTables = $this->Bill->EmployeePlace->find('all', $options);
$this->set(compact('empTables'));
The above query results the following array:-
Array
(
[0] => Array
(
[EmployeePlace] => Array
(
[id] => 1
[employee_id] => 1
[place_id] => 1
[Date] => 2011-02-02
)
[Employee] => Array
(
[id] => 1
[image] =>
[firstName] => Andy
[lastName] => Murray
[date_of_joining] => 2010-09-02
[date_of_leaving] => 2011-02-02
[date_of_birth] => 1991-08-10
[gender] => Male
[supervisor] => 0
[designation] => Manager
[user_id] => 0
)
[Place] => Array
(
[id] => 1
[placeName] => table-1
[section_id] => 1
[position] => Left
[seating_capacity] => 4
)
)
)
I just want firstName from employee - placeName from place table in the dropdown. How do I do this using condition in find.
Use containable: http://book.cakephp.org/view/1323/Containable
You will then only be dealing with data that you require.
Loop over the results and build the strings you want. Add the strings to an array indexed against the value you want the selection to return. Pass the array out to the view.
How are you going to handle the values? What do you want to do when a user has made a selection?
OR:
You could create a db view, which is a table that combines data from other tables on the fly. CakePHP can access this as a normal table.