CakePHP: How to retrieve data from two tables using an inner join? - php

I have two tables in a database, one as user(id,first_name,last_name), and the other one as location(id,country).
I need to perform an inner join with these two tables based on the condition user.id = location.id, and the query result should contain the columns first_name, last_name and country.
I have tried the following query in CakePHP:
$this->set('users', $this->User->find('list', array(
'fields' => array(
'User.id',
'User.first_name',
'location.country'
),
array(
'joins' => array(
array(
'table' => 'location',
'alias' => 'location',
'type' => 'INNER',
'conditions' => array('User.id = location.id')
)
)
)
)));
which however produces this error:
Unknown column 'location.country' in 'field list'
What could be the problem?

I think your syntax is wrong because the options array should have a key for the joins. You appear to have an extra array. Try:
$this->set('users',$this->User->find('list',
array(
'fields' => array('User.id', 'User.first_name','location.country'),
'joins' => array(array('table' => 'location',
'alias' => 'location',
'type' => 'INNER',
'conditions' => array('User.id = location.id')
))
)
));

Related

CakePHP join tables in find query

I have Workposition model. It is linked in database with Orders with belongsTo relationship. So, I need to find specific workpositions bz the conditions related to Orders model. So, When I use for example suck kind of find:
$workpositions = $this->Workposition->find('all', array(
'conditions' => array(
'Order.type' => 'N'
)
));
CakePHP understand the Order.id notation. But when i'm trying to use joins tables:
$workpositions = $this->Workposition->find('all', array(
'conditions' => array(
'Order.type' => 'N'
)
'joins' => array(
array('table' => 'ordergroups_orders',
'alias' => 'OrdergroupsOrder',
'type' => 'INNER',
'conditions' => array(
'Order.id = OrdergroupsOrder.order_id',
'OrdergroupsOrder.ordergroup_id' => '3',
)
)
)));
It gives me an error : Column not found: 1054 Unknown column 'Order.id' in 'on clause'. So it doesn't understand Order.id notation. What can be the problem ?
I tried also to make something like this :
$workpositions = $this->Workposition->find('all', array(
'conditions' => $conditions,
'joins' => array(
array('table' => 'orders',
'alias' => 'Orders',
'type' => 'INNER',
),
array('table' => 'ordergroups_orders',
'alias' => 'OrdergroupsOrder',
'type' => 'INNER',
'conditions' => array(
'Orders.id = OrdergroupsOrder.order_id',
'OrdergroupsOrder.ordergroup_id' => $ordergroup_ids,
)
)
)));
But i get error Column not found: 1054 Unknown column 'Array' in 'on clause'(Array to string conversion). So it doesn't undestand my array of ids, while it undestands it without the binding of Order model, when the find Method sees Order.
The join conditions must be the array value, not key => value.
Try change the line
'OrdergroupsOrder.ordergroup_id' => $ordergroup_ids,
to
'OrdergroupsOrder.ordergroup_id = $ordergroup_ids',
$ordergroup_ids is an Array? Try to use a single id.

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

Searching CakePHP HABTM relationship for results where 'all' terms match, not 'any'

In CakePHP 2.4, I'm trying to generate conditions for a search query that searches for data that has been tagged across a HABTM relationship.
My problem is twofold: I can't get my query to return only data tagged with ALL of my search terms, and I can't get my query to return results for partial tags.
This loop generates a working query returning data with ANY of the tags in the search query.
foreach($tags as $tag) {
$conditions['Tag.name'][] = $tag;
}
$query = $this->Tagged->getQuery('all', array(
'conditions' => $conditions,
'fields' => array('foreign_key'),
'contain' => array('Tag')
));
I'd like to get the loop to generate conditions returning only data tagged with ALL the search terms, not data tagged with any of them, and to return matches for partial terms.
EDIT 2:
My database schema looks like this (I'm using the CakeDC Tags plugin to add tags)
Posts
id | other_data
public $hasAndBelongsToMany = array(
'Tag' => array(
'with' => 'Tagged'));
Tagged
id | model | foreign_key | tag_id
Tags
id | name
This is the join I'm trying to use: It complains when I use Tag as an alias: Not unique table/alias: 'Tag'.
$joins = array(
array(
'table' => 'tags',
'alias' => 'queryTag',
'type' => 'LEFT',
'conditions' => array(
'queryTag.name' => 'keyword'
)
),
);
$query = $this->Tagged->getQuery('all', array(
//'conditions' => array('Tag.name LIKE' => '%' . $data['tags'] . '%'),
'joins' => $joins,
'fields' => array('foreign_key'),
'contain' => array('Tag')
));
return $query;
Its hard to tell without your full database schema but i think you need to join two tables like-
$options['joins'] = array(
array(
'table' => 'tagged_tags',
'alias' => 'TaggedTag',
'type' => 'LEFT',
'conditions' => array(
'TaggedTag.tagged_id' => 'Tagged.id'
)
),
array(
'table' => 'tags',
'alias' => 'Tag',
'type' => 'LEFT',
'conditions' => array(
'TaggedTag.tag_id' => 'Tag.id'
)
)
);

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