'Laravel Eloquent - Do something before returning models from a relationship

We have some circular dependencies between a couple of models and we would like to optimize that.

Here is a quick overview of the DB schema

Person : id, main_address_id
Address: id, addressable_type, addressable_id

Which translates into Eloquent models:

class Person {
  function addresses() {
      return $this->morphMany(Address::class, 'addressable', 'addressable_type', 'addressable_id');     
  }

  function main_address() {
      return $this->belongsTo(Address::class, 'main_address_id');     
  }
}

class Address {
  public function addressable(): MorphTo
  {
      return $this->morphTo('addressable', 'addressable_type', 'addressable_id');
  }

  public function isMainAddress(): bool {
      return $this->id === $this->addressable?->main_address_id;
  }
}

Everything is working pretty good but we noticed we could save quite a lot of DB queries when dealing with the following type of code:

$addresses = $person->addresses;

foreach ($addresses as $address) {
    $address->isMainAddress(); // Loads the addressable from DB to check the IDs
}

One easy fix on the above code is to add a call to setRelation:

$addresses = $person->addresses->each(fn($a) => $a->setRelation('addressable', $person));

That way, the addressable relationship from each address is not queried.

All is fine in a simple case like that, we want to avoid manually doing the setRelation in each place we need it.

Basically we are looking for a way to run a callback on the models returned by the relationship after each time it gets queries. Something like that basically:

  function addresses() {
      return $this->morphMany(Address::class, 'addressable', 'addressable_type', 'addressable_id')
          ->prepareModels(fn($a) => $a->setRelation('addressable', $this));     
  }

  function main_address() {
      return $this->belongsTo(Address::class, 'main_address_id')
          ->prepareModels(fn($a) => $a->setRelation('addressable', $this));     
  }

Kindly note that this is a pure Laravel question. I am not looking for advice regarding how the DB schema would be better another way.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source