Saturday, August 7, 2010

Using Controller Filters

So, I'm sure by now everyone's used the very familiar "accessControl" filter, but so much of that happens behind the scenes. This post goes into how to create your own filters and how they can be useful.

Information about how to set up filters can be found here: http://www.yiiframework.com/doc/guide/basics.controller#filter

The method-based implementation of the filters is extremely straight forward, simply add the method to the controller in the form of public function filterXYZ( $c ) then add XYZ to your array of filters:



public function filters()
{
   return array( 'XYZ' );
}

public function filterXYZ( $c )
{
   // Do some pre-action checking here.
   // This tells the filter chain $c to keep processing. 
   $c->run(); 
}   

If you want to use the filters in combination with accessControll, you simply need to return from the method without continuing the filter run - throw your 403 exception or whatever is appropriate. This is particularly useful if you're using accessControl with bizRules

This is also extremely useful if you have some business logic that needs to be run in several actions of the controller, so that you don't have to explicitly call it for each of the actions.

Lets say that I want to configure some menu options the same way for multiple actions, then let my views add on specific additions when they come into play, rather than adding the same menu 10 times.

I set up my filter as such:
public function filterMenuPresets( $c )
{
   $this->menu = array( 
       //add all my standard menu items here.
   );
   // This tells the filter chain $c to keep processing. 
   $c->run(); 
} 

and then I add it to my filters:

public function filters()
{
   // This will cause the filter to be run on all actions
   return array( 'menuPresets' );
}

Remember, your filters will run in the order that they're listed, so if you're using this with accessControl, you need to decide where it is most appropriate in the list. For this example I would probably run 'menuPresets' first, since my unauthorized page will still have the most basic menu.


Now, lets say I have 4 different actions for this controller (actionA-actionD), but I want this filter to run on only actionA and actionB.

public function filters()
{
   // This will cause the filter to be run on all actions
   return array( 'menuPresets+ actionA actionB' );
}

And if I want it to run on all actions except actionC:

public function filters()
{
   // This will cause the filter to be run on all actions
   return array( 'menuPresets- actionC' );
}

That's it! Class based filters work very much the same way, except that you can define pre action and post action responses/business logic. Will cover that another day ;)

2 comments:

  1. Hey, great post. After reading, I took out some repetitive access checking code in a few actions that require parameters for the business rule.

    /**
    *
    * @param $c
    */
    public function filterAccessControlView($c)
    {
    $model = $this->getInspection();

    if (!Yii::app()->user->checkAccess('inspection_view', array('model'=>$model)))
    {
    throw new CHttpException(403, 'You are not authorized to perform this action.');
    }

    $c->run();
    }

    ReplyDelete