I'm pretty new to OOP/OOD, and I realize I have a lot to learn, so I'd like to ask the SO community for their input.
Basically, I'm using CakePHP's MVC framework, and the online store I'm building is just using 2 models, Category and Product, described by the following CREATE statements:
CREATE TABLE `categories` (
`id` int(11) unsigned NOT NULL auto_increment,
`name` varchar(255) default NULL,
`parent_id` int(11) default NULL REFERENCES categories(`id`),
`lft` int(11) default NULL,
`rght` int(11) default NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `products` (
`id` int(11) unsigned NOT NULL auto_increment,
`name` varchar(255) default NULL,
`artist_id` int(11) default NULL REFERENCES artists(`id`),
`description` text default NULL,
`category_id` int(11) default NULL REFERENCES categories(`id`),
`status` enum('in stock', 'pre-order', 'out of stock') NOT NULL,
`price` decimal(6,2) default NULL,
`picture` varchar(255) default NULL,
`picture2` varchar(255) default NULL,
PRIMARY KEY (`id`)
);
The online store is going to encompass:
Music
CDs
DVDs
Apparel
Hoodies
T-shirts
Long-sleeve Tees
Babydolls
Hats
Misc Merch
Stickers
Posters
Tote Bags
Downloads
Ringtones
MP3s
Basically, that's the structure of the category tree, though we may add new categories in the future. Right now all products are treated equally as Product class objects, with their category_id being the only way to distinguish different types of products. The online store is also just one section of the site. The rest of the site contains information such as artist bios and discographies; thus I also have models/tables for: Artist, Album, Track.
Is this a good approach? Or should I be creating separate subclasses for CDs/T-shirts/MP3s/... which inherit from the Product class? I'd like to link each music CD in the store to the discography entry to automatically generate track listings and other information for the product description.
If this is not a good way to go about things, how would you do it instead? Also, what methods/properties should I include in my Category/Product class? And should I only list products in leaf nodes of the category tree?
Edit:
Obviously this design is incomplete. As Cyril pointed out, the Product.price field was missing, and there are still no provisions for sales. I'm actually still trying to work out how best to design & implement those aspects of the store. Most likely, I will just have another model called Orders, but actually processing orders is made more complicated by the fact that we are applying different shipping rates based on shipping destination and what the order contains.
I like more a configuration+behavior approach instead of the subclasses.
This can be bits of behaviors at the category level. Depending on the scenario, some behaviors could be associated to separate product types, like for something that applies to certain products regardless of the categories. For this last piece Smoking suggestion would also make a lot of sense, since you could have these extra behaviors associated to additional categories. This way as long as you are working with already coded behaviors, you can add new categories indicating which behaviors it will support.
A small variation, would be list of behaviors associated to the categories instead of the bits.
The additional classes would be for the behaviors, instead of having to mix it all under the product classes. If you need to associate an UI for these extra things, you can relate it to the behaviors, regardless of product types.
You could still keep the products table and make different "subcategories of products" if you use the polymorphic behavior. That's an easy way to keep all the common data in one place (price etc).
Alternatively, you could use the same polymorphic behavior on a table attributes and add anything product-specific as an attribute (name=>value pairs). It all depends on the idea in your head and the amount of data.
First solution is probably better if you plan to have a large database.
Yes, you probably should create separate subclasses for the different types of products, especially if they will have type specific functionality. Like the automatically generating of track listings you mention.
But even without that, by using separately named product classes you will make the code more readable and more logical when dealing with product type specific code.
You also might want to create a more specific database model.
Like
table: product
prod_id
...common stuff..
product_clothes
prod_id
...Clothes specific stuff...
product_music
prod_id
...music specific stuff...
product_merch
prod_id
...merchandise specific stuff..
Because music doesn't have a size measured in inches and clothes don't have a bitrate.
You should skip the category_id column and model the relationship more like a tag cloud. You will want to associate each product with one or more categories.
You should skip the artist_id column and model the relationship more like a tag cloud. You will eventually want to associate some product with multiple artists.
Categories are dated, you HAVE to use tag clouds. As a rule you should NEVER create tables with a references statement; UNLESS every column in the table has a references statement.
No,
You should definitely not create separate subclasses unless you need some mumbo-jumbo magic (like custom t-shirt logos, etc) which cannot be achieved a simple category division). Even in that case you may be safe putting in an extra flag field in the category class (HasLogo for example).
But the design does not impress me yet. What kind of store has no provision for sales? What about price field?
You could also put the Picture in a separate table for the sake of flexibility.
What are you using lft/rght for? You can use a simple Inde property to store the category's place in the tree, but remember you will need to update each time you delete/add a category.
Related
I have a table "products" for my website. There is the product_id, image and bunch of information like manufacturer, health, age etc. When I started to work on my projekts I chose to save everything as ENUMs like this:
manufacturer ENUM('manufacturer1', 'manufacturer2', 'manufacturer3', 'manufacturer4'),
However dealing with more stuff I have realized that this is not the best approach for me. So I decided to create reference tables. For manufacturer it looks like this:
CREATE TABLE manufacturer(
manufacturer_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
PRIMARY KEY (manufacturer_id ),
UNIQUE INDEX name (name)
);
but in products table I now have:
manufacturer_id INT(10),
The question is on what is the best way to assign value from reference table. I am using CI framework and it was very easy to display product in view. However now when I run query for product I get "3" instead of "manufacturer3".
I could use JOIN but I would have 7 JOINs. I created library with function that ataches the value. But I have to save all the possible values in that function or to query every reference table every time I use library.
I mean there are a lot of articles about why not to use ENUMs and I get that - I run in problems with ENUM in my project myself. I dont get how to convert those reference ids to real value (efectivelly, I dont think that 7 JOINs is a good way to that especially if I have to do that very often)
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.
what is the way of designing an optimized schema's for a shopping website that can provide different filter options for different categories.
In TV Category the filters available are Type, Display Features, Display Size. Whereas in computer accessories like keyboard the filters available are Type and Interface. and the filters change for various products.
Filtering the result by cost and brand can be done easily as they have separate column in the table.
but how to design a table of items which have different types of multiple filters which varies by category. My guess would be having a column named filter for every item. that holds these data's. But is it the best way of using filters as the column filter have multiple filters.
Having one column surely is not the best way if you have a lot of data.
As I suppose, you can have some table tbl_product_filters with M:1 relation to your tbl_products that contains filter names and values for each product id.
Something like:
CREATE TABLE `tbl_product_filters`
(
`id` int(10) unsigned auto_increment PRIMARY KEY,
`product_id` ...,
`name` varchar(25) NOT NULL,
`value` varchar(255)
)..;
You can also normalize it further and isolate filter names as filter types in separate table to substitute string name to integer type_id. It would be faster I suppose.
I was just wondering if I might get some advice on the following:
I'm building a site in CodeIgniter in which exists a content type "portfolio_item".
The same portfolio item may be displayed in 3 places via checkbox controls:
Homepage, Member Page and Client Page.
I'm just wondering what the best way to implement this relationship in the database is. I could have 3 relationship tables for each of the above scenarios, but to me this seems overkill (the site is quite small).
I was thinking of using one relationship table with the following fields:
type (homepage, client or member)
show_on_id
portfolio_id
The intent of the type field is to determine which table to retrieve the "show_on_id" from (either clients or member), if the type is homepage then show_on_id is not required.
Is there any obvious disadvantage of doing it this way?
Yes, there is a bit of a disadvantage. You could end up with multiple rows for the same setting, each might contradict each other. So, how you insert rows into this table, is very important.
If you will not add any more sections, might as well have the portfolio table:
CREATE TABLE `portfolio`
(`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`content` TEXT NULL,
`showHome` BOOLEAN NULL,
`showClient` BOOLEAN NULL,
`showMember` BOOLEAN NULL)
And then the table which links the users to their portfolios,
CREATE TABLE `portfolio_user`
(`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`portfolio` INT NOT NULL,
`user` INT NOT NULL)
If you are going to add more places where the portfolio can be displayed later, or if these places are dynamic, your method will work. I would just change 'type' to 'place' as that is easier to understand, then either use ENUM or another table to define the places that the portfolio can be shown.
Can there be one or many portfolio items for each location?
If many, use link tables, if only one use direct foreign key field to link to the portfolio.
I would advice against using one link table for all three based on personal experience with exactly such designs and the problems that can surface later.
Hey I have two models User and City, where a user belongs to a city and a city has many users.
On the city table I have city_id and city_name.
I've used used cake bake and created the user model and when I add a new user the combos box under the city field is showing the id of the city instead of the name.
I've tried several methods to show the name of city instead of the id but I've failed.
You don't mention what you've tried, but usually you'd use the model's displayName property to set the field that should be used a record's "title". In your case, I'd think:
$this->displayName = 'city_name';
Note that in 1.3, it looks like the attribute name is displayField rather than displayName.
Cake requires a specific table layout for the automation to work. Rob explained how to get around it. But if you want to avoid writing the extra lines of code, you should consider organizing your tables accordingly.
CREATE TABLE `cities` (
`id` int(11) AUTO_INCREMENT NOT NULL,
`name` varchar(25) NOT NULL,
PRIMARY KEY (`id`)
)
When you bake this model, Cake will automatically use name as the display name. This will apply to every table you create. In addition, if you use id for the table, it will automatically know how to reference it as a foreign key from other tables as city_id.