joni
joni

Reputation: 5462

CakePHP: Containable behaviour doesn't work with find() even if contain() is called in beforeFind

My Problem: Linked to my Employees table I've got an Address table containing a virtual field called full_name (I guess you can imagine by yourself what it does). I added the Containable Behaviour and this function

function beforeFind() {
    $this->contain('Address.full_name');
}

to my Employees model, so that I don't have to call $this->contain(..) in every single controller action (I need the full_name field in pretty every action). BUT id doesn't work then if the controller action does just a $this->Employee->find('all') (or read(..). Contrary, it works if

Upvotes: 1

Views: 1599

Answers (2)

Leo
Leo

Reputation: 6571

As far as I recall, a contain() statement only works once, for the query operation immediately following it. Subsequent queries will require their own contain() statement e.g.

$this->Employee->contain('Address.full_name');
$this->Employee->find('all'); //first find
// returns all of Employee + Address.full_name

$this->Employee->find('all'); //second find
// returns all of Employee + all of Address + all of any other associated tables.

I don't recommend using contain() in beforeFind() as it is intended to modify specific returns. It soon becomes second nature to use it before each query where you will then have fine control of the data returned.

If you have a widespread requirement for a limited return, you can set that up in the associations on the model.

Upvotes: 1

Martz
Martz

Reputation: 428

1) Your Model beforeFind() should accept a param $queryData and return true if the find() should be executed, or false if it should abort beforeFind. Generally the beforeFind($queryData) method will modify the $queryData array and return it.

function beforeFind($queryData) {
   // Modify $queryData here if you want
   return $queryData
}

2) Trying to maintain a persistant $contain is a bit strange. Containable obviously contains assocations so that extra/additional information is fetched. Your virtual field should be returned in a normal find() operation. If you want to restrict the fields which are returned you should define those either in the Model or in the Model association

var $belongsTo = array(
    'Employee' => array(
         'className' => 'Employee',
         'fields' => array('Employee.id', 'Employee.full_name'),
         'conditions' => array()
    )
);

Containable will rebind associations quickly for you, but you should instead define default fields/conditions through the Model association ($belongsTo, $hasMany, $hasOne etc)

Another alternative is to actually create Model methods which reflect the data you are trying to fetch, a very basic example:

function activeEmployees() {
     $contain = array('Address');
     $conditions array('Employee.started' => '2010-09-01');
     $this->find('all', array('conditions' => $conditions, 'contain' => $contain));
}

And then call these convienience methods from the Controller just like you would a find

$this->Employee->activeEmployees();

You could also choose to pass a param to Employee::activeEmployees(); which is an array of additional $conditions or $contain options which are merged with the standard options.

Upvotes: 0

Related Questions