CakePHP how I will fetch category wise products? - cakephp

I have 3 tables
categories, product_categories , products
Here categories hasMany product_categories and product_categories hasMany products. I am trying to fetch latest 20 products under categories.
I have written below query
$categories = \Cake\ORM\TableRegistry::getTableLocator()->get( 'Categories' )->find()
->contain(
[
'ProductCategories.Products' => function($q){
return $q->limit(20);
}
]
)
By this query I am getting 20 products for per ProductCategories. I just need 20 products for categories not by per ProductCategories. How can I solve this problem ?
After #ndm comment I have tried below library https://github.com/icings/partitionable
In CategoriesTable table I have written below code
$this
->partitionableBelongsToMany('RecentProducts')
->setClassName('Products')
->setThrough('ProductCategories')
->setLimit(9)
->setSort([
'RecentProducts.created' => 'DESC',
])
;
My query looks like
SELECT
ProductCategories.id AS RecentProducts_CJoin__id,
ProductCategories.name AS RecentProducts_CJoin__name,
ProductCategories.slug AS RecentProducts_CJoin__slug,
ProductCategories.img AS RecentProducts_CJoin__img,
ProductCategories.category_id AS RecentProducts_CJoin__category_id,
ProductCategories.sub_category_id AS RecentProducts_CJoin__sub_category_id,
ProductCategories.created AS RecentProducts_CJoin__created,
ProductCategories.modified AS RecentProducts_CJoin__modified,
RecentProducts.id AS RecentProducts__id,
RecentProducts.name AS RecentProducts__name,
RecentProducts.item_id AS RecentProducts__item_id,
RecentProducts.product_category_id AS RecentProducts__product_category_id,
RecentProducts.crawl_uniquekey AS RecentProducts__crawl_uniquekey,
RecentProducts.website_name AS RecentProducts__website_name,
RecentProducts.original_price AS RecentProducts__original_price,
RecentProducts.discount_amount AS RecentProducts__discount_amount,
RecentProducts.description AS RecentProducts__description,
RecentProducts.img_url AS RecentProducts__img_url,
RecentProducts.total_review AS RecentProducts__total_review,
RecentProducts.review_average AS RecentProducts__review_average,
RecentProducts.price AS RecentProducts__price,
RecentProducts.percentage AS RecentProducts__percentage,
RecentProducts.expiry AS RecentProducts__expiry,
RecentProducts.price_after_discount AS RecentProducts__price_after_discount,
RecentProducts.price_zero_padded AS RecentProducts__price_zero_padded,
RecentProducts.created AS RecentProducts__created,
RecentProducts.modified AS RecentProducts__modified
FROM
products RecentProducts
INNER JOIN product_categories ProductCategories ON ProductCategories.id = (
RecentProducts.product_category_id
)
WHERE
(
ProductCategories.category_id in (1, 2)
AND ProductCategories.id in (
SELECT
__ranked__RecentProducts.id AS id
FROM
(
SELECT
ProductCategories.id AS RecentProducts_CJoin__id,
ProductCategories.name AS RecentProducts_CJoin__name,
ProductCategories.slug AS RecentProducts_CJoin__slug,
ProductCategories.img AS RecentProducts_CJoin__img,
ProductCategories.category_id AS RecentProducts_CJoin__category_id,
ProductCategories.sub_category_id AS RecentProducts_CJoin__sub_category_id,
ProductCategories.created AS RecentProducts_CJoin__created,
ProductCategories.modified AS RecentProducts_CJoin__modified,
ProductCategories.id AS id,
(
ROW_NUMBER() OVER (
PARTITION BY ProductCategories.category_id
ORDER BY
RecentProducts.created DESC
)
) AS __row_number,
RecentProducts.id AS RecentProducts__id,
RecentProducts.name AS RecentProducts__name,
RecentProducts.item_id AS RecentProducts__item_id,
RecentProducts.product_category_id AS RecentProducts__product_category_id,
RecentProducts.crawl_uniquekey AS RecentProducts__crawl_uniquekey,
RecentProducts.website_name AS RecentProducts__website_name,
RecentProducts.original_price AS RecentProducts__original_price,
RecentProducts.discount_amount AS RecentProducts__discount_amount,
RecentProducts.description AS RecentProducts__description,
RecentProducts.img_url AS RecentProducts__img_url,
RecentProducts.total_review AS RecentProducts__total_review,
RecentProducts.review_average AS RecentProducts__review_average,
RecentProducts.price AS RecentProducts__price,
RecentProducts.percentage AS RecentProducts__percentage,
RecentProducts.expiry AS RecentProducts__expiry,
RecentProducts.price_after_discount AS RecentProducts__price_after_discount,
RecentProducts.price_zero_padded AS RecentProducts__price_zero_padded,
RecentProducts.created AS RecentProducts__created,
RecentProducts.modified AS RecentProducts__modified
FROM
products RecentProducts
INNER JOIN product_categories ProductCategories ON ProductCategories.id = (
RecentProducts.product_category_id
)
WHERE
ProductCategories.category_id in (1, 2)
) __ranked__RecentProducts
WHERE
__ranked__RecentProducts.__row_number <= 9
)
)
ORDER BY
RecentProducts.created DESC
My query builder code
$categories = \Cake\ORM\TableRegistry::getTableLocator()->get( 'Categories' )->find()
->contain(
[
'CategoryKeywords',
'RecentProducts'
]
)
Here I am getting 18 items but expectation is 9.

Assuming that you have following relation
Categories hasMany ProductCategories
ProductCategories hasMany Products
Product belongsTo ProductCategories
$this->loadModel('Products');
$product = $this->Products->find('all')
->contain(['ProductCategories'])
->limit(20)
->groupBy('product_categories_id');

Related

Does the left join order matter in linq query (EF Core 2)?

I have book table which has a relationship with three tables UserFavorites, UserRates, DiscountItems and last one (DiscountItems) has a relationship with Discounts table so I want to load discount with book if it exist
When create linq left join query with this order (join userfavoirtes then discountitems then discount then userate) generated SQL code is fine as expected
But when change order like below (join userfavoirtes then userate then discountitems then discount) generated query only contain first two join only
var books = (from b in result
join uf in UnitOfWork.Context.UserFavorites
on new { b.Id, RelatedType = RelatedTypeEnum.Book, UserId = userId }
equals new { Id = uf.RelatedId, uf.RelatedType, uf.UserId } into buf
from f in buf.DefaultIfEmpty()
join di in UnitOfWork.Context.DiscountItems
on b.Id equals di.BookId into bdi
from di in bdi.DefaultIfEmpty()
join d in UnitOfWork.Context.Discounts
on (di != null ? di.DiscountId : 0) equals d.Id into bd
from d in bd.DefaultIfEmpty()
join ur in UnitOfWork.Context.UserRates
on new { b.Id, UserId = userId }
equals new { Id = ur.BookId, UserId = ur.CreatedBy } into bur
from r in bur.DefaultIfEmpty()
select new BookViewModel
{
Id = b.Id,
Title = b.Title,
Price = b.Price,
BookImage = SetDefaultImageIfNoImage(b.BookImage),
IsFavorite = f != null ? f.IsFavorite : false,
Rate = bur.Any() ? Math.Round(bur.Average(a => a.Value), 1) : 0,
Discount = d
})
.OrderBy(a => a.CategoryId)
.ThenBy(a => a.Authors)
.Take(10).ToList();
this is generated sql query
SELECT *FROM [Book] AS [a]
LEFT JOIN [UserFavorites] AS [uf] ON (([a].[Id] = [uf].[RelatedId]) AND (3 = [uf].[RelatedType])) AND [uf].[UserId] IS NULL
LEFT JOIN [DiscountItems] AS [di] ON [a].[Id] = [di].[BookId]
LEFT JOIN [Discounts] AS [d] ON CASE
WHEN [di].[Id] IS NOT NULL
THEN [di].[DiscountId] ELSE 0
END = [d].[Id]
LEFT JOIN [UserRates] AS [ur] ON ([a].[Id] = [ur].[BookId]) AND [ur].[CreatedBy] IS NULL
WHERE ((([a].[Active] = 1) AND ([a].[Id] <> 62)) AND ([a].[Status] = 1))
but when write join with this order
var books = (from b in result
join uf in UnitOfWork.Context.UserFavorites
on new { b.Id, RelatedType = RelatedTypeEnum.Book, UserId = userId }
equals new { Id = uf.RelatedId, uf.RelatedType, uf.UserId } into buf
from f in buf.DefaultIfEmpty()
join ur in UnitOfWork.Context.UserRates
on new { b.Id, UserId = userId }
equals new { Id = ur.BookId, UserId = ur.CreatedBy } into bur
from r in bur.DefaultIfEmpty()
join di in UnitOfWork.Context.DiscountItems
on b.Id equals di.BookId into bdi
from di in bdi.DefaultIfEmpty()
join d in UnitOfWork.Context.Discounts
on (di != null ? di.DiscountId : 0) equals d.Id into bd
from d in bd.DefaultIfEmpty()
select new BookViewModel
{
Id = b.Id,
Title = b.Title,
Price = b.Price,
BookImage = SetDefaultImageIfNoImage(b.BookImage),
IsFavorite = f != null ? f.IsFavorite : false,
Rate = bur.Any() ? Math.Round(bur.Average(a => a.Value), 1) : 0,
Discount = d
})
.OrderBy(a => a.CategoryId)
.ThenBy(a => a.Authors)
.Take(10).ToList();
generated SQL query contains only the first two joins
SELECT *
FROM [Book] AS [a]
LEFT JOIN [UserFavorites] AS [uf] ON (([a].[Id] = [uf].[RelatedId]) AND (3 = [uf].[RelatedType])) AND [uf].[UserId] IS NULL
LEFT JOIN [UserRates] AS [ur] ON ([a].[Id] = [ur].[BookId]) AND [ur].[CreatedBy] IS NULL
WHERE ((([a].[Active] = 1) AND ([a].[Id] <> 62)))

CakePHP 3 paginating a result from union query

I have two tables. The first one has the main products data, another one has the secondary descriptions of these products, linked by products_id foreign key on the second table.
I've created the SQL and everything looks good. But when I convert to ORM of CakePHP 3, with the same result SQL code, it shows a big problem: Every page shows the secondary descriptions from the second data table.
At the first page pagination index, who has the primary product into, works as expected. At first, shows the main products, and after that description shows the secondary descriptions that are inside the secondary table data. But when I click to see the next pages, these secondary products are there, when I expected anything from this table data.
I need to show these secondary descriptions only when the main products are on that page and when it isn't there, it doesn't shows any record from that table. Like in SQL script result.
On this SQL script you can see what I need:
(SELECT
Products.id,
(null) AS product_titles_id,
(Products.title) AS title_,
Products.product_groups_id,
Products.product_types_id,
Products.code,
Products.title,
Products.ean,
Products.ncm,
Products.obs,
Products.minimum,
Products.maximum,
ProductTypes.id,
ProductTypes.code,
ProductTypes.title,
ProductTypes.calc_cost,
ProductGroups.id,
ProductGroups.code,
ProductGroups.title
FROM `products` Products
LEFT JOIN `product_groups` ProductGroups ON Products.product_groups_id = ProductGroups.id
LEFT JOIN `product_types` ProductTypes ON Products.product_types_id = ProductTypes.id)
UNION
(SELECT
Products.id,
ProductTitles.id AS product_titles_id,
(ProductTitles.title) AS title_,
Products.product_groups_id,
Products.product_types_id,
-- ProductTitles.products_id,
ProductTitles.code,
ProductTitles.title,
Products.ean,
Products.ncm,
ProductTitles.obs,
Products.minimum,
Products.maximum,
ProductTypes.id,
ProductTypes.code,
ProductTypes.title,
ProductTypes.calc_cost,
ProductGroups.id,
ProductGroups.code,
ProductGroups.title
FROM `product_titles` ProductTitles
LEFT JOIN `products` Products ON products.id = ProductTitles.products_id
LEFT JOIN `product_groups` ProductGroups ON Products.product_groups_id = ProductGroups.id
LEFT JOIN `product_types` ProductTypes ON Products.product_types_id = ProductTypes.id)
ORDER BY title_
This a result from that SQL script:
id product_titles_id title_
0080 NULL SUPORTE DE PRESSAO
7545 NULL BA RET.
3177 NULL CORT RDX
3177 17 EMEN RDX (secondary description)
3177 18 PART RDX (secondary description)
6623 NULL LATAO CALCO CALIB.
8079 NULL TRANSM DE PRESSAO
4242 NULL GY 6/6 P.U CORAL
This is what I did on CakePHP ORM script:
//Main table of products
$qry_products = $this->Products->findByParametersId('85')
->select(['Products.id', 'product_titles_id' => 'null',
'title_' => 'Products.title',
'Products.product_groups_id',
'Products.product_types_id',
'Products.code', 'Products.title',
'Products.ean', 'Products.ncm',
'Products.obs', 'Products.minimum',
'Products.maximum', 'ProductTypes.id',
'ProductTypes.code', 'ProductTypes.title',
'ProductTypes.calc_cost', 'ProductGroups.id',
'ProductGroups.code', 'ProductGroups.title',
'ProductTitles' => '0'
])
->where($where)
->join([
'ProductGroups' => ['table' => 'product_groups',
'type' => 'LEFT',
'conditions' => 'Products.product_groups_id = ProductGroups.id'
],
'ProductTypes' => ['table' => 'product_types',
'type' => 'LEFT',
'conditions' => 'Products.product_types_id = ProductTypes.id'
]
])
->limit(20);
//Secondary table of products' descriptions:
$qry_productTitles = $this->ProductTitles->findByParametersId('85')
->select(['Products.id',
'product_titles_id' => 'ProductTitles.id',
'title_' => 'ProductTitles.title',
'product_groups_id' => 'Products.product_groups_id',
'product_types_id' => 'Products.product_types_id',
'ProductTitles.code', 'ProductTitles.title',
'ean' => 'Products.ean',
'ncm' => 'Products.ncm',
'obs' => 'Products.obs',
'minimum' => 'Products.minimum',
'maximum' => 'Products.maximum',
'ProductTypes.id', 'ProductTypes.code',
'ProductTypes.title',
'ProductTypes.calc_cost',
'ProductGroups.id', 'ProductGroups.code',
'ProductGroups.title',
'ProductTitles' => '1'
])
->join([
'Products' => ['table' => 'products',
'type' => 'LEFT',
'conditions' => 'ProductTitles.products_id = Products.id'
],
'ProductGroups' => ['table' => 'product_groups',
'type' => 'LEFT',
'conditions' => 'Products.product_groups_id = ProductGroups.id'
],
'ProductTypes' => ['table' => 'product_types',
'type' => 'LEFT',
'conditions' => 'Products.product_types_id = ProductTypes.id'
]
]);
//Union these two tables and put some where conditions and ordanation:
$products = $qry_products->union($qry_productTitles)
->where($where)
->epilog('ORDER BY title_');
//->order(['title_ ASC']);
This is a SQL result generated from CakePHP ORM:
'sql' => '
(SELECT Products.id AS `Products__id`,
null AS `product_titles_id`, Products.title AS `title_`,
Products.product_groups_id AS `Products__product_groups_id`,
Products.product_types_id AS `Products__product_types_id`,
Products.code AS `Products__code`,
Products.title AS `Products__title`,
Products.ean AS `Products__ean`,
Products.ncm AS `Products__ncm`,
Products.obs AS `Products__obs`,
Products.minimum AS `Products__minimum`,
Products.maximum AS `Products__maximum`,
ProductTypes.id AS `ProductTypes__id`,
ProductTypes.code AS `ProductTypes__code`,
ProductTypes.title AS `ProductTypes__title`,
ProductTypes.calc_cost AS `ProductTypes__calc_cost`,
ProductGroups.id AS `ProductGroups__id`,
ProductGroups.code AS `ProductGroups__code`,
ProductGroups.title AS `ProductGroups__title`,
0 AS `ProductTitles`
FROM products Products
LEFT JOIN product_groups ProductGroups ON Products.product_groups_id = ProductGroups.id
LEFT JOIN product_types ProductTypes ON Products.product_types_id = ProductTypes.id
ORDER BY title_ LIMIT 20)
UNION
(SELECT Products.id AS `Products__id`,
ProductTitles.id AS `product_titles_id`,
ProductTitles.title AS `title_`,
Products.product_groups_id AS `product_groups_id`,
Products.product_types_id AS `product_types_id`,
ProductTitles.code AS `ProductTitles__code`,
ProductTitles.title AS `ProductTitles__title`,
Products.ean AS `ean`, Products.ncm AS `ncm`,
Products.obs AS `obs`, Products.minimum AS `minimum`,
Products.maximum AS `maximum`,
ProductTypes.id AS `ProductTypes__id`,
ProductTypes.code AS `ProductTypes__code`,
ProductTypes.title AS `ProductTypes__title`,
ProductTypes.calc_cost AS `ProductTypes__calc_cost`,
ProductGroups.id AS `ProductGroups__id`,
ProductGroups.code AS `ProductGroups__code`,
ProductGroups.title AS `ProductGroups__title`,
1 AS `ProductTitles`
FROM product_titles ProductTitles
LEFT JOIN products Products ON ProductTitles.products_id = Products.id
LEFT JOIN product_groups ProductGroups ON Products.product_groups_id = ProductGroups.id
LEFT JOIN product_types ProductTypes ON Products.product_types_id = ProductTypes.id)
ORDER BY title_',
The problem was solved using the code below:
$products_union = "((SELECT
Products.id AS `id`,
Products.product_groups_id,
Products.product_types_id,
Products.code AS `code`,
Products.title AS `title`,
Products.ean AS `ean`,
Products.ncm AS `ncm`,
Products.obs AS `obs`,
Products.minimum AS `minimum`,
Products.maximum AS `maximum`,
ProductTypes.id AS `ProductTypes__id`,
ProductTypes.code AS `ProductTypes__code`,
ProductTypes.title AS `ProductTypes__title`,
ProductTypes.calc_cost AS `ProductTypes__calc_cost`,
ProductGroups.id AS `ProductGroups__id`,
ProductGroups.code AS `ProductGroups__code`,
ProductGroups.title AS `ProductGroups__title`
FROM `products` Products
LEFT JOIN `product_groups` ProductGroups
ON Products.product_groups_id = ProductGroups.id
LEFT JOIN `product_types` ProductTypes
ON Products.product_types_id = ProductTypes.id)
UNION ALL
(SELECT
Products.id AS `id`,
Products.product_groups_id,
Products.product_types_id,
ProductTitles.code AS `code`,
ProductTitles.title AS `title`,
Products.ean AS `ean`,
Products.ncm AS `ncm`,
ProductTitles.obs AS `obs`,
Products.minimum AS `minimum`,
Products.maximum AS `maximum`,
ProductTypes.id AS `ProductTypes__id`,
ProductTypes.code AS `ProductTypes__code`,
ProductTypes.title AS `ProductTypes__title`,
ProductTypes.calc_cost AS `ProductTypes__calc_cost`,
ProductGroups.id AS `ProductGroups__id`,
ProductGroups.code AS `ProductGroups__code`,
ProductGroups.title AS `ProductGroups__title`
FROM `product_titles` ProductTitles
LEFT JOIN `products` Products
ON Products.id = ProductTitles.products_id
LEFT JOIN `product_groups` ProductGroups
ON Products.product_groups_id = ProductGroups.id
LEFT JOIN `product_types` ProductTypes
ON Products.product_types_id = ProductTypes.id
))";
$products = $this->Products->find('all')
->from([$this->Products->alias() => $products_union])
->contain(['ProductTypes', 'ProductGroups'])
->where($where)
->order(['Products.title']);
Now, the 'LIMIT' and 'OFFSET' are in the right place. I still using the 3.4 CakePHP version.

Laravel DB Query Builder "whereNotIn" to another table

How i can use query builder to condition whereNotIn to an other table without using DB::raw();
$query ="select * from project
where prj_usr_id= $user->id
and now()<prj_expiry
and prj_id not in(select bd_prj_id from bid where bd_status=1)
and prj_status='open'
order by prj_updated_date desc;
I solved by this.
$results = DB::table('project')
->where('prj_usr_id', '=' , 1 )
->where('prj_status', '=' , 'open' )
->where('prj_expiry', '<' , Carbon::Now() )
->whereNotIn ('prj_id',function ($query)
{
$query->select(DB::raw(1))
->from('bid')
->where('bd_status', '=' , '1' )
->get(['bd_prj_id']);
})
->orderBy('prj_updated_date', 'desc')
->paginate(5);
return $results;

Zend_Db join query

I am trying to apply join query on 3 tables in Zend_Db.My query is as follow:
$id_array = array("1","2");
$query = $this->select();
$query->from(array('b' => 'brands'), array('b.brand_id','b.brand_name'))->where('b.brand_id NOT in (?)', $id_array)->order('RAND()')->limit(5);
$query->join(array('p' => 'product'), 'b.brand_id = p.brand_id', array('p.product_id', 'p.product_price'));
$query->join(array('pimg' => 'product_img_map'), 'p.product_id = pimg.product_id', array('pimg.img_location'));
$query->setIntegrityCheck(false);
$resultRows = $this->fetchAll($query);
return $resultRows;
In that one brand may have more than one product,but in query I am applying limit to brand table i.e 5. By default it is applied to product also because if one brand having 5 products it is only giving information of one brand.Is there any suggestion on this.
Thanks.
You could use a sub-query to obtain the desired results:
$id_array = array("1","2");
$subQuery = $this->select()
->from(array('b' => 'brands'), array('b.brand_id','b.brand_name'))
->where('b.brand_id NOT in (?)', $id_array)
->order('RAND()')
->limit(5);
$query = $this->select()
->from(array('b' => $subQuery), array('*'))
->join(array('p' => 'product'), 'b.brand_id = p.brand_id', array('p.product_id', 'p.product_price'))
->join(array('pimg' => 'product_img_map'), 'p.product_id = pimg.product_id', array('pimg.img_location'));

How to do this SQL in LINQ

Morning i would like to know how to do this bit of (MS) SQL in LINQ...
SELECT CONVERT(CHAR(10),orderDate,110) AS OrderDate,
SUM(1) AS TotalOrders
FROM Orders
WHERE OrderDate>getdate()-30
GROUP BY CONVERT(CHAR(10),orderDate,110)
ORDER BY OrderDate DESC
Many thanks in advance.
UPDATE
I ended using and edited version of the solutions provided below, thought i would share it...
using (DataDataContext dc = new DataDataContext())
{
var query = from o in dc.Orders
where o.orderDate > DateTime.Now.AddDays(-30)
let dt = o.orderDate
group o by new DateTime(dt.Year, dt.Month, dt.Day) into g
select new OrderCounts
{
OrderDate = String.Format("{0:d}", g.Key.Date),
TotalOrders = g.Count()
};
query.GroupBy(o => o.OrderDate);
query.OrderBy(o => o.OrderDate);
return query.ToList();
}
var result =(from s in Orders
where s.OrderDate > DateTime.Now.AddDays(-30)
group s by new { date = s.OrderDate.ToString() } into g
// or use ((DateTime)s.OrderDate).ToShortDateString() instead of s.OrderDate.ToString()
select new
{
OrderDate = g.Key.date,
TotalOrders = g.Count()
}).OrderByDescending(x=>x.OrderDate);;
The query will be
var query = from o in res
where o.OrderDate > DateTime.Now.AddDays(-30)
orderby o.OrderDate descending
group o by o.OrderDate into g
select new
{
OrderDate = g.Key.Date.ToString("MM/dd/yyyy")
,
TotalOrders = g.Sum(i=> 1)
};
OR by Lambda expression
var query= res
.Where(i => i.OrderDate > DateTime.Now.AddDays(-30))
.OrderByDescending(o => o.OrderDate)
.GroupBy(g => g.OrderDate)
.Select(s => new { OrderDate = s.Key.Date.ToString("MM/dd/yyyy"), TotalOrders = s.Sum(i => 1) });

Resources