Thanks Chris,

Chris Hartjes wrote:
>>  - join models
> CakePHP offers that via the same sort of hasMany, belongsTo 
> and hasManyAndBelongsTo functionality.

That seems to have been misunderstood.  I was thinking of using 
real join Models in the Domain Model for many-to-many 
relationships instead of "mere" join tables at the database level 
that have no representation whatsoever in the object-oriented 
layer.  So, instead of this:

        CREATE TABLE `groups_users`
        ( `group_id` INTEGER UNSIGNED NOT NULL    DEFAULT 0
        , `user_id`  INTEGER UNSIGNED NOT NULL    DEFAULT 0
        , PRIMARY KEY `groups_users_id` (`groups_id`, `users_id`)
        , FOREIGN KEY (`group_id`) REFERENCES `groups`(`id`)
        , FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)
        );

with

        User has and belongs to many Groups

        Group has and belongs to many Users

You would instead have this:

        CREATE TABLE `memberships`
        ( `id`       INTEGER UNSIGNED PRIMARY KEY auto_increment
        , `group_id` INTEGER UNSIGNED NOT NULL    DEFAULT 0
        , `user_id`  INTEGER UNSIGNED NOT NULL    DEFAULT 0
        , FOREIGN KEY (`group_id`) REFERENCES `groups`(`id`)
        , FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)
        );

with

        Membership belongs to User
        Membership belongs to Group

        User has many Memberhips
        User has many Groups through Memberships

        Group has many Memberships
        Group has many Users through Memberships

Basically, our poor little anymous join table has been promoted 
to a real Membership Model in our application.

Using a real full-fledged join model gives you much more power 
and flexibility.  Consider, for example, trying to design a 
RESTful CRUDdy API for this application.  In the first example 
above, it is not immediately obvious how to join a User to a 
Group.  Of course, it's possible to add a joinGroup action to 
your User model or an acceptUser action to your Group model.  But 
that wouldn't be RESTful and CRUDdy.  In the second example, it's 
blindingly obvious: joining a Group is the same as creating a new 
Membership.

Modeling relationships between models as models in their own 
right is a very powerful concept.  Unfortunately, many courses 
still teach this naive noun/verb modeling concept, where you 
basically write up your use cases in plain English and then 
underline all nouns which become your objects and all verbs which 
become your actions.  In this way you usually overlook concepts 
such as Relationship, Authorship, Membership etc., which might 
very well deserve their own objects, instead of being just 
associations.

>>  - object-relational mapping of inheritance
> I think CakePHP allows this through object chaining, which to 
> me is a very powerful yet underutilized concept.

Can you elaborate on that?  I just googled for object chaining 
and only got a few results concerning fluent interfaces which is 
probably not what you meant.

AFAICS, there are basically 6 possibilities how to do 
object-relational mapping of inheritance:

 1. cop-out #1: don't do inheritance
 2. cop-out #2: don't do object-relational mapping, use a "real" 
      object-oriented database/persistence layer instead
 3. map the complete inheritance hierarchy to a single table 
      (a.k.a. the "One Inheritance Tree - One Table", "Single 
      Table Mapping", "Flat Mapping", "Union Mapping", "Typed 
      Mapping", "Filtered Mapping" or "Single Table Inheritance" 
      design pattern)
 4. map each concrete class to a single table (a.k.a. the "One 
      Inheritance Path - One Table", "Horizontal Mapping" or 
      "Concrete Table Inheritance" design pattern)
 5. map each class to a single table (a.k.a the "One Class - 
      One Table", "Vertical Mapping" or "Class Table Inheritance" 
      design pattern)
 6. use a generic "meta-mapping" schema

All of these have their advantages and disadvantages.

#3 is probably the simplest of those (except #1, of course), it 
works like this: in your database you have one single table for 
the entire inheritance hierarchy which includes the union of all 
attributes of the classes in the hierarchy plus an additional 
type column.  So, given a hierarchy like this:

        class Article {
                var $title;
                var $body;
        }

        class BlogPost extends Article {
                var $category;
        }

        class Comment extends Article {
                var $blogpost;
        }

your schema would look like this:

        CREATE TABLE `articles`
        ( `id`                      INTEGER UNSIGNED PRIMARY KEY
auto_increment
        , `title`                   VARCHAR(255)     NOT NULL
        , `body`                    TEXT             NOT NULL
        , `blog_post_category`      VARCHAR(255)     NULL
        , `comment_blog_post_id`    INTEGER UNSIGNED NULL
        , `type`                    VARCHAR(255)     NOT NULL
        , FOREIGN KEY (`comment_blog_post_id`) REFERENCES
`articles`(`id`)
        );

The way this works is that the object-relational mapping layer 
looks at the `type`column to actually decide what type of model 
to return.  Note the "NULL" columns: since a BlogPost doesn't 
have a parent $blogpost and since a Comment doesn't have a 
$category and an Article has neither, those columns *must* allow 
NULL values.

However, I'm not sure this belongs in a CakePHP application or a 
plugin.  It looks rather like it belongs into Active Record 
proper.

>>  - polymorphic associations
> PHP doesn't support the polymorphic stuff at this time as far 
> as I'm aware.

Okay.  That's rather nifty stuff though, it would be kind of cool 
to have.  For those that are not familiar with that concept, let 
me explain a bit.  A polymorphic association basically is an 
association for which the type of the associated model is not 
known.  A typical example would be tagging, rating, voteing or 
commenting: you can tag/rate/comment/vote a blog post, a photo, 
a video, CakePHP plugin, ... whatever.  If you just have Posts, 
you can easily comment on them like this:

        CREATE TABLE `comments`
        ( `id`               INTEGER UNSIGNED PRIMARY KEY
auto_increment
        , `title`            VARCHAR(255)     NOT NULL
        , `body`             TEXT             NOT NULL
        , `post_id`          INTEGER UNSIGNED NOT NULL
        , FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`)
        );

If we now add photos into the mix, it gets more complicated.  One 
solution would be to create a separate Model PhotoComment, 
possibly inheriting from Comment.  But actually, a PhotoComment 
is the same as a Comment, so why have two models?  If we want to 
add more and more commentable Models we would also have to add 
more and more Comment Models, all doing essentially the same.

Another solution would be to extend the Comment Model so that it 
can associate either to a Post or to a Photo, this is the 
resulting schema:

        CREATE TABLE `comments`
        ( `id`               INTEGER UNSIGNED PRIMARY KEY
auto_increment
        , `title`            VARCHAR(255)     NOT NULL
        , `body`             TEXT             NOT NULL
        , `post_id`          INTEGER UNSIGNED NULL
        , `photo_id`         INTEGER UNSIGNED NULL
        , FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`)
        , FOREIGN KEY (`photo_id`) REFERENCES `photos`(`id`)
        );

If we want to add more and more commentable Models, we would have 
to add more and more associations to our Comment Model and 
foreign key columns to our `comments` table.

The way Rails' Active Record layer solves this, is via 
polymorphic (some would say "promiscous") associations.  It works 
by creating a kind of virtual interface out of thin air and using 
that to connect (in my example) comments to commentable objects.  
In your models you would say

        Comment belongs to Commentable, polymorphic

        Post has many Comments as Commentable

        Photo has many Comments as Commentable

Please note that there doesn't actually exist a Model 
"Commentable".  In the database, this is implemented using a 
similar technique as in Single Table Inheritance:

        CREATE TABLE `comments`
        ( `id`               INTEGER UNSIGNED PRIMARY KEY
auto_increment
        , `title`            VARCHAR(255)     NOT NULL
        , `body`             TEXT             NOT NULL
        , `commentable_id`   INTEGER UNSIGNED NOT NULL
        , `commentable_type` VARCHAR(255)     NOT NULL
        , UNIQUE KEY `commentable` (`commentable_id`,
`commentable_type`)
        );

Basically, `commentable_id` and `commentable_type` together form 
a foreign key to the table that corresponds to the Model name 
given in `commentable_type`, but that cannot be expressed in SQL.

>>  - Acts (acts allow you to decorate a model's behaviour with
>>      additional orthogonal aspects and encapsulate that behaviour
>>      in a single, central place)
> Nope...but I believe that is due to the fact that PHP does 
> not support closures, a key feature that makes all sorts of 
> awesome feature available in Rails.

I'm not sure that an implementation of Acts would actually 
require the sophisticated metaprogramming features of Ruby.  It 
sure would help a lot, but I don't think it's necessary.

>>  - join models
> Not sure.

Yeah, I'm also not sure why I asked that twice (-;

>>  - the migration DSL (in fact, all of Rails' DSLs, including but
>>      not limitied to the Model DSL)
> There's no migrations...but that would be an interesting project.
> Maybe I should look into this as it would be something very 
> interesting.

There is a migrations plugin somewhere on CakeForge, but its DSL 
is based on YAML, not PHP.  The interesting thing about Rails' 
migration DSL is that it is based on Ruby, which means that you 
have the full power of Ruby and Active Record at your fingertips 
when writing migrations.  Thus, you can not only do schema 
migrations but also very complex data migrations.  For example, 
if you have an ASIN column in your books table and want to change 
that to an ISBN column, you can actually use Ruby's Amazon.com 
webservice bindings to call out to Amazon and fetch the ISBNs 
from there, all in your migrations!  This is, of course, a pretty 
contrived and extreme example.  However, this is only possible 
because the migrations are written in Ruby instead of, say, 
SQL/DDL/DML, XML or YAML.

Personally, I think that Rails' DSLs are both one of the most 
important and most undervalued assets of Rails.

> Hope that helps.

Indeed it does!  Thank you very much!

jwm


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Cake 
PHP" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at http://groups.google.com/group/cake-php
-~----------~----~----~----~------~----~------~--~---

Reply via email to