Saturday 15 October 2011

Play with Cakephp's find query


CakePHP allows you to use your own "find-types" for the Model::find() methodology. Those of your who are familiar with the find() method know that there are currently four types in the core: 'list', 'all', 'first' and 'count'. However, sometimes it is nice to specify your own type so you can have a call like this:
$this->Comment->find('pending');
So how would you go about implementing this? Correct, you would overwrite the find() method in your model and provide the default functionality if there is one of the default types used. Let's have a look at how you could implement this pending find from above:
class Comment extends AppModel {
  var $belongsTo = array('User');

  function find($type, $queryData = array()) {
    switch ($type) {
      case 'pending':
        return $this->find('all', array(
          'fields' => array('User.email', 'User.first_name', 'User.last_name', 'Comment.name')
          , 'conditions' => array(
            'Comment.active' => 0
            , 'Comment.blocked' => 0
          )
          , 'contain' => array('User')
        ));
      default:
        return parent::find($type, $queryData);
    }
  }
}
So this is actually not very difficult code, but it is very powerful. You could specify different sets of conditions, fields, orders, groups by's and containments just by adding a new entry to the switch statement. Please note that there is no break needed within the switch as we return home.
Let's add some code for finding pending users that were invited by the currently logged-in user. This is what we could use in the controller:
$this->Comment->find('pending', array('created_by_id' => User::get('id')));
Now for the model we would need to add some code that tracks if $queryData['conditions'] is set, and use that as well in conjunction with the conditions specified in our find type. While we are at it, let's also add some code that would handle fields, order, group, recursive and contain statements:
class Comment extends AppModel {
  var $belongsTo = array('User');

  function find($type, $queryData = array()) {
    $defaults = array(
      'conditions' => null
      , 'fields' => array()
      , 'order' => null
      , 'recursive' => null
      , 'contain' => null
    );
    $queryData = am($defaults, $queryData);

    switch ($type) {
      case 'pending':
        return $this->find('all', array(
          'fields' => am(
            array('User.email', 'User.first_name', 'User.last_name', 'Comment.name')
            , $queryData['fields']
          )
          , 'conditions' => am(array(
            'Comment.active' => 0
            , 'Comment.blocked' => 0
          ), $queryData['conditions'])
          , 'contain' => am(array('User'), $queryData['contain'])
        ));
      default:
        return parent::find($type, $queryData);
    }
  }
}
With some default values we can happily use our custom find type alongside dynamic conditions inserted by our controllers. If you think this am() stuff is overkill, you could as well just provide another find type:
$this->Comment->find('pending-created-by-logged-in-user');
class Comment extends AppModel {
  var $belongsTo = array('User');

  function find($type, $queryData = array()) {
    switch ($type) {
      case 'pending':
        return $this->find('all', array(
          'fields' => array('User.email', 'User.first_name', 'User.last_name', 'Comment.name')
          , 'conditions' => array(
            'Comment.active' => 0
            , 'Comment.blocked' => 0
          )
          , 'contain' => array('User')
        ));
      case 'pending-created-by-logged-in-user':
        return $this->find('all', array(
          'fields' => array('User.email', 'User.first_name', 'User.last_name')
          , 'conditions' => array(
            'Comment.active' => 0
            , 'Comment.blocked' => 0
            , 'Comment.created_by_id' => User::get('id')
          )
          , 'contain' => array('User')
        ));
      default:
        return parent::find($type, $queryData);
    }
  }
}
... well you get the idea. By the way, do you notice how useful using a static functions like this User::get() methods are for fetching properties and data from the currently logged in user? More on that later.

No comments:

Post a Comment