CakePHP find not inserting correct quotes into query - php

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?

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

How can I convert this query in to CakePHP query?

How can I convert this query into a CakePHP query?
SELECT COUNT(invoices.id) FROM invoices,sales
WHERE invoices.id=sales.invoice_id AND to_id='13' AND is_updated='0'
GROUP BY invoice_id
I have two controllers and two models, invoices and sales.
For aggregate functions like COUNT, you need to create virtual field.
$this->Invoice->virtualFields['total'] = 'COUNT(`Invoice`.`id`)';
$result = $this->Invoice->find('all', array(
'fields' => array('Invoice.total'),
'joins' => array(
array(
'table' => 'sales',
'alias' => 'Sale',
'type' => 'INNER',
'conditions' => array(
'Invoice.id = Sale.invoice_id',
)
)
),
'conditions' => array(
'to_id' => 13,
'is_updated' => 0
),
'group' => array('Sale.invoice_id')
));
pr($result); // Check your result
Use the following links as references:
Joining tables
Retrieving Your Data
Virtual fields
Try to use Cake's Cookbook solution:
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#prepared-statements

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'
)
)
)
));

Complex paginate with NOT IN condition cakephp 2

i have a small problem to get my query work the right way.
My query should give me all lectures in the actual semester, but just those, where i'm not participating yet. So in SQL it would be:
SELECT * FROM lectures WHERE semester = $currentSemester AND lectures.id NOT IN (SELECT lectures_id FROM participaters WHERE user_id = $this->Auth->user('id'))
And what i already got is the following:
$this->paginate = array(
'conditions' => array(
'Lecture.semester >=' => $currentSemester,
'not' => array(
'Lecture.id' => array(
),
),
),
'order' => array(
'Lecture.name' => 'asc',
),
'contain' => array(
'Participater',
),
);
$this->set('lectures', $this->paginate($this->Lecture));
How can i define the condition with the user_id? Maybe someone of you can help? And sorry for my english, if here are any mistakes :)
First find a list of ids you want to exclude in the query, then use this list of ids with a condition like 'Lecture.id !=' => $ids.
Assuming you have model Participater linked to model lecture, the queries could as shown below.
//retrieve the list of excluded ids
$excluded_ids = $this->Lecture->Participater->find('list',array(
'conditions' => array(
'Participater.user_id' => $this->Auth->user('id')
),
'fields' => array('lecture_id')
);
$this->paginate = array(
'conditions' => array(
'Lecture.semester >=' => $currentSemester,
'Lecture.id !=' => $excluded_ids,//put the excluded ids here
),
'order' => array(
'Lecture.name' => 'asc',
),
'contain' => array(
'Participater',
),
);
$this->set('lectures', $this->paginate($this->Lecture));

How to change the sequence of 'joins' in CakePHP?

I have the problem with the sequence of joins. The similar problem was in another question Manipulating Order of JOINS in CakePHP. The answer was to use Containable behavior. In my case that is unacceptable because I have deeper associations and containable generates too many queries. Containable does not generate joins for the three level associations. It generates additional queries for every entry from the second level table.
My query is:
$this->LevelOne->find('all', array(
'joins' => array(array(
'table' => 'level_three',
'alias' => 'LevelThree',
'type' => 'LEFT',
'conditions' => array(
'LevelThree.id = LevelTwo.level_three_field_id'
)
))
));
The problem here is that cake generates several joins but the join of the LevelThree table is done before the joins of the LevelTwo tables and that throws an SQL error "Unknown column 'LevelTwo.level_three_field_id' in 'on clause'". If the LevelThree join would be at the end of the query after all LevelTwo joins the query would be okay.
So, the question is how to change the sequence of joins?
Finally I figured out how to do that:
$this->LevelOne->unbindModel(array('belongsTo' => array('LevelTwo')));
$this->LevelOne->find('all', array(
'joins' => array(
array(
'table' => 'level_two',
'alias' => 'LevelTwo',
'type' => 'LEFT',
'conditions' => array(
'LevelTwo.id = LevelOne.level_two_field_id'
)
),
array(
'table' => 'level_three',
'alias' => 'LevelThree',
'type' => 'LEFT',
'conditions' => array(
'LevelThree.id = LevelTwo.level_three_field_id'
)
)
)
));
To those who has similar problems but in relations a-la $belongsTo, to have correct order you should set it correctly.
For example when you have such code:
var $belongsTo = array(
'Vacancy',
'Applicant' => array(
'className' => 'Person',
),
'Recruiter' => array(
'className' => 'Person',
),
'Company' => array(
'conditions' => array('Company.id = Vacancy.company_id'),
),
);
But in the result always receive results where Vacancy always joins last, you should do juts simple thing: add those "Vacancy" model not as array value, but as a key=>value, as others:
var $belongsTo = array(
'Vacancy' => array(), // Just add empty array here -- all magic is here :)
'Applicant' => array(
'className' => 'Person',
),
'Recruiter' => array(
'className' => 'Person',
),
'Company' => array(
'conditions' => array('Company.id = Vacancy.company_id'),
),
);
Now all will be in straight order: Vacancy, Applicant, Recruiter & only then Company.
Have you thought about creating a HABTM model and inserting your own 'finderQuery' to override the model query?
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasandbelongstomany-habtm
Sometimes with complex queries, it make more sense to create the custom query so cake doesn't have to deal with it. To do that, you can just create the function in the model and then call it like you would any other query.
function customJoin(){
return $this->query('CUSTOM QUERY HERE');
}
Then call it from the controller:
$this->ModelName->customJoin();
I think sometimes we rely too heavily on the automation that a framework provides and forget that WE are in control and can implement code outside of the core. I do it all the time. Hope this helps.
public function selectdata(){
$option= $this->Group->find('all',
array(
'joins' =>
array(
array(
'table'=> 'user_groups',
'alias'=>'g',
'type'=> 'INNER',
'conditions'=> array('g.group_id =Group.id ')
),
array(
'table'=> 'users',
'alias'=>'u',
'type'=> 'INNER',
'conditions'=> array('u.id = g.user_id')
),
),'fields'=>array('Group.name,Group.created,Group.modified,Group.status,Group.created_by,u.first_name,u.last_name'),
'conditions'=>array('g.group_id=Group.created_by or g.user_id=u.id')
// ."'".$created_by."'"
)
);
var_dump($option);//die();
if(isset($this->params['requested']))
{
return $option;
}
$this->set('groups', $option);
}

Categories