I discovered the joys of NamedScope for Ruby on Rails quite a while ago now, and have always been an admirer. Its makes performing finds on models very elegant and convenient, by automagically creating model methods based on your pased scope params. Just like this:

Named Scope in Rails
  1. class User < ActiveRecord::Base
  2.   named_scope :active, :conditions => {:active => true}
  3.   named_scope :inactive, :conditions => {:active => false}
  4. end
  5.  
  6. # Standard usage
  7. User.active    # same as User.find(:all, :conditions => {:active => true})
  8. User.inactive # same as User.find(:all, :conditions => {:active => false})

Then a few months ago I read this blog post and discovered that someone had created similar functionality for CakePHP, in the form of a model behavior. Although it’s not quite as powerful as it’s Rails cousin, it does let you define named scopes for any model quite easily. However, I wasn’t crazy about a few things.

Named Scopes are defined like this:

Don't like this:
  1. class User extends AppModel {
  2.   var $actsAs = array(
  3.     'NamedScope' => array(
  4.       'activated' => array('User.activated in not null'),
  5.       'online' => array('date_add(User.last_activity, interval 5 minute) > now()')
  6.     )
  7.   );
  8. }

The above format means we can only pass find conditions to a named scope, and cannot pass any other params, such as ORDER and FIELDS.

Then a named scope is then called like this:

this is not nice either
  1. $this->User->find('all', array('scope' => 'activated'));
  2. $this->User->find('all',
  3.   array('conditions' => 'points > 10', 'scope' => array('activated', 'online')))

I have to pass enough params to a find call as it is, so I don’t want anymore.

What I want to do is this:

This is better
  1. class User extends AppModel {
  2.   var $actsAs = array(
  3.     'NamedScope' => array(
  4.       'active' => array(
  5.         'conditions' => array(
  6.           'User.is_active' => true
  7.         ),
  8.         'order' => 'User.name ASC'
  9.       )
  10.     )
  11.   );
  12. };

and this

Easy peasy!
  1. $active_users = $this->User->active('all');

I can even do this:

This is just lovely
  1. $active_users = $this->User->active('all', array(
  2.     'conditions' => array(
  3.         'User.created' => '2008-01-01'
  4.     ),
  5.     'order' => 'User.name ASC'
  6. ));

Much improved I think, and so much more powerful. So I’ve only gone and coded the damn thing! You can find my version of Named Scope for Cake on Github at http://github.com/joelmoss/cakephp-namedscope.

And you know what? Thanks to the power of CakePHP, the actual code is seven lines shorter than the aforementioned version.