Actions code reuse with CAction

  1. Introduction
  2. CAction to the Rescue
  3. Final Notes

Introduction

We all know how good 'gii' automates the code for us and we normally tend to be happy with what that tool offers at the beginning of our Yii learning curve. But as soon as you start working in larger and larger projects, you realize that its code is too repetitive to maintain and having a small pitfall in general actions means to go over and over through them to fix the issues.

CAction to the Rescue

I have already explained how to use widgets as action providers to encapsulate the actions. What I am going to explain here is how can we easily create an action to work throughout different controllers.

Gii provides us normally with the following code on the 'actionCreate':

public function actionCreate()
{
   $model=new ModelName;

   // Uncomment the following line if AJAX validation is needed
   // $this->performAjaxValidation($model);

   if(isset($_POST['ModelName']))
   {
       $model->attributes=$_POST['ModelName'];
       if($model->save())
         $this->redirect(array('view','id'=>$model->id));
   }

   $this->render('create',array(
       'model'=>$model,
   ));
}

For a normal project and with the default CMS layout of Yii, does suit our regular needs and we tend to leave it as it is. But, as I said before, imagine that we need to include a new parameter in our redirection for example? In order to avoid that we can tweak a bit the code and develop a general action.

Step 1 - Creating the Action

For the sake of the example, create the following action and save it on your protected/components/actions folder

class Create extends CAction {

    public function run() {
	$controller = $this->getController();

	// get the Model Name
	$model_class = ucfirst($controller->getId());

	// create the Model
	$model = new $model_class();

	// Uncomment the following line if AJAX validation is needed
	// $this->performAjaxValidation($model);
	if (isset($_POST[$model_class])) {
	    $model->attributes = $_POST[$model_class];

	    if ($model->save())
		$controller->redirect(array('view', 'id' => $model->id));
	}
	$controller->render('create', array(
	    'model' => $model,
	));
    }

}
Step 2 - Declare the action on the Controller

Once we have the action class created, the only thing we need to do is declare it in our controller's actions function in order to use it.

public function actions(){
   return array(
      'create'=>'application.components.actions.create',
   );
}

After declaring the action we can call it: http://myhost/index.php?r=controller/create, just like any other.

Final Notes

In the example above I have used 'getController()' and 'getId()' in order to access the model, but we can actually use properties as CAction is a class. This could be the declaration of an action passing the model name to load:

// Assuming the action class has the 
// following public properties:
// public model_name
// -----------------
// ModelClass is a test model class name
// -----------------
// On the controller: 
public function actions(){
   return array(
      'create'=>array(
          'class'=>'application.components.actions.create',
          'model_name'=>'ModelClass',
   );
}