Laravel Eloquent模型create时递归自动加载所有关联的原因
问题场景
项目模型关联规则如下:
- Customer模型与Order模型为一对多关联(Customer has many Orders)
- Customer模型与Detail模型为一对一关联(Customer has one Detail)
执行如下代码创建新订单:
$order = Order::create($data);返回的
$order对象自动递归加载了所有层级关联,数据结构如下:Order - order_id - product_id - purchase_date - customer_id - customer - customer_id - customer_name - order - order_id - product_id - purchase_date - customer_id - detail - detail_id - customer_id - address - phone运行环境为Laravel 5.5 + PHP 7.2。
触发原因
核心原因是模型全局预加载配置$with出现了循环引用,结合Laravel 5.5的模型加载逻辑触发了递归加载:
Order模型中配置了全局预加载属性protected $with = ['customer'];,所有返回Order模型实例的操作(包括create()新建、find()查询、get()列表查询),都会自动加载声明的customer关联。- 同时
Customer模型中又配置了protected $with = ['orders', 'detail'];,所有返回Customer模型实例的操作,都会自动加载关联的orders和detail数据。 - 两者形成加载死循环:Order实例触发加载Customer → 加载完成的Customer实例触发加载Order → 拿到的Order实例再次触发加载Customer,递归执行直到达到PHP嵌套限制终止,最终返回多层嵌套的关联结构。
如果没有配置$with属性,可排查是否在模型的boot方法、created/retrieved等模型事件中手动编写了递归调用load()加载关联的逻辑,这类写法也会触发相同问题。
修复方式
- 移除关联模型中互相引用的全局
$with配置,禁止在两个互相关联的模型中同时将对方加入全局预加载列表,需要关联数据时调用with()方法按需预加载即可。 - 若必须保留部分全局预加载规则,需打断循环引用:例如Order中保留全局加载Customer,Customer中就不要全局加载Order,仅保留全局加载Detail即可。
- 若为事件/
boot方法中手动写的加载逻辑,删除多余的递归load()调用即可。
内容的提问来源于stack exchange,提问作者Sagar Gautam
相关产品推荐
相关产品推荐

