Laravel中多态关联是如何实现的?
- 内容介绍
- 文章标签
- 相关推荐
本文共计726个文字,预计阅读时间需要3分钟。
该代码段展示了如何使用Laravel框架中的Eloquent ORM进行模型实例化和关联查询。以下是简化后的内容:
- 不是数据库特性,是 Eloquent 的映射机制
-
commentable_type必须是类的 FQCN(带命名空间),不能简写为Post或小写post - 如果类名变更(比如从
App\Post改成App\Models\Post),已有数据里的commentable_type值不会自动更新,查不到关联对象
必须用 morphs() 迁移,别手写字段
手动写 unsignedBigInteger('commentable_id') 和 string('commentable_type') 看似一样,但会漏掉关键细节:
-
$table->morphs('commentable')自动加索引:commentable_id+commentable_type联合索引,否则whereHasMorph类查询极慢 - 字段类型默认对齐:Laravel 10+ 中
morphs()生成的commentable_id是BIGINT,若你手动写成INTEGER,而主键是BIGINT(如使用id()),关联查询会静默失败 - 字段名固定不可改:不能写成
target_id/target_type,Eloquent 只认{name}_id和{name}_type格式
morphTo() 和 morphMany() 的参数不能省
看似可以只写 $this->morphTo(),但实际容易出错:
-
Comment::commentable()默认找commentable_id和commentable_type字段;如果表里是loggable_id/loggable_type,必须显式传参:$this->morphTo('loggable') -
Post::comments()默认反查commentable_id字段;如果Comment表里字段叫subject_id,就得写:$this->morphMany(Comment::class, 'subject') - 所有参数必须两端一致:
morphTo('xxx')和morphMany(…, 'xxx')的字符串必须完全相同,大小写敏感
预加载和条件查询要换写法,不能套用普通关联
想查「所有评论,并附带所属文章或视频」,不能写 Comment::with('commentable') 就完事:
-
with('commentable')可以工作,但底层是 N+1 查询;更高效的是with(['commentable' => fn ($q) => $q->select('id', 'title')])控制字段 - 想筛选「只属于文章的评论」,不能用
where('commentable_type', 'App\Models\Post')—— 容易拼错类名,且无法利用索引;应改用whereHasMorph('commentable', Post::class) -
whereHasMorph支持数组:whereHasMorph('commentable', [Post::class, Video::class]),但不支持通配或模糊匹配
commentable_type 字段一旦存错(比如大小写错误、命名空间缺失、类已删除),关联就彻底失效,而且没有任何报错提示——它只是安静地返回 null。本文共计726个文字,预计阅读时间需要3分钟。
该代码段展示了如何使用Laravel框架中的Eloquent ORM进行模型实例化和关联查询。以下是简化后的内容:
- 不是数据库特性,是 Eloquent 的映射机制
-
commentable_type必须是类的 FQCN(带命名空间),不能简写为Post或小写post - 如果类名变更(比如从
App\Post改成App\Models\Post),已有数据里的commentable_type值不会自动更新,查不到关联对象
必须用 morphs() 迁移,别手写字段
手动写 unsignedBigInteger('commentable_id') 和 string('commentable_type') 看似一样,但会漏掉关键细节:
-
$table->morphs('commentable')自动加索引:commentable_id+commentable_type联合索引,否则whereHasMorph类查询极慢 - 字段类型默认对齐:Laravel 10+ 中
morphs()生成的commentable_id是BIGINT,若你手动写成INTEGER,而主键是BIGINT(如使用id()),关联查询会静默失败 - 字段名固定不可改:不能写成
target_id/target_type,Eloquent 只认{name}_id和{name}_type格式
morphTo() 和 morphMany() 的参数不能省
看似可以只写 $this->morphTo(),但实际容易出错:
-
Comment::commentable()默认找commentable_id和commentable_type字段;如果表里是loggable_id/loggable_type,必须显式传参:$this->morphTo('loggable') -
Post::comments()默认反查commentable_id字段;如果Comment表里字段叫subject_id,就得写:$this->morphMany(Comment::class, 'subject') - 所有参数必须两端一致:
morphTo('xxx')和morphMany(…, 'xxx')的字符串必须完全相同,大小写敏感
预加载和条件查询要换写法,不能套用普通关联
想查「所有评论,并附带所属文章或视频」,不能写 Comment::with('commentable') 就完事:
-
with('commentable')可以工作,但底层是 N+1 查询;更高效的是with(['commentable' => fn ($q) => $q->select('id', 'title')])控制字段 - 想筛选「只属于文章的评论」,不能用
where('commentable_type', 'App\Models\Post')—— 容易拼错类名,且无法利用索引;应改用whereHasMorph('commentable', Post::class) -
whereHasMorph支持数组:whereHasMorph('commentable', [Post::class, Video::class]),但不支持通配或模糊匹配
commentable_type 字段一旦存错(比如大小写错误、命名空间缺失、类已删除),关联就彻底失效,而且没有任何报错提示——它只是安静地返回 null。
