Laravel如何通过类似hasManyThrough的方式从Account直接获取关联分类及其子分类
代码笔误修正
你给出的模型代码存在两处需要修正的问题,否则关联会无法正常运行:
- Post模型的
positions()方法当前关联的是Category::class,但你定义的分类模型类名为Position,需要统一类名 - Position模型的
post()方法里Post ::class多了空格,需修改为Post::class - 中间表
post_categories的外键默认按命名规则应为post_id和position_id,如果实际命名不同需要对应调整后续代码的字段名
方案1:直接定义调用方法(最常用)
在Account模型中新增如下方法,即可直接获取该账号下所有Post使用过的分类及其所有子分类:
public function getAllUsedPositionsWithChildren() { // 先查询该账号所有Post关联的分类ID,去重 $usedPositionIds = $this->posts() ->join('post_categories', 'posts.id', '=', 'post_categories.post_id') ->distinct() ->pluck('post_categories.position_id'); // 利用嵌套集扩展的查询方法,获取上述分类及所有子分类,结果自动去重 return Position::whereIn('id', $usedPositionIds) ->orWhere(function ($query) use ($usedPositionIds) { foreach ($usedPositionIds as $positionId) { $query->orWhereDescendantOf($positionId); } }) ->get() ->unique('id'); }
调用方式:
// 拿到账号实例后直接调用即可 $positions = $account->getAllUsedPositionsWithChildren(); // 如果需要整理为树形结构,调用toTree()方法即可 $positionTree = $positions->toTree();
方案2:定义类hasManyThrough关联(支持预加载)
如果你需要支持关联预加载,可先在Account模型中定义直接关联的分类关联:
public function positions() { return $this->hasManyThrough( Position::class, Post::class, 'account_id', // Post表关联Account的外键 'id', // Position表主键 'id', // Account表主键 'id' // Post表主键 )->join('post_categories', 'posts.id', '=', 'post_categories.post_id') ->whereColumn('post_categories.position_id', 'positions.id') ->distinct(); }
需要加载子分类时,用预加载嵌套集自带的descendants关联即可:
// 预加载账号的所有关联分类及对应子分类 $account = Account::with(['positions.descendants'])->find($accountId);
性能优化建议
- 给中间表
post_categories的post_id和position_id字段添加联合索引 - 如果分类数据量极大,可先查询所有用到的分类根节点,再一次性加载所有后代节点减少查询次数
内容的提问来源于stack exchange,提问作者mohammad hossein
相关产品推荐
相关产品推荐

