Cake PHP 2 isn't joining linked hasMany relationships - php

I'm trying to join all AffiliateId models for each user through the Cake PHP 2 query builder, I'm seeing my users through the following query, but I get no affiliates despite there being affiliates for some users. I don't want to join affiliates if there are none for a particular user, what am I missing?
$users = $this->User->find('all', array(
'joins' => array(
array(
'table' => 'affiliate_ids',
'alias' => 'AffiliateId',
'type' => 'inner',
'conditions' => array(
'User.id = AffiliateId.user_id'
)
)
),
'order' => array(
'User.username' => 'ASC'
),
'recursive' => -1
));

So, joins will just add a join to the query, it won't select the fields. You can use fields to select them.
However, using Containable and proper associations is the better solution, as it would nest them properly in the result and would also auto select the associated fields:
$users = $this->User->find('all', array(
'contain' => array('AffiliateId'),
'order' => array('User.username' => 'ASC'),
'recursive' => -1
));

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

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

CakePHP paginate causing weird DISTINCT behavior

I have a pretty basic paginate query which works fine until I put DISTINCT on one of the columns.
The following works:
$this->paginate = array(
'conditions' => array(Message.recipient_id' => $this->Auth->user('id')),
'fields' => array(
'DISTINCT Message.sender_id',
'Message.recipient_id',
'Message.thread_id',
),
'limit' => 15,
'order' => array('Message.created' => 'DESC')
);
This query doesn't work:
$this->paginate = array(
'conditions' => array(Message.recipient_id' => $this->Auth->user('id')),
'fields' => array(
'DISTINCT Message.sender_id',
'Message.recipient_id',
'Message.thread_id',
// If I add any of the following columns, the DISTINCT doesn't work at all
'Message.created',
'Message.modified',
'Message.id'
),
'limit' => 15,
'order' => array('Message.created' => 'DESC')
);
Why would any of those other columns in the 'fields' option trip up the DISTINCT keyword?
It doesn't make any sense to me.
If you mean by "it doesn't work" that despite the distinct column you are getting multiple messages with the same sender_id... Then the reason for that is because when using multiple columns with the distinct keyword, it looks for unique combinations of all the columns. Message.created, Message.modified, and Message.id are going to be unique per row most or all of of the time, so those columns by themselves will make your combinations of fields distinct. Try using group by Message.sender_id instead.
$this->paginate = array(
'conditions' => array(Message.recipient_id' => $this->Auth->user('id')),
'fields' => array(
'Message.sender_id',
'Message.recipient_id',
'Message.thread_id',
'Message.created',
'Message.modified',
'Message.id'
),
'limit' => 15,
'order' => array('Message.created' => 'DESC'),
'group' => array('Message.sender_id'),
);

CakePHP - Searching 3 tables efficiently using JOIN

I'm currently trying to implement a search engine function in my CakePHP site, trying to return information from 3 tables efficiently. The main usage will be numeric searches, free text will be extremely minimal and as such I'm not trying to optimise for this scenario.
Companies hasMany Products
Products hasMany Prices
Ideally I'd like to be able to use something like the following function in the Products controller (this won't work due to distant relationships):
$results = $this->Product->find('all', array(
//conditions can be defined against all 3 tables
'conditions' =>. array(
'company.name LIKE' => '%'.$search_term.'%',
'product.feature' => $product_feature,
'price.price <' => $price
),
//fields restricted against all 3 tables
'fields' => array(
'company.name',
'product.feature',
'price.price'
)
));
I've tried using Containable behaviour to include the three models but to no avail.
I believe the solution lies with JOINS but my experience with these is limited, I've tried code similar to the following within the find function above:
'joins' => array(
array(
'table' => 'companies',
'alias' => 'Company',
//tried a mix of joins (LEFT, RIGHT, INNER)
'type' => 'LEFT',
'conditions' => array(
'Company.id = Product.company_id'
)
),
array(
'table' => 'prices',
'alias' => 'Price',
//tried a mix of joins (LEFT, RIGHT, INNER)
'type' => 'LEFT',
'conditions' => array(
'Price.product_id = Product.id'
)
),
),
'recursive' => 1,
Edit: The result of the above joins is that when i specify the price in the conditions or fields that it can't be found, I tried changing the names such as Product.Price.price to account for the one to many relationship but still without luck.
I'd appreciate any help in finding a solution!
I prefer less code with cake model/table naming convention (db table products - model name Product, db table prices - model name Price) for further project management. It's looks like You want to do:
$results = $this->Product->find('all', array(
'fields' => array(
'Company.name',
'Product.feature',
'Price.price'
),
'joins' => array(
'LEFT JOIN companies AS Company ON Product.company_id = Company.id
LEFT JOIN prices AS Price ON Product.id = Price.product_id'
),
'conditions' => array(
'Company.name LIKE' => '%'.$search_term.'%',
'Product.feature' => $product_feature,
'Price.price <' => $price
),
));
but if You want to get products with Your all criteria (company and price) only, You should use INNER JOIN, and GROUP BY Product (group option).
Also, if You want to get all products with many prices and companies result and You set/link model relations, You can use contain option, like:
$contain = array(
'Company' => array(
// ...
'conditions' => array('Company.name LIKE' => '%'.$search_term.'%'),
// ...
),
'Price' => array(
// you can set: 'fields' => array('id', ...),
'conditions' => array('Price.price <' => $price),
// you can set order: 'ordder' => '....'
)
);
$this->Product->attach('Containable');
$post = $this->Product->find('all', array(
// ...
'contain' => $contain,
'conditions' => array('Product.feature' => $product_feature),
// ...
));
So You will get all products with feature => $product_feautre, and You get LEFT JOIN companies and prices to this products.
Hope this helps.

Categories