Action Controller Filters

Action Controller Filters

Filters enable controllers to run shared pre and post processing code for its actions. These filters can be used to do authentication, caching, or auditing before the intended action is performed. Or to do localization or output compression after the action has been performed.

Filters have access to the request, response, and all the instance variables set by other filters in the chain or by the action (in the case of after filters). Additionally, it's possible for a pre-processing beforeFilter to halt the processing before the intended action is processed by returning false or performing a redirect or render. This is especially useful for filters like authentication where you're not interested in allowing the action to be performed if the proper credentials are not in order.

Filter inheritance

Controller inheritance hierarchies share filters downwards, but subclasses can also add new filters without affecting the superclass. For example:

class BankController extends AkActionController 
{
    function __construct()
    {
        $this->beforeFilter('_audit');
    }
 
    function _audit(&$controller)
    {
        // record the action and parameters in an audit log  
    }
}
 
class VaultController extends BankController 
{
    function __construct()
    {
        $this->beforeFilter('_verifyCredentials');
    }
 
    function _verifyCredentials(&$controller)
    {
        // make sure the user is allowed into the vault
    }
}

Now any actions performed on the BankController will have the audit method called before. On the VaultController, first the audit method is called, then the _verifyCredentials method. If the _audit method returns false, then _verifyCredentials and the intended action are never called.

Filter types

A filter can take one of three forms: method reference, external class, or inline method. The first is the most common and works by referencing a method somewhere in the inheritance hierarchy of the controller by use of a method name. In the bank example above, both BankController and VaultController use this form.

Using an external class makes for more easily reused generic filters, such as output compression. External filter classes are implemented by having a static filter method on any class and then passing this class to the filter method. Example:

class OutputCompressionFilter
{
    function filter(&$controller)
    {
        $controller->response->body = compress($controller->response->body);
    }
}
 
class NewspaperController extends AkActionController 
{
    function __construct()
    {
        $this->afterFilter(new OutputCompressionFilter());
    }
}

The filter method is passed the controller instance and is hence granted access to all aspects of the controller and can manipulate them as it sees fit.

Filter chain ordering

Using beforeFilter and afterFilter appends the specified filters to the existing chain. That's usually just fine, but some times you care more about the order in which the filters are executed. When that's the case, you can use prependBeforeFilter and prependAfterFilter. Filters added by these methods will be put at the beginning of their respective chain and executed before the rest. For example:

class ShoppingController extends AkActionController 
{
    function __construct()
    {
        $this->beforeFilter('verifyOpenShop');
    }
}
 
 
class CheckoutController extends AkActionController 
{
    function __construct()
    {
        $this->prependBeforeFilter('ensureItemsInCart', 'ensureItemsInStock');
    }
}

The filter chain for the CheckoutController is now ensureItemsInCart, ensureItemsInStock, verifyOpenShop. So if either of the ensure filters return false, we'll never get around to see if the shop is open or not.

You may pass multiple filter arguments of each type.

Around filters

In addition to the individual before and after filters, it's also possible to specify that a single object should handle both the before and after call. That's especially useful when you need to keep state active between the before and after, such as the example of a benchmark filter below:

class WeblogController extends AkActionController 
{
    function __construct()
    {
        $this->aroundFilter(new BenchmarkingFilter());
    }
 
    // Before this action is performed, BenchmarkingFilter->before($controller) is executed
   function index()
   {
   }
    // After this action has been performed, BenchmarkingFilter->after($controller) is executed
}
 
class BenchmarkingFilter
{
    function before(&$controller)
    {
        start_timer();
    }
 
    function after(&$controller)
    {
        stop_timer();
        report_result();   
    }
}

Filter chain skipping

Some times its convenient to specify a filter chain in a superclass that'll hold true for the majority of the subclasses, but not necessarily all of them. The subclasses that behave in exception can then specify which filters they would like to be relieved of. Examples

class ApplicationController extends AkActionController 
{
    function __construct()
    {
        $this->beforeFilter('authenticate');
    }
}
 
class WeblogController extends ApplicationController
{
    // will run the authenticate filter
}
 
class SignupController extends AkActionController 
{
    function __construct()
    {
        $this->skipBeforeFilter('authenticate');
    }
    // will not run the authenticate filter
}

Filter conditions

Filters can be limited to run for only specific actions. This can be expressed either by listing the actions to exclude or the actions to include when executing the filter. Available conditions are only or except, both of which accept an arbitrary number of method references. For example:

  class Journal extends AkActionController 
  {
      function __construct()
      {   // only require authentication if the current action is edit or delete
          $this->beforeFilter(array('_authorize'=>array('only'=>array('edit','delete')));
      }
 
      function _authorize(&$controller)
      {
        // redirect to login unless authenticated
      }
  }
 
filters-and-verification.txt · Last modified: 2008/06/14 22:49 by thijs
 

The Akelos Framework was created by Bermi Ferrer and other contributors.
Potions of the code and documentation have been ported from Ruby on Rails.

The Akelos Framework is released under the LGPL license.

"Akelos", "Akelos Framework", and the Akelos logo are trademarks of Bermi Labs All rights reserved.

Wiki driven by DokuWiki