fahaddsheikh's avatar

Structuring Product Attributes

So I'm building an eCommerce application that I am working on from scratch and I'm stuck with a problem with product attributes.

  • An attribute is a property of a product. Possible values are color and size
  • A product could have many attributes
  • An attribute can have many values like Red, Green Blue

The possible structure I've found to make sense is having two tables. Attributes and Attribute Values.

I'm struggling with relations. I can define that a product has many attributes and fetch through the relationship but how do I define relationship with attributevalues.

For ex: I can fetch color for product A but how do I fetch the attribute values only for the defined product.

0 likes
2 replies
Ola-Yusuf's avatar

Hello, I suggest you use this pattern

Create Product table with the following column (id, name, ..... )

Create ProductAttribute table with the following column (id, productId, color, size,........ )

Note you can make columns in ProductAttribute to be nullable but never unique. This is to allow a single product to have multiple attributes

martinbean's avatar
Level 80

@fahaddsheikh Hey! So I worked on quite a few e-commerce projects. How you structure attributes depends on if you want them to reusable or not.

If you want attributes to be reusable, them you could have an Attribute model and an AttributeValue model like you mention. You would then create essentially a three-way pivot table between your products, attributes, and attribute values:

Schema::create('channel_user', function (Blueprint $table) {
    $table->primary(['product_id', 'attribute_id']);
    $table->foreignId('product_id')->constrained()->cascadeOnDelete();
    $table->foreignId('attribute_id')->constrained()->cascadeOnDelete();
    $table->foreignId('attribute_value_id')->constrained()->cascadeOnDelete();
});

There’s a compound primary key ensuring that the product can only have an attribute once. The columns are then just foreign keys pointing to the other tables.

With this setup, you can then retrieve all the attributes for a product, and also the value using the pivot column:

class Product extends Model
{
    public function attributes()
    {
        return $this->belongsToMany(Attribute::class)->withPivot('attribute_value_id');
    }
}

Accessing attribute_value_id will yield the primary key of the value model only though. You may want to create a custom pivot model so you can access attributes on the related AttributeValue model as well. Then you could do something like:

foreach ($product->attributes as $attribute) {
    // $attribute->pivot would be pivot model

    $attributeName = $attribute->name; // i.e. Color
    $attributeValue = $attribute->value->name; // i.e. Red if AttributeValue model has name column
}

Please or to participate in this conversation.