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.
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.
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, )); } }
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.
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', ); }
Total 13 comments
You do not have: 'actionCartsession', the following statement is wrong-
@Müller
Thank you, that worked.
But still this does not:
When I run the view function I get the same error: ProductController and its behaviors do not have a method or closure named "actionCartsession".
@globaleyeglasses
Instead of:
You should write:
See the difference?
When I run the action it gives a error: http://domain.com/product/hello The system is unable to find the requested action "hello".
@waterloomatt
You need to create separate action classes for all the custom implementations that you need to use.
For example, your standard create action could be implemented in CreateAction class, and the custom upload implementation in CreateUploadAction class. And then you declare these actions where you need them:
Hi,
Good article. Can you explain how to use this for methods that have custom implementations?
Fake scenario: user/actionCreate is standard (generated by Gii) but project/actionCreate needs to upload a file. The file is not mass assigned i.e. not marked as safe in the model.
Am I missing something or are general actions only useful if the code is duplicated exactly - through mass assignment.
Cheers,
Matt
I rather prefer action class named CreateAction rather than Create... it's more obvious purpose of that class. Also you can put actions in controllers/controllerName directory. Read definitive guide for more information: http://www.yiiframework.com/doc/guide/1.1/en/basics.controller#action
Small addition to "Final notes" paragraph.
In case you are building a "Widget + action provider" (see Using a Widget as action provider ) passing parameters can be made in this way: (see CController API documentation )
public function actions() { return array( 'providerName.'=>array( 'class'=>'path.to.ProviderClass', 'action1'=>array( 'property1'=>'value1', ), 'action2'=>array( 'property2'=>'value2', ), ), ) }Actually its working perfectly, I was just calling it too soon (after __construct) Thanks for the manual
Cheers
Gustavo
Great article There seens to be a bug in version 1.1.7, I cant assign variables to the action class like you described in "Final Notes", which makes it much less powerful
This article not only applies to general standard CRUD actions, you can actually create any action with this method as long as it is an action that is repeated along your application.
For example, you may have an action display that is exact on every class. As you can see on the article, you render the controller's view.
Cheers
I get it. This only applies to standard CRUD actions created by Gii.
Good article! =)
So why not just change the action method actionCreate? Now you created a new class (never good) for a single action that always overwrites the existing one... Did I get that right? Why not just change the existing one?
Leave a comment
Please login to leave your comment.