Best approach for table modeling: Linking tables based on condition

Hello,

I’m developing a shop application which we have different products with different properties. Items also have common properties like ‘title’ and ‘description’.

I’m wondering what is the best approach to implement this scenario that works charmly with YII.

Until now I’ve came up with these two approaches:

  1. separate tables for each product type:

[size=2]2: have common properties in a table and link to the appropriate table based on the condition(product type)

[/size]3: Is there a better approach?? [size=2]

[/size]

TO BETTER CLARIFYING PLS SEE THE DIAGRAMS IN ATTACHMENT.

Thanks in advance.

If your database is shared by more than application, then this is more a database design issue than it is a Yii issue. Speaking strictly from the database perspective first, your alternative 2 is more normalized. For example, if a product name has to be a book or a course, but can’t be both, that business rule is harder to enforce with two separate, complete tables.

In a shared database, ideally you enforce this type of integrity at the database level using constraints, not with application code, although the application in this case should be designed to surface the exceptions thrown by the database. However, this is not an issue if your application is the only one that manipulates the data because, for example in Yii, you can use rules to enforce your data policy.

If the data is not too large and you have use cases that manage the products together, you can consider a 3rd (less attractive to DB purists) design, i.e. put all the columns in a single table, making specific columns conditionally required based on the product type. In your Yii code, you could then abstract a base model with required properties and rules for the common fields and extend that model with specific properties, rules and other functions for each product type, i.e.,




class Product extends \yii\db\ActiveRecord 

{

    public static function tableName()

    {

        return 'Products';

    }


    public function rules()

    {

        return [

            [['id', 'name', 'description'], 'required'],

            /* more common rules */

        ];

    }  

    /* etc... */

}


class Book extends Product 

{

    public function rules()

    {

        return [

            [['isbn', 'nor_pages'], 'required'],

            /* more Book rules */

        ];

    }  

    /* etc... */

}


class Course extends Product

{

    public function rules()

    {

        return [

            [['duration'], 'required'],

            /* more Course rules */

        ];

    }  

    /* etc... */

}



Hope this gives you an idea or 2.

Best,

Joe

Yes, the alternative 2 can be much more database normalized, but would be much harder to enforce business rules.

Now I’ve decided to follow the approach you recommended, although as you said it’s not considered as database normalized, but I think this will work for projects that are not too huge and of course with much less efforts. Especially for project like this one that the most properties are common and product types are few, this is looking to be a greatly working solution.

Thanks Joe.

It really depends on how your displaying/using data and the relationship types.

Will each "item" have only one course?

Will each "item" have only one book?

Will courses have books?

Will books have courses?

Will books be show its courses?

Will courses show books for that course?

If you answer those questions someone will be able to give you a better opinion. You will need do testing to actually have the best solution for your environment.

On as side note you should switch your varchar(45) to char(45) it will save a little bit of space and be slightly faster. This will only work if they are all char (in your case they would be) i.e. if you have 1 char (45) and 1 varchar(45) MySQL will treat all of them as varchar.

I disagree with this. Unless every column holds data exactly 45 positions in length in every row, VARCHAR is more efficient storage-wise. It appears OP’s modeling tool uses 45 as a length default, as I don’t know of any ISBN that is actually 45 positions. And number of pages is probably best defined as an INT.

In terms of retrieval efficiency, CHAR being "slightly faster" is only true if the entire row is fixed size.

Also, CHAR values are right-padded for storage but the pad is ignored during retrieval. VARCHAR, on the other hand, stores trailing spaces and retrieves them. So, in a CHAR(5) column, ‘aaaa’ is stored as 'aaaa ', but retrieved as ‘aaaa’. In VARCHAR(5) ‘aaaa’ is stored and retrieved as ‘aaaa’ but 'aaaa ’ is stored and retrieved as 'aaaa '.

Best,

Joe

At this point all items are separated and doesn’t have any relation(books, courses, etc.) and they’re only related with their category.

In my application I list them by their category and their type.

But as the project gets more complicated, maybe I will need to relate them in some other ways, Maybe books have different editions or have supplementary courses.