vendredi 7 juin 2019

How to morph or cast an eloquent model to one of its child based on the value of a parent type relation

I have a room table with a foreign key room_type_id to a room_type table.

I have the corresponding models in Laravel as well as some child classes for the different rooms:

class RoomType extends Model
{
    //
}

class Room extends Model
{
    protected $with = ['roomType'];

    public function roomType()
    {
        return $this->belongsTo('RoomType', 'room_type_id');
    }
}

class Bedroom extends Room
{
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('type', function (Builder $builder) {
            $builder->whereHas('roomType', function ($query) {
                $query->where('code', 'like', 'BEDROOM');
            });
        });
    }
}

class Kitchen extends Room
{
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('type', function (Builder $builder) {
            $builder->whereHas('roomType', function ($query) {
                $query->where('code', 'like', 'KITCHEN');
            });
        });
    }
}

What I'm trying to do is when querying for a room, have the child (bedroom or kitchen) be returned based on the type.

What I'm doing right now:

$results = Room::where('...')->get();
foreach ($results as $key => $record) {
    switch ($record->roomType->code) {
        case 'BEDROOM':
            $results{$key} = (new Bedroom)
            ->newInstance([], true)
            ->setRawAttributes($record->getAttributes());
            break;
        case 'KITCHEN':
            $results{$key} = (new Kitchen)
            ->newInstance([], true)
            ->setRawAttributes($record->getAttributes());
            break;
    }
}
// returns a Collection of `Bedroom` and `Kitchen` models

I don't like that I need to create each and every model with setRawAttributes(), plus I would need to add all the eager loaded relations back in, plus I loose the original attributes should I need them, well, it just looks wrong the way I do it.

What I would like to achieve is for the same query to return a Collection of child models based on its roomType->code relation value:

$results = Room::where('...')->get();
// returns a Collection of `Room` (shocker!)
// i want a Collection of `Bedroom` and 'Kitchen`

I thought I could use polymorphism somehow and morphMap but I can't get it to work, I'm not in a typical polymorphic relationship.

Any help or hint would be warmly welcomed.



via Chebli Mohamed

Aucun commentaire:

Enregistrer un commentaire