CakePHP: Working with ‘Associations’
This is probably going to be the first of many postings of my exploration with CakePHP. This post will briefly look at the CakePHP’s ‘Associations’ feature. Associations is “the relational mapping provided by the [CakePHP] model”.
In non-Cake speak, we’re talking about standard SQL joins - the mapping of relations between SQL tables. As you may know, doing SQL joins can be quite messy with unwieldy SQL strings. Thankfully, CakePHP provides a very simple way of joining tables.
This tutorial will extend from the Cake Blog Tutorial, by adding comments to each blog post. So you need to be have completed the Cake Blog Tutorial if you want to ‘hands-on’.
In this tutorial, I won’t be showing add, edit, delete comment actions since it follows pretty much the same idea as add, edit and delete posts. I’m only going as far as displaying comments associated with each blog post.
According to the Cake docs, there are four types of associations within CakePHP. These are…
- hasOne
- hasMany
- belongsTo
- hasAndBelongsTo
Here, I’m just going to use the hasMany associations between posts and comments. We’re basically saying that each post hasMany comments.
Create the comments table
First thing to do is to create the comments table and fill it up with one or two rows of data. Execute the following SQL statements in your favourite MySQL DB Manager.
1 2 3 4 5 6 7 8 9 10 11 | CREATE TABLE `comments` ( `id` INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT , `body` TEXT NOT NULL , `post_id` INT( 10 ) NOT NULL , `created` DATETIME NULL , PRIMARY KEY ( `id` ) ); INSERT INTO `comments` (`id` ,`body` ,`post_id` ,`created` ) VALUES ( NULL , 'this is the first comment', '1', '2008-05-08 15:59:22'); INSERT INTO `comments` (`id` ,`body` ,`post_id` ,`created` ) VALUES ( NULL , 'this is the second comment', '1', '2008-05-08 16:38:20'); |
Create a Comment model
Now that you have the DB table and data rows in place, you can create a new Comment model. Cake’s models go into the /app/models directory. Create a file with the follow code and save it to /app/models/comment.php.
1 2 3 4 5 6 | <?php class Comment extends AppModel { var $name = 'Comment'; } ?> |
Create the association
The next thing to do is to create the hasMany association between Post and Comment models. I didn’t bother to do specify any Comments Controllers, since I won’t be implementing the add, edit, delete functions for comments.
Open up the /app/models/post.php file, and add the $hasMany definition to your existing post.php. You should have something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <?php class Post extends AppModel { var $name = 'Post'; //add the following $hasMany definition var $hasMany = array('Comment' => array('className' => 'Comment', 'conditions' => '', 'order' => 'Comment.created DESC', 'limit' => '5', 'foreignKey' => 'post_id', 'dependent' => true, 'exclusive' => false, 'finderQuery' => '', 'fields' => '', 'offset' => '', 'counterQuery' => '' ) ); var $validate = array( 'title' => VALID_NOT_EMPTY, 'body' => VALID_NOT_EMPTY ); } ?> |
let’s look at the $hasMany array in more detail:
- className (required) - is the name of the model we are associating with. In this case it’s the Comment model.
- conditions - is where you would specify any SQL conditions if required. This is like specifying the “WHERE” portion of a normal SQL statement.
- order - is where you define the sort order of the comments. This is equivalent to the “ORDER BY” portion of a standard SQL statement. In my case, I’m listing the comments in descending order based on comment created timestamp.
- limit - is where you define the number of comments to show, similar to “LIMIT” in a standard SQL statement. In my case, I’m specifying a maximum of 5 comments per post.
- foreignKey - is the name of the foreign key that points to the associated model, which is the post_id field in the comments table.
- dependant - if set to true, the associated Comment model is destroyed when the Post model is.
- exclusive - If set to true, all the associated objects are deleted in one SQL statement without having their beforeDelete callback run.
- finderQuery - This is a good way to do those complex associations, for example over multiple tables, if Cake’s automatic assocations aren’t working for you.
- fields - You can use this to specify only the fields from the associated model you wish to fetch. Since I didn’t specify anything, it will fetch all the fields from the Comment model.
- offset - This is number of records to skip before associating to the current model.
- counterQuery - Here you can use an SQL statement to define the number of records that should be associated from the Comment model.
Modify view.thtml to show comments
After you have specified the $hasMany array, the next thing to do is to change the /app/views/posts/view.thtml file to show comments after the post body.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <h1><?php echo $post['Post']['title']?></h1> <p><small>Created: <?php echo $post['Post']['created']?></small></p> <p><?php echo $post['Post']['body']?></p> <hr> <p>Comments:</p> <?php foreach ($post['Comment'] as $comment) { print '<p>'.$comment['created'].'<br/>'.$comment['body'].'<br/><br/></p>'; } ?> |
Done!
If all has gone well, you should get something like the following screen capture. There will only be comments for the first post, since we only specify post_id as ‘1′ in the comments table.

Final thoughts…
All in all, it took me about 30-40 mins to digest the documentation on associations, and to work out this simple example. I would say that’s pretty fast to go from zero to a working example of how table joins are done.
- CakePHP: Using scaffolding for rapid application building
- CodeIgniter: Extending the native ‘Model’ and make it your own.
- CakePHP: Baking in frustration
- Using comments in your PHP scripts
- Writing your first PHP script






A note for beginners, something that really helped me when I was first starting CakePHP is the pr() function.
If you add pr($variable) to your view, it will show your entire variable within a tag. Yes, it’s essentially php’s print_r() function, but pr() is shorter, easier, and it comes with a little more functionality than plain old print_r(). Check it out in the CakePHP API, the documentation explains everything.
Hi, how does the controller works here?
how can you sort the comments listing by a field in the post model/table?
@chacha:
Sorry this is a bit late. This post extends from the Blog application sample at http://manual.cakephp.org/view/326/the-cake-blog-tutorial, so the controller remains the same. Once associated, the comments becomes available.
@Shaun:
I know what you’re asking. I think when you make the call $this->Post->findAll() in your controller, you can specify the order. Check out the cake docs.
In any case, I’ve moved onto 1.2xx version, so the function calls are a bit different now. I think it would have been something like:
$this->Post->find(’all’,array(”order”=>”Comments.body DESC”)) to sort by the ‘body’ field. Not sure if it’s the same on 1.1.x version tho.
I am a newbie to cakephp.I am building the login page where the user can upload his album.For the first time the user will not see any album list and the page will display no album found and a link to upload album and album will be displayed in descending order by datecreated.I have albums_controller for album and users_controller through which i created the index page where those above mentioned information.I declared hasMany relationship on users model but don’t know to produce the album views on my User index page.Any help will be highly appreciated.
I believe I’ve followed the tutorials correctly for the hasMany relationship, but for some reason the data is not getting saved to the database table. The save is failing and I’m not sure how to tell what is causing the issue.
Anyone else seen this issue?
Here are some details:
Ticket Model with a hasMany to a Change model:
class Ticket extends AppModel
{
var $name = ‘Ticket’;
var $hasMany = array(’Change’ =>
array(’className’ => ‘Change’, ‘conditions’ => ”,
‘order’ => ‘Change.modified_date DESC’,
‘limit’ => ‘10′,
‘foreignKey’ => ‘ticket_id’,
‘dependent’ => ‘true’,
‘exclusive’ => ‘false’,
‘finderQuery’ => ”,
‘fields’ => ”,
‘offset’ => ”,
‘counterQuery’ => ”));
…
}
class Change extends AppModel
{
var $name = ‘Change’;
}
my ‘changes’ table has ‘id’ and ‘ticket_id’ columns, plus some others.
my ‘tickets’ table has ‘id’ and ‘change_id’ columns, plus some others.
I call (from the tickets_controller):
$this->Ticket->save($this->data);
(this saves good)
then I call (from the tickets_controller):
$this->Ticket->Change->save($this->data)
(this fails, not sure why, how can I extract the error)
Any tips are appreciated.
Thanks, Kevin
BTW, is it a problem the Cake framework if there are other [model]_id fields in that table but are not associated in the Model classes? Thanks!
I figured it out!
Your post has the title “Working with associations”. “Displaying associations” would be more correct, because there is no mention about creating/updating/destroying associations. Am I missing something?
zoli: The largest section of the post is entitled “Creating the association”. If you do anything with associations, you are working with associations; the title is accurate.
But thank you for taking the time to post. While the starving children of the world still won’t have anything to eat or a home that’s not a garbage can, they can face death knowing you will be out there on the internet, policing post titles. You’re a real hero.
To answer your question: you’ve missed everything.
Hi,
Does anyone know if you can put a VARIABLE in the ‘order’ portion of the Model definition? I want to put in an order like:
order by field(language, $_SESSION[’languageorder’])
where the order is specific to each session, and is a comma separated list of ids (3,1,4) for example. I cannot seem to use the variable in the code, it isn’t expecting the variable and gives an error….
Thanks
One more question, can you tell me how to do the FORM for a hasMany relationship to people can enter in MULTIPLE items at once, and so the save will work correctly? I have it written in PHP code, but want to take advantage of CAKE if possible.
The problem that I’m trying to solve is to input information for Name, Description, and Title for example, in 3 different languages on the same page. When I hit SAVE, I want it to update/add 3 records in the info table. I’m not sure if this is a good enough description of my problem, but if someone is willing to help me out I can give more info. Thanks in advance.