发现 ORM 方法 MorphToMany 的一个 Bug

person 表
id integer
name string
address_rel 表
id integer
lsc integer (11 or 101) 此处在 MorphMap 定义映射关系
lid integer
address integer
address 表
id integer
country integer 国家 ID
province integer 省 ID
city integer 市区 ID
….. 省略

首先,在 AppServiceProvider 我定义了 morphMap:

Relation::morphMap([
    '11' => Person::class,
    '101' => Company::class,
]);

然后,在 Person 模型中,定义了获取所有地址的方法:

public function address()
{
    return $this->morphToMany(
        Address::class,  //地址表
        'lsc',  //多态关联表中的映射表关系
        'address_rel',  //多态关联表
        'lid', //映射表ID
        'address' //地址ID
    );
}

最后,在 Controller 层,来获取 Person 的所有地址:

$person = Person::find(1);
$person->address->toArray();

此时报错信息为:

"SQLSTATE[42S22]: Column not found: 1054 Unknown column 'lu_address_rel.lsc_type' in 'field list' (SQL: select `lu_address`.*, `lu_address_rel`.`lid` as `pivot_lid`, `lu_address_rel`.`address` as `pivot_address`, `lu_address_rel`.`lsc_type` as `pivot_lsc_type` from `lu_address` inner join `lu_address_rel` on `lu_address`.`id` = `lu_address_rel`.`address` where `lu_address_rel`.`lid` = 56 and `lu_address_rel`.`lsc_type` = 11)"

通过上面可以看到,我们传的 lsc 在 Laravel 中自动拼上了 _type,而 lid 是正确的,这时我们通过追查源码发现

发现 ORM 方法 MorphToMany 的一个 Bug
如上图所示,在我们传入 $foreignPivotKey 字段时,Laravel 做了相应的判断处理,如果传了这个字段(也就是 lid),则完全使用用户传入的值,否则将拼接为 $name . ‘_id’ ,这里的 $name 就是我们在 Model 中传入的第二个参数

那么我们接着来看 $name 参数是如何处理的

发现 ORM 方法 MorphToMany 的一个 Bug
追查到这里发现,Laravel 是直接将用户传入的 $name 参数拼上了 _type ,这就出现了上面的报错信息。

通过以上信息发现我们如果要用到 MorphToMany 方法,就必须要把表设计改为 Laravel 的规范,否则,目前的 Laravel 版本中用 MorphToMany 是完成不了需求的。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 4

我靠,那链接不就是我的么。。。 :grin:

4年前 评论
邢闯洋 (楼主) 4年前
黑将军

或许不能算 bug 吧,只是扩展性差,确实用这个得按照 laravel 标准来设计表,不方便,之前也打算用 MorphToMany,也是遇到相同的问题

4年前 评论
邢闯洋 (楼主) 4年前

发现 wx 群友一枚 :smile:

4年前 评论