Laravel中BelongsToMany关联附加Pivot后需fresh()的替代方案
优化Laravel BelongsToMany附加Pivot数据后的关联更新方案
当你通过attach()给BelongsToMany关联附加带Pivot字段的数据后,无需调用fresh()刷新整个模型(避免重复查询其他已加载关联),可以用以下几种更高效的方式更新内存中的关联集合:
方法1:手动将附加项添加到已加载的关联集合
既然你已经持有要附加的$productsCollection,可以直接给每个产品设置Pivot数据,再添加到$subscription->products集合中:
$productsToSync = $productsCollection->mapWithKeys(fn ($subscriptionProduct) => [ $subscriptionProduct->id => [ 'quantity' => $subscriptionProduct->quantity, 'amount' => $subscriptionProduct->amount, 'item_id' => $subscriptionProduct->item_id, ] ])->toArray(); $subscription->products()->attach($productsToSync); // 手动更新内存中的关联集合 $productsCollection->each(function ($product) use ($subscription) { // 创建Pivot实例并赋值 $pivot = new \App\Models\SubscriptionProduct(); $pivot->subscription_id = $subscription->id; $pivot->product_id = $product->id; $pivot->quantity = $product->quantity; $pivot->amount = $product->amount; $pivot->item_id = $product->item_id; // 将Pivot关联到产品模型 $product->setRelation('subscriptionProducts', $pivot); // 添加到已加载的集合中 $subscription->products->push($product); }); // 此时验证会通过,无需fresh() throw_if( $subscription->products->isEmpty(), new \Exception("Products not attached to subscription WTF!?!") );
方法2:重新加载单个关联(推荐)
使用load()方法仅重新加载products关联,其他已加载的关联会被保留,避免整个模型刷新的开销:
$productsToSync = $productsCollection->mapWithKeys(fn ($subscriptionProduct) => [ $subscriptionProduct->id => [ 'quantity' => $subscriptionProduct->quantity, 'amount' => $subscriptionProduct->amount, 'item_id' => $subscriptionProduct->item_id, ] ])->toArray(); $subscription->products()->attach($productsToSync); // 仅重新加载products关联,不影响其他已加载的关联 $subscription->load('products'); // 验证通过 throw_if( $subscription->products->isEmpty(), new \Exception("Products not attached to subscription WTF!?!") );
如果不确定products是否已经加载,可以用loadMissing('products'),它只会在关联未加载时才执行查询。
原理说明
attach()仅操作数据库,不会自动更新内存中已加载的关联集合;fresh()会重新查询整个模型及所有关联,导致额外开销;- 上述两种方法要么直接操作内存集合,要么仅重新加载目标关联,能有效减少不必要的数据库查询和实例化操作。
内容的提问来源于stack exchange,提问作者Tyler Wall
相关产品推荐
相关产品推荐

