CakePHP Relationships Explained

February 20th, 2011 Leave a comment 3 comments
Like the article?
CakePHP Relationships

If you’ve developed with SQL databases in the past, you’ve probably had experience with the various SQL relationship types: one-to-one, one-to-many and many-to-many. In CakePHP, each database table is typically associated with a model. The relationships between the items in these tables are referred to as associations in CakePHP. CakePHP has an association for each type of relationship. We’re going to look at the four CakePHP associations, how they relate to the traditional SQL relationship and how to work with them in a CakePHP application. To describe these associations, we’ll be using a real world example – a blog application.

hasOne

The hasOne association is how a one-to-one relationship is handled within CakePHP. Looking at our real world example, each author has a bio. This is the typical one-to-one relationship. Each bio is only associated with a single author and each author is only associated with a single bio. When you setup a hasOne association, the other model will contain a foreign key. In our example, the bio table should have an author_id field as a foreign key. Defining the relationship in CakePHP would look something like this:

<?php
class Author extends AppModel {
  var $name = 'Author';
  var $hasOne = 'Bio';
}
?>

Once this association is defined, a find call to retrieve an author will an array with the Author record as an array and the Bio record as an array.

belongsTo

The belongsTo association is a special case in CakePHP. It allows us to link the model on the opposite side of a many-to-one or one-to-one relationship. So in our earlier example where an author has one bio, a bio also belongs to an author. When setting up the database table, the foreign key is part of the current model’s table. So again, the bio table will include a reference to the author_id. Setting this association up in CakePHP looks like this:

<?php 
class Bio extends AppModel {
  var $name = 'Bio';
  var $belongsTo = 'Author';
}
?>

Doing this causes the find method for Bio to include the associated Author record as well. This can be very convenient as you can now retrieve the related record from either side of the relationship.

hasMany

The hasMany association is how CakePHP handles a traditional one-to-many relationship. Again in our example, an author may write many blog posts. This results in a one-to-many relationship between the author and his or her blog posts. In a hasMany association, the other table contains the foreign key. So if an author has many blog posts, the post table will contain a foreign key called author_id. Setting this relationship up in CakePHP looks like this:

<?php
class Author extends AppModel {
  var $name = 'Author';
  var $hasOne = 'Bio';
  var $hasMany = 'Post';
}
?>

Notice that models can have more than one relationship. When a hasMany relationship is defined, a the find method for the Author model will return all the associated Post records as well. To map the other side of this relationship, you could add a belongsTo relationship on the Post side. Remember that belongsTo can be used to map the opposite direction of a one-to-many as well as a one-to-one relationship. My mapping the other side, you can access the author data from a Post object. Here’s a sample of how to do that:

<?php
class Post extends AppModel {
  var $name = 'Post';
  var $belongsTo = 'Author';
}
?>

hasAndBelongsToMany (HABTM)

The hasAndBelongsToMany association is how CakePHP handles a many-to-many relationship. For example, a blog post can have many tags and a tag can have many blog posts. Typically in SQL, we use a join table to manage a many-to-many relationship. This table contains the ids of both related models as well as possibly a primary id for that table.

The convention in CakePHP is for the join table to be named using the names of the two related models in alphabetical order and separated by an underscore. So for our example, the table for managing this relationship would be posts_tags and it would consist of three fields: id, posts_id and tags_id.

To define this relationship, we use an array syntax. It looks something like this:

<?php
class Post extends AppModel {
  var $name = 'Post';
  var $belongsTo = 'Author';
  var $hasAndBelongsToMany = array(
    'className' => 'Tag',
    'joinTable' => 'posts_tags',
    'foreignKey' => 'post_id',
    'associationForeignKey' => 'tag_id'
  );
}
?>

The foreignKey in this array refers to the key in the join table that references the current model. The associationForeignKey indicates the key in the join table that references the associated model. If you want to be able to access posts from the tag model, you’ll need to set up a hasAndBelongsToMany association from the Tag model as well.

When you save HABTM models, you need to set the id of the associated model when saving a new one. For our post model, we’d set the tag_id prior to saving the post. This would cause the association to be saved. One important caveat, when saving a HABTM association, CakePHP will delete all the related rows from the join table prior to saving. So if a post has 10 tags and you now save it with 2, the post will now contain 2 tags, not 12 like you might expect.

There is a work around for this behavior. You can think of a HABTM association as a third model. Each of the related models has a hasMany and belongsTo association with this third model. You can define this third model and create the hasMany and belongsTo associations between it and your other 2 models. In fact, if your join table contains additional fields, this is the simplest way to handle a HABTM relationship.

We’ve quickly gone over the basic associations between models in CakePHP and looked at how they relate to the traditional SQL relationships of one-to-one, one-to-many, many-to-one and many-to-many. You can now use this to better understand and define the relationships between your CakePHP models.

Help us spread the word!
  • Twitter
  • Facebook
  • LinkedIn
  • Pinterest
  • Delicious
  • DZone
  • Reddit
  • Sphinn
  • StumbleUpon
  • Google Plus
  • RSS
  • Email
  • Print
If you liked this article, consider enrolling in one of these related courses:
Don't miss another post! Receive updates via email!

3 comments

  1. Alidine says:

    Hi Michael, very useful post.
    Thanks

  2. ibex says:

    Thanks for the clean and useful post. Keep up the good work!

  3. pardeep says:

    thanks..very simple and easy to understandable.

Comment