Laravel 8 多态关联如何单次查询正确加载对应类型关联模型
解决方案
步骤1:修正BookmarkItem模型关联定义
首先在BookmarkItem模型中新增统一的多态关联入口,替代拆分的posts/comments关联:
class BookmarkItem extends Model { // 新增统一多态关联 public function item() { return $this->morphTo('bookmark_item'); } // 原有posts、comments关联可以保留不影响旧逻辑 public function posts(): \Illuminate\Database\Eloquent\Relations\MorphToMany { return $this->morphedByMany(Post::class, 'bookmark_item'); } public function comments(): \Illuminate\Database\Eloquent\Relations\MorphToMany { return $this->morphedByMany(Comment::class, 'bookmark_item'); } }
步骤2:使用多态关联按需预加载实现单次查询
Laravel原生支持对morphTo类型关联的按类型预加载,查询代码如下:
use Illuminate\Database\Eloquent\Relations\MorphTo; $result = $bookmark->items() ->with([ 'item' => function (MorphTo $morphTo) { // 按关联类型加载对应模型 $morphTo->morphWith([ Post::class => [], // 可在数组中添加Post需要额外预加载的关联 Comment::class => [], // 可在数组中添加Comment需要额外预加载的关联 ]); } ]) // 此处可统一添加筛选、排序逻辑,例: // ->whereDate('created_at', '>=', '2024-01-01') // ->orderBy('created_at', 'desc') ->get() // 可选:调整返回结构,匹配原有的posts/comments字段格式 ->map(function ($bookmarkItem) { $itemData = $bookmarkItem->toArray(); // 按类型把关联数据放到对应键下,移除统一的item字段 if ($bookmarkItem->bookmark_item_type === \App\Models\Post::class) { $itemData['posts'] = $itemData['item']; } elseif ($bookmarkItem->bookmark_item_type === \App\Models\Comment::class) { $itemData['comments'] = $itemData['item']; } unset($itemData['item']); return $itemData; }) ->toArray();
方案说明
- 该方案仅执行4次查询:1次查询bookmark_items表、1次批量查询匹配的Post数据、1次批量查询匹配的Comment数据,性能与拆分两次查询的方案基本一致
- 所有筛选、排序逻辑只需要编写一次,不需要拆分两次查询实现
- 不会出现多余关联加载的问题,每个书签条目只会加载对应类型的关联数据
内容的提问来源于stack exchange,提问作者HashtagForgotName
相关产品推荐
相关产品推荐

