code tunes

Web applications, software engineering, Ruby on Rails, Cake PHP, JavaScript, etc.

Named scope in CakePHP

Some time ago I wrote how to improve Cake controllers’ callback system basing on a solution from Ruby on Rails. Today I am going to port another cool feature of Rails to CakePHP - named scope.

Check this Rails example first if you don’t know what the named scope is:

# model definition
class User < ActiveRecord::Base
  # only activated users
  named_scope :activated, :conditions => "activated_at is not null" 
 
  # only currently online users
  named_scope :online, :conditions => "date_add(last_activity, interval 5 minute) < now()"
end

This is very simple example. In fact, named scope functionality can be used for much more than this. However my Cake version I present below is limited to defining conditions.

So we defined some scopes and gave them names. Now we can apply them to searching.

# let's find all activated users
users = User.activated.find(:all) 
 
# let's find all activated users who are currently online and has more than 10 points
users = User.activated.online.find(:all, :conditions => "points > 10")

I bet you can already see advantages of such notation. Often used conditions (for example: often you want to limit search results to activated users only) are placed in named_scope definition, the others (probably more situation-specific ones) go to usual :conditions option. Improved readability, less writing, fewer chances to make mistakes.

Let me present you NamedScopeBehavior which will give you similat functionality in CakePHP. What it does is converting scope definitions to conditions parameter of find() method and that is done transparently in beforeFind callback method.

Download named_scope.php file from repository and place it it app/models/behaviors folder of your Cake app. Then attach this behavior to a model and define named scopes.

class User extends AppModel {
  var $actsAs = array(
    'NamedScope' => array(
      'activated' => array('User.activated in not null'),
      'online' => array('date_add(User.last_activity, interval 5 minute) > now()')
    )
  );
}

Then you can use it.

// in your controller
$this->User->find('all', array('scope' => 'activated'));
$this->User->find('all',
  array('conditions' => 'points > 10', 'scope' => array('activated', 'online')))

Pagination also works fine with scopes.

// in your controller
$paginate = array(
  'User' => array(
    'order' => 'created ASC',
    'limit' => 20,
    'scope' => array('online', 'activated')
  )
);

Happy developing!

Related posts

5 comments

Written by Michał Szajbe

September 5th, 2008 at 2:35 pm

5 Responses to 'Named scope in CakePHP'

Subscribe to comments with RSS

  1. Nice, very nice!

    Joel Moss

    6 Sep 08 at 01:37

  2. [...] 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 [...]

  3. In current implementation your behaviour doesn’t properly handle key-value conditions like:
    ‘activated’ => array(’User.activated’ => 1)
    (only ‘activated’ => array(’User.activated = 1′)) works currently.

    This is because
    foreach ($conditions as $condition) {
    $queryData['conditions'][] = $condition;
    }
    loop ignores keys (it uses ‘[]‘ to assign elements).

    If you change it to something like:
    foreach ($conditions as $key => $condition) {
    if (is_string($key)) {
    $queryData['conditions'][$key] = $condition;
    } else {
    $queryData['conditions'][] = $condition;
    }
    }
    it will work properly for both cases (with and without array key).
    I use this improved version of your code in one of my projects and it works very nicely (keyd conditions are IMO nicer to use and more standard for Cake 1.2 than plain string ones).

    Wojtek

    10 Dec 08 at 16:37

  4. Thanks, Wojtek. I’ll look into the issue soon.

    Michał Szajbe

    11 Dec 08 at 22:58

  5. [...] Named scope in CakePHP at code tunes A port of a useful Rails feature. (tags: cakephp namedscope scope behaviour) [...]

Sorry, comments are closed for this post.