mercredi 17 mars 2021

Laravel Strategy-like Design Pattern

What would be the best way to achieve this in Laravel? I'm mentioning Laravel specifically because I'm still rather new to Laravel, I'm trying to implement it therein and I'm sure there's an aspect of Laravel that I'm not familiar with yet that will solve this elegantly.

Let me clarify with a simple example:

The original design

Suppose you have a car model:

class Car extends Model
{
    protected $fillable = [
        'manufacturer',
        'model',
        'engine', //json field emulating a table
    ]
}

The non-normalized 'engine' string field contains json. The json will always have a 'type' attribute but from there it goes in all directions. Examples (using my incomplete knowledge of car engines):

  • { "type": "petrol", "attributes":{"base_engine_type":"V-8", "size":"5.7L"...} }
  • { "type": "electric", "attributes":{"battery":"Lithium-Ion", "charge_period_hours":"20"...} }
  • { "type": "steam", "attributes":{"pressure_bar":"300", "spontanious_steampunk_world_transport_percent":"42"...} } and so on.

Use case

The Car model will have a Vue page with a dropdown list of the various engine types. On selection, a component specific to the engine type and it's respective attributes will be presented.

My solutions

  1. Keep it as is

I use an Engine JsonResource to emulate a model for communication between the controller and the page.

  1. A single Engine model with dynamically added attributes and then inject it into the Car model. I've only speculated on this, the attributes added in the constructor doesn't reflect. But the gist of it is:
class Engine extends Model
{
    public function __construct(array $attributes=[])
    {
        parent::__construct($attributes);

        foreach ($this->attributes()->get() as $attr) {
            $name = $attr->name;

            $this->append($name)->$name = $attr->value;
            $this->fillable[] = $name;
        }
    }

    public function attributes() {
        return $this->hasMany(EngineAttribute::class);
    }
}

class EngineAttribute extends Model
{
    protected $fillable = [
        'name',
        'value',
    ];
}

The wood and the trees

The first solution works the best so far but violates 1NF and isn't properly scalable. The second solution introduces horrible complexity further on in the app and I'd rather avoid it.

Suggestions, alternate solutions, suggested revisions to my question or general illumination will be greatly appreciated.

Thanks.

(edit: fixed the json examples)

Aucun commentaire:

Enregistrer un commentaire