3. Associations

Associations are the glue between two related logical units. There are four types of associations in CakePHP:

Its best to explain associations using an example since they can get pretty confusing.

Let's say that you owned an extensive collection of books and one day you are sitting in your vast library when you decide that you want to catalog the whole thing. You decide that in your catalog you want to list the following for each book:

Now lets look at each type of association individually, using our example.

The hasOne relationship specifies that a logical unit has one and only one other logical unit of type X related to it. In our catalog example each book will have one and only one title.

The hasMany relationship specifies that a logical unit has many other logical units of type X related to it. In our catalog example each book could have many different authors.

The belongsTo relationship is the brother of hasOne and hasMany. A logical unit belongs to another logical unit of type X. An author belongs to a book . Notice how this association doesn't specify whether that book has many authors or only one. All you can tell from a belongsTo association is that this particular author belongs to this book. To find out if there were any other authors of this book you would have to look at the association from the books perspective. Does the book have a hasOne or hasMany relationship to authors?

The last relationship hasAndBelongsToMany specifies that a logical unit has many other logical units of type X and those units themselves have many recipricol units. This is probably the most confusing association, but can also be one of the most used Lets look at the catalog example to help us understand it better.

You would like to be able to categorize each book by the following categories:

Now lets say you have a book that is a biography of Alan Turing, this would belong to the categories of Biography and History, so you migh think this is a hasMany association, each book has many categories. However, the next book is an historical guide to the space program so this should be in the history category too. This breaks our hasMany rule, now each book can have many categories AND each category can belong to many books. This is the hasAndBelongsToMany association.

What does all this have to do with CakePHP? Cake can handle much of the blood, sweat, and tears that traditionally go into programming association with just a line of code in the controller.

Lets go back to our example. Rather than making a card for each book you decide to go modern and put the whole thing in a database.

Lets make a table called books, each book is an entry or record in the table and looks like this:

Title | Author(s) | Description | Categories

| | |

You start with the history of space exploration book and everything goes well. Then you get to the historical biography of Mr. Turing and find you have a problem. There is only one column for category but you have two categories that this book belongs to. You think there might be a couple solutions to this problem.

(work in progress... will be finished later)

<?php
/**
* @var mixed $belongsTo this can be a string or an array.
*
* Example below is using an array and setting more than one belongsTo.
*
* Using $belongsTo as an array gives you more control over the association
*
* association  - Table holding the association
* className    - Specify the class name of the association.
*                   Use it only if that name can not be inferred from the association name.
*                     So
*                       [code]
*                         var $belongsTo = array(array('Author'));
*                       [/code]
*                     will by default be linked to the Author class, but if the real class name is Person,
*                     you will have to specify it with this option.
* conditions   - Specify the conditions that the associated object must meet in order to be included
*                  as a "WHERE" SQL fragment, such as "authorized = 1".
* order        - Specify the order from which the associated object will be picked at the top.
                    Specified as an "ORDER BY" SQL fragment, such as "last_name, first_name DESC"
* foreignKey   - Specify the foreign key used for the association.
*                  By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
*                  So a Person class that makes a belongsTo association to a Boss class will use "boss_id" as the default foreign_key.
* counterCache - Caches the number of belonging objects on the associate class through use of increment_counter and decrement_counter.
*                  The counter cache is incremented when an object of this class is created and decremented when it is destroyed.
*                  This requires that a column named "#{table_name}_count" (such as comments_count for a belonging Comment class)
*                  is used on the associated class (such as a Post class).
*
* Setting $belongsTo to a string limits you a lot.
* To use $belongsTo as a string like this:
* [code]
* var $belongsTo = 'Association';
* [/code]
*
* You can also set more than one association in the string by separating them with a comma
* [code]
* var $belongsTo = 'Association,Association2,Association3';
* [/code]
*/
 
var $belongsTo           = array('Association1' =>
                       array('className'    => 'ClassName',
                             'conditions'   => 'these conditions',
                             'order'        => 'this order by DESC',
                             'foreignKey'   => 'foreign key',
                             'counterCache' => 'table name _count' ),
                                    
                             'Association2' =>
                       array('className'    => 'ClassName',
                             'conditions'   => 'these conditions',
                             'order'        => 'this order by',
                             'foreignKey'   => 'foreign key',
                             'counterCache' => 'table name _count' ),
                                    
                             'Association3' =>
                       array('className'    => 'ClassName',
                             'conditions'   => 'these conditions',
                             'order'        => 'this order by',
                             'foreignKey'   => 'foreign key',
                             'counterCache' => 'table name _count' ));
                                   
                                   
/**
* @var mixed $hasOne this can be a string or an array.
*
* Example below is using an array and setting more than one hasOne.
*
* Using $hasOne as an array gives you more control over the association
*
* association  - Table holding the association
* className    - Specify the class name of the association.
*                  Use it only if that name can not be inferred from the association name.
*                     So
*                       [code]
*                         var $hasOne = array(array('Manager'));
*                       [/code]
*                  will by default be linked to the Manager class, but if the real class name is Person,
*                you will have to specify it with this option.
* conditions   - Specify the conditions that the associated object must meet in order to be included
*                  as a "WHERE" sql fragment, such as "authorized = 1".
* order        - Specify the order from which the associated object will be picked at the top.
*                  Specified as an "ORDER BY" sql fragment, such as "last_name, first_name DESC"
* dependent    - If set to true, the associated object is destroyed when this object is.
*                  It is also destroyed if another association is assigned.
* foreignKey   - Specify the foreign key used for the association.
*                  By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
*                  So a Person class that makes a belongsTo association to a Boss class will use "boss_id" as the default foreign_key.
*
* Setting $belongsTo to a string limits you a lot.
* To use $hasOne as a string like this:
* [code]
* var $hasOne  = 'association';
* [/code]
*
* You can also set more than one association in the string by separating them with a comma
* [code]
* var $hasOne = 'Association,Association2,Association3';
* [/code]
*/
var $hasOne              = array('association1' =>
                       array('className'    => 'class',
                             'conditions'   => 'these conditions',
                             'order'        => 'this order by',
                             'dependent'    =>  true,
                             'foreignKey'   => 'foreign key'),
                                    
                             'association2' =>
                       array('className'    => 'class',
                             'conditions'   => 'these conditions',
                             'order'        => 'this order by',
                             'dependent'    =>  true,
                             'foreignKey'   => 'foreign key'),
                                    
                             'association3' =>
                       array('className'    => 'class',
                             'conditions'   => 'these conditions',
                             'order'        => 'this order by',
                             'dependent'    =>  true,
                             'foreignKey'   => 'foreign key'));
                                           
                                    
/**
* @var mixed $hasMany this can be a string or an array.
*
* Example below is using an array and setting more than one hasMany.
*
* Using hasMany as an array gives you more control over the association
*
* association - Table holding the association
* className   - Specify the class name of the association.
*                  Use it only if that name can not be inferred from the association name.
*                     So
*                       [code]
*                         var $hasMany = array(array('products'));
*                       [/code]
*                  will by default be linked to the Product class, but if the real class name is SpecialProduct,
*                  you'll have to specify it with this option.
* conditions  - Specify the conditions that the associated objects must meet in order to be included as a "WHERE" sql fragment,
*                such as "price > 5 AND name LIKE 'B%'".
* order       - Specify the order in which the associated objects are returned as a "ORDER BY" sql fragment,
*                 such as "last_name, first_name DESC"
* foreignKey  - Specify the foreign key used for the association.
*                 By default this is guessed to be the name of this class in lower-case and "_id" suffixed.
*                 So a Person class that makes a has_many association will use "person_id" as the default foreign_key.
* dependent   - If set to true all the associated object are destroyed alongside this object.
*                 May not be set if exclusive is also set.
* exclusive   - If set to true all the associated object are deleted in one SQL statement without having their beforeBestroy callback run.
*                 This should only be used on associations that depend solely on this class and don not need to do any clean-up in beforeDestroy.
*                 The upside is that it's much faster, especially if there's a counterCache involved.
*                 May not be set if dependent is also set.
* finderSql   - Specify a complete SQL statement to fetch the association.
*                 This is a good way to go for complex associations that depends on multiple tables.
*                 Note: When this option is used, findInCollection is not used.
* counterSql  - Specify a complete SQL statement to fetch the size of the association.
*                 If finderSql is specified but counterSql, counterSql will be generated by
*                 replacing SELECT * FROM with SELECT COUNT(*) FROM.
*
*
* Setting $hasMany to a sting limits you alot.
* To use $hasMany as a string like this:
* [code]
* var $hasMany  = 'association';
* [/code]
*
* You can also set more than one association in the string by seperating them with a coma
* [code]
* var $hasMany = 'association,association2,association3';
* [/code]
*/                                    
var $hasMany             = array('association1' =>
                       array('className'    => 'class',
                             'conditions'   => 'these conditions',
                             'order'        => 'order by',
                             'foreignKey'   => 'foreign key',
                             'dependent'    =>  true,
                             'exclusive'    =>  false,
                             'finderSql'    => 'custom SQL',
                             'counterSql'   => 'custom SQL'),
                                   
                             'association2' =>
                       array('className'    => 'class',
                             'conditions'   => 'these conditions',
                             'order'        => 'order by',
                             'foreignKey'   => 'foreign key',
                             'dependent'    =>  false,
 
                             'exclusive'    =>  true,
                             'finderSql'    => 'custom SQL',
                             'counterSql'   => 'custom SQL'),
                                      
                             'association3' =>
                       array('className'    => 'class',
                             'conditions'   => 'these conditions',
                             'order'        => 'order by',
                             'foreignKey'   => 'foreign key',
                             'dependent'    =>  true,
                             'exclusive'    =>  false,
                             'finderSql'    => 'custom SQL',
                             'counterSql'   => 'custom SQL'));                                
                                    
/**
* @var mixed $hasAndBelongsToMany this can be a string or an array.
*
* Example below is using an array and setting more than one hasAndBelongsToMany.
*
* Using hasAndBelongsToMany as an array gives you more control over the association
*
* association - Table holding the association
* className - Specify the class name of the association.
*                Use it only if that name can not be inferred from the association name.
*                   So
*                       [code]
*                         var $hasAndBelongsToMany = array(array('projects'));
*                       [/code]
*                    will by default be linked to the Project class, but if the real class name is SuperProject,
*                    you will have to specify it with this option.
* joinTable - Specify the name of the join table if the default based on lexical order is not what you want.
*               WARNING: If you're overwriting the table name of either class,
*               the tableName method MUST be declared underneath any hasAndBelongsToMany declaration in order to work.
* foreignKey - Specify the foreign key used for the association.
*                By default this is guessed to be the name of this class in lower-case and "_id" suffixed.
*                So a Person class that makes a has_and_belongs_to_many association will use "person_id" as the default foreign_key.
* associationForeignKey - Specify the association foreign key used for the association.
*                           By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
*                           So the associated class is Project that makes a hasAndBelongsToMany association will use "project_id" as the default association foreignKey.
* conditions - Specify the conditions that the associated object must meet in order to be included as a "WHERE" SQL fragment, such as "authorized = 1".
* order - Specify the order in which the associated objects are returned as an "ORDER BY" SQL fragment, such as "last_name, first_name DESC"
* uniq - If set to true, duplicate associated objects will be ignored by accessors and query methods
* finderSql - Overwrite the default generated SQL used to fetch the association with a custom one
* deleteSql - Overwrite the default generated SQL used to remove links between the associated classes with a custom one
* insertSql - Overwrite the default generated SQL used to add links between the associated classes with a custom one
*
*
* Setting $hasAndBelongsToMany to a string limits you a lot.
* To use $hasAndBelongsToMany as a string like this:
* [code]
* var $hasAndBelongsToMany  = 'Association';
* [/code]
*
* You can also set more than one association in the string, by separating them with a comma:
* [code]
* var $hasAndBelongsToMany = 'Association,Association2,Association3';
* [/code]
*/                        
var $hasAndBelongsToMany = array('Association1'          =>
                       array('className'             => 'ClassNameInCamelCase',
                             'joinTable'             => 'table to join on',
                             'foreignKey'            => 'foreign key',
                             'associationForeignKey' => 'foreign key',
                             'conditions'            => 'these conditions',
                             'order'                 => 'ORDER BY',
                             'uniq'                  =>  true,
                             'finderQuery'           => 'custom SQL',
                             'deleteQuery'           => 'custom SQL',
                             'insertQuery'           => 'custom SQL'),
 
                             'association2'  =>
                       array('className'             => 'class',
                             'joinTable'             => 'table to join on',
                             'foreignKey'            => 'foreign key',
                             'associationForeignKey' => 'foreign key',
                             'conditions'            => 'these conditions',
                             'order'                 => 'ORDER BY',
                             'uniq'                  =>  true,
                             'finderQuery'           => 'custom SQL',
                             'deleteQuery'           => 'custom SQL',
                             'insertQuery'           => 'custom SQL'),
 
                             'association3'          =>
                       array('className'             => 'class',
                             'joinTable'             => 'table to join on',
                             'foreignKey'            => 'foreign key',
                             'associationForeignKey' => 'foreign key',
                             'conditions'            => 'these conditions',
                             'order'                 => 'ORDER BY',
                             'uniq'                  =>  true,
                             'finderQuery'           => 'custom SQL',
                             'deleteQuery'           => 'custom SQL',
                             'insertQuery'           => 'custom SQL'));                                
 
 
?>