Search code examples
phplaraveleloquentmodel

Laravel fresh() needed after attaching to BelongsToMany with Pivot


Following Laravel docs (and Laravel attach pivot to table with multiple values) I'm unable to see the attached values without calling fresh() on the top-level model. Is there a better way to attach these models?

I don't want to refresh the entire model because other relationships are already loaded. Doing so would cause re-querying, instantiating, and overhead.

Attaching

$productsToSync = $productsCollection->mapWithKeys(fn ($subscriptionProduct) => [
    $subscriptionProduct->id => [
        'quantity' => $subscriptionProduct->quantity,
        'amount' => $subscriptionProduct->amount,
        'item_id' => $subscriptionProduct->item_id,
    ]
])->toArray();

$subscription->products()->attach($productsToSync);

// This exception passes and is NOT thrown --> SubscriptionProducts are in the DB
throw_unless(
  SubscriptionProducts::whereSubscriptionId($subscription->id)->count() > 0,
  new \Exception("No SubscriptionProducts in DB?!)
);

// required NOT to throw ... but it refreshes and subsequently requires re-querying 
// any already loaded relationships
// $subscription = $subscription->fresh();

throw_if(
  $subscription->products->isEmpty(), 
  new \Exception("Products not attached to subscription WTF!?!")
);

Subscription Class

class Subscription extends Model {
    public function products(): BelongsToMany
    {
        return $this->belongsToMany(Products::class, 'subscription_products', 'subscription_id', 'product_id')
            ->using(SubscriptionProduct::class)
            ->as('subscriptionProducts')
            ->withPivot('id', 'item_id', 'quantity', 'amount');
    }
}

Solution

  • You can reload a specific relationship with load instead of refreshing the entire model.

    $subscription->products()->attach($productsToSync);
    
    // this will mutate $subscription
    // no need to do $subscription = $subscription->...
    $subscription->load('products'); 
    
    throw_if(
      $subscription->products->isEmpty(), 
      new \Exception("Products not attached to subscription WTF!?!")
    );