Cant get data from 2 tables using containable in cakephp - php

I cant get data from 2 tables with conditions on each.
There is no example in the docs.
I just need rows from students table where a field is flagged as inactive and corresponding rows from Guardian table the email field is not null.
Guardian has many Students.
I get results but I get null values for Guardian email.
I have tried many combinations with ID, model name etc but I am just not getting it.
$guardianFound = $this->Student->find('all', array(
'contain' => array( 'Guardian', array(
'conditions' => array('guardian_email !=' => null),
'fields' => array('id,guardian_email','guardian_first_name,guardian_last_name')
)),
'conditions' => array('student_inactive' => 1),
'fields'=> array('student_inactive' ),
'recursive'=> -1
));
Result:
(int) 0 => array(
'Student' => array(
'student_inactive' => true
),
'Guardian' => array(
'guardian_email' => '',
'guardian_first_name' => 'Tulay',
'guardian_last_name' => 'Karadavut',
'id' => '100'
)
)
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html

It looks like your query is working as expected, except that you probably want to exclude where guardian_email is null OR empty.
To specify multiple values for a field in CakePHP, you can usually wrap it in another array, but for your case, since you're negating NULL and empty, it's a little complicated because of how CakePHP handles db field types. See this question for more info: Cake PHP complex find 'OR' is not working properly with null and empty string. So to simplify it you can set your Guardian conditions like so:
'conditions' => array(
'guardian_email IS NOT NULL',
'guardian_email != ""'
)
You don't need the recursive key here since you're specifying what to contain using containable. So the final find call would be:
$guardianFound = $this->Student->find('all', array(
'contain' => array(
'Guardian' => array(
'conditions' => array(
'guardian_email IS NOT NULL',
'guardian_email != ""'
),
'fields' => array(
'id', 'guardian_email', 'guardian_first_name', 'guardian_last_name'
)
)
),
'conditions' => array('student_inactive' => 1),
'fields' => array('student_inactive')
));
Note that you're finding all inactive students, then only performing the guardian conditions on the guardians associated with that student. If no Guardian record matches the conditions the field would still be returned but with all the values as NULL.
I'm assuming here that because Student belongsTo Guardian, and there would only be one Guardian per Student, you actually want to only return student records where the guardian email is provided. If that's the case, you just need to move the guardian conditions into the main conditions.
$guardianFound = $this->Student->find('all', array(
'contain' => array(
'Guardian' => array(
'fields' => array(
'id', 'guardian_email', 'guardian_first_name', 'guardian_last_name'
)
)
),
'conditions' => array(
'student_inactive' => 1,
'Guardian.guardian_email IS NOT NULL',
'Guardian.guardian_email != ""'
),
'fields' => array('student_inactive')
));

Related

find using contain with condition

hello I have the following models
Restaurant(id, name)
RestaurantMenu(id, name, restaurant_id) [each restaurant has many main menus]
RestaurantMenuItem(id, name, restaurant_menu_id) [Each Menu has many menu items]
I need to find those RestaurantMenu which has at least 1 RestaurantMenuItem. If It has not then it shouldn't have to get the RestaurantMenu also. Atm it is giving me empty array RestaurantMenuItem which I don't want. I don't want an empty object.
$this->Behaviors->attach('Containable');
return $this->find('all', array(
'contain' => array('RestaurantMenu.RestaurantMenuItem'),
'conditions' => array(
'Restaurant.id' => $restaurant_id,
),
'recursive' => 0
));
This could be achieved by applying an INNER join with RestaurantMenuItem on the RestaurantMenu containment. That would cause RestaurantMenu to be selected only if at least 1 associated RestaurantMenuItem row exists, which should satisfy both of your conditions.
Unfortunately there's no joins and group option for containments, so one has to workaround this by for example querying the RestaurantMenu data manually, which should be pretty easy in your case, as you're only selecting a single Restaurant (btw, you should probably use the first finder instead of the all one in that case):
$restaurant = $this->find('first', array(
'conditions' => array(
'Restaurant.id' => $restaurant_id,
),
'recursive' => 0
));
if ($restaurant) {
// RestaurantMenu needs to have the containable behavior attached
// in order for this to work
$restaurant['RestaurantMenu'] = $this->RestaurantMenu->find('all', array(
'contain' => array(
'RestaurantMenuItem'
),
'joins' => array(
array(
'table' => 'restaurant_menu_item',
'alias' => 'RestaurantMenuItem',
'type' => 'INNER',
'conditions' => array(
'RestaurantMenu.id = RestaurantMenuItem.restaurant_menu_id'
)
)
),
'conditions' => array(
'RestaurantMenu.restaurant_id' => $restaurant_id,
),
'group' => array(
'RestaurantMenu.id'
)
));
}
return $restaurant;
or by using the finderQuery option to define a custom query that includes the join:
return $this->find('all', array(
'contain' => array(
'RestaurantMenu' => array(
'finderQuery' => '
SELECT
RestaurantMenu.*
FROM
restaurant_menus AS RestaurantMenu
INNER JOIN
restaurant_menu_items AS RestaurantMenuItem ON
RestaurantMenuItem.restaurant_menu_id = RestaurantMenu.id
WHERE
RestaurantMenu.restaurant_id IN ({$__cakeID__$})
GROUP BY
RestaurantMenu.id
',
'RestaurantMenuItem'
)
),
'conditions' => array(
'Restaurant.id' => $restaurant_id,
)
));
This could also be applied in the RestaurantMenu association configuration in the Restaurant model.
See also
Cookbook > Models > Associations: Linking Models Together > Joining tables
Cookbook > Models > Associations: Linking Models Together > hasMany

CakePHP - saveAssociated/saveAll behaves like saveMany

I have two Models, namely Product and ProductSpecification which have the following relations in place:
(Product Model)
public $hasMany = array(
'ProductSpecification' => array(
'className' => 'ProductSpecification',
'foreignKey' => 'product_id',
'dependent' => true
)
);
and
(ProductSpecification Model)
public $belongsTo = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id'
)
);
Using the CakePHP Form helper I post ProductSpecification data and then I use the saveAll method (or saveAssociated, I've tried both) to save the data. debug($this->request->data) gives me the following output after POSTing:
array(
'Product' => array(
'id' => '2'
),
'ProductSpecification' => array(
'title' => 'test',
'step' => '1',
'position' => '1'
)
)
This is great, right..? Now, the line after the debug I use the following code to save (I've also tried saveAssociated):
if($this->Product->saveAll($this->request->data))
For some odd reason this saves three(!) empty rows in my ProductSpecification table, with only the product_id field (and id) set; the fields title, step and position are empty. Exactly the same behavior happens when I run saveAssociated. What am I doing wrong?
I'm running CakePHP 2.x.
Your save data should look more like this:-
array(
'Product' => array(
'id' => '2'
),
'ProductSpecification' => array(
array(
'title' => 'test',
'step' => '1',
'position' => '1'
)
)
);
The values for ProductSpecification need to be passed as a numerically indexed array for the hasMany relationship.
Also, make sure you use saveAssociated() rather than saveAll() as you are passing associated data so there is no need to use the wrapper method (which should be avoided wherever possible).

cakephp join and mysql join

UPDATED!
i am relativly new in cakephp but having experience with mysql and php.
The model look like:
Person->Father
Father is self refered to person.
I wrote the following based on mysql query which gives back the father of "1" person
mysql:
SELECT `Father`.name,`Father`.id from persons as `Father` left join persons as `Person` on `Person`.`father_id`=`Father`.`id` where `Person`.id=1
cakephp
$options = array(
'fields' => array(
//'Father.name',
'Father.id',
),
'joins' => array(
array(
'conditions' => array(
'Person.father_id = Father.id',
),
'table' => 'persons',
// 'alias' => 'Person', i commented because having conflict with scaffolded model
'type' => 'left',
),
),
'conditions' => array(
'Person.id' => '1',
),
'contain' => array(
'Person','Father',
),
);
$data = $this->Person->find('first', $options);
$fatherquery=$this->Person->find('first',array('conditions'=>array('Person.id'=>$data['Father']['id'])));
To get the same as mysql i have add this extra line(the last one $father=....,but now it seems subquery and look like the join isn't working) because of Father and Person are not the same model and if i have
$data['Father']['name'] and $data['Person']['name'] they are not equal
By the way i have a solution already,but maybe i misunderstand some concept.
Is there a way the get the mysql query easier?
Try this. (Notice the lack of a contain. The only reason to use contain is if you're not already getting the data in the main-model's find() or a join):
//Person model (cake 2.x code)
$this->find('first', array(
'fields' => array(
'Father.id',
'Father.name'
),
'conditions' => array(
'Person.id' => 1
),
'joins' => array(
array(
'table' => 'persons',
'alias' => 'Father',
'type' => 'left',
'conditions' => array(
'Person.father_id = Father.id'
)
)
)
));

CakePHP find not inserting correct quotes into query

I am using this code to make a list of truck types counted and sorted by type.
$count['type'] = $this->Type->find('all',
array('joins' => array(
array(
'table' => 'truck_has_types',
'alias' => 'TruckHasTypes',
'type' => 'LEFT',
'conditions' => array(
'Type.id' => 'TruckHasTypes.types_id',
)
)
),
'fields' => array(
'Type.id',
'Type.name',
'COUNT(TruckHasTypes.types_id) as N'),
'group' => 'TruckHasTypes.types_id'
)
);
The query only returns a single result even thought there should be many more. I found the culprit. The query looks like this (got this from sql_dump)
SELECT `Type`.`id`, `Type`.`name`, COUNT(`TruckHasTypes`.`types_id`) as N FROM `douglass_cake`.`types` AS `Type` LEFT JOIN `douglass_cake`.`truck_has_types` AS `TruckHasTypes` ON (`Type`.`id` = 'TruckHasTypes.types_id') WHERE 1 = 1 GROUP BY `TruckHasTypes`.`types_id`
You can see that
ON (`Type`.`id` = 'TruckHasTypes.types_id')
Does no have the same quotes as
ON (`Type`.`id` = `TruckHasTypes`.`types_id`)
I have added those quotes manually in phpmyadmin and the query is successful, but I cannot not get cakephp to automatically generate this query. Any ideas?
Thank you!
Ryan
Do it this way:
$count['type'] = $this->Type->find('all',
array(
'joins' => array(
array(
'table' => 'truck_has_types',
'alias' => 'TruckHasTypes',
'type' => 'LEFT',
'conditions' => array(
'Type.id=TruckHasTypes.types_id',
)
)
),
'fields' => array(
'Type.id',
'Type.name',
'COUNT(TruckHasTypes.types_id) as N'
),
'group' => 'TruckHasTypes.types_id'
)
);
Instead of writing joins do it "Cake way" — write model association: Associations: Linking Models Together
Also does 'group' => 'TruckHasTypes.types_id' needs to be one level upper?

How to count the result using find() in CakePHP 2.2.4?

I am using Cakephp 2.2.4 and I need to retrive a list of Lead that belongs to the user (id = 106).
The result of the query is:
array(
(int) 0 => array(
'Lead' => array(
'id' => '6',
'user_id' => '106',
'date' => '2012-12-31 22:15:23',
'ip' => '127.0.0.1',
'service_id' => '1',
'location' => 'Rome',
'message' => 'Message Message',
'telephone' => null,
'active' => null
),
'User' => array(
'id' => '106',
'email' => 'daje#daje.it',
'pwd' => '0433c024cb08be13000d59a347e640482843f46f177e95749dc6599c259617fd3491dcb940b47693cbbc7f65a2cc5ef62deca2e600c1be133ad54170f7d1fbd1',
'role_id' => '3',
'active' => '1'
),
'Service' => array(
'id' => '1',
'name' => 'Primo servizio'
),
'Estimate' => array(
(int) 0 => array(
'id' => '1',
'lead_id' => '6',
'user_id' => '106'
)
)
)
)
It looks good but I need to count the Estimates (Estimate array), I would like to retrive the number of the estimates, and not the array with all the fields (of estimates table).
How can i do it?
I need :
Lead array as it shown
User array as it shown
Service array as it shown
Estimate (only the total number of the estimates... in this case 1)
The find is very simple:
$options = array('conditions' => array('User.id' => 106));
debug($this->Lead->find('all', $options));
Try something like this, not 100% sure it'll work but worth a go if not I'd advise trawling the cakephp docs for retrieving your data:
$options = array(
'fields' => array('Lead.*', 'User.*', 'Service.*', 'count(Estimate.id)'),
'conditions' => array('User.id' => 106)
);
Without diving too far into the internals of the Cake ORM, assuming you don't need to do this immediately at query time, couldn't you just get the count of the estimate array programmatically after the fetch?
http://php.net/manual/en/function.count.php
$leads = $this->Lead->find('all',$options);
foreach($leads as $lead){
//get the number of estimates for every lead result
$lead_num = count($lead['Estimate']);
}
Alternatively, you could manually write a join query for this one fetch and execute it using the Cake Model class's query method. Without knowing the specifics of your table schema and model relations its hard to give specifics about how to structure the query, but this shouldn't be too difficult by just look at your table spec and extracting a sql COUNT statement for every Estimate with given id.

Categories