CakePHP 1.3: Model Bindings

Exclusive offer: get 50% off this eBook here
CakePHP 1.3 Application Development Cookbook

CakePHP 1.3 Application Development Cookbook — Save 50%

Over 70 great recipes for developing, maintaining, and deploying web applications

$23.99    $12.00
by Mariano Iglesias | March 2011 | Open Source PHP

This article deals with one of the most important aspects of a CakePHP application: the relationship between models, also known as model bindings or associations. Being an integral part of any application's logic, it is of crucial importance that we master all aspects of how model bindings can be manipulated to get the data we need, when we need it.

In this article Mariano Iglesias, author of CakePHP 1.3 Application Development Cookbook, we will cover:

  • Adding Containable to all models
  • Limiting the bindings returned in a find
  • Modifying binding parameters for a find
  • Modifying binding conditions for a find
  • Changing the JOIN type of one-to-one associations
  • Defining multiple associations to the same model

 

CakePHP 1.3 Application Development Cookbook

CakePHP 1.3 Application Development Cookbook

Over 70 great recipes for developing, maintaining, and deploying web applications

        Read more about this book      

(For more resources on CakePHP, see here.)

Introduction

This article deals with one of the most important aspects of a CakePHP application: the relationship between models, also known as model bindings or associations.

Being an integral part of any application's logic, it is of crucial importance that we master all aspects of how model bindings can be manipulated to get the data we need, when we need it.

In order to do so, we will go through a series of recipes that will show us how to change the way bindings are fetched, what bindings and what information from a binding is returned, how to create new bindings, and how to build hierarchical data structures.

Adding Containable to all models

The Containable behavior is a part of the CakePHP core, and is probably one of the most important behaviors we have to help us deal with model bindings.

Almost all CakePHP applications will benefit from its functionalities, so in this recipe we see how to enable it for all models.

How to do it...

Create a file named app_model.php and place it in your app/ folder, with the following contents. If you already have one, make sure that either you add the actsAs property shown as follows, or that your actsAs property includes Containable.

<?php
class AppModel extends Model {
public $actsAs = array('Containable');
}
?>

How it works...

The Containable behavior is nothing more and nothing less than a wrapper around the bindModel() and unbindModel() methods, defined in the CakePHP's Model class. It is there to help us deal with the management of associations without having to go through a lengthy process of redefining all the associations when calling one of these methods, thus making our code much more readable and maintainable.

This is a very important point, because a common mistake CakePHP users make is to think that Containable is involved in the query-making process, that is, during the stage where CakePHP creates actual SQL queries to fetch data.

Containable saves us some unneeded queries, and optimizes the information that is fetched for each related model, but it will not serve as a way to change how queries are built in CakePHP.

Limiting the bindings returned in a find

This recipe shows how to use Containable to specify what related models are returned as a result of a find operation. It also shows us how to limit which fields are obtained for each association.

Getting ready

To go through this recipe we need some sample tables to work with.

  1. Create a table named families, using the following SQL statement:

    CREATE TABLE `families`(
    `id` INT UNSIGNED AUTO_INCREMENT NOT NULL,
    `name` VARCHAR(255) NOT NULL,
    PRIMARY KEY(`id`)
    );

  2. Create a table named people, using the following SQL statement:

    CREATE TABLE `people`(
    `id` INT UNSIGNED AUTO_INCREMENT NOT NULL,
    `family_id` INT UNSIGNED NOT NULL,
    `name` VARCHAR(255) NOT NULL,
    `email` VARCHAR(255) NOT NULL,
    PRIMARY KEY(`id`),
    KEY `family_id`(`family_id`),
    CONSTRAINT `people__families` FOREIGN KEY(`family_id`)
    REFERENCES `families`(`id`)
    );

  3. Create a table named profiles, using the following SQL statement:

    CREATE TABLE `profiles`(
    `id` INT UNSIGNED AUTO_INCREMENT NOT NULL,
    `person_id` INT UNSIGNED NOT NULL,
    `website` VARCHAR(255) default NULL,
    `birthdate` DATE default NULL,
    PRIMARY KEY(`id`),
    KEY `person_id`(`person_id`),
    CONSTRAINT `profiles__people` FOREIGN KEY(`person_id`)
    REFERENCES `people`(`id`)
    );

  4. Create a table named posts, using the following SQL statement:

    CREATE TABLE `posts`(
    `id` INT UNSIGNED AUTO_INCREMENT NOT NULL,
    `person_id` INT UNSIGNED NOT NULL,
    `title` VARCHAR(255) NOT NULL,
    `body` TEXT NOT NULL,
    `created` DATETIME NOT NULL,
    `modified` DATETIME NOT NULL,
    PRIMARY KEY(`id`),
    KEY `person_id`(`person_id`),
    CONSTRAINT `posts__people` FOREIGN KEY(`person_id`)
    REFERENCES `people`(`id`)
    );

    Even if you do not want to add foreign key constraints to your tables, make sure you use KEYs for each field that is a reference to a record in another table. By doing so, you will significantly improve the speed of your SQL queries when the referenced tables are joined.

  5. Add some sample data, using the following SQL statements:

    INSERT INTO `families`(`id`, `name`) VALUES
    (1, 'The Does');
    INSERT INTO `people`(`id`, `family_id`, `name`, `email`) VALUES
    (1, 1, 'John Doe', 'john.doe@example.com'),
    (2, 1, 'Jane Doe', 'jane.doe@example.com');
    INSERT INTO `profiles`(`person_id`,`website`,`birthdate`) VALUES
    (1, 'http://john.example.com', '1978-07-13'),
    (2, NULL, '1981-09-18');
    INSERT INTO `posts`(`person_id`, `title`, `body`, `created`,
    `modified`) VALUES
    (1, 'John\'s Post 1', 'Body for John\'s Post 1', NOW(),
    NOW()),
    (1, 'John\'s Post 2', 'Body for John\'s Post 2', NOW(),
    NOW());

  6. We need Containable added to all our models.
  7. We proceed now to create the main model. Create a file named person.php and place it in your app/models folder with the following contents:

    <?php
    class Person extends AppModel {
    public $belongsTo = array('Family');
    public $hasOne = array('Profile');
    public $hasMany = array('Post');
    }
    ?>

  8. Create the model Family in a file named family.php and place it in your app/models folder with the following contents:

    <?php
    class Family extends AppModel {
    public $hasMany = array('Person');
    }
    ?>

How to do it...

When Containable is available for our models, we can add a setting to the find operation called contain. In that setting we specify, in an array-based hierarchy, the associated data we want returned. A special value contain can receive is false, or an empty array, which tells Containable not to return any associated data.

For example, to get the first Person record without associated data, we simply do:

$person = $this->Person->find('first', array(
'contain' => false
));

Another way to tell CakePHP not to obtain related data is through the use of the recursive find setting. Setting recursive to -1 will have exactly the same effect as setting contain to false.

If we want to obtain the first Person record together with the Family they belong to, we do:

$person = $this->Person->find('first', array(
'contain' => array('Family')
));

Using our sample data, the above query will result in the following array structure:

array(
'Person' => array(
'id' => '1',
'family_id' => '1',
'name' => 'John Doe',
'email' => 'john.doe@example.com'
),
'Family' => array(
'id' => '1',
'name' => 'The Does'
)
)

Let's say that now we also want to obtain all Post records for the person and all members in the family that Person belongs to. We would then have to do:

$person = $this->Person->find('first', array(
'contain' => array(
'Family.Person'
'Post'
)
));

The above would result in the following array structure (the created and modified fields have been removed for readability):

array(
'Person' => array(
'id' => '1',
'family_id' => '1',
'name' => 'John Doe',
'email' => 'john.doe@example.com'
),
'Family' => array(
'id' => '1',
'name' => 'The Does',
'Person' => array(
array(
'id' => '1',
'family_id' => '1',
'name' => 'John Doe',
'email' => 'john.doe@example.com'
),
array(
'id' => '2',
'family_id' => '1',
'name' => 'Jane Doe',
'email' => 'jane.doe@example.com'
)
)
),
'Post' => array(
array(
'id' => '1',
'person_id' => '1',
'title' => 'John\'s Post 1',
'body' => 'Body for John\'s Post 1'
),
array(
'id' => '2',
'person_id' => '1',
'title' => 'John\'s Post 2',
'body' => 'Body for John\'s Post 2'
)
)
)

We can also use Containable to specify which fields from a related model we want to fetch. Using the preceding sample, let's limit the Post fields so we only return the title and the Person records for the person's Family, so we only return the name field. We do so by adding the name of the field to the associated model hierarchy:

$person = $this->Person->find('first', array(
'contain' => array(
'Family.Person.name',
'Post.title'
)
));

The returned data structure will then look like this:

array(
'Person' => array(
'id' => '1',
'family_id' => '1',
'name' => 'John Doe',
'email' => 'john.doe@example.com'
),
'Family' => array(
'id' => '1',
'name' => 'The Does',
'Person' => array(
array(
'name' => 'John Doe',
'family_id' => '1',
'id' => '1'
),
array(
'name' => 'Jane Doe',
'family_id' => '1',
'id' => '2'
)
)
),
'Post' => array(
array(
'title' => 'John\'s Post 1',
'id' => '1',
'person_id' => '1'
),
array(
'title' => 'John\'s Post 2',
'id' => '2',
'person_id' => '1'
)
)
)

You may notice that even when we indicated specific fields for the Family => Person binding, and for the Post binding, there are some extra fields being returned. Those fields (such as family_id) are needed by CakePHP, and known as foreign key fields, to fetch the associated data, so Containable is smart enough to include them in the query.

Let us say that we also want a person's e-mail. As there is more than a field needed, we will need to use the array notation, using the fields setting to specify the list of fields:

$person = $this->Person->find('first', array(
'contain' => array(
'Family' => array(
'Person' => array(
'fields' => array('email', 'name')
)
),
'Post.title'
)
));

How it works...

We use the contain find setting to specify what type of containment we want to use for the find operation. That containment is given as an array, where the array hierarchy mimics that of the model relationships. As the hierarchy can get deep enough to make array notation complex to deal with, the dot notation used throughout this recipe serves as an useful and more readable alternative.

If we want to refer to the model Person that belongs to the model Family, the proper contain syntax for that is Person => Family (we can also use Person.Family, which is more concise.)

We also use the fields setting to specify which fields we want fetched for a binding. We do that by specifying an array of field names as part of the binding Containable setting.

Containable looks for the contain find setting right before we issue a find operation on a model. If it finds one, it alters the model bindings to be returned by issuing unbindModel() calls on the appropriate models to unbind those relationships that are not specified in the contain find setting. It then sets the recursive find setting to the minimum value required to fetch the associated data.

Let us use a practical example to further understand this wrapping process. Using our Person model (which has a belongsTo relationship to Family, a hasOne relationship to Profile, and a hasMany relationship to Post), the following Containable based query:

$person = $this->Person->find('first', array(
'contain' => array('Family.Person')
));

or the same query using array notation:

$person = $this->Person->find('first', array(
'contain' => array('Family' => 'Person')
));

is equivalent to the following set of instructions, which do not use Containable, but the built in unbindModel() method available in CakePHP's Model class:

$this->Person->unbindModel(array(
'hasOne' => array('Profile'),
'hasMany' => array('Post')
));
$person = $this->Person->find('first', array(
'recursive' => 2
));

Not using Containable is not only much more complicated, but can also pose a problem if we decide to alter some of our relationships. In the preceding example, if we decide to remove the Profile binding, or change its relationship type, we would have to modify the unbindModel() call. However, if we are using Containable, the same code applies, without us having to worry about such changes.

Format of the contain find parameter

We have seen how to use the contain find parameter to limit which bindings are returned after a find operation. Even when its format seems self-explanatory, let us go through another example to have a deeper understanding of Containable's array notation. Assume that we have the models and relationships shown in the following diagram:

CakePHP 1.3 Application Development

Transforming that diagram to something the Containable behavior understands is as simple as writing it using an array structure. For example, if we are issuing a find operation on the User model and we want to refer to the Profile relationship, a simple array('Profile') expression would suffice, as the Profile model is directly related to the User model.

If we want to refer to the Comment relationship for the Article records the User is an owner of, which belongs to an Article that itself belongs to our User model, then we add another dimension to the structure, which is now represented as array('Article' => 'Comment').

We can already deduce how the next example will look like. Assume we want to obtain the Comment together with the Profile of the User that commented on each Article. The structure will then look like: array('Article' => array('Comment' => array('User' => 'Profile'))).

Sometimes we want to simplify the readability, and fortunately the Containable behavior allows the above expression to be rewritten as array('Article.Comment.User.Profile'), which is known as dot notation. However, if you want to change other parameters to the binding, then this syntax would have to be changed to the full array-based expression.

Reset of binding changes

When you issue a find operation that uses the Containable behavior to change some of its bindings, CakePHP will reset all bindings' changes to their original states, once the find is completed. This is what is normally wanted on most cases, but there are some scenarios where you want to keep your changes until you manually reset them, such as when you need to issue more than one find operation and have all those finds use the modified bindings.

To force our binding changes to be kept, we use the reset option in the contain find parameter, setting it to false. When we are ready to reset them, we issue a call to the resetBindings() method added by the Containable behavior to our model. The following sample code shows this procedure:

$person = $this->Person->find('first', array(
'contain' => array(
'reset' => false,
'Family'
)
));
// ...
$this->Person->resetBindings();

Another way to achieve the same result is by calling the contain() method (setting its first argument to the contained bindings, and its second argument to false to indicate that we wish to keep these containments), available to all models that use Containable, issue the find (without, need to use the contain setting), and then reset the bindings:

$this->Person->contain(array('Family'), false);
$person = $this->Person->find('first');

// ...
$this->Person->resetBindings();

 

CakePHP 1.3 Application Development Cookbook Over 70 great recipes for developing, maintaining, and deploying web applications
Published: March 2011
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

 

        Read more about this book      

(For more resources on CakePHP, see here.)

Modifying binding parameters for a find

This recipe shows how to use Containable to change some of the parameters that affect model bindings.

Getting ready

To go through this recipe, we need some sample tables to work with.

  1. Create a table named users, using the following SQL statement:

    CREATE TABLE `users`(
    `id` INT UNSIGNED AUTO_INCREMENT NOT NULL,
    `name` VARCHAR(255) NOT NULL,
    `email` VARCHAR(255) NOT NULL,
    PRIMARY KEY(`id`)
    );

  2. Create a table named profiles, using the following SQL statement:

    CREATE TABLE `profiles`(
    `id` INT UNSIGNED AUTO_INCREMENT NOT NULL,
    `user_id` INT UNSIGNED NOT NULL,
    `website` VARCHAR(255) default NULL,
    `birthdate` DATE default NULL,
    PRIMARY KEY(`id`),
    KEY `user_id`(`user_id`),
    CONSTRAINT `profiles__users` FOREIGN KEY(`user_id`)
    REFERENCES `users`(`id`)
    );

  3. Create a table named articles, using the following SQL statement:

    CREATE TABLE `articles`(
    `id` INT UNSIGNED AUTO_INCREMENT NOT NULL,
    `user_id` INT UNSIGNED NOT NULL,
    `title` VARCHAR(255) NOT NULL,
    `body` TEXT NOT NULL,
    `published` TINYINT NOT NULL default 1,
    `created` DATETIME NOT NULL,
    `modified` DATETIME NOT NULL,
    PRIMARY KEY(`id`),
    KEY `user_id`(`user_id`),
    CONSTRAINT `articles__users` FOREIGN KEY(`user_id`)
    REFERENCES `users`(`id`)
    );

  4. Add some sample data, using the following SQL statements:

    INSERT INTO `users`(`id`, `name`, `email`) VALUES
    (1, 'John Doe', 'john.doe@example.com'),
    (2, 'Jane Doe', 'jane.doe@example.com');

    INSERT INTO `profiles`(`user_id`, `website`, `birthdate`) VALUES
    (1, 'http://john.example.com', '1978-07-13'),
    (2, NULL, '1981-09-18');

    INSERT INTO `articles`(`user_id`, `title`, `body`, `published`,
    `created`, `modified`) VALUES
    (1, 'John\'s Post 1', 'Body for John\'s Post 1', 1, NOW(),
    NOW()),
    (1, 'John\'s Post 2', 'Body for John\'s Post 2', 1, NOW(),
    NOW()),
    (1, 'John\'s Post 3', 'Body for John\'s Post 3', 0, NOW(),
    NOW()),
    (1, 'John\'s Post 4', 'Body for John\'s Post 4', 1, NOW(),
    NOW()),
    (2, 'Jane\'s Post 1', 'Body for Jane\'s Post 1', 1, NOW(),
    NOW());

  5. Add the Containable behavior to all your models.
  6. Now we need to create the main model. Create a file named user.php and place it in your app/models folder with the following contents:

    <?php
    class User extends AppModel {
    public $hasOne = array('Profile');
    public $hasMany = array('Article');
    }
    ?>

How to do it...

If we want to obtain the first User record together with the Article records that the User owns, but ordered by latest articles first, we use the order binding setting (we also use the fields setting to limit the fields returned for each Article):

$user = $this->User->find('first', array(
'contain' => array(
'Article' => array(
'fields' => array('Article.title'),
'order' => array(
'Article.created' => 'desc',
'Article.id' => 'desc'
)
)
)
));

Using our sample data, the above query will result in the following array structure:

array(
'User' => array(
'id' => '1',
'name' => 'John Doe',
'email' => 'john.doe@example.com',
),
'Article' => array(
array(
'title' => 'John\'s Post 4',
'user_id' => '1'
),
array(
'title' => 'John\'s Post 3',
'user_id' => '1'
),
array(
'title' => 'John\'s Post 2',
'user_id' => '1'
),
array(
'title' => 'John\'s Post 1',
'user_id' => '1'
)
)
)

If we want to get the same data, but make sure we only obtain the latest Article a User has written, we use the limit binding setting:

$user = $this->User->find('first', array(
'contain' => array(
'Article' => array(
'fields' => array('Article.title'),
'order' => array(
'Article.created' => 'desc',
'Article.id' => 'desc'
),
'limit' => 1
)
)
));

Using our sample data, the above query will result in the following array structure:

array(
'User' => array(
'id' => '1',
'name' => 'John Doe',
'email' => 'john.doe@example.com',
),
'Article' => array(
array(
'title' => 'John\'s Post 4',
'user_id' => '1'
)
)
)

Another option that is useful on some scenarios is offset, applicable to the hasMany and hasAndBelongsToMany bindings. Using the example above, we now want to obtain the two most recent articles a User created, after the latest Article.

$user = $this->User->find('first', array(
'contain' => array(
'Article' => array(
'fields' => array('Article.title'),
'order' => array(
'Article.created' => 'desc',
'Article.id' => 'desc'
),
'limit' => 2,
'offset' => 1
)
)
));

The returned data structure now looks like this:

array(
'User' => array(
'id' => '1',
'name' => 'John Doe',
'email' => 'john.doe@example.com',
),
'Article' => array(
array(
'title' => 'John\'s Post 3',
'user_id' => '1'
),
array(
'title' => 'John\'s Post 2',
'user_id' => '1'
)
)
)

How it works...

The Containable behavior uses the built-in bindModel() method defined in CakePHP's Model class to alter the binding settings defined in the contain find setting.

It goes through the defined bindings and checks to see whether there are defined binding settings. If there are, it passes them to the bindModel() method for each of the specified bindings.

Some binding settings make sense only on some relationship types. For example, the limit setting used previously would not be useful on belongsTo or hasOne relationships.

The following list includes which settings can be specified for each relationship type:

  • belongsTo: className, conditions, foreignKey, order.
  • hasOne: className, conditions, foreignKey, order.
  • hasMany: className, conditions, finderQuery, foreignKey, limit, offset, order.
  • hasAndBelongsToMany: associationForeignKey, className, conditions, deleteQuery, finderQuery, foreignKey, insertQuery, joinTable, limit, offset, order, unique, with.

Modifying binding conditions for a find

This recipe shows how to use Containable to change the conditions used to fetch data related to a model through a binding.

Getting ready

We need to have Containable added to our models, and we also need some sample models and data to work with.

How to do it...

If we want to obtain the first User record together with the published Article records that user owns, but ordered by latest articles first, and limiting some of the returned fields, we use the conditions binding setting:

$user = $this->User->find('first', array(
'contain' => array(
'Article' => array(
'fields' => array('Article.title'),
'conditions' => array(
'Article.published' => 1
)
)
)
));

Using our sample data, the preceding query will result in the following array structure:

array(
'User' => array(
'id' => '1',
'name' => 'John Doe',
'email' => 'john.doe@example.com',
),
'Article' => array(
array(
'title' => 'John\'s Post 1',
'user_id' => '1'
),
array(
'title' => 'John\'s Post 2',
'user_id' => '1'
),

How it works...

The conditions binding setting is another binding parameter, such as those shown in the recipe. As such, the Containable behavior uses the built-in bindModel() method defined in CakePHP's Model class to alter the binding conditions defined in the contain find operation.

Changing the JOIN type of one-to-one associations

When we are querying a model that has other associated models, CakePHP will issue a new query to fetch the associated data, or use a LEFT JOIN SQL statement if the associated model has a one-to-one relationship with the main model (through a binding defined with belongsTo or hasOne.)

However there are times where we need to change the join type for one-to-one associations, to use either a RIGHT JOIN or an INNER JOIN. This recipe shows us how to change the join type for belongsTo and hasOne associations.

Getting ready

Follow the Getting ready section of the recipe, Limiting the bindings returned in a find.

How to do it...

  1. Edit the Person model, and change the binding definitions for belongsTo and hasOne associations, as shown below:

    <?php
    class Person extends AppModel {
    public $belongsTo = array('Family' => array('type' =>
    'INNER'));
    public $hasOne = array('Profile' => array('type' =>
    'RIGHT'));
    public $hasMany = array('Post');
    }
    ?>

How it works...

When we add bindings to a model, we can pass an array of settings to the binding definition to configure different aspects of the binding. One of those settings is type, only applicable to belongsTo and hasOne bindings.

The type setting allows us to define what type of JOIN CakePHP will use when fetching the associated model (only when querying the main model.) The available JOIN types are:

  • INNER JOIN: Joins and only returns records from associated models that match the default join condition. When a binding is set to use this join type, only records that have a record for the binding will be returned. In the example above, only Person records that belong to a Family will be returned.
  • LEFT JOIN: This is the default join type used by CakePHP. All records are returned even if there is no record for the binding. In the example above, if the Family binding type is set to LEFT, then Person records will be returned even if they don't belong to a Family.
  • RIGHT JOIN: The opposite of LEFT JOIN, shows all records from the related model even if they are not related to the main model, and shows only records in the main model that are linked to the related model.

Defining multiple associations to the same model

This recipe shows how to set up more than one association from one model to the same model, a need that normally arises on most applications.

Getting ready

To go through this recipe we need some sample tables to work with.

  1. Create a table named addresses, using the following SQL statement:

    CREATE TABLE `addresses`(
    `id` INT UNSIGNED AUTO_INCREMENT NOT NULL,
    `address` TEXT NOT NULL,
    `city` VARCHAR(255) default NULL,
    `state` VARCHAR(255) NOT NULL,
    `zip` VARCHAR(10) NOT NULL,
    `country` CHAR(3) NOT NULL,
    PRIMARY KEY(`id`)
    );

  2. Create a table named users, using the following SQL statement:

    CREATE TABLE `users`(
    `id` INT UNSIGNED AUTO_INCREMENT NOT NULL,
    `billing_address_id` INT UNSIGNED default NULL,
    `home_address_id` INT UNSIGNED default NULL,
    `name` VARCHAR(255) NOT NULL,
    `email` VARCHAR(255) NOT NULL,
    PRIMARY KEY(`id`),
    KEY `billing_address_id`(`billing_address_id`),
    KEY `home_address_id`(`home_address_id`),
    CONSTRAINT `addresses__billing_address_id` FOREIGN
    KEY(`billing_address_id`) REFERENCES `addresses`(`id`),
    CONSTRAINT `addresses__home_address_id` FOREIGN KEY(`home_
    address_id`) REFERENCES `addresses`(`id`)
    );

  3. Add some sample data, using the following SQL statements:

    INSERT INTO `addresses`(`id`, `address`, `city`, `state`, `zip`,
    `country`) VALUES
    (1, '123 Street', 'Palo Alto', 'CA', '94310', 'USA'),
    (2, '123 Street', 'London', 'London', 'SE10AA', 'GBR');

    INSERT INTO `users`(`billing_address_id`, `home_address_id`,
    `name`, `email`) VALUES
    (1, 2, 'John Doe', 'john.doe@example.com');

  4. Now we need to create the main model. Create a file named user.php and place it in your app/models folder with the following contents:

    <?php
    class User extends AppModel {
    }
    ?>

How to do it...

Edit the User model, and add the binding definitions to include both references to the Address model:

<?php
class User extends AppModel {
public $belongsTo = array(
'BillingAddress' => array(
'className' => 'Address'
),
'HomeAddress' => array(
'className' => 'Address'
)
);
}
?>

If we issue a find operation to fetch the User, we would obtain the following data structure:

array(
'User' => array(
'id' => '1',
'billing_address_id' => '1',
'home_address_id' => '2',
'name' => 'John Doe',
'email' => 'john.doe@example.com',
),
'BillingAddress' => array(
'id' => '1',
'address' => '123 Street',
'city' => 'Palo Alto',
'state' => 'CA',
'zip' => '94310',
'country' => 'USA'
),
'HomeAddress' => array(
'id' => '2',
'address' => '123 Street',
'city' => 'London',
'state' => 'London',
'zip' => 'SE10AA',
'country' => 'GBR'
)
)

There's more...

In this example, the naming convention we used for the bindings is the standard CakePHP uses for field names, where each uppercase letter is prefixed by an underscore sign, everything is converted to lowercase, and the suffix _id is added. Thus, the standard field name the binding named BillingAddress is billing_address_id.

However there are times where we need to use a field name that does not comply with this standard. In that case, we can use the foreignKey binding setting to specify the field name. For example, we could change the User model definition so the name of the HomeAddress becomes Address, which would make the User model look like this:

<?php
class User extends AppModel {
public $belongsTo = array(
'BillingAddress' => array(
'className' => 'Address'
),
'Address' => array(
'className' => 'Address',
'foreignKey' => 'home_address_id'
)
);
}
?>

When we use different aliases to refer to the same model, certain model callback implementations, such as beforeSave, will need to be changed to avoid using the name of the model directly, and instead use the property alias, available in all models. More information about this can be obtained from Nick Baker's article available at http://www.webtechnick.com/blogs/view/230/The_Power_of_CakePHP_aliases.

Summary

In this article we took a look at recipes that showed us how to change the way bindings are fetched, what bindings and what information from a binding is returned, how to create new bindings, and how to build hierarchical data structures.


Further resources on this subject:


CakePHP 1.3 Application Development Cookbook Over 70 great recipes for developing, maintaining, and deploying web applications
Published: March 2011
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Mariano Iglesias

Mariano has been programming since the early age of 11. He quickly became proficient with C and C++, producing his first commercial applications at 15 years of age. He then continued to learn several other programming languages, PHP being one of them. He has contributed to several open source projects, becoming part of the core development team of both CakePHP and Lithium, two of the best PHP frameworks. He currently divides his time between open source programming, his commercial work, his hobbies, and spending time with his family.

Books From Packt


Expert PHP 5 Tools
Expert PHP 5 Tools

PHP jQuery Cookbook
PHP jQuery Cookbook

PHP 5 E-commerce Development
PHP 5 E-commerce Development

PHP 5 Social Networking
PHP 5 Social Networking

AJAX and PHP: Building Modern Web Applications 2nd Edition
AJAX and PHP: Building Modern Web Applications 2nd Edition

PHP 5 CMS Framework Development - 2nd Edition
PHP 5 CMS Framework Development - 2nd Edition

CMS Design Using PHP and jQuery
CMS Design Using PHP and jQuery

CodeIgniter 1.7 Professional Development
CodeIgniter 1.7 Professional Development


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
N
G
6
1
x
X
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software