Hi Ryan:

With your proposed design, you can control the Content "isOnlyOneOf"
using the beforeSave(), but consider the following:

1) What if, in the future, you want to create another specialized
Content class?  That means you will need to not only create a new
table, but also add another column to the contents table.  Changing
the table structure when you've already accumulated a lot of rows can
take a while, and will require a table lock.

2) Since the Content will generally be accessed in the context of the
specialized class, each of the 'specialized_id' fields should be
indexed; without these individual indexes, the database will need to
execute a table scan to find the appropriate Content matching the
selected Bookmark/Message/Post, and can take longer as the contents
table grows.  The more indexes a table has, the more overhead involved
when changing the rows (INSERT/UPDATE/DELETE), since it needs to
update the associated indexes as well (even NULL values might be
processed by the database when updating the indexes, see:
http://stackoverflow.com/questions/289001/does-mysql-index-null-values).
Depending on the database vendor implementation, as well as the
storage medium for the tables (MySQL allows MyISAM, and InnoDB as some
of the options), this could have implications, such as a full table
lock for the duration of the DML (delaying other users from making
changes to other rows in the contents table until complete) or
increased storage requirements.  Updating a single 2-column index
*MAY* be more efficient than multiple 1-column indexes.

To address your concern about wasted space, one variation to the model/
model_id design is to define the model field as an integer instead of
a string.  Your Content class can define constants to map the values
to the appropriate model names (see:
http://www.dereuromark.de/2010/06/24/static-enums-or-semihardcoded-attributes/).
Another alternative if you are using MySQL, is to define the column as
enum; note, however, that CakePHP doesn't directly support enum
datatypes, and is discouraged by some programmers because it prevents
the app from being "portable".  Using the enum datatype also has the
same disadvantage as item #1 above -- you need to change the table
structure if you want to add additional values.

Regarding foreign keys with the model/model_id design: I haven't tried
this, and it may introduce other problems, but...

  CREATE VIEW content_models AS
  SELECT id AS model_id, 'Bookmark' AS model
    FROM bookmarks
  UNION
  SELECT id AS model_id, 'Message' AS model
    FROM messages
  UNION
  SELECT id AS model_id, 'Post' AS model
    FROM posts;

  ALTER TABLE contents
    ADD FOREIGN KEY fk_content_model (model_id, model)
    REFERENCES content_models (model_id, model);

You should carefully test this because
a) you cannot create an index on a view (at least, not in MySQL).
Hopefully, by using model_id as the FIRST column in the foreign key
clause, it would utilize the primary key of the underlying tables to
access the reference quickly
and
b) foreign key constraints add overhead like indexes do, as each row
in an INSERT/UPDATE/DELETE statement has to be validated against each
constraint, blocking other access to the table until it completes.

I have my preferences, but am open to other suggestions on design
patterns addressing this issue, as well as other advantages/
disadvantages/clarifications of these patterns.


On Feb 12, 10:42 am, Ryan Schmidt <google-2...@ryandesign.com> wrote:
> I've heard that recommended before. Is that really the best practice? (I 
> honestly don't know, and am trying to learn.) It surprises me, because it 
> means that I cannot (in my database engine) declare a foreign key on the 
> model_id column, which means I lose the benefits that in-database foreign key 
> definitions provide. It also seems to waste space, storing the model name 
> string over and over again in every record.
>
> If I were designing this, I think my contents table would have fields 
> bookmark_id, message_id and post_id, each of which is defined as allowing 
> NULL, with the intention that only one of these be filled for any given 
> record; I'd have a beforeSave() method in the Content model ensuring that. 
> Does that make sense, or is there a reason why I wouldn't want to do it that 
> way?

-- 
Our newest site for the community: CakePHP Video Tutorials 
http://tv.cakephp.org 
Check out the new CakePHP Questions site http://ask.cakephp.org and help others 
with their CakePHP related questions.


To unsubscribe from this group, send email to
cake-php+unsubscr...@googlegroups.com For more options, visit this group at 
http://groups.google.com/group/cake-php

Reply via email to