structure-controllers.md 18.3 KB
Newer Older
Qiang Xue committed
1 2
Controllers
===========
3

Qiang Xue committed
4
Controllers are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture.
Qiang Xue committed
5
They are objects responsible for processing requests and generating responses. In particular, after
Qiang Xue committed
6 7 8
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.
Qiang Xue committed
9

10
Controllers are composed by *actions* which are the most basic units that end users can address and request for
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
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);
Qiang Xue committed
28
        if ($model === null) {
29 30
            throw new NotFoundHttpException;
        }
Qiang Xue committed
31 32 33 34

        return $this->render('view', [
            'model' => $model,
        ]);
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
    }

    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.
60 61


62
## Routes <a name="routes"></a>
63

64
End users address actions through the so-called *routes*. A route is a string that consists of the following parts:
65

66 67 68 69
* 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 t hat uniquely identifies the action among all actions within the same controller.
Qiang Xue committed
70

71
Routes take the following format:
Qiang Xue committed
72

73 74 75
```
ControllerID/ActionID
```
Qiang Xue committed
76

77
or the following format if the controller belongs to a module:
Qiang Xue committed
78

79 80 81
```php
ModuleID/ControllerID/ActionID
```
Qiang Xue committed
82

83 84 85
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.
Qiang Xue committed
86 87


88
## Creating Controllers <a name="creating-controllers"></a>
Qiang Xue committed
89

90 91 92
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:
Qiang Xue committed
93 94 95 96 97 98 99 100 101 102 103 104

```php
namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
}
```


105
### Controller IDs <a name="controller-ids"></a>
Qiang Xue committed
106

107 108 109
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.
Qiang Xue committed
110

111 112 113
By default, controller IDs should contain these characters only: English letters in lower case, digits,
underscores, dashes and forward slashes. For example, `article`, `post-comment`, `admin/post2-comment` are
all valid controller IDs, while `article?`, `PostComment`, `admin\post` are not.
Qiang Xue committed
114

115 116 117 118 119 120 121 122 123 124 125
The dashes in a controller ID are used to separate words, while the forward slashes to organize controllers in
sub-directories.


### 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.
Qiang Xue committed
126
* Append the suffix `Controller`.
127 128 129 130 131 132 133 134 135 136 137 138 139
* 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/post2-comment` derives `app\controllers\admin\Post2CommentController`.

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`.
Qiang Xue committed
140

141 142 143
> 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).
Qiang Xue committed
144

145 146 147 148 149 150 151 152 153

### 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:
Qiang Xue committed
154 155 156 157 158

```php
[
    'controllerMap' => [
        [
159
            // declares "account" controller using a class name
Qiang Xue committed
160
            'account' => 'app\controllers\UserController',
161 162

            // declares "article" controller using a configuration array
Qiang Xue committed
163 164 165 166 167 168 169 170 171 172
            'article' => [
                'class' => 'app\controllers\PostController',
                'enableCsrfValidation' => false,
            ],
        ],
    ],
]
```


173
### Default Controller <a name="default-controller"></a>
Qiang Xue committed
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

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',
]
```


189
## Creating Actions <a name="creating-actions"></a>
Qiang Xue committed
190

Carsten Brandt committed
191
Creating actions can be as simple as defining the so-called *action methods* in a controller class. An action method is
192 193
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`:
194 195 196 197 198 199 200 201

```php
namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
202 203 204 205 206
    public function actionIndex()
    {
        return $this->render('index');
    }

Qiang Xue committed
207
    public function actionHelloWorld()
208
    {
Qiang Xue committed
209
        return 'Hello World';
210
    }
211 212 213
}
```

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235

### 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:
236

Qiang Xue committed
237 238 239
* Turn the first letter in each word of the action ID into upper case;
* Remove dashes;
* Prepend the prefix `action`.
Mark committed
240

241
For example, `index` becomes `actionIndex`, and `hello-world` becomes `actionHelloWorld`.
242

243 244 245 246
> 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.
247 248


249 250 251
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*.
252

253

254
### Standalone Actions <a name="standalone-actions"></a>
255

Qiang Xue committed
256
Standalone actions are defined in terms of action classes extending [[yii\base\Action]] or its child classes.
257
For example, in the Yii releases, there are [[yii\web\ViewAction]] and [[yii\web\ErrorAction]], both of which
Qiang Xue committed
258 259
are standalone actions.

260 261
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:
Qiang Xue committed
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278

```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' => '',
        ],
    ];
}
```

279 280 281
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.
Qiang Xue committed
282 283 284


To create a standalone action class, you should extend [[yii\base\Action]] or its child class, and implement
285
a public method named `run()`. The role of the `run()` method is similar to that of an action method. For example,
Qiang Xue committed
286 287 288 289 290 291 292

```php
<?php
namespace app\components;

use yii\base\Action;

293
class HelloWorldAction extends Action
Qiang Xue committed
294 295 296 297 298 299 300 301 302
{
    public function run()
    {
        return "Hello World";
    }
}
```


303
### Action Results <a name="action-results"></a>
Qiang Xue committed
304

305 306
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.
307

308 309
The return value can be a [response](runtime-responses.md) object which will be sent to as the response
to end users.
310

311 312 313 314
* 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.
315

316 317 318 319
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):
320

321 322
```php
public function actionForward()
Qiang Xue committed
323
{
324 325
    // redirect the user browser to http://example.com
    return $this->redirect('http://example.com');
Qiang Xue committed
326 327
}
```
328

Qiang Xue committed
329

330 331 332 333 334 335
### 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.
336

337
In the following example, the `view` action (an inline action) has declared two parameters: `$id` and `$version`.
338 339 340 341 342 343

```php
namespace app\controllers;

use yii\web\Controller;

344
class PostController extends Controller
345
{
346 347
    public function actionView($id, $version = null)
    {
348 349 350 351
        // ...
    }
}
```
352

353
The action parameters will be populated as follows for different requests:
354

355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
* `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)
{
    // ...
370 371 372
}
```

373 374 375
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.
376

377 378
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.
Qiang Xue committed
379 380


381
### Default Action <a name="default-action"></a>
382

383 384 385 386 387 388
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:
389 390 391 392 393 394

```php
namespace app\controllers;

use yii\web\Controller;

395
class SiteController extends Controller
396
{
397 398 399
    public $defaultAction = 'home';

    public function actionHome()
400
    {
401
        return $this->render('home');
402
    }
403 404 405
}
```

406

407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
## 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).
Mark committed
431

Qiang Xue committed
432 433 434 435

## 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.
436 437
If your controller is rather complicated, it usually indicates that you should refactor it and move some code
to other classes.
Qiang Xue committed
438

439
In summary, controllers
Qiang Xue committed
440 441

* may access the [request](runtime-requests.md) data;
442 443 444 445
* 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).