CakePHP 3无法按深度关联表排序问题咨询
我之前也碰到过一模一样的问题!CakePHP对深层的hasOne/belongsTo关联默认会用子查询来拉取数据,这就导致你要排序的字段根本不在主查询的结果集里,自然没法排序。针对你说的Users.Articles.Posts关联链,给你几个可行的解决方案:
方法1:手动显式JOIN所有关联表
直接用QueryBuilder的join()方法把所有需要的表都JOIN到主查询里,这样排序字段就会出现在主查询的上下文里了:
// 在UsersTable中执行查询 $query = $this->Users->find() // 按需选择需要的字段 ->select(['Users.id', 'Users.username', 'Articles.title', 'Posts.id']) ->join([ 'Articles' => [ 'table' => 'articles', 'type' => 'INNER', 'conditions' => 'Articles.user_id = Users.id' ], 'Posts' => [ 'table' => 'posts', 'type' => 'INNER', 'conditions' => 'Posts.article_id = Articles.id' ] ]) ->order(['Posts.id' => 'ASC']);
这种方式最直接,完全掌控关联的JOIN逻辑,适合复杂排序场景。
方法2:用innerJoinWith()强制JOIN深层关联
CakePHP提供了innerJoinWith()方法,可以让深层关联以JOIN的方式加入主查询,而不是默认的子查询:
$query = $this->Users->find() ->contain(['Articles', 'Articles.Posts']) ->innerJoinWith('Articles.Posts') ->order(['Posts.id' => 'ASC']);
innerJoinWith()会把Posts表以INNER JOIN的方式整合到主查询中,这样Posts的字段就能用于排序了。如果需要左连接,可以用leftJoinWith()替代。
方法3:修改关联的默认获取策略
你也可以在关联定义的时候,直接把深层关联的获取策略改成JOIN,这样每次查询这个关联都会用JOIN而不是子查询:
// 在ArticlesTable的initialize方法中定义Posts关联 $this->hasOne('Posts', [ 'foreignKey' => 'article_id', 'strategy' => \Cake\ORM\Association::STRATEGY_JOIN, ]);
之后当你查询Users并包含Articles.Posts时,Posts会自动以JOIN方式加入主查询,直接用Posts.id排序就生效了。
为什么原来的方式失效?
简单来说:CakePHP对直接关联(比如Users->Articles)默认采用JOIN方式,关联表的字段会出现在主查询的FROM和SELECT里,所以可以排序;但对于深层关联(Users->Articles->Posts),默认用子查询(SELECT子查询)来获取数据,这部分字段不属于主查询的结果集,自然没法用它来排序主查询的结果。
内容的提问来源于stack exchange,提问作者lorigio

