必须用 wheremorphedto 或手动约束 morph_type + morph_id 过滤多态关联,仅 where morph_type 会导致数据错乱;wheremorphedto 专为反向查询设计,自动双重匹配且要求传入类名或实例;动态类型需校验白名单并组合两个条件;with + wherehas 中闭包必须显式指定 morph_type 字段。

多态关联不能直接用 where 过滤类型字段(如 type 或 morph_type),必须通过 whereMorphedTo 或手动约束 morph_type + morph_id 才能安全、准确地筛选。
whereMorphedTo 是唯一推荐的类型过滤方式
它专为多态关联设计,自动处理 morph_type 和 morph_id 的双重匹配,避免手写 SQL 时漏掉任一条件导致数据错乱或越权暴露。
- 只适用于「反向多态查询」:即从多态目标模型(如
Comment)查出属于某类主模型(如Post或Video)的记录 - 语法简洁:
Comment::whereMorphedTo('commentable', Post::class)->get() - 底层生成的 SQL 会同时检查
commentable_type = 'App\Models\Post'和commentable_id = ?,缺一不可 - 不支持传入字符串类名(如
'App\Models\Post'),必须传 class 实例或 FQCN —— 否则静默失败
手动 where + whereIn 组合容易漏掉 morph_id 约束
常见错误是只写 where('commentable_type', 'App\Models\Post'),却没限制 commentable_id 对应的真实主键范围,结果可能混入已被删除或 ID 冲突的脏数据。
- 若你确实需要动态类型名(比如来自请求参数),必须显式校验:
in_array($type, ['App\Models\Post', 'App\Models\Video']) - 然后组合两个条件:
->where('commentable_type', $type)->whereNotNull('commentable_id') - 注意:不能只用
whereNotNull,还得配合业务逻辑确保commentable_id指向有效记录(例如加whereIn('commentable_id', [...]))
with + whereHas 链式过滤多态关系时,闭包内不能省略 morph_type
当在父模型上 eager load 多态关联并想进一步过滤类型时(例如查所有带「图片评论」的 Posts),whereHas 闭包里必须显式指定 morph_type 字段值,否则 Eloquent 不知道该匹配哪个类型。
- 错误写法:
->whereHas('comments', fn ($q) => $q->where('body', 'like', '%pic%'))—— 它会扫描所有commentable_type的评论,性能差且语义不清 - 正确写法:
->whereHas('comments', fn ($q) => $q->where('commentable_type', Post::class)->where('body', 'like', '%pic%')) - 更稳妥的做法是用
whereMorphedTo替代字符串比较:$q->whereMorphedTo('commentable', Post::class)
真正容易被忽略的是:多态字段名(如 commentable_type)不是固定写死的,它取决于你在关系定义中传给 morphTo() 的第一个参数。一旦模型里写成 morphTo('target'),对应数据库字段就是 target_type 和 target_id —— 没有统一命名规则,必须翻代码确认。











