Eloquent复合键变通方案:更新正常但创建新行失败的技术问询
嘿,我之前也碰到过类似的情况,咱们来一步步理清楚问题出在哪,以及怎么解决:
首先回顾下你的场景:你给OrderItem模型定义了和StockMovement的hasMany关联,还加了where('order_id', '=', $this->order_id)的条件,查询现有记录、用increment更新都没问题,但调用关联的create方法新增行时,order_id总是不对或者没填上。
问题出在哪?
你加的那个where条件,只会限制这个关联能查询到哪些StockMovement记录,以及更新时的目标范围,但Laravel不会自动把这个where里的order_id作为新记录的属性填充进去。关联的create方法默认只会自动填充关联的外键(也就是这里的product_id),其他where条件里的字段它是不会自动处理的。
两种靠谱的解决办法
办法一:创建时手动带上order_id
最简单直接的方式,就是在调用create的时候,把当前OrderItem的order_id加到要保存的数组里:
$this->stockMovement()->create([ 'qty_picked' => $yourQuantity, 'order_id' => $this->order_id // 其他需要的字段按需添加 ]);
这种方式适合偶尔调用的场景,简单直接,不用改动原有关联定义。
办法二:封装关联逻辑,自动带上order_id(推荐)
如果需要频繁通过这个关联创建StockMovement,不如在模型里封装一下,省得每次都手动传order_id:
// 在OrderItem模型里 public function stockMovements() { return $this->hasMany(StockMovement::class, 'product_id', 'product_id') ->where('order_id', '=', $this->order_id); } // 新增一个专门的创建方法 public function createStockMovement(array $data) { // 自动把当前模型的order_id塞进数据里 $data['order_id'] = $this->order_id; return $this->stockMovements()->create($data); }
之后创建的时候,直接调用$this->createStockMovement(['qty_picked' => 5])就可以了,省心又不容易忘。
另外还有个小技巧:你也可以在关联定义里用withDefault预设order_id,但这个主要是针对空关联的默认值,创建时还是建议用上面的方法更稳妥。
补充说明
为啥查询和更新没问题?因为where条件会帮你筛选出属于当前order_id的StockMovement,所以查询能拿到正确的记录,更新也只会改这些记录;但创建是生成全新的记录,Laravel不会把查询条件自动转成新记录的属性,所以得咱们手动补上~
内容的提问来源于stack exchange,提问作者Adam Lambert

