0

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');
    }
}
Tyler Wall
  • 3,747
  • 7
  • 37
  • 52
  • To follow naming convention fully, pivot model should be `SubscriptionProduct`. – Tpojka May 05 '23 at 23:24
  • @Tpojka The above produces `$subscription->products->subscriptionProducts` (a collection of `SubscriptionProduct` models) – Tyler Wall May 06 '23 at 19:16
  • Just saying that this `$this->belongsToMany(Products::clas, 'subscription_products', 'subscription_id', 'product_id')` should be `$this->belongsToMany(Product::class, 'subscription_product', 'subscription_id', 'product_id')`. Pay attention to singular and plural names and you'll have less headaches (and more important you'll use more default framework's code). Actually if you name it right, it's just `$this->belongsToMany(Product::class)`. – Tpojka May 06 '23 at 23:22

1 Answers1

1

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!?!")
);
IGP
  • 14,160
  • 4
  • 26
  • 43