mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 06:37:55 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			452 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			452 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
Controllers
 | 
						|
===========
 | 
						|
 | 
						|
Controllers are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture.
 | 
						|
They are objects of classes extending from [[yii\base\Controller]] and are responsible for processing requests and
 | 
						|
generating responses. In particular, after taking over the control from [applications](structure-applications.md),
 | 
						|
controllers will analyze incoming request data, pass them to [models](structure-models.md), inject model results
 | 
						|
into [views](structure-views.md), and finally generate outgoing responses.
 | 
						|
 | 
						|
 | 
						|
## Actions <a name="actions"></a>
 | 
						|
 | 
						|
Controllers are composed by *actions* which are the most basic units that end users can address and request for
 | 
						|
execution. A controller can have one or multiple actions.
 | 
						|
 | 
						|
The following example shows a `post` controller with two actions: `view` and `create`:
 | 
						|
 | 
						|
```php
 | 
						|
namespace app\controllers;
 | 
						|
 | 
						|
use Yii;
 | 
						|
use app\models\Post;
 | 
						|
use yii\web\Controller;
 | 
						|
use yii\web\NotFoundHttpException;
 | 
						|
 | 
						|
class PostController extends Controller
 | 
						|
{
 | 
						|
    public function actionView($id)
 | 
						|
    {
 | 
						|
        $model = Post::findOne($id);
 | 
						|
        if ($model === null) {
 | 
						|
            throw new NotFoundHttpException;
 | 
						|
        }
 | 
						|
 | 
						|
        return $this->render('view', [
 | 
						|
            'model' => $model,
 | 
						|
        ]);
 | 
						|
    }
 | 
						|
 | 
						|
    public function actionCreate()
 | 
						|
    {
 | 
						|
        $model = new Post;
 | 
						|
 | 
						|
        if ($model->load(Yii::$app->request->post()) && $model->save()) {
 | 
						|
            return $this->redirect(['view', 'id' => $model->id]);
 | 
						|
        } else {
 | 
						|
            return $this->render('create', [
 | 
						|
                'model' => $model,
 | 
						|
            ]);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
In the `view` action (defined by the `actionView()` method), the code first loads the [model](structure-models.md)
 | 
						|
according to the requested model ID; If the model is loaded successfully, it will display it using
 | 
						|
a [view](structure-views.md) named `view`. Otherwise, it will throw an exception.
 | 
						|
 | 
						|
In the `create` action (defined by the `actionCreate()` method), the code is similar. It first tries to populate
 | 
						|
the [model](structure-models.md) using the request data and save the model. If both succeed it will redirect
 | 
						|
the browser to the `view` action with the ID of the newly created model. Otherwise it will display
 | 
						|
the `create` view through which users can provide the needed input.
 | 
						|
 | 
						|
 | 
						|
## Routes <a name="routes"></a>
 | 
						|
 | 
						|
End users address actions through the so-called *routes*. A route is a string that consists of the following parts:
 | 
						|
 | 
						|
* a module ID: this exists only if the controller belongs to a non-application [module](structure-modules.md);
 | 
						|
* a controller ID: a string that uniquely identifies the controller among all controllers within the same application
 | 
						|
  (or the same module if the controller belongs to a module);
 | 
						|
* an action ID: a string that uniquely identifies the action among all actions within the same controller.
 | 
						|
 | 
						|
Routes take the following format:
 | 
						|
 | 
						|
```
 | 
						|
ControllerID/ActionID
 | 
						|
```
 | 
						|
 | 
						|
or the following format if the controller belongs to a module:
 | 
						|
 | 
						|
```php
 | 
						|
ModuleID/ControllerID/ActionID
 | 
						|
```
 | 
						|
 | 
						|
So if a user requests with the URL `http://hostname/index.php?r=site/index`, the `index` action in the `site` controller
 | 
						|
will be executed. For more details how routes are resolved into actions, please refer to
 | 
						|
the [Routing](runtime-routing.md) section.
 | 
						|
 | 
						|
 | 
						|
## Creating Controllers <a name="creating-controllers"></a>
 | 
						|
 | 
						|
In [[yii\web\Application|Web applications]], controllers should extend from [[yii\web\Controller]] or its
 | 
						|
child classes. Similarly in [[yii\console\Application|console applications]], controllers should extend from
 | 
						|
[[yii\console\Controller]] or its child classes. The following code defines a `site` controller:
 | 
						|
 | 
						|
```php
 | 
						|
namespace app\controllers;
 | 
						|
 | 
						|
use yii\web\Controller;
 | 
						|
 | 
						|
class SiteController extends Controller
 | 
						|
{
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
 | 
						|
### Controller IDs <a name="controller-ids"></a>
 | 
						|
 | 
						|
Usually, a controller is designed to handle the requests regarding a particular type of resource.
 | 
						|
For this reason, controller IDs are often nouns referring to the types of the resources that they are handling.
 | 
						|
For example, you may use `article` as the ID of a controller that handles article data.
 | 
						|
 | 
						|
By default, controller IDs should contain these characters only: English letters in lower case, digits,
 | 
						|
underscores, dashes and forward slashes. For example, `article` and `post-comment` are both valid controller IDs,
 | 
						|
while `article?`, `PostComment`, `admin\post` are not.
 | 
						|
 | 
						|
A controller ID may also contain a subdirectory prefix. For example, `admin/article` stands for an `article` controller
 | 
						|
in the `admin` subdirectory under the [[yii\base\Application::controllerNamespace|controller namespace]].
 | 
						|
Valid characters for subdirectory prefixes include: English letters in lower and upper cases, digits, underscores and
 | 
						|
forward slashes, where forward slashes are used as separators for multi-level subdirectories (e.g. `panels/admin`).
 | 
						|
 | 
						|
 | 
						|
### Controller Class Naming <a name="controller-class-naming"></a>
 | 
						|
 | 
						|
Controller class names can be derived from controller IDs according to the following rules:
 | 
						|
 | 
						|
* Turn the first letter in each word separated by dashes into upper case. Note that if the controller ID
 | 
						|
  contains slashes, this rule only applies to the part after the last slash in the ID.
 | 
						|
* Remove dashes and replace any forward slashes with backward slashes.
 | 
						|
* Append the suffix `Controller`.
 | 
						|
* And prepend the [[yii\base\Application::controllerNamespace|controller namespace]].
 | 
						|
 | 
						|
The followings are some examples, assuming the [[yii\base\Application::controllerNamespace|controller namespace]]
 | 
						|
takes the default value `app\controllers`:
 | 
						|
 | 
						|
* `article` derives `app\controllers\ArticleController`;
 | 
						|
* `post-comment` derives `app\controllers\PostCommentController`;
 | 
						|
* `admin/post-comment` derives `app\controllers\admin\PostCommentController`;
 | 
						|
* `adminPanels/post-comment` derives `app\controllers\adminPanels\PostCommentController`.
 | 
						|
 | 
						|
Controller classes must be [autoloadable](concept-autoloading.md). For this reason, in the above examples,
 | 
						|
the `article` controller class should be saved in the file whose [alias](concept-aliases.md)
 | 
						|
is `@app/controllers/ArticleController.php`; while the `admin/post2-comment` controller should be
 | 
						|
in `@app/controllers/admin/Post2CommentController.php`.
 | 
						|
 | 
						|
> Info: The last example `admin/post2-comment` shows how you can put a controller under a sub-directory
 | 
						|
  of the [[yii\base\Application::controllerNamespace|controller namespace]]. This is useful when you want
 | 
						|
  to organize your controllers into several categories and you do not want to use [modules](structure-modules.md).
 | 
						|
 | 
						|
 | 
						|
### Controller Map <a name="controller-map"></a>
 | 
						|
 | 
						|
You can configure [[yii\base\Application::controllerMap|controller map]] to overcome the constraints
 | 
						|
of the controller IDs and class names described above. This is mainly useful when you are using some
 | 
						|
third-party controllers which you do not control over their class names.
 | 
						|
 | 
						|
You may configure [[yii\base\Application::controllerMap|controller map]] in the
 | 
						|
[application configuration](structure-applications.md#application-configurations) like the following:
 | 
						|
 | 
						|
```php
 | 
						|
[
 | 
						|
    'controllerMap' => [
 | 
						|
        [
 | 
						|
            // declares "account" controller using a class name
 | 
						|
            'account' => 'app\controllers\UserController',
 | 
						|
 | 
						|
            // declares "article" controller using a configuration array
 | 
						|
            'article' => [
 | 
						|
                'class' => 'app\controllers\PostController',
 | 
						|
                'enableCsrfValidation' => false,
 | 
						|
            ],
 | 
						|
        ],
 | 
						|
    ],
 | 
						|
]
 | 
						|
```
 | 
						|
 | 
						|
 | 
						|
### Default Controller <a name="default-controller"></a>
 | 
						|
 | 
						|
Each application has a default controller specified via the [[yii\base\Application::defaultRoute]] property.
 | 
						|
When a request does not specify a [route](#ids-routes), the route specified by this property will be used.
 | 
						|
For [[yii\web\Application|Web applications]], its value is `'site'`, while for [[yii\console\Application|console applications]],
 | 
						|
it is `help`. Therefore, if a URL is `http://hostname/index.php`, it means the `site` controller will handle the request.
 | 
						|
 | 
						|
You may change the default controller with the following [application configuration](structure-applications.md#application-configurations):
 | 
						|
 | 
						|
```php
 | 
						|
[
 | 
						|
    'defaultRoute' => 'main',
 | 
						|
]
 | 
						|
```
 | 
						|
 | 
						|
 | 
						|
## Creating Actions <a name="creating-actions"></a>
 | 
						|
 | 
						|
Creating actions can be as simple as defining the so-called *action methods* in a controller class. An action method is
 | 
						|
a *public* method whose name starts with the word `action`. The return value of an action method represents
 | 
						|
the response data to be sent to end users. The following code defines two actions `index` and `hello-world`:
 | 
						|
 | 
						|
```php
 | 
						|
namespace app\controllers;
 | 
						|
 | 
						|
use yii\web\Controller;
 | 
						|
 | 
						|
class SiteController extends Controller
 | 
						|
{
 | 
						|
    public function actionIndex()
 | 
						|
    {
 | 
						|
        return $this->render('index');
 | 
						|
    }
 | 
						|
 | 
						|
    public function actionHelloWorld()
 | 
						|
    {
 | 
						|
        return 'Hello World';
 | 
						|
    }
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
 | 
						|
### Action IDs <a name="action-ids"></a>
 | 
						|
 | 
						|
An action is often designed to perform a particular manipulation about a resource. For this reason,
 | 
						|
action IDs are usually verbs, such as `view`, `update`, etc.
 | 
						|
 | 
						|
By default, action IDs should contain these characters only: English letters in lower case, digits,
 | 
						|
underscores and dashes. The dashes in an actionID are used to separate words. For example,
 | 
						|
`view`, `update2`, `comment-post` are all valid action IDs, while `view?`, `Update` are not.
 | 
						|
 | 
						|
You can create actions in two ways: inline actions and standalone actions. An inline action is
 | 
						|
defined as a method in the controller class, while a standalone action is a class extending
 | 
						|
[[yii\base\Action]] or its child class. Inline actions take less effort to create and are often preferred
 | 
						|
if you have no intention to reuse these actions. Standalone actions, on the other hand, are mainly
 | 
						|
created to be used in different controllers or be redistributed as [extensions](structure-extensions.md).
 | 
						|
 | 
						|
 | 
						|
### Inline Actions <a name="inline-actions"></a>
 | 
						|
 | 
						|
Inline actions refer to the actions that are defined in terms of action methods as we just described.
 | 
						|
 | 
						|
The names of the action methods are derived from action IDs according to the following criteria:
 | 
						|
 | 
						|
* Turn the first letter in each word of the action ID into upper case;
 | 
						|
* Remove dashes;
 | 
						|
* Prepend the prefix `action`.
 | 
						|
 | 
						|
For example, `index` becomes `actionIndex`, and `hello-world` becomes `actionHelloWorld`.
 | 
						|
 | 
						|
> Note: The names of the action methods are *case-sensitive*. If you have a method named `ActionIndex`,
 | 
						|
  it will not be considered as an action method, and as a result, the request for the `index` action
 | 
						|
  will result in an exception. Also note that action methods must be public. A private or protected
 | 
						|
  method does NOT define an inline action.
 | 
						|
 | 
						|
 | 
						|
Inline actions are the most commonly defined actions because they take little effort to create. However,
 | 
						|
if you plan to reuse the same action in different places, or if you want to redistribute an action,
 | 
						|
you should consider defining it as a *standalone action*.
 | 
						|
 | 
						|
 | 
						|
### Standalone Actions <a name="standalone-actions"></a>
 | 
						|
 | 
						|
Standalone actions are defined in terms of action classes extending [[yii\base\Action]] or its child classes.
 | 
						|
For example, in the Yii releases, there are [[yii\web\ViewAction]] and [[yii\web\ErrorAction]], both of which
 | 
						|
are standalone actions.
 | 
						|
 | 
						|
To use a standalone action, you should declare it in the *action map* by overriding the
 | 
						|
[[yii\base\Controller::actions()]] method in your controller classes like the following:
 | 
						|
 | 
						|
```php
 | 
						|
public function actions()
 | 
						|
{
 | 
						|
    return [
 | 
						|
        // declares "error" action using a class name
 | 
						|
        'error' => 'yii\web\ErrorAction',
 | 
						|
 | 
						|
        // declares "view" action using a configuration array
 | 
						|
        'view' => [
 | 
						|
            'class' => 'yii\web\ViewAction',
 | 
						|
            'viewPrefix' => '',
 | 
						|
        ],
 | 
						|
    ];
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
As you can see, the `actions()` method should return an array whose keys are action IDs and values the corresponding
 | 
						|
action class names or [configurations](concept-configurations.md). Unlike inline actions, action IDs for standalone
 | 
						|
actions can contain arbitrary characters, as long as they are declared in the `actions()` method.
 | 
						|
 | 
						|
 | 
						|
To create a standalone action class, you should extend [[yii\base\Action]] or its child class, and implement
 | 
						|
a public method named `run()`. The role of the `run()` method is similar to that of an action method. For example,
 | 
						|
 | 
						|
```php
 | 
						|
<?php
 | 
						|
namespace app\components;
 | 
						|
 | 
						|
use yii\base\Action;
 | 
						|
 | 
						|
class HelloWorldAction extends Action
 | 
						|
{
 | 
						|
    public function run()
 | 
						|
    {
 | 
						|
        return "Hello World";
 | 
						|
    }
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
 | 
						|
### Action Results <a name="action-results"></a>
 | 
						|
 | 
						|
The return value of an action method or the `run()` method of a standalone action is significant. It stands
 | 
						|
for the result of the corresponding action.
 | 
						|
 | 
						|
The return value can be a [response](runtime-responses.md) object which will be sent to as the response
 | 
						|
to end users.
 | 
						|
 | 
						|
* For [[yii\web\Application|Web applications]], the return value can also be some arbitrary data which will
 | 
						|
  be assigned to [[yii\web\Response::data]] and be further converted into a string representing the response body.
 | 
						|
* For [[yii\console\Application|console applications], the return value can also be an integer representing
 | 
						|
  the [[yii\console\Response::exitStatus|exit status]] of the command execution.
 | 
						|
 | 
						|
In the examples shown above, the action results are all strings which will be treated as the response body
 | 
						|
to be sent to end users. The following example shows how an action can redirect the user browser to a new URL
 | 
						|
by returning a response object (because the [[yii\web\Controller::redirect()|redirect()]] method returns
 | 
						|
a response object):
 | 
						|
 | 
						|
```php
 | 
						|
public function actionForward()
 | 
						|
{
 | 
						|
    // redirect the user browser to http://example.com
 | 
						|
    return $this->redirect('http://example.com');
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
 | 
						|
### Action Parameters <a name="action-parameters"></a>
 | 
						|
 | 
						|
The action methods for inline actions and the `run()` methods for standalone actions can take parameters,
 | 
						|
called *action parameters*. Their values are obtained from requests. For [[yii\web\Application|Web applications]],
 | 
						|
the value of each action parameter is retrieved from `$_GET` using the parameter name as the key;
 | 
						|
for [[yii\console\Application|console applications]], they correspond to the command line arguments.
 | 
						|
 | 
						|
In the following example, the `view` action (an inline action) has declared two parameters: `$id` and `$version`.
 | 
						|
 | 
						|
```php
 | 
						|
namespace app\controllers;
 | 
						|
 | 
						|
use yii\web\Controller;
 | 
						|
 | 
						|
class PostController extends Controller
 | 
						|
{
 | 
						|
    public function actionView($id, $version = null)
 | 
						|
    {
 | 
						|
        // ...
 | 
						|
    }
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
The action parameters will be populated as follows for different requests:
 | 
						|
 | 
						|
* `http://hostname/index.php?r=post/view&id=123`: the `$id` parameter will be filled with the value
 | 
						|
  `'123'`,  while `$version` is still null because there is no `version` query parameter.
 | 
						|
* `http://hostname/index.php?r=post/view&id=123&version=2`: the `$id` and `$version` parameters will
 | 
						|
  be filled with `'123'` and `'2'`, respectively.
 | 
						|
* `http://hostname/index.php?r=post/view`: a [[yii\web\BadRequestHttpException]] exception will be thrown
 | 
						|
  because the required `$id` parameter is not provided in the request.
 | 
						|
* `http://hostname/index.php?r=post/view&id[]=123`: a [[yii\web\BadRequestHttpException]] exception will be thrown
 | 
						|
  because `$id` parameter is receiving an unexpected array value `['123']`.
 | 
						|
 | 
						|
If you want an action parameter to accept array values, you should type-hint it with `array`, like the following:
 | 
						|
 | 
						|
```php
 | 
						|
public function actionView(array $id, $version = null)
 | 
						|
{
 | 
						|
    // ...
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
Now if the request is `http://hostname/index.php?r=post/view&id[]=123`, the `$id` parameter will take the value
 | 
						|
of `['123']`. If the request is `http://hostname/index.php?r=post/view&id=123`, the `$id` parameter will still
 | 
						|
receive the same array value because the scalar value `'123'` will be automatically turned into an array.
 | 
						|
 | 
						|
The above examples mainly show how action parameters work for Web applications. For console applications,
 | 
						|
please refer to the [Console Commands](tutorial-console.md) section for more details.
 | 
						|
 | 
						|
 | 
						|
### Default Action <a name="default-action"></a>
 | 
						|
 | 
						|
Each controller has a default action specified via the [[yii\base\Controller::defaultAction]] property.
 | 
						|
When a [route](#ids-routes) contains the controller ID only, it implies that the default action of
 | 
						|
the specified controller is requested.
 | 
						|
 | 
						|
By default, the default action is set as `index`. If you want to change the default value, simply override
 | 
						|
this property in the controller class, like the following:
 | 
						|
 | 
						|
```php
 | 
						|
namespace app\controllers;
 | 
						|
 | 
						|
use yii\web\Controller;
 | 
						|
 | 
						|
class SiteController extends Controller
 | 
						|
{
 | 
						|
    public $defaultAction = 'home';
 | 
						|
 | 
						|
    public function actionHome()
 | 
						|
    {
 | 
						|
        return $this->render('home');
 | 
						|
    }
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
 | 
						|
## Controller Lifecycle <a name="controller-lifecycle"></a>
 | 
						|
 | 
						|
When processing a request, an [application](structure-applications.md) will create a controller
 | 
						|
based on the requested [route](#routes). The controller will then undergo the following lifecycle
 | 
						|
to fulfill the request:
 | 
						|
 | 
						|
1. The [[yii\base\Controller::init()]] method is called after the controller is created and configured.
 | 
						|
2. The controller creates an action object based on the requested action ID:
 | 
						|
   * If the action ID is not specified, the [[yii\base\Controller::defaultAction|default action ID]] will be used.
 | 
						|
   * If the action ID is found in the [[yii\base\Controller::actions()|action map]], a standalone action
 | 
						|
     will be created;
 | 
						|
   * If the action ID is found to match an action method, an inline action will be created;
 | 
						|
   * Otherwise an [[yii\base\InvalidRouteException]] exception will be thrown.
 | 
						|
3. The controller sequentially calls the `beforeAction()` method of the application, the module (if the controller
 | 
						|
   belongs to a module) and the controller.
 | 
						|
   * If one of the calls returns false, the rest of the uncalled `beforeAction()` will be skipped and the
 | 
						|
     action execution will be cancelled.
 | 
						|
   * By default, each `beforeAction()` method call will trigger a `beforeAction` event to which you can attach a handler.
 | 
						|
4. The controller runs the action:
 | 
						|
   * The action parameters will be analyzed and populated from the request data;
 | 
						|
5. The controller sequentially calls the `afterAction()` method of the controller, the module (if the controller
 | 
						|
   belongs to a module) and the application.
 | 
						|
   * By default, each `afterAction()` method call will trigger an `afterAction` event to which you can attach a handler.
 | 
						|
6. The application will take the action result and assign it to the [response](runtime-responses.md).
 | 
						|
 | 
						|
 | 
						|
## Best Practices <a name="best-practices"></a>
 | 
						|
 | 
						|
In a well-designed application, controllers are often very thin with each action containing only a few lines of code.
 | 
						|
If your controller is rather complicated, it usually indicates that you should refactor it and move some code
 | 
						|
to other classes.
 | 
						|
 | 
						|
In summary, controllers
 | 
						|
 | 
						|
* may access the [request](runtime-requests.md) data;
 | 
						|
* may call methods of [models](structure-models.md) and other service components with request data;
 | 
						|
* may use [views](structure-views.md) to compose responses;
 | 
						|
* should NOT process the request data - this should be done in [models](structure-models.md);
 | 
						|
* should avoid embedding HTML or other presentational code - this is better done in [views](structure-views.md).
 |