izshreyansh's avatar

Eloquent morph toMany

I have a table setup like this for an ecommerce platform, So that i can save products with variation & sub variation data.

products 
    id
    name
    description
    sku

options
    option_key
    option_value
    product_id
    sku
    quantity

option_keys
    id
    name

option_values
    id
    value

Eloquent model is setup like this:

class Product extends Model
{
    public function options()
    {
        return $this->hasMany('App\Option');
    }
}
class Option extends Model
{
    public function product()
    {
        return $this->belongsTo('App\Product');
    }

    public function type()
    {
        return $this->hasOne('App\OptionKey','id','option_key');
    }

    public function value()
    {
        return $this->hasOne('App\OptionValue','id','option_value');
    }
}

Now every-time i want to access option keys & values i will have to write it verbosely like this:

$product = Product::with(['options'])->first();
foreach($app->options as $option):
    echo ($option->type->name . ': ' . $option->value->value);
endforeach;

Do you think there is a better way to acheive what i'm trying using morphTomany? I played around with it but i couldn't wrap my head around how this can be achieved. Or if this refactoring is optimal at all?

0 likes
3 replies
lostdreamer_nl's avatar

I used to do this pattern a lot, but since MySQL now has JSON fields, I find myself using those a lot more these days.

Everytime you get your product, you probably also want to show its options right? Create a json type field on the products table, called 'options'.

Put this in the Product model:

    protected $casts = ['options' => 'json'];

This will automatically save an array into a json string, and decode that json string back into an array when you fetch it from the DB.

$product = Product::find(1);
$product->options = [
    'color' => 'Blue',
    'size' => 'medium',
    'weight' => 'heavy',
];
$product->save();

$blueProducts = Product::where('options->color', 'Blue')->get();

dd($blueProducts );

A lot easier then keeping those 3 tables.

izshreyansh's avatar

@lostdreamer_nl Actually i plan to keep stock, price & location data based on variations, Also any variation can have child variation linked to it.

Like so: Phone size -> 16GB -> Color: Blue / Red Phone size -> 32GB -> Color: Blue / Red

Do you think i should take the JSON approach?

lostdreamer_nl's avatar

in that case, I'd think about it like this:

For some products, you want to have variants with perhaps even different prices (8GB = normal price, 16GB = +4 euro, 32GB = +20 euro) and different stock.

Those types, I would suggest to go with a separate model (key = hd-size, value = 16GB / 32GB, price = +4 / +20) These would be queried only on the product page itself where you could show a dropdown with the different variant's of the product.

But you might also have 'filters' in the list views of the products, where you want to filter all Blue phones with 32GB.

For that part I would use the json fields.

So the json field would only be extra parameters to set during search / filtering while the variants would be a sort of a sub selection for the product type.

Please or to participate in this conversation.