hcphoon
hcphoon

Reputation: 548

Conditional relationship within model

I have a model containing a unit_id column and a type column, 2 rows can have the same unit_id but different types. I have a method in the model

public function unit()
{
  if ($this->type == 'controller') {
    return $this->belongsTo('App\Models\FMS\Controller', 'unit_id', 'id');
  } else {
    return $this->belongsTo('App\Models\FMS\Unit', 'unit_id', 'id');
  }
}

that is meant to conditionally return a relationship depending on if the type column equals controller or not, although the check does not work and it just returns the second relationship, even if the type is controller

I understand it is not possible to access $this within a model so is there any way else I could get around this?

Upvotes: 1

Views: 980

Answers (2)

Rwd
Rwd

Reputation: 35170

$this will be available in your relationship methods, however, if you're eager loading the unit relationship (not lazy eager loading) type will be null. This is because the type attribute won't have been set when the relationship query is built.

The code you have should work if you loading the relationship after the initial class has been loaded.

If you're using load() on an Eloquent Collection then I believe it will use whichever type is on the first Model in the collection.


Solution

What you have lends itself pretty will to Polymorphic relationship.

Change your unit() method to be:

public function unit()
{
    return $this->morphTo('unit', 'type', 'unit_id');
}

The default for Polymorphic relationships with Laravel is to have the fully qualified namespace of the class as the type but since the type in this case is just going to be either controller or unit you'll need to tell Laravel how to map those words to the relevant classes. To do this you can use Relation::morphMap().
In the boot() method of your AppServiceProvider (to be fair, it can be any service provider) add the following:

Relation::morphMap([
    'unit'       => 'App\Models\FMS\Unit',
    'controller' => 'App\Models\FMS\Controller',
]);

Upvotes: 1

Hafez Divandari
Hafez Divandari

Reputation: 9029

It's possible to use $this on the model class and your relationship may work somehow.

But I suggest you to use Polymorphic Relationships instead.

/**
 * Get the unit.
 */
public function unit()
{
    return $this->morphTo(__FUNCTION__, 'type', 'unit_id');
}

The type column is used by Eloquent to determine which "type" of parent model to return when accessing the unit relation, it would be either App\Models\FMS\Controller or App\Models\FMS\Unit.

To use your own custom types, you have to register the morphMap in the boot function of your AppServiceProvider:

use Illuminate\Database\Eloquent\Relations\Relation;

Relation::morphMap([
    'controller' => 'App\Models\FMS\Controller',
    'unit' => 'App\Models\FMS\Unit',
]);

Upvotes: 0

Related Questions