https://tray2.se/posts/database-design
This is my goto article on database design
I am wondering what is the cleanest simplest approach to store Product Attributes & Variants.
for example : Let's assume I have different types of Products / T shirts - Electronics and these products have attributes and each attribute have multiple variants .. all differ from a product to another.
was thinking maybe I add [ attributes_1 / attribute_1_value | attribute_2 / attribute_2_value ] columns in my product table . or should I create different tables and use relationships
but in the first approach I will have to create multiple entries for the same product in the products table, which I really don't like much.
Examples :
Product : T shirt Attribute/s : Color | Size | Material Variants : Blue - Black - White | M - L - XL | Cotton - Leather
Product : TV Attribute/s : Color | Size Variants : Silver - Black | 21 inch - 55 inch
What is the best way to store such data
https://tray2.se/posts/database-design
This is my goto article on database design
@Sinnbeck this article is really awesome, very informative and complete... A must read.
Thanks a lot for sharing it.
This would typically be solved using a variants table - a Product has many Variant. You can then decide if the Variant instance should store the attributes (size/color etc) or reference attributes in other table(s).
Hello @tykus thanks for replying .. If I understood right, so I would have to write down the same product multiple times and just change variant_id in the products table ?? where variant is different colors or whatever ? and just in the variant table .. I would have attribute_id .. referencing id in attributes table .. am I right or did I miss something.
+--------------+--------+-------+
| id | product name | variant_id |
+--------------+--------+-------+
| 1 | White T shirt | 1 |
| 2 | Red T shirt | 2 |
| 3 | Blue T shirt | 3 |
| 4 | Yellow T shirt | 4 |
| 5 | Green T shirt | 5 |
+--------------+--------+-------+
Be sure to plan out that combinations can be sold out. So a black xl shirt is sold out but black medium isn't.
I would probably use two tables. One for the product_id and stock and another that is the variations that reference this table
@mahmoudmonem You don’t want to be trying to add a column per attribute, because that’s going to make your products table massive and also have lots of redundant data, i.e. electronics products with lots of columns for clothing attributes empty, clothing products with columns for electronic attributes empty, and so on.
“Variants” is also completely different to attributes. A variant is usually a particular SKU (stock-keeping unit). So for a particular t-shirt, a small will have a different SKU to the medium one, even though they’re the same shirt design, colour, etc. So in e-commerce projects, I tend to model them as such: I’ll have a top-level Product model, that has many Sku models. The product model contains the common data for a product like name, description. The Sku model then contains SKU-specific information like the SKU code itself, and price for that particular variant.
For attributes, what’s worked well for me in the past is to have an Attribute model that belongs to a Product. I can then create a pivot table between attributes and SKUs, with a column to hold the value. So, if my t-shirt product has an attribute named “size”, I can then specify what size each t-shirt SKU is:
+--------------+--------+-------+
| attribute_id | sku_id | value |
+--------------+--------+-------+
| 1 | 1 | S |
| 1 | 2 | M |
| 1 | 3 | L |
| 1 | 4 | XL |
| 1 | 5 | XXL |
+--------------+--------+-------+
So for a product, you can see what attributes it has that are customisable (size, colour, etc), and then each SKU for that product will have a pivot record defining a value for each of the product’s attributes.
Hello @martinbean, thanks for replying .. I think it's quite similar to tykus approach and please correct me if I'm wrong.. so I would have to write down the same product multiple times and just change sku_id in the products table ?? where sku is different colors or sizes or whatever ? and just in the sku table .. I would have attribute_id .. referencing id in attributes table ..
+--------------+--------+-------+
| id | product name | sku_id |
+--------------+--------+-------+
| 1 | White T shirt | 1 |
| 2 | Red T shirt | 2 |
| 3 | Blue T shirt | 3 |
+--------------+--------+-------+
attributes table
+--------------+--------+-------+
| id | attribute_value
+--------------+--------+-------+
| 1 | Color|
+--------------+--------+-------+
and skus table would be
+--------------+--------+-------+
| id | attribute_id | sku_value|
+--------------+--------+-------+
| 1 | 1 | White |
| 2 | 1 | Red |
| 3 | 1 | Blue |
+--------------+--------+-------+
@MahmoudMonem No. You’d only have one product row for each product. But if that product comes in multiple variants (i.e. size, colour) then you would have a SKU representing each combination, like you would in a real world store.
If a bricks-and-mortar store sells t-shirt, and that t-shirt came in multiple sizes, then each size would have its own unique SKU.
@martinbean What I didn't understand when you write "an attribute belongs to a product file" means that the attribute table (size, color, dimension) has a relationship with the product table?
I didn't cover this in my post about database design that @sinnbeck linked, but I will probably cover it in another post. However if you have a products table and a product can have multiple properties you can just create a properties table and a pivot to connect the products and the properties (product_property)
The properties table would have these columns
Type can then be anything and the value as well.
property Value
---------------
color white
size xxl
horse power 400
and so on.
You can of course have a product type as well to make the entering of new product easier
Rather than fully reinventing the wheel you might take a look at existing, well used, ecommerce solutions and see what they (magento, drupal commerce, woocommerce, etc) do to store variants of product attributes.
When there's only one attribute it's easy, but yeah as discussed above once you start having multiple criteria the number of combinations grows, and tracking stock of all the various combinations is more challenging.
Off the top of my head, I would suggest that your best bet is to track each variant's sku, price, cost and stock in a table, with a 'belongs to' relation to the overall product record that houses description/etc., and 'has many' relations to the various attributes
I would suggest that your best bet is to track each variant's sku, price, cost and stock in a table, with a 'belongs to' relation to the overall product record that houses description/etc.
@willvincent I wouldn’t.
“SKU” stands for “stock keeping unit”. It is the unit any stock should be tracked for. When a red, small t-shirt is sold, you deduct the stock for that SKU.
If you just link stock to the top-level product then that serves no use as you don’t know how many small t-shirts you have, how many medium t-shirts you have, how many large t-shirts you have; just that you would have n t-shirts.
@martinbean Perhaps I didn't outline my thought clearly, or you just misunderstood.
A SKU should be tied to the specific variant.
Suppose you have a t-shirt shop, with n designs. and Those designs are available on a variety of shirt size/color combinations.. perhaps more variations if you also offer different shirt types, etc. But a sku should be associated with a specific variant. I think we're really saying the same thing here. 👍
my approach will be
product table
id name price
stock table
id product_id colour_id size_id qty
size table
name
colour table
name
@tatenda And how would you then track say, electrical products that don’t have a size but other attributes? That’s not going to fit in your stock table.
The OP needs:
products table to store the top-level product information (name, description, etc).skus table to store variants and their prices.attributes table to define the attributes for a particular product (size, colour, RAM, disk size, etc).attribute_sku pivot table that defines the attribute values for a particular SKU (i.e. a SKU is red).stock_level_adjustments table that tracks additions and deductions in stock (i.e. delivery received from supplier, units sold in order).@martinbean I actually like this a lot, but I have a couple of questions. what do you mean by prices in the skus table ? how the structure of the sku table would look like .. because I think if we added the price, then we must reference the product as well .. because for example ; multiple products may have the sku " red". so that's different prices.. OR you mean we can have many " red " skus equal to the total available qty , which all belong to 1 product ? and just connect the skus table with the attribute table .. which has relationship with the products table .. hope if i got u right .. sorry if I sound confused, but actually this thing burnt my brain already haha <3
@MahmoudMonem A product would have many SKUs. Some SKUs may have a different price. For example, a XXL t-shirt might be a couple of dollars more than a S t-shirt because of the extra material needed.
Same with a stick of a RAM. There may be one “ACME DDR RAM” product, but with multiple SKUs for different memory capacities. Obviously a stick of say, 16 GB RAM should cost more than a stick of 8 GB RAM.
@martinbean Alright got it .. thank you, really appreciate your help.
@martinbean I didn't understand a thing. Above you write that you need a skus table where you can insert the "VARIANTS" but what are these variants? attribute values or what?
@MahmoudMonem Were you able in the end to achieve what you wanted?
Hello everyone, I reopen this post because I also have the same problem, I created a small management software where the variants for the values of the attributes are created. But I didn't quite understand how to combine them. I have followed the whole discussion but I have not come to the end. How do I combine the various attributes with each other?
In this approach there is a problem, the visitor can choose blue color with size M, but size medium is not available how can I solve this? How do I prevent the visitor from choosing options that are not available?
| attribute_id | sku_id | value |
|--------------|--------|-------|
| 1 | 1 | red |
| 2 | 1 | s |
| 1 | 2 | red |
| 2 | 2 | m |
| 1 | 3 | blue |
| 2 | 3 | s |
@mahmoudhussien what about adding qty column inside your SKU table which is decreased by qty number when a purchase is actually completed ..
For example if an SKU has qty of 5 and someone completed purchase for 2 items .. then the qty will be decreased to 3 .. and so on till its 0 .. when the qty is 0 .. this sku will not even appear in the foreach loop because you have a condition qty to be > 0
In that case when you loop through the skus you can loop where sku qty is > 0
Also you can only loop through UNIQUE .. so if you have 5 skus for the color black .. you can only show (first) .. so that when a customer choose that sku and actually buy you update the qty of that apecific sku .. hope this makes sense to u
@mahmoudhussien Either display a list of all available SKUs:
@foreach($skus as $sku)
<label for="sku-{{ $sku->id }}">
<input name="sku" type="radio" value="{{ $sku->id }}">
<span>{{ implode(' / ', $sku->attributes->pluck('value')) }}</span>
</label>
@endforeach
Or update the available SKUs in realtime using something like JavaScript when a user selects an option.
@martinbean to make it clear
product model
public function skus()
{
return $this->hasMany(Sku::class,'product_id');
}
sku model
public function product(){
return $this->belongsTo(Product::class,'product_id');
}
public function attributes(){
return $this->belongsToMany(Attribute::class,'attribute_sku')
->withPivot(['value']);
}
attributes model
public function skus(){
return $this->belongsToMany(Sku::class,'attribute_sku')->withPivot(['value']);
}
pivot table (attribute_sku)
Schema::create('attribute_sku', function (Blueprint $table) {
$table->foreignId('attribute_id')->constrained('attributes')->onDelete('cascade');
$table->foreignId('sku_id')->constrained('skus')->onDelete('cascade');
$table->string('value');
// $table->primary(['attribute_value_id','sku_id'],'attribute_v_product_d_foreign');
});
response
[
{
"id":6,
"sku":"sku-65cde5760ca3c",
"product_id":10,
"price":55,
"order":1,
"quantity":100,
"is_default":0,
"attributes":[
{
"id":1,
"name":"Color",
"pivot":{
"sku_id":6,
"attribute_id":1,
"value":"red"
}
},
{
"id":2,
"name":"Size",
"pivot":{
"sku_id":6,
"attribute_id":2,
"value":"small"
}
}
]
},
{
"id":7,
"sku":"sku-65cde576963e1",
"product_id":10,
"price":56,
"order":1,
"quantity":100,
"is_default":0,
"attributes":[
{
"id":1,
"name":"Color",
"pivot":{
"sku_id":7,
"attribute_id":1,
"value":"red"
}
},
{
"id":2,
"name":"Size",
"pivot":{
"sku_id":7,
"attribute_id":2,
"value":"medium"
}
}
]
},
{
"id":8,
"sku":"sku-65cde577349dc",
"product_id":10,
"price":57,
"order":1,
"quantity":120,
"is_default":0,
"attributes":[
{
"id":1,
"name":"Color",
"pivot":{
"sku_id":8,
"attribute_id":1,
"value":"blue"
}
},
{
"id":2,
"name":"Size",
"pivot":{
"sku_id":8,
"attribute_id":2,
"value":"small"
}
}
]
}
]
In order to separate the attributes from each other
$product->skus->pluck('attributes')->flatten()->groupBy('name')->map(function ($item) {
return $item->keyBy('pivot.value');
});
and result
{
"Color":{
"red":{
"id":1,
"name":"Color",
"pivot":{
"sku_id":7,
"attribute_id":1,
"value":"red"
}
},
"blue":{
"id":1,
"name":"Color",
"pivot":{
"sku_id":8,
"attribute_id":1,
"value":"blue"
}
}
},
"Size":{
"small":{
"id":2,
"name":"Size",
"pivot":{
"sku_id":8,
"attribute_id":2,
"value":"small"
}
},
"medium":{
"id":2,
"name":"Size",
"pivot":{
"sku_id":7,
"attribute_id":2,
"value":"medium"
}
}
}
}
Is there a way to combine similar colors while separating the sizes How do I prevent the visitor from choosing options that are not available ?
One idea is to just display the colors and then fetch the sizes based on the color chosen? But I don't know how to do this query
Please or to participate in this conversation.