Tags

, , ,

Update 01/07/2010: Good news everyone, for those switching over to cake 1.3, there is a great new feature:
http://book.cakephp.org/view/1608/Virtual-fields
————–

How about a little trick to extend the find(‘list’) functionality?..

Let’s say we need to display a list of users, but instead of just User.id and User.name we need to have User.id, as well as User.name and User.email combined. Unfortunately find(‘list’) doesn’t have that type of functionality out-of-the-box. Well, that’s OK, we’ll make our own…

Before I continue, I’d like to thank grigri for a very creative way to use Set::combine() as well as cakebaker for his tips on extending the find() method functionality.

Alright, let’s add this to our app model:

 function find($type, $options = array()) {
        switch ($type) {
            case 'superlist':
                if(!isset($options['fields']) || count($options['fields']) < 3) {
                    return parent::find('list', $options);
                }

                if(!isset($options['separator'])) {
                    $options['separator'] = ' ';
                }

                $options['recursive'] = -1;              
                $list = parent::find('all', $options);

                for($i = 1; $i <= 2; $i++) {
                    $field[$i] = str_replace($this->alias.'.', '', $options['fields'][$i]);               
                }           

                return Set::combine($list, '{n}.'.$this->alias.'.'.$this->primaryKey,
                                 array('%s'.$options['separator'].'%s',
                                       '{n}.'.$this->alias.'.'.$field[1],
                                       '{n}.'.$this->alias.'.'.$field[2]));
            break;                      

            defau<              
                return parent::find($type, $options);
            break;
        }
    }

Now all we have to do in our controller is:

$this->User->find('superlist', array('fields'=>array('User.id',
                                                                  'User.name',
                                                                  'User.email'),
                                              'separator'=>' * '));

Which ultimately gives us something like:

...
<option value=&quot;1&quot;>Bob * bob@hotmail.com</option>
...

Let’s quickly go over what we’ve done…

We overrode the default Model::find() method by extending it with a new find type called ‘superlist’. We also allow a new key ‘separator’ to specify the delimiter between the fields (for the sake of the example I chose a ‘ * ‘).

The code is not very complicated, so I hope you can figure out what’s going just by examining our custom find() function… but just a few points to help you along:

  • Specify at least 3 fields, or the method will default to the regular find(‘list’)
  • Field order is important
  • We need to specify the primary key field to extract the appropriate value
  • Model’s primary key field is always used for the option value
  • Fields can be specified as ‘fieldName’ or ‘Model.fieldName’
  • If you do not specify the ‘separator’ key, it will default to a space character

Keep in mind that this is more of an exercise, and you can achieve the same results without any overrides, but this certainly helps to keep your controllers nice, clean and skinny.