HowTo: CakePHP addContain() for all models

mashpie » 03 September 2009 » In howto »

As of CakePHP 1.2 Behaviours came up and people started working with the “Containable” (see http://book.cakephp.org/view/474/Containable). In brief you get much better control over associations and selects on theses in find operations. But when associations grow up in your datamodel things might get overhauling. You’ll have to keep exiting associations while adding contains to your query. This can be simplified by adding an addContain()-Method to your models.

get it to work

As you’ll probably know you can add an app-wide app_model to your application to get some kind of general methods to be used by your models. So create that file in your app directory (app/app_model.php) :

class AppModel extends Model{
 
}

You might want to add the Behaviour right here, ie:

class AppModel extends Model{
/**
* Activate Containable-Behaviour for all models.
*/
   var $actsAs = array('Containable');
}

Now just add the method itself like this:

class AppModel extends Model{
/**
* Activate Containable-Behaviour for all models.
*/
   var $actsAs = array('Containable');
 
/**
 * Method to let you add containables to existing associations.
 *
 * @param $arr array of containables as usual
 * @return void
 * @author Marcus Spiegel
 **/
    function addContain( $arr=NULL ){
        // get all existing associations
        $assoc = $this->getAssociated();
        if(!empty($assoc)){
            // merge associations with contains
            $this->contain(am(array_keys($assoc), $arr));
        }else{
            $this->contain($arr);
        }
    } // end addContain
}

That’s it… now see how to use it.

Example

Let’s say you have a model called “article” that belongsTo your model “group” and hasManyean“. In a normal find you’ll get something like this:

Array
(
    [Article] => Array
        (
            [id_article] => 14
            [id_group] => 64
            [code] => 1204
        )

[Group] => Array
    (
        [id_group] => 64
        [id_parent] => 6
        [parent_id] => 6
        [sort] => 53
        [auto_visible] => 1
        [display] => 1
        [code] => 407
        [hex] => #FFFFFF
    )

[Ean] => Array
    (
        [0] => Array
            (
                [id_ean] => 65
                [ean] => 4005546102009
                [ve_factor] => 1
                [id_ve] => 10
                [id_color] => 23
                [id_article] => 14
            )
    )

)

"ean" again belongsTo "color" and "ve". So you might want to setup a contain in your controller like this:

        $this->Article->contain(array(
            'Ean' => array(
                'Color'     => array('ColorLocale'),
                'Ve'        => array('VeLocale'),
                ),
        ));

Look, color and ve themselves have additional accosiations. Now you'll get:

Array
(
    [Article] => Array
        (
            [id_article] => 14
            [id_group] => 64
            [code] => 1204
        )

[Ean] => Array
    (
        [0] => Array
            (
                [id_ean] => 65
                [ean] => 4005546102009
                [ve_factor] => 1
                [id_ve] => 10
                [id_color] => 23
                [id_article] => 14
                [Color] => Array
                    (
                        [id_color] => 23
                        [code] => 25
                        [hex] => #FFFFFF
                        [img] => /pic_men/color/25.gif
                        [ColorLocale] => Array
                            (
                                [id_lang_color] => 35
                                [id_color] => 23
                                [id_locale] => 1
                                [descr] => 25 - verzinkt
                            )
                    )

                [Ve] => Array
                    (
                        [id_ve] => 10
                        [code] => 203
                        [ve] => 100
                        [VeLocale] => Array
                            (
                                [id_lang_ve] => 10
                                [id_ve] => 10
                                [id_locale] => 1
                                [descr] => Pg. à 100 Stück
                            )
                    )
            )
    )

)

Nice, but Group is missing although already associated in your article-model. So use addContain() instead of contain():

        $this->Article->addContain(array(
            'Ean' => array(
                'Color'     => array('ColorLocale'),
                'Ve'        => array('VeLocale'),
                ),
        ));

Now you'll get the Group too - like expected:

Array
(
    [Article] => Array
        (
            [id_article] => 14
            [id_group] => 64
            [code] => 1204
        )

[Group] => Array
    (
        [id_group] => 64
        [id_parent] => 6
        [parent_id] => 6
        [sort] => 53
        [auto_visible] => 1
        [display] => 1
        [code] => 407
        [hex] => #FFFFFF
    )

[Ean] => Array
    (
        [0] => Array
            (
                [id_ean] => 65
                [ean] => 4005546102009
                [ve_factor] => 1
                [id_ve] => 10
                [id_color] => 23
                [id_article] => 14
                [Color] => Array
                    (
                        [id_color] => 23
                        [code] => 25
                        [hex] => #FFFFFF
                        [ColorLocale] => Array
                            (
                                [id_lang_color] => 35
                                [id_color] => 23
                                [id_locale] => 1
                                [descr] => 25 - verzinkt
                            )
                    )

                [Ve] => Array
                    (
                        [id_ve] => 10
                        [code] => 203
                        [ve] => 100
                        [VeLocale] => Array
                            (
                                [id_lang_ve] => 10
                                [id_ve] => 10
                                [id_locale] => 1
                                [descr] => Pg. à 100 Stück
                            )
                    )
            )
    )

)

You still might want to fetch additional data of that group, too:

        $this->Article->addContain(array(
            'Ean' => array(
                'Color'     => array('ColorLocale'),
                'Ve'        => array('VeLocale'),
                ),
            'Group'     => array('GroupLocale'),
        ));

what results in:

Array
(
    [Article] => Array
        (
            [id_article] => 14
            [id_group] => 64
            [code] => 1204
        )

[Group] => Array
    (
        [id_group] => 64
        [id_parent] => 6
        [parent_id] => 6
        [sort] => 53
        [auto_visible] => 1
        [display] => 1
        [code] => 407
        [hex] => #FFFFFF
        [GroupLocale] => Array
            (
                [id_lang_group] => 64
                [id_group] => 64
                [id_locale] => 1
                [descr] => Briefklammern und Scheren
            )
    )

[Ean] => Array
    (
        [0] => Array
            (
                [id_ean] => 65
                [ean] => 4005546102009
                [ve_factor] => 1
                [id_ve] => 10
                [id_color] => 23
                [id_article] => 14
                [Color] => Array
                    (
                        [id_color] => 23
                        [code] => 25
                        [hex] => #FFFFFF
                        [ColorLocale] => Array
                            (
                                [id_lang_color] => 35
                                [id_color] => 23
                                [id_locale] => 1
                                [descr] => 25 - verzinkt
                            )
                    )

                [Ve] => Array
                    (
                        [id_ve] => 10
                        [code] => 203
                        [ve] => 100
                        [VeLocale] => Array
                            (
                                [id_lang_ve] => 10
                                [id_ve] => 10
                                [id_locale] => 1
                                [descr] => Pg. à 100 Stück
                            )
                    )
            )
    )

)

So that's all quite nice, but you think that this can be done by a simple use of contain() too. You're right! But now think of the following: Later in production you have to add some more associations, let's say an article hasMany images, belongsTo additional associated articles and might get some download files as hasMany.... so on so on.

All you need to do is add these in your model and you'll get them in every find() operation. Well, as a rule of thumb addContain() comes in handy when you want to get your complete model-data and add some additional extra data contained by associated models.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • LinkedIn
  • MisterWong
  • MySpace
  • RSS
  • Slashdot
  • Technorati
  • Twitter

Tags: , ,

Trackback URL

No Comments on "HowTo: CakePHP addContain() for all models"

Hi Stranger, leave a comment:

ALLOWED XHTML TAGS:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

Subscribe to Comments