CakePHP3 实现每个分类关联查询限制3个产品
解决CakePHP中hasMany关联下每个分类限制关联产品数量的问题
我明白你遇到的问题了——在CakePHP里用contain给hasMany关联加limit(3),结果变成了全局只取3个产品,只有第一个分类能拿到数据,其他分类都空着。这是因为默认的关联查询会把所有关联产品拉出来后统一做limit,而不是按每个分类单独限制数量。
下面给你两种靠谱的解决方案,根据你的数据库版本和需求选择:
方案一:使用数据库窗口函数(推荐,适合MySQL 8.0+/PostgreSQL等支持窗口函数的数据库)
这种方法直接在数据库层面用窗口函数按分类分组,每个分组取前3个产品,性能最优,尤其数据量大的时候:
public function test() { $this->viewBuilder()->layout(false); $Menus = $this->loadModel('Categories'); // 通过窗口函数筛选每个分类下的前3个产品 $menu_top = $Menus->find() ->contain(['Subcats']) ->join([ 'table' => '( SELECT Products.*, ROW_NUMBER() OVER (PARTITION BY Products.category_id ORDER BY Products.created DESC) AS rn FROM products Products ) AS limited_products', 'conditions' => 'limited_products.category_id = Categories.id AND limited_products.rn <= 3', 'type' => 'LEFT' ]) ->enableAutoFields(true) ->group(['Categories.id']); // 把关联的产品整理到分类的products属性里(方便视图层调用) foreach ($menu_top as $category) { $category->products = iterator_to_array($category->limited_products); unset($category->limited_products); } $this->set('menu_top', $menu_top); }
这里的ROW_NUMBER() OVER (PARTITION BY Products.category_id ORDER BY Products.created DESC)会给每个分类下的产品按创建时间倒序编号,然后我们只取编号≤3的记录。记得把created换成你实际需要的排序字段(比如销量、优先级等)。
方案二:子查询筛选法(兼容旧版本数据库)
如果你的数据库不支持窗口函数,可以用子查询先找出每个分类下的前3个产品ID,再关联这些产品:
public function test() { $this->viewBuilder()->layout(false); $Menus = $this->loadModel('Categories'); // 子查询:获取每个分类下的前3个产品ID $productIdsQuery = $Menus->Products->find() ->select(['Products.id']) ->order(['Products.created' => 'DESC']) ->limit(3) ->where(function($exp) { return $exp->equalFields('Products.category_id', 'Categories.id'); }); // 主查询:关联筛选后的产品 $menu_top = $Menus->find() ->contain(['Subcats', 'Products']) ->where(['Products.id IN' => $productIdsQuery]); $this->set('menu_top', $menu_top); }
这个方法的原理是先为每个分类单独查询出3个产品的ID,再在主查询里只关联这些ID对应的产品,这样就能保证每个分类都能拿到3个产品。
两种方法都能解决你的问题,优先选窗口函数的方案,性能更优哦!
内容的提问来源于stack exchange,提问作者Satu Sultana
相关产品推荐
相关产品推荐

