views:

212

answers:

5

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.

+1  A: 

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.

esnoeijs
Having subclasses not always make a system more readable/logical. Of course this vary based on the scenario, but I have seen pretty much identical systems where one followed a subclass approach and the other a configuration based approach and it was a lot easier to understand - added in an answer.
eglasius
One of the reason I've been hestitant to use separate models/tables is because I wasn't sure how to normalize such a database. How would you search for products if they're all in different tables? Do I search each category separately and combine the results in the controller?
Calvin
@Calvin notice esnoejis mentioned a common product table, where you can apply the search. If you want to search based on anything being displayed on the page, consider integrating it with a search engine.
eglasius
I need to get my eyes checked. I totally missed that (at least when I originally asked that question).
Calvin
A: 

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.

Cyril Gupta
The lft/rght are required for CakePHP's Tree behavior, which implements an MPTT structure.
Calvin
+1  A: 

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.

SmokingRope
I don't think we will have any products associated with more than one category (other than parent categories). And aside from our samplers, each product will only be associated with one artist. But I do have a Tag model setup, I just don't know how I can apply across the site.
Calvin
And why shouldn't I create tables with references statements? Aren't they inert? I include them just to show the model associations.
Calvin
+1 for tag clouds approach - mentioned something related to it in my answer.
eglasius
The reason to avoid tables with 'references' statements is to encourage a tag cloud like model. This is also called a relation, and in general relations do not contain data but exclusively act to associate two or more tables. You can enforce one tag (category) to one product using a relation.
SmokingRope
You would not however, want to add more columns to your product table if you wanted to support multiple categories.
SmokingRope
+2  A: 

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.

eglasius
+1  A: 

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.

dr Hannibal Lecter