How should I create this customized e-commerce database model? - php

I am building an e-commerce website from scratch and have to make a special product configuration page.
It's only 1 type of product, but it is configurable on several levels:
Color (about 4 different options). Value is a VARCHAR.
Material (about 10 different options). Value is a VARCHAR.
Size (About 30 different options). Has 2 Values, a width column and a height column.
Finish (About 20 different options). Value is a VARCHAR.
Other various VARCHAR options etc.
My question is what would this look like in a typical MySQL database. Do I have a table for each type of option or just one table and somehow give it enough columns and have it store all options? I will need to store orders and be able to store the information for the order in a table as well.
I also want to be able to have off the shelf products that aren't customizeable, just like a normal store.
Any help is appreciated!!

I suggest that you go with one master table, with all of the product information, and a slew of lookup tables, that connects to the master table.
It should look like this:
Product_table:
ID primary key,
ColorInt int foreign key lkp_Color_table.ID,
Material_int foreign key lkp_Material_table.ID,
// snip the rest
And here's the lookup table:
lkp_color_table:
ID primary key,
ColorStr varchar,
description varchar
lkp_Material_table:
ID primary key,
MaterialStr varchar,
description varchar
The Product_table can look like this:
ID||ColorInt||Material
1 ||1 ||1
2 ||1 ||2
Whereas the lkp_color_table can look like this:
ID||colorstr||description
1 ||red ||red color
2 ||blue ||blue color
Note that lkp_color_table can contain unused color , same goes for other lookup tables. So if you have 30 possible colors, you just have to populate lkp_color_table with 30 items, and so on.
There is no need to create a separate Product_id for each color-material- combination, you just have to create a product_id if you use it.

2 + N tables:
Products (every single product in the catalog has a record here
SpecialKindOfProduct (Your configurable product type, a product_id, and a bunch foreign keys to lookup tables (color_id, size_id, etc)
N Lookup tables (colors, sizes, etc)
When you add a second kind of configurable product in the future, you just create AnotherSpecialKindOfProduct.
Hope this is clear enough to follow.
The main advantage is that all your products have some shared attributes (the columns of "Products"), but can have extended attributes as well.
You can easily select everything you need by LEFT JOINing Products to SpecialKindOfProduct, etc.

I'd say that you'll need:
OrderTable (OrderID, CustomerID, Date, Price, Shipment, ...)
OrderProductTable (OrderID, ProductID, Quantity)
ProductTable (ProductID, StandardBit, ColorID, Option2ID, ..., OptionNID);
StandardBit - to know if product is standard or configurable.
CustomerTable (CustomerID, ...)
ShipmentTable ()
OptionTable for every configurable option (OptionID, Description).

Related

Getting an unique hash ID from array of UUIDs in PHP

What is the best way to create a unique identifier for an array of UUIDs?
I have a Product that consists of multiple components, each component has an UUID assigned to it.
When creating a new Product with it's components, I need to know if there is already another product having exactly the same components. I think going through MySQL, loading each product with it's components and checking them one by one will be time consuming.
This is my idea:
products table
--------------
id, name, components_uuid_hash
product_components table
------------------------
product_id, component_id, ...
components table
----------------
id, uuid, name, ...
I would calculate the components_uuid_hash:
$product_component_uuids = ['227A0140-F0FB-4FDA-B780-85152AB02927', 'FA0E6D52-F0E4-4F55-87F5-8D73625AEDA3'];
sort($product_component_uuids);
$component_uuid_hash = md5(serialize($product_component_uuids));
I would do this for each product, giving me a unique hash of the whole UUID array that I could simply lookup in the Products table to know if there has already been a different product with the same hash.
Can someone please confirm if this would work or if there is a different better approach?
Assuming you already have a list of uuids you can try to do it in MySQL with something like
select pc.product_id, count(c.id) as co from
product_components pc left join components c on (
pc.component_id=c.id and
c.uuid in ('227A0140-F0FB-4FDA-B780-85152AB02927', 'FA0E6D52-F0E4-4F55-87F5-8D73625AEDA3')
)
group by pc.product_id having co=2;
This will return all products that have exactly two components with UUIDs matching those in the list.
You need to adjust the count value at the end to match the number of uuids.
This will still do a full scan on the product_components table and will probably use a temporary table, so your plan to keep a hash of the UUIDs in the products table and search by it will probably be faster from a MySQL point of view, but you will have to update it every time a product or a component changes.

mySQL Database - Storing Multi-Criteria Ratings

I've been doing a lot of searching and reading about rating systems, but couldn't find a solution to what I'm trying to achieve...
I have a website where users, once logged in, can submit a product. Now I want other users to be able to rate those products according to 3 different criteria. I'm using php and mySQL databases to store all of the information which is working great, I'm just not sure how to incorporate the ratings now.
At the moment I have a PRODUCTS database, which holds various tables according to their category. Here's an example of a table:
TOASTERS
---
ID (auto-incrementing)
Brand
Set
Number
Name
Edition
Image (stores the location of the image the user uploads)
Any user can then rate that row of the table out of 10 for 3 criteria (Quality, Price, Aesthetic). The user average of each criteria is displayed on each product page but I would like to store each of the user's individual ratings so that I can show a short history of their ratings on their profile page. Or have a live feed of the latest user ratings on the homepage.
What I'm trying to do is quite a lot like awwwwards.com. (See bottom-right of page to see the livefeed I'm talking about)
Thanks in advance!
I think you should use single PRODUCTS table or at least create PRODUCTS table and emulate inheritance between it and category tables.
Having a table for each category can give some advantages if each category has some specific properties, but it can lead to neccesity of writing separate code to work with each table. Alternatively you can use two tables to store all custom properties 'vertically': PROPERTIES(propertyID,PropertyName), PROPVALUES(productID,propertyID,PropertyValue).
If you choose to have multiple tables and emulate inheritance, it can be achieved like this:
PRODUCTS
---
ID (auto-incrementing)
Brand
Set
Number
Name
Edition
Image
VoteCount <+
SumQuality +-updated by trigger
SumPrice |
SumAesthetic <+
TOASTERS
---
productID (PK and FK to PRODUCTS)
(toaster specific fields go here, if any)
Than you will be able to create table VOTES, referencing table PRODUCTS
VOTES
---
productID (FK to PRODUCTS)
userID (FK to USERS)
Quality
Price
Aesthetic
VoteDateTime
If it is true that overall product rating is queried much more often than voting history, as an optimization you can add fields VoteCount, AvgQuality, AvgPrice, AvgAesthetic to PRODUCTS table, as srdjans already supposed. You can update this extra fields by trigger on table VOTES or manually in PHP code.
Create separate table for storing user individual ratings (primary key, user id, product id and ratings). Create additional fields in "products" to store averages. Every time some user rates some product, you insert record in "ratings" table, then calculate averages again for given product, and update rows in products. Doing this you will have easy access to ratings, and also, you can analyse user individual ratings.
Ps - You may also wish to store how many users rated some product.

MySQL: SQL and DB for product with multiple categories

I am working on a website which hold millions of records now (apologies cannot reveal which site) initially it had few hundred records so the query below was acceptable
Query: SELECT * FROM….WHERE category LIKE ‘%,3,%’;
But now it just kills the database as for each query it has to go through the entire 2Mil records with above query
Category table
ID NAME
1 Female
2 Fashion
3 Clothing
4 Accessories
5 Top
6 Dress
7 Earring
8 Short dress
9 Long dress
10 Male
Product table
ID…..Category….other bits
1 ,1,2,3,6,9, ……
2 ,1,2,4,7,
3 ,1,2,3,5,
4 ,10,2,3,4,
you have the picture as what is happening above. Now if I do FullText index on category row in product table it gives only 1 cardinality :(
How can I overcome this?
I have considered duplicating row with each category but the database is huge currently 2 GIG and with duplicates it will turn roughly 10 GIG… more like a problem then a solution
Keep in mind that storing numbers as strings takes about twice as many bytes per digit as storing numbers as integers. Plus all those commas.
So if you're concerned about space, it won't be as much expansion as you fear to store the data in a normalized fashion.
And it will allow you to write proper queries that take advantage of indexes. So if there is some expansion, you will have traded a little bit of storage space for a big improvement in speed.
Tip: if you're using InnoDB, the primary key doesn't cost any storage because the table itself is stored as the primary key index. You should define your normalized table with the category id first and then the product id second, if you need to optimize for searches by category.
CREATE TABLE CategoryProduct (
categoryid INT,
productid INT,
PRIMARY KEY (categoryid, productid)
);
See also my answer to Is storing a delimited list in a database column really that bad? for more disadvantages to using comma-separated lists.
I would consider a new table, say Product_Category (unimaginative I know) where each row contains a column for a Foreign Key (FK) relation to the Product.id and a column for the category.
The category column can probably be a TINYINT which would only require 1 byte to store while I guess the FK column would be the same as the Product.id column (probably INT - 4 bytes), you could then index both columns so you can either find out which categories a product belongs to as well as which products belong in a category. Also, this table wouldn't need to have a Primary Key (i.e. id), saving you an an extra 4 bytes.
(see MySQL Data Type Storage Requirements)
With this solution each row in this new database would take up about 5 bytes. Since each character in the sting takes up 1 byte (Assuming ASCII and latin1 encoding), you would be looking at an increase of 3 bytes (including comma) per category per product by removing Product.category and putting the items into Product_Category, however that's no where near as big a gain as duplicating entire product rows. However, there is the cost of changing your code (unless you're far better than I am at joins).
Does this help any?
One solution I've seen is to use three tables:
categories lists your categories
products lists your products, without any attached category information
category_map is a special table: each row links a product_id to a category_id
To look up products by category, you can then match rows in category_map against rows in products.
This is an imperfect example, but it gets the gist of it:
SELECT * FROM
(
SELECT * FROM category_map
WHERE category_id=1
) AS map
INNER JOIN products
ON products.id = map.product_id;
Table joins are a very powerful tool; you may want to spend some time reading up on them, if you're new to using them. Coding Horror has a visual explanation that skims over the details.
It would be a good idea to set up foreign key constraints or otherwise make sure that entries in category_map correspond to existing entries in products and categories.

How do I Normalize this Auto Parts Database?

I am am new to PHP/Databases... But I am picking it up fairly swiftly. What I would like to ask you guys is pretty simple. I want to normalize my database and am not positive how to go about it. I get the concept, but see multiple ways to do it. Figure I'd ask people with some experience.
Here is my Database (2 tables so far):
Brands
Products
***Brands Breakdown:***
1 id int(6)
**Note:** Above, I will probably use 4-Letter codes for each brand instead of primary/int/auto.
2 name text
3 logo varchar(20)
4 phone varchar(20)
5 website varchar(30)
6 contact_name text
7 contact_number varchar(20)
8 contact_email varchar(30)
9 warehouse varchar(20)
10 pricing varchar(15)
11 bio varchar(300)
***Products Breakdown***
id (INT(6) / Auto_Increment)
brand (This is where I'll insert the four letter code for brand)
category (e.g. Brakes)
subCategory (e.g. Brake Rotors)
details (e.g. Drilled and Slotteed 'Razr')
sku (Part #)
minYear
maxyear
make (e.g. Subaru)
model (e.g. Impreza)
subModel (e.g. WRX STi)
description (Paragraph on part describing it)
specs (I imagine this can be expanded on. need cells somewhere for sizes / colors / engine codes / etc.)
msrp
jobber
price
cost
weight (of part)
warehouse (Could be moved to brand's table)
image (URL of image for the part)
So My main question is: Do I make each brand have there own table similar to my current 'products' table? or have 'category' tables? 'subCategories'? How would you guys normalize this data?
I would like to have a solid database while I'm learning this stuff so I learn the right way. Any advice would be appreciated.
UPDATE:
To anyone who comes across this question who is trying to learn how to structure their database, one major thing I was unaware of when I asked this was something called "cardinality". Research this topic and learn how to apply it to your database schemas!
Don't make each brand have its own table. That's not normalization, that's partitioning. Don't do that until your data base gets very large.
It's not clear what your brand table means. I am guessing you mean parts-manufacturer, but I'm not sure. The rest of this discussion assumes that you do mean parts-manufacturer.
Here's my suggestion.
Rename your brand table. Call it "Manufacturer" and split it in two, for Manufacturer and Contact.
Manufacturer:
mfrid (your four letter code, primary key)
mfrname text
mrflogo varchar(20)
mfrwebsite varchar(30)
mfrphone varchar(20)
warehouse varchar(20)
Contact:
mfrid (four letter code) (part of primary key)
contactid (autoincrement) (part of primary key)
contact_name text
contact_number varchar(20)
contact_email varchar(30)
bio varchar(300)
Why is "pricing" an attribute of the manufacturer? What do you mean by "pricing?" Isn't it an attribute of an individual part?
Split your parts table into two. One table will have a row for each part sku. The other will have a table for each application (that is, each make and model of car in which the part may be used). Like so:
SKU:
sku (your stock-keeping unit number, primary key).
mfrid (maker of the PART, not the vehicle in which it fits, foreign key to mfr table).
mfrsku (the brand's stock keeping unit, not necessarily unique in your system)
category (e.g. Brakes)
subCategory (e.g. Brake Rotors)
details (e.g. Drilled and Slotteed 'Razr')
description (Paragraph on part describing it)
saleprice (?)
cost (?)
Application:
ApplicationID (auto incrementing primary key)
make (e.g. Subaru)
model (e.g. Impreza)
subModel (e.g. WRX STi)
firstYear.
lastYear.
Then, you'll need a join table (because each Application can have zero or more SKUs and vice versa; that is, your SKU and Application entities can have many-to-many relationships). In your example, you know that multiple models of Subarus often take the same parts. This schema allows for that.
ApplicationSKU:
ApplicationID
SKU
The trick to normalizing is to understand your application domain. Figure out what entities you have: e.g.
manufacturers like Delco and Subaru
contact people like Joe and Harry
parts like Left Front Wiper Assembly and Rear Wiper Assembly
applications like 1999-2006 Subaru Forester and 1998-2007 Subaru Impreza
Create a table that matches each entity you have. Figure out how you will uniquely identify each entity (in other words, figure out what you will use for a primary key).
Create join tables when you have many-to-many relationships between entities.
Create foreign keys to connect the various entities together.
I hope this helps.
Change products.brand to products.brand_id and have it be a foreign key to brands.id.
Create a categories table and with fields id, name and parent_id(allow NULL) which will house the categories.id of its parent (NULL means top-level category). Alternatively, you can use a nested set model. products would then have a products.category_id field (no subCategory field necessary).
And remember when you get to the part where you actually have orders or put things in the warehouse inventory, store theactual price at the time the action was taken. Price on a product is a lookup, it changes over time but the orders or the value of the item in inventory should be related to the actual costs at the time the record was entered.
One product can fit more than one car -- for example, you might have a wiper blade that fits a 2010 Toyota Camry, a 2009 Scion tC and a 2011 Acura TL. So, you will need to split year/make/model out of the products table, and make a separate table for vehicles (id, year, make, model) and a cross table (id, product_id, vehicle_id) that joins them.

MySQL search in field (or other solutions)

I have a table with products that fall under specific categories, but the products within each category can contain multiple meta data tracking field
Table: products
id name category metadata
1 something 1 blue,red,purple
2 something else 2 left,right,middle
I have been trying to contemplate the best method to have a single product table but can't seem to squeeze the metadata in conveniently. for now I have created a table with all the metadata and fields for tracking the related category (the sequence is so i can order them withing a dropdown etc..)
Updated table: products
id name category metadata
1 something 1 1,2,3
2 something else 2 4,5,6
Table: metadata
id category sequence option
1 1 1 blue
2 1 2 red
3 1 3 purple
4 2 1 left
5 2 2 right
6 2 3 middle
If this format makes sense .. I am trying to generate a query that will search for values in my product table and grab each and all of the related meta values. The issue I am having is trying to find a unique value in the products field. if I do a MySQL search for LIKE(%1%) I will get matches for 1, 11, 21, 31 etc ... I thought of adding a leading and trailing comma to the field by default and then search for ",1," which would be unique .. but there has to be a better way ...
Any recommendations (regarding format or query)?
It's not an ideal design to have comma-separated values within a single database field. Aside from the problem you mentioned (difficult to search), your queries will be less efficient, as the DB won't be able to use indices for the lookup.
I'd recommend making a separate table products_metadata with a many-to-one relationship to the products table. Have the *metadata_id*, and the *product_id*, which is a foreign key linking back to the products table. That will make your job much easier.
You want to add another table, which links products to their metadata. It will have two columns: productid and metadataid which refer to the relevant entries in the products and metadata tables respectively. Then you no longer keep metadata in the products table, but JOIN them together as required.

Categories