I have a query which looks like
$entity = $this->Products
->findById($id)
->contain( [ 'Categories',
'Categories.Sizes',
'ProductsPrices.Sizes' => function($q) {
return $q->select('Sizes.name');
}
])->first();
In this case a Product belongTo a Category and in turn Category hasMany Sizes Similerly Producthas many prices depending on it's size.
For example if a Product is available in 4 different sizes, then it will have 4 prices in the ProductsPrices table.
The query is returning the desired results, but problem is it is returning all the fields from each row. For example from Categories table i only need it's name. And from Categories.Sizes only id and name
Can I limit it to only fields which I need. I tried with ->select at different levels but not working. For example 'ProductsPrices.Sizes' I see only the Sizes query not the ProductsPrices one
Read more:
http://book.cakephp.org/3.0/en/orm/retrieving-data-and-resultsets.html
$entity = $this->Products
->findById($id)
->contain( [
'Categories' => function($q){
return $q->select(['name'])
->contain([
'Sizes' => function($q) {
return $q->select(['id','name']);
}
]);
},
// -------------
])->first();
Related
Products belongsToMany Categories and Categories hasMany Products, inside my Product view I'm showing a list of all it's categories but I want to paginate or limit these results.
My current code on ProductsController is:
$product = $this->Products
->findBySlug($slug_prod)
->contain(['Metas', 'Attachments', 'Categories'])
->first();
$this->set(compact('product'));
I know I need to set $this->paginate() to paginate something but I can't get it working to paginate the categories inside the product. I hope you guys can understand me.
UPDATE: Currently I have this going on:
$product = $this->Products->findBySlug($slug_prod)->contain([
'Metas',
'Attachments',
'Categories' => [
'sort' => ['Categories.title' => 'ASC'],
'queryBuilder' => function ($q) {
return $q->order(['Categories.title' => 'ASC'])->limit(6);
}
]
])->first();
The limit works but I don't know how to paginate yet
The paginator doesn't support paginating associations, you'll have to read the associated records manually in a separate query, and paginate that one, something along the lines of this:
$product = $this->Products
->findBySlug($slug_prod)
->contain(['Metas', 'Attachments'])
->first();
$categoriesQuery = $this->Products->Categories
->find()
->innerJoinWith('Products', function (\Cake\ORM\Query $query) use ($product) {
return $query->where([
'Products.id' => $product->id,
]);
})
->group('Categories.id');
$paginationOptions = [
'limit' => 6,
'order' => [
'Categories.title' => 'ASC'
]
];
$categories = $this->paginate($categoriesQuery, $paginationOptions);
$this->set(compact('product', 'categories'));
Then in your view template you can display your $product and separately paginate $categories as usual.
See also
Cookbook > Controllers > Components > Pagination
Cookbook > Views > Helper> Paginator
Cookbook > Database Access & ORM > Query Builder > Filtering by Associated Data
Products belongsToMany Categories and Categories hasMany Products, inside my Product view I'm showing a list of all it's categories but I want to paginate or limit these results.
My current code on ProductsController is:
$product = $this->Products
->findBySlug($slug_prod)
->contain(['Metas', 'Attachments', 'Categories'])
->first();
$this->set(compact('product'));
I know I need to set $this->paginate() to paginate something but I can't get it working to paginate the categories inside the product. I hope you guys can understand me.
UPDATE: Currently I have this going on:
$product = $this->Products->findBySlug($slug_prod)->contain([
'Metas',
'Attachments',
'Categories' => [
'sort' => ['Categories.title' => 'ASC'],
'queryBuilder' => function ($q) {
return $q->order(['Categories.title' => 'ASC'])->limit(6);
}
]
])->first();
The limit works but I don't know how to paginate yet
The paginator doesn't support paginating associations, you'll have to read the associated records manually in a separate query, and paginate that one, something along the lines of this:
$product = $this->Products
->findBySlug($slug_prod)
->contain(['Metas', 'Attachments'])
->first();
$categoriesQuery = $this->Products->Categories
->find()
->innerJoinWith('Products', function (\Cake\ORM\Query $query) use ($product) {
return $query->where([
'Products.id' => $product->id,
]);
})
->group('Categories.id');
$paginationOptions = [
'limit' => 6,
'order' => [
'Categories.title' => 'ASC'
]
];
$categories = $this->paginate($categoriesQuery, $paginationOptions);
$this->set(compact('product', 'categories'));
Then in your view template you can display your $product and separately paginate $categories as usual.
See also
Cookbook > Controllers > Components > Pagination
Cookbook > Views > Helper> Paginator
Cookbook > Database Access & ORM > Query Builder > Filtering by Associated Data
I have a query
$p = $this->Products
->findById($id)
->select(['name'])
->contain(['Categories.Sizes' => function($q) {
return $q->select(['id', 'name']);
}
]);
which is only returning product's name and not the Size of the product's category. But if the remove the select function which accepts the field names then it delivers also the Sizes
Is there any solution for that?
According to the CakePHP 3 book, in the area "Selecting Rows From A Table", you can specify which fields you want returned by including them in a select array:
$query = $articles
->find()
->select(['id', 'name']) // <-- Notice this line
->where(['id !=' => 1])
->order(['created' => 'DESC']);
So for yours, you're limiting the fields that are returned because you're specifying that you only want the 'name' field.
I have a many to many relation between sizes and toppings and and have a thrird table which contains extra fields the table is named toppings_sizes
Now I want to select only one field from toppings_sizes, at the moment I have a query which delivers all available field in all 3 tables.
$rs = $this->Toppings
->find()
->contain(['Sizes'])
->where(['category_id' => $categoryId]);
debug( json_encode($rs, JSON_PRETTY_PRINT));
I this can I need only 2 fields Sizes.name and ToppingsSizes.price
One way is by adding ->select to specify fields:
$rs = $this->Toppings
->find()
->select(['Sizes.name', 'ToppingsSizes.price'])
->contain(['Sizes'])
->where(['category_id' => $categoryId]);
$rs = $this->Toppings
->find()
->contain([
'Sizes' => function ($q) {
return $q
->select(['name']);
},
'ToppingsSizes' => function ($q) {
return $q
->select(['price']);
}
]);
->where(['category_id' => $categoryId]);
By Using this code you can get desired fields from table.
You need to pass Relationship model names into contain() method array which model entity you need in result data.
$rs = $this->Toppings
->find()
->select(['Sizes.name', 'ToppingsSizes.price'])
->contain(['Sizes','ToppingsSizes'])
->where(['Toppings.category_id' => $categoryId]);
Here I have a events field which have a realtion with passwords.I need to order all passwords by download_count DESC. So I have tried below code. It's not giving me any error but desc is not working.
$event = $this->Events->get($id, [
'contain' => [
'EventPasswordAll.Passwords' => [
'queryBuilder' => function (Query $q) {
return $q->order(['download_count' => 'DESC']);
}
]
]
]);
I have also tried
return $q->order(['Passwords.download_count' => 'DESC']);
I get the query
SELECT EventPasswordAll.id AS `EventPasswordAll__id`, EventPasswordAll.event_id AS `EventPasswordAll__event_id`, EventPasswordAll.password_id AS `EventPasswordAll__password_id`, Passwords.id AS `Passwords__id`, Passwords.name AS `Passwords__name`, Passwords.collection_id AS `Passwords__collection_id`, Passwords.photo_count AS `Passwords__photo_count`, Passwords.download_count AS `Passwords__download_count`, Passwords.created AS `Passwords__created`, Passwords.expires_at AS `Passwords__expires_at`, Passwords.recommended AS `Passwords__recommended`, Passwords.accepted AS `Passwords__accepted`, Passwords.user_id AS `Passwords__user_id` FROM event_password_all EventPasswordAll INNER JOIN passwords Passwords ON Passwords.id = (EventPasswordAll.password_id) WHERE EventPasswordAll.event_id in (14)
How to add order in queryBuilder for associative data ?
With an association of EventPasswordAll belongsTo Passwords, the passwords table record will be retrieved via a join, as can be seen in the query. In this case there is no query for Passwords, and thus the query builder is not being invoked.
You have to set the order for the EventPasswordAll query builder instead, like
$event = $this->Events->get($id, [
'contain' => [
'EventPasswordAll' => [
'queryBuilder' => function (Query $q) {
return $q->order(['Passwords.download_count' => 'DESC']);
},
'Passwords'
]
]
]);