New feature: bindModel/unbindModel

Published on and tagged with cakephp  feature

This feature allows you to bind or turn off associations on the fly, and will be available in the next release (RC6?) of CakePHP.

Let us start with an unbindModel example. We have three models: User, Project and Supportrequest. A user has many projects and many support requests. If we do

debug($this->User->findAll());

the output would be something like:

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [id] => 1
                    [firstname] => Daniel
                    [lastname] => Hofstetter
                )
           [Project] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [user_id] => 1
                            [name] => project A
                        )
                )
            [Supportrequest] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [user_id] => 1
                            [comment] => a support request
                        )
                    [1] => Array
                        (
                            [id] => 2
                            [user_id] => 1
                            [comment] => another support request
                        )
                )
        )
)

Ok, but what if we are only interested in the users and the projects? Well, we could simply ignore the support requests. Or we could turn off the association to the support requests with unbindModel:

$this->User->unbindModel(array('hasMany' => array('Supportrequest')));
debug($this->User->findAll());

That gives us the following output:

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [id] => 1
                    [firstname] => Daniel
                    [lastname] => Hofstetter
                )
           [Project] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [user_id] => 1
                            [name] => project A
                        )
                )
)

Notice: The associations are reset after each call to a find function.

Ok, that was the unbindModel example. Let us go to the bindModel example. That example will be a little bit artificial as I do not see a real use case for bindModel at the moment. Maybe you know one? Ok, we have two Models: User and Supportrequest. The model do not have any associations. If we do

debug($this->User->findAll());

we get the following output:

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [id] => 1
                    [firstname] => Daniel
                    [lastname] => Hofstetter
                )
        )
)

For some reason we want to have a temporary “hasMany” association between user and support request. We define it in the following way using the usual association syntax:

$this->User->bindModel(array('hasMany' => array('Supportrequest' => 
                             array('foreignKey' => 'user_id'))));
debug($this->User->findAll());

And that is the output:

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [id] => 1
                    [firstname] => Daniel
                    [lastname] => Hofstetter
                )
            [Supportrequest] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [user_id] => 1
                            [comment] => a support request
                        )
                    [1] => Array
                        (
                            [id] => 2
                            [user_id] => 1
                            [comment] => another support request
                        )
                )
        )
)

That’s it :)

14 comments baked

  • odpocinek

    $this->User->hasMany = array();
    $this->User->__createLinks();

    in controller, or

    $this->hasMany = array();
    $this->__createLinks();

    in model User does a similar (not very clean) trick.

  • Larry E. Masters aka PhpNut

    odpocinek,

    Your using private methods that should not be used.

    $this->__createLinks();

  • mika76

    I suppose the idea behind bindModel is so that you don’t have to link a very large and rarely used association.

    For one, I’ve got a customer linked to transactions. There are _loads_ of transactions, so I will only bind it if and when I need it, which is in far fewer places than I need to use customer.

  • Jim Plush

    I can give you an example of why I needed to use the bindModel method…
    I have a list of users and the users have many images but one image is their main profile image so when I display their profile I want all the images but when I display their name in the forums I just want where img_main = 1

    so that I don’t get an array of 30 images when I just need the one.

    great framework by the way, I’m loving it so far

  • cakebaker

    @Jim Plush: thanks for the example :)

  • fraisouille

    Could it work for “submodels” ?

    I explain my problem: I’m working in my “news” controller. I’m retrieving the data for a particular news.

    A news has many newsRead. A newsRead is the association between a news and a user, so a newsRead belongs to news and to user.

    But when I retrieve my news newsRead list, I don’t want to have the whole news data again with my recursive mode. So I would like to unbind temporary the associaction between newsRead and user.

    But when I do my unbind in my news controller (I’m not in my newsRead controller !) it does not have effect for my $this->News->findAllById($id)
    (I’ve tried with $this->NewsRead->findAll and there it works.)

    Am I trying to do something impossible ? Or is there an unknown trick ?

  • purepear

    OK… my problem is similar to this.

    The example:
    Category->Subcategory->Product They all have hasMany and belongsTo as usual.

    I want to get all Products that belongs to a certain Category … and i want the results to to be sth. like this:

    Array{
    [Product] => array{

    }

    [Subcategory] => array{

    [Category] => array{

    }
    }

    }

    or :

    Array{
    [Product] => array{

    [Subcategory] => array{

    [Category] => array{

    }
    }
    }
    }

    How do i do this ?
    I tried the Product->recursive = 2 and it went to a deep recursion doubling all my Subcategoris and Products inside each other… big mess.
    I played around with unbindModel() too but i didn’t get the result i wanted.

    Just a simple example should be enough.
    Thanks in advance.

  • purepear

    OK solved :

    $this->Category->unbindModel(array(‘hasMany’ => array(‘Subcategory’)));
    $this->Category->Subcategory->unbindModel(array(‘hasMany’ => array(‘Product’)));

    $conditions = array(‘Subcategory.category_id’ => $id);
    $data = $this->Product->findAll($conditions, NULL, $order, $limit, $page);

    I wonder what i got wrong before :)
    anyway .. it works

  • fraisouille

    That was the solution !
    I didn’t thought we could call $this->Category->Subcategory…

    Thanks !

  • cakebaker

    @purepear, fraisouille: I am glad you could solve your issues :)

  • sonic

    Perhaps I’m not going the right way about this but in relation to your statement about not seeing a real-world use of the bindModel() function, here’a where I use it:

    I have a shopping cart.
    Cart HABTM Product
    My link table is ‘carts_products’ and holds the products that are currently in the users cart. The link table usually will only have cart_id and product_id. However, I want to store other information in the carts_products table, e.g. ‘quantity’ (the quantity of a particular item the customer has in their cart). In order to update the CartsProduct model, I use bindModel() from the Carts model.
    e.g.

    $this->bindModel(array(
    ‘hasMany’ => array(
    ‘CartsProduct’ => array(
    ‘fields’ => ‘cart_id,
    product_id,
    quantity’))));

    This way I can work with any extra fields in the link table.

  • cakebaker

    @sonic: Thanks for your example.

  • naxis

    I want to pass value to my model to filter the datas retrieved
    I need some help.This is my case.
    I’m now working on a mutilanguage program.
    I want to pass the the value I generated in the AppController to the model to retrieve the language informations.

    When I write

    “conditions”=”language=’en'”

    everything is OK.But when I write

    “conditions”=”language=’$this->lang'”

    I get error.

    How can I write that?

  • cakebaker

    @naxis: What error do you get?

Bake a comment




(for code please use <code>...</code> [no escaping necessary])

© daniel hofstetter. Licensed under a Creative Commons License