inner join with containable cakephp - cakephp

I have 3 tables, restaurants, orders, users. orders table has fields restaurant_id, status and user_id.
restaurant -> hasMany orders
orders belogsTo Restaurant
orders belongsTo users
I need to find only those restaurants that have order with status = 1, and at the same time need to fetch user's info.
So, I am making inner join with orders table,
$options['joins'] = array(
array('table' => 'orders',
'alias' => 'Order',
'type' => 'INNER',
'conditions' => array(
'Restaurant.id = Order.restaurant_id',
)
)
);
But how Can I also get users' information along with this, because according to cake documentation http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables, allowed params here are table, alias, type, conditions. Is there a some way that I can embed the sql query of getting user's info in the final query? Yes, the alternative will be to write the custom query, but is not there a way to do with cake.
Thanks
update
The best solution in this case was to write custom sql query.

Have you just tried an additional Join to get the Users as well?
Something like this:
$this->Restaurant->find('all', array(
'joins' => array(
array('table' => 'orders',
'alias' => 'Order',
'type' => 'INNER',
'conditions' => array(
'Restaurant.id = Order.restaurant_id',
'Order.status = 1'
)
),
array('table' => 'users',
'alias' => 'User',
'type' => 'RIGHT',
'conditions' => array(
'Order.user_id = User.id'
)
),
));
Note: What I usually find easiest (with complicated queries that you're having trouble figuring out) is to build the query 100% in SQL and get it working to your liking, then just re-build it in CakePHP.

List of orders with status 1 and with restaurant and user info:
$this->Restaurant->Order->find('all', array('conditions'=>array('Order.status'=>1), 'contain'=>array('Restaurant', 'User')))

Related

Getting data by search filter from associated models

i have an question regarding search filter in cakephp. Without complicating my question, below are the structure of what i want....
1) I have a projects table.
2) another one is project_funder_names table which is associated with projects table. project_id is in project_funder_names table. i have made project_funder_names table because i need multiple funder names for a single project, thats why i have made this table.
3) now the main point is i want if i search multiple funders in search filter which is coming in dropdown with checkbox, i will get project details according to these values. so how it would happen.
here is my cakephp find all query....
$project_info = $this->Project->find('all', array(
'conditions' =>
array(
'Project.status' => 1,
'OR' => array($search)),
'fields' => array('id', 'title', 'short_description', 'budget_allocation', 'currency', 'total_comments', 'published_date'),
'contain' => array(
'ProjectFunderName' => array(
'conditions' => array($search_funder)),
'Currency' => array('currency_symbol'),
'ProjectBookmark' => array('project_id', 'user_id')
)
)
);
problem is in $search_funder.
please help me for this.. thanks.
Looks like you need to search results based on associated models. One drawback of using containable behavior is if you're trying to assign a condition to an associated model, the main model will be retrieved no matter what.
In situations where you'd want to retrieve the main as well as the associated records based on a condition for the associated model, I'd suggest you to use join.
$joins = array(
array('table' => 'project_funder_names',
'alias' => 'ProjectFunderName',
'type' => 'LEFT',
'conditions' => array('ProjectFunderName.project_id = Project.id')
),
array('table' => 'currencies',
'alias' => 'Currency',
'type' => 'LEFT',
// It's unclear how currencies is associated with the other tables. Use appropriate table join condition here
),
array('table' => 'project_bookmarks',
'alias' => 'ProjectBookmark',
'type' => 'LEFT',
'conditions' => array('ProjectBookmark.project_id = Project.id')
)
)
);
$project_info = $this->Project->find('all',
array(
"joins" => $joins,
"fields" => array(.........) // Specify all your desired fields here
"conditions" => array(....) // Specify all conditions for Project, ProjectFunderName models.
)
);
Hope this helps.
Peace! xD

Select records which have atleast one hasMany relation row in Cakephp

I'm facing a problem with cakephp associations in Models.
I have to Select records which have atleast one hasMany reation row
Model
class Category extends AppModel
{
public $hasMany = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'CategoryId',
)
);
}
Query
$categories = $this->Category->find('all');
I only needed the categories which have atleast one product entry
Categories Like : Shirts, Footwear, Glasses etc
Products like :
Small, medium, large (Shirts)
With Frame, UV protected (Glass)
So, i jus want to get Shirts and Glasses Categories only because for the above example there is no products for Footwear
Use counterCache or joins
Please refer to CakePHP - Find and count how many associated records exist
The most simple way with the best performance would be using a properly indexed counter cache field as shown in the linked answer.
Sice the linked answer is not an exact duplicate with respect to the join, here's some additional info, instead of using HAVING COUNT with the join you'd use a IS NOT NULL condition. Here's an (untested) example:
$this->Category->find('all', array(
'joins' => array(
array(
'table' => 'products',
'alias' => 'Product',
'type' => 'LEFT',
'conditions' => array('Category.id = Product.CategoryId')
)
),
'conditions' => array(
'Product.CategoryId IS NOT NULL'
)
'group' => 'Category.id'
));
Depending on the used DBMS and version you might get better performance using an inner join:
$this->Category->find('all', array(
'joins' => array(
array(
'table' => 'products',
'alias' => 'Product',
'type' => 'INNER',
'conditions' => array('Category.id = Product.CategoryId')
)
),
'group' => 'Category.id'
));

CakePHP - find conditions with associations

I have an issue with a cakePHP find conditions. I have a Club model, a User model and a Post model :
Club hasMany Post
Club HABTM User
Basically, my clubs_users table also contains additional fields such as let's say 'limit' and 'diff' that respectively indicate the maximum number of posts a user want to display and how old those posts are allowed to be. I'd like to select the appropriate posts for every club related to a given user. I'm doing someting like this
$clubs = $this->Club->User->find('first', array(
'conditions' => array('User.id' => $id),
'contain' => array(
'Club' => array(
'fields' => array(
'id',
'name'
),
'Post' => array(
'fields' => array(
'id',
'title',
'description',
'created',
),
'order' => array('Post.created' => 'DESC'),
'conditions' => array(
'DATEDIFF(NOW(), `Post.created`) <= /* 1 */',
),
'limit' => '/* 2 */'
)
)
)
));
What should i put instead of 1 and 2 for this to work ? I tried ClubsUser.diff and ClubsUser.limit but i got an error stating that those fields were unknown in the where clause.
Any help would be welcome.
Thanks for reading.
edit
After bancer's comment, i looked deeper into the MySQL doc and it appeared that LIMIT expects only numeric arguments. So I now just want to return the posts that are not too old. My new find is (with the actual fields name)
$overview = $this->Club->Follower->find('first', array(
'conditions' => array('Follower.id' => $this->Auth->user('id')),
'contain' => array(
'Club' => array(
'fields' => array(
'id',
'name'
),
'Post' => array(
'fields' => array(
'id',
'title',
'description',
'created',
),
'order' => array('Post.created' => 'DESC'),
'conditions' => array(
'DATEDIFF(NOW(), `Post.created`) <= ClubsUser.max_time_posts',
),
'limit' => 10
)
)
)
));
It generates the three following SQL queries (i replaced the fields name by * for clarity reasons) :
SELECT * FROM `users` AS `Follower`
WHERE `Follower`.`id` = 1
LIMIT 1
SELECT * FROM `clubs` AS `Club`
JOIN `clubs_users` AS `ClubsUser`
ON (`ClubsUser`.`user_id` = 1 AND `ClubsUser`.`club_id` = `Club`.`id`)
ORDER BY `ClubsUser`.`role_id` DESC
SELECT * FROM `posts` AS `Post`
WHERE DATEDIFF(NOW(), `Post`.`created`) <= `ClubsUser`.`max_time_posts` AND `Post`.`club_id` = (1)
ORDER BY `Post`.`created` DESC
LIMIT 10
The last query returns the error : field 'ClubsUser.max_time_posts' unknown in where clause
Ideally, i would like to get a query close to the one below instead of the last two queries above :
SELECT * FROM `clubs` AS `Club`
JOIN `clubs_users` AS `ClubsUser`
ON (`ClubsUser`.`user_id` = 1 AND `ClubsUser`.`club_id` = `Club`.`id`)
LEFT JOIN `posts` AS `Post`
ON (`Post`.`club_id` = `Club`.`id` AND DATEDIFF(NOW(), `Post`.`created`) <= `ClubsUser`.`max_time_posts`)
ORDER BY `ClubsUser`.`role_id` DESC, `Post`.`created` DESC
LIMIT 10
Any ideas ?
You should not use HABTM if you have extra fields in the join table, because Cake actually deletes and recreates those joins. You should use a "hasMany through" association:
Club hasMany ClubUser
User hasMany ClubUser
ClubUser belongsTo Club
ClubUser belongsTo User
When you do your find on User, you just contain ClubUser then contain Club.
$this->User->find('all', array(
'contain' => array(
'ClubUser' => array(
'fields' => array(
'diff',
'limit'),
'Club'))));
More details here:
http://book.cakephp.org/1.3/view/1650/hasMany-through-The-Join-Model
and here:
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany-through

Problem with CakePHP model joins, missing fields from joined table.

Im probably missing something painfully obvious here. Im trying to join multiple tables together in Cake, but Im only getting the fields from the first table. Consider the following code..
$joins = array();
$joins[] = array(
'table' => 'products',
'alias' => 'Product',
'type' => 'LEFT',
'conditions' => array(
'Device.id = Product.device_id'
)
);
$joins[] = array(
'table' => 'pricepoints',
'alias' => 'Pricepoints',
'type' => 'LEFT',
'conditions' => array(
'Pricepoints.product_id = Product.id'
)
);
$all_products = $this->Device->find('all', array("joins" => $joins);
And this returns the following SQL
SELECT `Device`.`id`, `Device`.`manufacturer_id`, `Device`.`name`, `Device`.`type_id`, `Manufacturer`.`id`, `Manufacturer`.`name` FROM `devices` AS `Device` LEFT JOIN products AS `Product` ON (`Device`.`id` = `Product`.`device_id`) LEFT JOIN pricepoints AS `Pricepoints` ON (`Pricepoints`.`product_id` = `Product`.`id`) LEFT JOIN `manufacturers` AS `Manufacturer` ON (`Device`.`manufacturer_id` = `Manufacturer`.`id`)
ie. it only returns the fields from the parent Model ie. Device. How do I get it to select ALL fields from the join? Im presuming its to do with the way I have my Model relationships set up, but I think I have these set up correctly.
Anyone any advice?
you can specify the fields in the find query:
$all_products = $this->Device->find('all', array("fields" => array('Device.*','Product.*','Pricepoints.*')
"joins" => $joins
);
Hope this helps
Do your join by calling bindModel() instead of manually specifying the join. See the cookbook for the details on creating and destroying associations on the fly

How to get all tags that do NOT have posts associated in CakePHP

I'm doing a blog in CakePHP, so I have two tables in my database that are HABTM related: posts and tags. Because they are HABTM related I also have a poststags table keeping track of relations.
I'd like to have a method in my tags_controller that will delete all unused tags.
How do I find all tags that are NOT associated with any posts?
You could use the following statement to delete all unused tags:
$this->query('delete from tags where not exists (select * from posts_tags where posts_tags.tag_id = tags.id)');
(And to find all tags not associated with any posts simply replace the "delete" with "select *")
$this->Tag->find('all', array(
'conditions' => array(
'PostsTag.id IS NULL'
),
'joins' => array(
array(
'table' => 'poststags',
'alias' => 'PostsTag',
'type' => 'LEFT',
'conditions' => array(
'PostsTag.tag_id' => 'Tag.id',
),
),
),
));

Resources