3

I have one problem. I try to create multiple models for one table in Laravel 5.6. I have a table for example Car, class for him:

class Car extends Model {
   $fillable = ['type'];
   public function test(){ }
}

So, the field type is required. E.g. model car can have multiple types (2-10..). Depending on the type I should run difference code in test function. Yes, i can do multiple if conctructions, but it's not true. Based on this I would like to create more classes that will be extended from Car and will to match to each of the types. For example:

class ElectricCar extends Car { $type = 'electric'; } ...
class SolarCar extends Car { $type = 'solar'; } ...

If I will create this structure than I get a few problems.

For example record for id = 1 is Electric car. What will I get if call Car::find(1) Yes, I will get Car class, but I need ElectricCar class. I saw https://github.com/Nanigans/single-table-inheritance, but it's not a good solution. it violates one of the SOLID rules, where we haven't to change parent class when creating child class. So what is a solution we can give me based on SOLID rules?

1 Answers1

3

You can achieve what you have outlined by using scopes.

Create a new file, something like app/Scopes/SolarScope.php and inside, put the following code:

namespace App\Scopes;

use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class SolarScope implements Scope
{
    /**
     * Apply the scope to a given Eloquent query builder.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        $builder->where('type', 'Solar');
    }
}

Then, inside of your new model, eg. SolarCar.php you need to specify the table to use for that model:

protected $table = 'cars';

and then you need to instruct that Model to use the scope via the boot function.

use App\Scopes\JobScope;

protected static function boot()
{
    parent::boot();
    static::addGlobalScope(new SolarScope);
    // optionally instruct saving default type:
    // static::saving(function ($car) {
    //   $car->type = 'Solar';
    // });
}

This will make sure whenever a SolarCar is referenced, the type is already scoped in both in retrieval and optionally in saving.

Grant
  • 5,709
  • 2
  • 38
  • 50
  • hi @Grant, thank you for your answer. It's a good idea, but I want a little different. In your variant, if I make Car::find(1) I will get **Car** instance and I can't to use SolarCar methods, or I will make `Car::where(...)->get()->transform(function(Car $car) { return $car->getSomething(); });` in this case i also cant to use Solar or Electric Car methods – Виктор Лащенко Dec 11 '19 at 08:59
  • 2
    Ah, if you want to utilise inheritance between parent & child models maybe look into a package solution, like this one: https://github.com/calebporzio/parental - just make sure you extend your car model with your new models, rather than extending Laravel's model as the package instructs. – Grant Dec 11 '19 at 09:29