Laravel Eloquent Polymorphic模型

I had a situation come up where I needed an Eloquent model to hydrate dynamically into a specific class that extends my "normal" eloquent model. Here's a generic layout loosely based on typical electronics:

Data Object:

['id', 'model_type', 'name', 'serial',...]
  • Model
    • Model Type A
    • Model Type B

If the data is all in a single table (MySQL), Laravel doesn't really have a way to pull that data directly as a polymorph. You can polymorph a relationship, but not a model directly.

Essentially the reasoning for this is to separate logic that may be concerned with one type, but not another. For example, Model Type A and Model Type B can both implement an interface which describes their capabilities, but the specific logic for A doesn't need to pollute B.

My solution for this is to override the newFromBuilder method on the model (Laravel 5.6). Like so:

App\Schemes\BaseScheme

abstract class BaseScheme extends Electronic
{
    // abstract methods to implement
    // ...
}

App\Schemes\ElectronicTypeA

class ElectronicTypeA extends BaseScheme
{
    // Electronic Type A logic
}

App\Schemes\ElectronicTypeB

class ElectronicTypeB extends BaseScheme
{
    // Electronic Type B logic
} 

App\Models\Electronic

use Illuminate\Database\Eloquent\Model;

class Electornic extends Model
{
    public function newFromBuilder($attributes = [], $connection = null)
    {
        if (!class_exists($attributes->model_type)) {
            throw new \Exception("Invalid Scheme ({$attributes->model_type})"); 
        }

        $class = $attributes->model_type;
        $model = new $class;

        if (!$model instanceof \App\Schemes\BaseScheme) {
            throw new \Exception("Scheme class is invalid ({$attributes->model_type})");
        }

        $model->exists = true;

        $model->setRawAttributes((array) $attributes, true);

        $model->setConnection($connection ?: $this->getConnectionName());

        $model->fireModelEvent('retrieved', false);

        return $model;
    }

Where \App\Schemes\BaseScheme is the abstract all the logic models extend. \App\Schemes\BaseScheme also extends the original Eloquent model.

The really nice thing about this is that it works on the returned collection too. So you can interact with the model just as if it were a normal model--but you're really interacting with the specific classes (typeA, typeB).