Commit fb248554 by AbrahamGreyson

copy files, prepare to translate.

parent 631dfd7c
Application Components
======================
Applications are [service locators](concept-service-locators.md). They host a set of the so-called
*application components* that provide different services for processing requests. For example,
the `urlManager` component is responsible for routing Web requests to appropriate controllers;
the `db` component provides DB-related services; and so on.
Each application component has an ID that uniquely identifies itself among other application components
in the same application. You can access an application component through the expression
```php
\Yii::$app->componentID
```
For example, you can use `\Yii::$app->db` to get the [[yii\db\Connection|DB connection]],
and `\Yii::$app->cache` to get the [[yii\caching\Cache|primary cache]] registered with the application.
An application component is created the first time it is accessed through the above expression. Any
further accesses will return the same component instance.
Application components can be any objects. You can register them by configuring
the [[yii\base\Application::components]] property in [application configurations](structure-applications.md#application-configurations).
For example,
```php
[
'components' => [
// register "cache" component using a class name
'cache' => 'yii\caching\ApcCache',
// register "db" component using a configuration array
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=demo',
'username' => 'root',
'password' => '',
],
// register "search" component using an anonymous function
'search' => function () {
return new app\components\SolrService;
},
],
]
```
> Info: While you can register as many application components as you want, you should do this judiciously.
Application components are like global variables. Using too many application components can potentially
make your code harder to test and maintain. In many cases, you can simply create a local component
and use it when needed.
## Bootstrapping Components <a name="bootstrapping-components"></a>
As mentioned above, an application component will only be instantiated when it is being accessed the first time.
If it is not accessed at all during a request, it will not be instantiated. Sometimes, however, you may want
to instantiate an application component for every request, even if it is not explicitly accessed.
To do so, you may list its ID in the [[yii\base\Application::bootstrap|bootstrap]] property of the application.
For example, the following application configuration makes sure the `log` component is always loaded:
```php
[
'bootstrap' => [
'log',
],
'components' => [
'log' => [
// configuration for "log" component
],
],
]
```
## Core Application Components <a name="core-application-components"></a>
Yii defines a set of *core* application components with fixed IDs and default configurations. For example,
the [[yii\web\Application::request|request]] component is used to collect information about
a user request and resolve it into a [route](runtime-routing.md); the [[yii\base\Application::db|db]]
component represents a database connection through which you can perform database queries.
It is with help of these core application components that Yii applications are able to handle user requests.
Below is the list of the predefined core application components. You may configure and customize them
like you do with normal application components. When you are configuring a core application component,
if you do not specify its class, the default one will be used.
* [[yii\web\AssetManager|assetManager]]: manages asset bundles and asset publishing.
Please refer to the [Managing Assets](output-assets.md) section for more details.
* [[yii\db\Connection|db]]: represents a database connection through which you can perform DB queries.
Note that when you configure this component, you must specify the component class as well as other required
component properties, such as [[yii\db\Connection::dsn]].
Please refer to the [Data Access Objects](db-dao.md) section for more details.
* [[yii\base\Application::errorHandler|errorHandler]]: handles PHP errors and exceptions.
Please refer to the [Handling Errors](tutorial-handling-errors.md) section for more details.
* [[yii\base\Formatter|formatter]]: formats data when they are displayed to end users. For example, a number
may be displayed with thousand separator, a date may be formatted in long format.
Please refer to the [Data Formatting](output-formatting.md) section for more details.
* [[yii\i18n\I18N|i18n]]: supports message translation and formatting.
Please refer to the [Internationalization](tutorial-i18n.md) section for more details.
* [[yii\log\Dispatcher|log]]: manages log targets.
Please refer to the [Logging](tutorial-logging.md) section for more details.
* [[yii\swiftmailer\Mailer|mail]]: supports mail composing and sending.
Please refer to the [Mailing](tutorial-mailing.md) section for more details.
* [[yii\base\Application::response|response]]: represents the response being sent to end users.
Please refer to the [Responses](runtime-responses.md) section for more details.
* [[yii\base\Application::request|request]]: represents the request received from end users.
Please refer to the [Requests](runtime-requests.md) section for more details.
* [[yii\web\Session|session]]: represents the session information. This component is only available
in [[yii\web\Application|Web applications]].
Please refer to the [Sessions and Cookies](runtime-sessions-cookies.md) section for more details.
* [[yii\web\UrlManager|urlManager]]: supports URL parsing and creation.
Please refer to the [URL Parsing and Generation](runtime-url-handling.md) section for more details.
* [[yii\web\User|user]]: represents the user authentication information. This component is only available
in [[yii\web\Application|Web applications]]
Please refer to the [Authentication](security-authentication.md) section for more details.
* [[yii\web\View|view]]: supports view rendering.
Please refer to the [Views](structure-views.md) section for more details.
Applications
============
Applications are objects that govern the overall structure and lifecycle of Yii application systems.
Each Yii application system contains a single application object which is created in
the [entry script](structure-entry-scripts.md) and is globally accessible through the expression `\Yii::$app`.
> Info: Depending on the context, when we say "an application", it can mean either an application
object or an application system.
There are two types of applications: [[yii\web\Application|Web applications]] and
[[yii\console\Application|console applications]]. As the names indicate, the former mainly handles
Web requests while the latter console command requests.
## Application Configurations <a name="application-configurations"></a>
When an [entry script](structure-entry-scripts.md) creates an application, it will load
a [configuration](concept-configurations.md) and apply it to the application, like the following:
```php
require(__DIR__ . '/../vendor/autoload.php');
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
// load application configuration
$config = require(__DIR__ . '/../config/web.php');
// instantiate and configure the application
(new yii\web\Application($config))->run();
```
Like normal [configurations](concept-configurations.md), application configurations specify how
to initialize properties of application objects. Because application configurations are often
very complex, they usually are kept in [configuration files](concept-configurations.md#configuration-files),
like the `web.php` file in the above example.
## Application Properties <a name="application-properties"></a>
There are many important application properties that you should configure in application configurations.
These properties typically describe the environment that applications are running in.
For example, applications need to know how to load [controllers](structure-controllers.md),
where to store temporary files, etc. In the following, we will summarize these properties.
### Required Properties <a name="required-properties"></a>
In any application, you should at least configure two properties: [[yii\base\Application::id|id]]
and [[yii\base\Application::basePath|basePath]].
#### [[yii\base\Application::id|id]] <a name="id"></a>
The [[yii\base\Application::id|id]] property specifies a unique ID that differentiates an application
from others. It is mainly used programmatically. Although not a requirement, for best interoperability
it is recommended that you use alphanumeric characters only when specifying an application ID.
#### [[yii\base\Application::basePath|basePath]] <a name="basePath"></a>
The [[yii\base\Application::basePath|basePath]] property specifies the root directory of an application.
It is the directory that contains all protected source code of an application system. Under this directory,
you normally will see sub-directories such as `models`, `views`, `controllers`, which contain source code
corresponding to the MVC pattern.
You may configure the [[yii\base\Application::basePath|basePath]] property using a directory path
or a [path alias](concept-aliases.md). In both forms, the corresponding directory must exist, or an exception
will be thrown. The path will be normalized by calling the `realpath()` function.
The [[yii\base\Application::basePath|basePath]] property is often used to derive other important
paths (e.g. the runtime path). For this reason, a path alias named `@app` is predefined to represent this
path. Derived paths may then be formed using this alias (e.g. `@app/runtime` to refer to the runtime directory).
### Important Properties <a name="important-properties"></a>
The properties described in this subsection often need to be configured because they differ across
different applications.
#### [[yii\base\Application::aliases|aliases]] <a name="aliases"></a>
This property allows you to define a set of [aliases](concept-aliases.md) in terms of an array.
The array keys are alias names, and the array values are the corresponding path definitions.
For example,
```php
[
'aliases' => [
'@name1' => 'path/to/path1',
'@name2' => 'path/to/path2',
],
]
```
This property is provided such that you can define aliases in terms of application configurations instead of
the method calls [[Yii::setAlias()]].
#### [[yii\base\Application::bootstrap|bootstrap]] <a name="bootstrap"></a>
This is a very useful property. It allows you to specify an array of components that should
be run during the application [[yii\base\Application::bootstrap()|bootstrapping process]].
For example, if you want a [module](structure-modules.md) to customize the [URL rules](runtime-url-handling.md),
you may list its ID as an element in this property.
Each component listed in this property may be specified in one of the following formats:
- an application component ID as specified via [components](#components).
- a module ID as specified via [modules](#modules).
- a class name.
- a configuration array.
- an anonymous function that creates and returns a component.
For example,
```php
[
'bootstrap' => [
// an application component ID or module ID
'demo',
// a class name
'app\components\Profiler',
// a configuration array
[
'class' => 'app\components\Profiler',
'level' => 3,
],
// an anonymous function
function () {
return new app\components\Profiler();
}
],
]
```
> Info: If a module ID is the same as an application component ID, the application component will be used during
the bootstrapping process. If you want to use the module instead, you may specify it using an anonymous function
like the following:
>```php
[
function () {
return Yii::$app->getModule('user');
},
]
```
During the bootstrapping process, each component will be instantiated. If the component class
implements [[yii\base\BootstrapInterface]], its [[yii\base\BootstrapInterface::bootstrap()|bootstrap()]] method
will also be called.
Another practical example is in the application configuration for the [Basic Application Template](start-installation.md),
where the `debug` and `gii` modules are configured as bootstrapping components when the application is running
in development environment,
```php
if (YII_ENV_DEV) {
// configuration adjustments for 'dev' environment
$config['bootstrap'][] = 'debug';
$config['modules']['debug'] = 'yii\debug\Module';
$config['bootstrap'][] = 'gii';
$config['modules']['gii'] = 'yii\gii\Module';
}
```
> Note: Putting too many components in `bootstrap` will degrade the performance of your application because
for each request, the same set of components need to be run. So use bootstrapping components judiciously.
#### [[yii\web\Application::catchAll|catchAll]] <a name="catchAll"></a>
This property is supported by [[yii\web\Application|Web applications]] only. It specifies
a [controller action](structure-controllers.md) which should handle all user requests. This is mainly
used when the application is in maintenance mode and needs to handle all incoming requests via a single action.
The configuration is an array whose first element specifies the route of the action.
The rest of the array elements (key-value pairs) specify the parameters to be bound to the action. For example,
```php
[
'catchAll' => [
'offline/notice',
'param1' => 'value1',
'param2' => 'value2',
],
]
```
#### [[yii\base\Application::components|components]] <a name="components"></a>
This is the single most important property. It allows you to register a list of named components
called [application components](#structure-application-components.md) that you can use in other places. For example,
```php
[
'components' => [
'cache' => [
'class' => 'yii\caching\FileCache',
],
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => true,
],
],
]
```
Each application component is specified as a key-value pair in the array. The key represents the component ID,
while the value represents the component class name or [configuration](concept-configurations.md).
You can register any component with an application, and the component can later be accessed globally
using the expression `\Yii::$app->ComponentID`.
Please read the [Application Components](structure-application-components.md) section for details.
#### [[yii\base\Application::controllerMap|controllerMap]] <a name="controllerMap"></a>
This property allows you to map a controller ID to an arbitrary controller class. By default, Yii maps
controller IDs to controller classes based on a [convention](#controllerNamespace) (e.g. the ID `post` would be mapped
to `app\controllers\PostController`). By configuring this property, you can break the convention for
specific controllers. In the following example, `account` will be mapped to
`app\controllers\UserController`, while `article` will be mapped to `app\controllers\PostController`.
```php
[
'controllerMap' => [
[
'account' => 'app\controllers\UserController',
'article' => [
'class' => 'app\controllers\PostController',
'enableCsrfValidation' => false,
],
],
],
]
```
The array keys of this property represent the controller IDs, while the array values represent the corresponding
controller class names or [configurations](concept-configurations.md).
#### [[yii\base\Application::controllerNamespace|controllerNamespace]] <a name="controllerNamespace"></a>
This property specifies the default namespace under which controller classes should be located. It defaults to
`app\controllers`. If a controller ID is `post`, by convention the corresponding controller class name (without
namespace) would be `PostController`, and the fully qualified class name would be `app\controllers\PostController`.
Controller classes may also be located under sub-directories of the directory corresponding to this namespace.
For example, given a controller ID `admin/post`, the corresponding fully qualified controller class would
be `app\controllers\admin\PostController`.
It is important that the fully qualified controller classes should be [autoloadable](concept-autoloading.md)
and the actual namespace of your controller classes match the value of this property. Otherwise,
you will receive "Page Not Found" error when accessing the application.
In case you want to break the convention as described above, you may configure the [controllerMap](#controllerMap)
property.
#### [[yii\base\Application::language|language]] <a name="language"></a>
This property specifies the language in which the application should display content to end users.
The default value of this property is `en`, meaning English. You should configure this property
if your application needs to support multiple languages.
The value of this property determines various [internationalization](tutorial-i18n.md) aspects,
including message translation, date formatting, number formatting, etc. For example, the [[yii\jui\DatePicker]] widget
will use this property value by default to determine in which language the calendar should be displayed and how
should the date be formatted.
It is recommended that you specify a language in terms of an [IETF language tag](http://en.wikipedia.org/wiki/IETF_language_tag).
For example, `en` stands for English, while `en-US` stands for English (United States).
More details about this property can be found in the [Internationalization](tutorial-i18n.md) section.
#### [[yii\base\Application::modules|modules]] <a name="modules"></a>
This property specifies the [modules](structure-modules.md) that the application contains.
The property takes an array of module classes or [configurations](concept-configurations.md) with the array keys
being the module IDs. For example,
```php
[
'modules' => [
// a "booking" module specified with the module class
'booking' => 'app\modules\booking\BookingModule',
// a "comment" module specified with a configuration array
'comment' => [
'class' => 'app\modules\comment\CommentModule',
'db' => 'db',
],
],
]
```
Please refer to the [Modules](structure-modules.md) section for more details.
#### [[yii\base\Application::name|name]] <a name="name"></a>
This property specifies the application name that may be displayed to end users. Unlike the
[[yii\base\Application::id|id]] property which should take a unique value, the value of this property is mainly for
display purpose and does not need to be unique.
You do not always need to configure this property if none of your code is using it.
#### [[yii\base\Application::params|params]] <a name="params"></a>
This property specifies an array of globally accessible application parameters. Instead of using hardcoded
numbers and strings everywhere in your code, it is a good practice to define them as application parameters
in a single place and use the parameters in places where needed. For example, you may define the thumbnail
image size as a parameter like the following:
```php
[
'params' => [
'thumbnail.size' => [128, 128],
],
]
```
Then in your code where you need to use the size value, you can simply use the code like the following:
```php
$size = \Yii::$app->params['thumbnail.size'];
$width = \Yii::$app->params['thumbnail.size'][0];
```
Later if you decide to change the thumbnail size, you only need to modify it in the application configuration
without touching any dependent code.
#### [[yii\base\Application::sourceLanguage|sourceLanguage]] <a name="sourceLanguage"></a>
This property specifies the language that the application code is written in. The default value is `'en-US'`,
meaning English (United States). You should configure this property if the text content in your code is not in English.
Like the [language](#language) property, you should configure this property in terms of
an [IETF language tag](http://en.wikipedia.org/wiki/IETF_language_tag). For example, `en` stands for English,
while `en-US` stands for English (United States).
More details about this property can be found in the [Internationalization](tutorial-i18n.md) section.
#### [[yii\base\Application::timeZone|timeZone]] <a name="timeZone"></a>
This property is provided as an alternative way of setting the default time zone of PHP runtime.
By configuring this property, you are essentially calling the PHP function
[date_default_timezone_set()](http://php.net/manual/en/function.date-default-timezone-set.php). For example,
```php
[
'timeZone' => 'America/Los_Angeles',
]
```
#### [[yii\base\Application::version|version]] <a name="version"></a>
This property specifies the version of the application. It defaults to `'1.0'`. You do not always need to configure
this property if none of your code is using it.
### Useful Properties <a name="useful-properties"></a>
The properties described in this subsection are not commonly configured because their default values
stipulate common conventions. However, you may still configure them in case you want to break the conventions.
#### [[yii\base\Application::charset|charset]] <a name="charset"></a>
This property specifies the charset that the application uses. The default value is `'UTF-8'` which should
be kept as is for most applications unless you are working with some legacy systems that use a lot of non-unicode data.
#### [[yii\base\Application::defaultRoute|defaultRoute]] <a name="defaultRoute"></a>
This property specifies the [route](runtime-routing.md) that an application should use when a request
does not specify one. The route may consist of child module ID, controller ID, and/or action ID.
For example, `help`, `post/create`, `admin/post/create`. If action ID is not given, it will take the default
value as specified in [[yii\base\Controller::defaultAction]].
For [[yii\web\Application|Web applications]], the default value of this property is `'site'`, which means
the `SiteController` controller and its default action should be used. As a result, if you access
the application without specifying a route, it will show the result of `app\controllers\SiteController::actionIndex()`.
For [[yii\console\Application|console applications]], the default value is `'help'`, which means the core command
[[yii\console\controllers\HelpController::actionIndex()]] should be used. As a result, if you run the command `yii`
without providing any arguments, it will display the help information.
#### [[yii\base\Application::extensions|extensions]] <a name="extensions"></a>
This property specifies the list of [extensions](structure-extensions.md) that are installed and used by the application.
By default, it will take the array returned by the file `@vendor/yiisoft/extensions.php`. The `extensions.php` file
is generated and maintained automatically when you use [Composer](http://getcomposer.org) to install extensions.
So in most cases, you do not need to configure this property.
In the special case when you want to maintain extensions manually, you may configure this property like the following:
```php
[
'extensions' => [
[
'name' => 'extension name',
'version' => 'version number',
'bootstrap' => 'BootstrapClassName', // optional, may also be a configuration array
'alias' => [ // optional
'@alias1' => 'to/path1',
'@alias2' => 'to/path2',
],
],
// ... more extensions like the above ...
],
]
```
As you can see, the property takes an array of extension specifications. Each extension is specified with an array
consisting of `name` and `version` elements. If an extension needs to run during the [bootstrap](runtime-bootstrapping.md)
process, a `bootstrap` element may be specified with a bootstrapping class name or a [configuration](concept-configurations.md)
array. An extension may also define a few [aliases](concept-aliases.md).
#### [[yii\base\Application::layout|layout]] <a name="layout"></a>
This property specifies the name of the default layout that should be used when rendering a [view](structure-views.md).
The default value is `'main'`, meaning the layout file `main.php` under the [layout path](#layoutPath) should be used.
If both of the [layout path](#layoutPath) and the [view path](#viewPath) are taking the default values,
the default layout file can be represented as the path alias `@app/views/layouts/main.php`.
You may configure this property to be `false` if you want to disable layout by default, although this is very rare.
#### [[yii\base\Application::layoutPath|layoutPath]] <a name="layoutPath"></a>
This property specifies the path where layout files should be looked for. The default value is
the `layouts` sub-directory under the [view path](#viewPath). If the [view path](#viewPath) is taking
its default value, the default layout path can be represented as the path alias `@app/views/layouts`.
You may configure it as a directory or a path [alias](concept-aliases.md).
#### [[yii\base\Application::runtimePath|runtimePath]] <a name="runtimePath"></a>
This property specifies the path where temporary files, such as log files, cache files, can be generated.
The default value is the directory represented by the alias `@app/runtime`.
You may configure it as a directory or a path [alias](concept-aliases.md). Note that the runtime path must
be writable by the process running the application. And the path should be protected from being accessed
by end users because the temporary files under it may contain sensitive information.
To simplify accessing to this path, Yii has predefined a path alias named `@runtime` for it.
#### [[yii\base\Application::viewPath|viewPath]] <a name="viewPath"></a>
This property specifies the root directory where view files are located. The default value is the directory
represented by the alias `@app/views`. You may configure it as a directory or a path [alias](concept-aliases.md).
#### [[yii\base\Application::vendorPath|vendorPath]] <a name="vendorPath"></a>
This property specifies the vendor directory managed by [Composer](http://getcomposer.org). It contains
all third party libraries used by your application, including the Yii framework. The default value is
the directory represented by the alias `@app/vendor`.
You may configure this property as a directory or a path [alias](concept-aliases.md). When you modify
this property, make sure you also adjust the Composer configuration accordingly.
To simplify accessing to this path, Yii has predefined a path alias named `@vendor` for it.
#### [[yii\console\Application::enableCoreCommands|enableCoreCommands]] <a name="enableCoreCommands"></a>
This property is supported by [[yii\console\Application|console applications]] only. It specifies
whether the core commands included in the Yii release should be enabled. The default value is `true`.
## Application Events <a name="application-events"></a>
An application triggers several events during the lifecycle of handling an request. You may attach event
handlers to these events in application configurations like the following,
```php
[
'on beforeRequest' => function ($event) {
// ...
},
]
```
The use of the `on eventName` syntax is described in the [Configurations](concept-configurations.md#configuration-format)
section.
Alternatively, you may attach event handlers during the [bootstrapping process](runtime-bootstrapping.md) process
after the application instance is created. For example,
```php
\Yii::$app->on(\yii\base\Application::EVENT_BEFORE_REQUEST, function ($event) {
// ...
});
```
### [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <a name="beforeRequest"></a>
This event is triggered *before* an application handles a request. The actual event name is `beforeRequest`.
When this event is triggered, the application instance has been configured and initialized. So it is a good place
to insert your custom code via the event mechanism to intercept the request handling process. For example,
in the event handler, you may dynamically set the [[yii\base\Application::language]] property based on some parameters.
### [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_AFTER_REQUEST]] <a name="afterRequest"></a>
This event is triggered *after* an application finishes handling a request but *before* sending the response.
The actual event name is `afterRequest`.
When this event is triggered, the request handling is completed and you may take this chance to do some postprocessing
of the request or customize the response.
Note that the [[yii\web\Response|response]] component also triggers some events while it is sending out
response content to end users. Those events are triggered *after* this event.
### [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_ACTION]] <a name="beforeAction"></a>
This event is triggered *before* running every [controller action](structure-controllers.md).
The actual event name is `beforeAction`.
The event parameter is an instance of [[yii\base\ActionEvent]]. An event handler may set
the [[yii\base\ActionEvent::isValid]] property to be `false` to stop running the action.
For example,
```php
[
'on beforeAction' => function ($event) {
if (some condition) {
$event->isValid = false;
} else {
}
},
]
```
Note that the same `beforeAction` event is also triggered by [modules](structure-modules.md)
and [controllers](structure-controllers.md). Application objects are the first ones
triggering this event, followed by modules (if any), and finally controllers. If an event handler
sets [[yii\base\ActionEvent::isValid]] to be `false`, all the following events will NOT be triggered.
### [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_AFTER_ACTION]] <a name="afterAction"></a>
This event is triggered *after* running every [controller action](structure-controllers.md).
The actual event name is `afterAction`.
The event parameter is an instance of [[yii\base\ActionEvent]]. Through
the [[yii\base\ActionEvent::result]] property, an event handler may access or modify the action result.
For example,
```php
[
'on afterAction' => function ($event) {
if (some condition) {
// modify $event->result
} else {
}
},
]
```
Note that the same `afterAction` event is also triggered by [modules](structure-modules.md)
and [controllers](structure-controllers.md). These objects trigger this event in the reverse order
as for that of `beforeAction`. That is, controllers are the first objects triggering this event,
followed by modules (if any), and finally applications.
## Application Lifecycle <a name="application-lifecycle"></a>
When an [entry script](structure-entry-scripts.md) is being executed to handle a request,
an application will undergo the following lifecycle:
1. The entry script loads the application configuration as an array.
2. The entry script creates a new instance of the application:
* [[yii\base\Application::preInit()|preInit()]] is called, which configures some high priority
application properties, such as [[yii\base\Application::basePath|basePath]].
* Register the [[yii\base\Application::errorHandler|error handler]].
* Configure application properties.
* [[yii\base\Application::init()|init()]] is called which further calls
[[yii\base\Application::bootstrap()|bootstrap()]] to run bootstrapping components.
3. The entry script calls [[yii\base\Application::run()]] to run the application:
* Trigger the [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] event.
* Handle the request: resolve the request into a [route](runtime-routing.md) and the associated parameters;
create the module, controller and action objects as specified by the route; and run the action.
* Trigger the [[yii\base\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] event.
* Send response to the end user.
4. The entry script receives the exit status from the application and completes the request processing.
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).
Models
======
Models are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture.
They are objects representing business data, rules and logic.
You can create model classes by extending [[yii\base\Model]] or its child classes. The base class
[[yii\base\Model]] supports many useful features:
* [Attributes](#attributes): represent the business data and can be accessed like normal object properties
or array elements;
* [Attribute labels](#attribute-labels): specify the display labels for attributes;
* [Massive assignment](#massive-assignment): supports populating multiple attributes in a single step;
* [Validation rules](#validation-rules): ensures input data based on the declared validation rules;
* [Data Exporting](#data-exporting): allows model data to be exported in terms of arrays with customizable formats.
The `Model` class is also the base class for more advanced models, such as [Active Record](db-active-record.md).
Please refer to the relevant documentation for more details about these advanced models.
> Info: You are not required to base your model classes on [[yii\base\Model]]. However, because there are many Yii
components built to support [[yii\base\Model]], it is usually the preferable base class for a model.
## Attributes <a name="attributes"></a>
Models represent business data in terms of *attributes*. Each attribute is like a publicly accessible property
of a model. The method [[yii\base\Model::attributes()]] specifies what attributes a model class has.
You can access an attribute like accessing a normal object property:
```php
$model = new \app\models\ContactForm;
// "name" is an attribute of ContactForm
$model->name = 'example';
echo $model->name;
```
You can also access attributes like accessing array elements, thanks to the support for
[ArrayAccess](http://php.net/manual/en/class.arrayaccess.php) and [ArrayIterator](http://php.net/manual/en/class.arrayiterator.php)
by [[yii\base\Model]]:
```php
$model = new \app\models\ContactForm;
// accessing attributes like array elements
$model['name'] = 'example';
echo $model['name'];
// iterate attributes
foreach ($model as $name => $value) {
echo "$name: $value\n";
}
```
### Defining Attributes <a name="defining-attributes"></a>
By default, if your model class extends directly from [[yii\base\Model]], all its *non-static public* member
variables are attributes. For example, the `ContactForm` model class below has four attributes: `name`, `email`,
`subject` and `body`. The `ContactForm` model is used to represent the input data received from an HTML form.
```php
namespace app\models;
use yii\base\Model;
class ContactForm extends Model
{
public $name;
public $email;
public $subject;
public $body;
}
```
You may override [[yii\base\Model::attributes()]] to define attributes in a different way. The method should
return the names of the attributes in a model. For example, [[yii\db\ActiveRecord]] does so by returning
the column names of the associated database table as its attribute names. Note that you may also need to
override the magic methods such as `__get()`, `__set()` so that the attributes can be accessed like
normal object properties.
### Attribute Labels <a name="attribute-labels"></a>
When displaying values or getting input for attributes, you often need to display some labels associated
with attributes. For example, given an attribute named `firstName`, you may want to display a label `First Name`
which is more user-friendly when displayed to end users in places such as form inputs and error messages.
You can get the label of an attribute by calling [[yii\base\Model::getAttributeLabel()]]. For example,
```php
$model = new \app\models\ContactForm;
// displays "Name"
echo $model->getAttributeLabel('name');
```
By default, attribute labels are automatically generated from attribute names. The generation is done by
the method [[yii\base\Model::generateAttributeLabel()]]. It will turn camel-case variable names into
multiple words with the first letter in each word in upper case. For example, `username` becomes `Username`,
and `firstName` becomes `First Name`.
If you do not want to use automatically generated labels, you may override [[yii\base\Model::attributeLabels()]]
to explicitly declare attribute labels. For example,
```php
namespace app\models;
use yii\base\Model;
class ContactForm extends Model
{
public $name;
public $email;
public $subject;
public $body;
public function attributeLabels()
{
return [
'name' => 'Your name',
'email' => 'Your email address',
'subject' => 'Subject',
'body' => 'Content',
];
}
}
```
For applications supporting multiple languages, you may want to translate attribute labels. This can be done
in the [[yii\base\Model::attributeLabels()|attributeLabels()]] method as well, like the following:
```php
public function attributeLabels()
{
return [
'name' => \Yii::t('app', 'Your name'),
'email' => \Yii::t('app', 'Your email address'),
'subject' => \Yii::t('app', 'Subject'),
'body' => \Yii::t('app', 'Content'),
];
}
```
You may even conditionally define attribute labels. For example, based on the [scenario](#scenarios) the model
is being used in, you may return different labels for the same attribute.
> Info: Strictly speaking, attribute labels are part of [views](structure-views.md). But declaring labels
in models is often very convenient and can result in very clean and reusable code.
## Scenarios <a name="scenarios"></a>
A model may be used in different *scenarios*. For example, a `User` model may be used to collect user login inputs,
but it may also be used for the user registration purpose. In different scenarios, a model may use different
business rules and logic. For example, the `email` attribute may be required during user registration,
but not so during user login.
A model uses the [[yii\base\Model::scenario]] property to keep track of the scenario it is being used in.
By default, a model supports only a single scenario named `default`. The following code shows two ways of
setting the scenario of a model:
```php
// scenario is set as a property
$model = new User;
$model->scenario = 'login';
// scenario is set through configuration
$model = new User(['scenario' => 'login']);
```
By default, the scenarios supported by a model are determined by the [validation rules](#validation-rules) declared
in the model. However, you can customize this behavior by overriding the [[yii\base\Model::scenarios()]] method,
like the following:
```php
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
public function scenarios()
{
return [
'login' => ['username', 'password'],
'register' => ['username', 'email', 'password'],
];
}
}
```
> Info: In the above and following examples, the model classes are extending from [[yii\db\ActiveRecord]]
because the usage of multiple scenarios usually happens to [Active Record](db-active-record.md) classes.
The `scenarios()` method returns an array whose keys are the scenario names and values the corresponding
*active attributes*. An active attribute can be [massively assigned](#massive-assignment) and is subject
to [validation](#validation-rules). In the above example, the `username` and `password` attributes are active
in the `login` scenario; while in the `register` scenario, `email` is also active besides `username` and `password`.
The default implementation of `scenarios()` will return all scenarios found in the validation rule declaration
method [[yii\base\Model::rules()]]. When overriding `scenarios()`, if you want to introduce new scenarios
in addition to the default ones, you may write code like the following:
```php
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios['login'] = ['username', 'password'];
$scenarios['register'] = ['username', 'email', 'password'];
return $scenarios;
}
}
```
The scenario feature is primarily used by [validation](#validation-rules) and [massive attribute assignment](#massive-assignment).
You can, however, use it for other purposes. For example, you may declare [attribute labels](#attribute-labels)
differently based on the current scenario.
## Validation Rules <a name="validation-rules"></a>
When the data for a model is received from end users, it should be validated to make sure it satisfies
certain rules (called *validation rules*, also known as *business rules*). For example, given a `ContactForm` model,
you may want to make sure all attributes are not empty and the `email` attribute contains a valid email address.
If the values for some attributes do not satisfy the corresponding business rules, appropriate error messages
should be displayed to help the user to fix the errors.
You may call [[yii\base\Model::validate()]] to validate the received data. The method will use
the validation rules declared in [[yii\base\Model::rules()]] to validate every relevant attribute. If no error
is found, it will return true. Otherwise, it will keep the errors in the [[yii\base\Model::errors]] property
and return false. For example,
```php
$model = new \app\models\ContactForm;
// populate model attributes with user inputs
$model->attributes = \Yii::$app->request->post('ContactForm');
if ($model->validate()) {
// all inputs are valid
} else {
// validation failed: $errors is an array containing error messages
$errors = $model->errors;
}
```
To declare validation rules associated with a model, override the [[yii\base\Model::rules()]] method by returning
the rules that the model attributes should satisfy. The following example shows the validation rules declared
for the `ContactForm` model:
```php
public function rules()
{
return [
// the name, email, subject and body attributes are required
[['name', 'email', 'subject', 'body'], 'required'],
// the email attribute should be a valid email address
['email', 'email'],
];
}
```
A rule can be used to validate one or multiple attributes, and an attribute may be validated by one or multiple rules.
Please refer to the [Validating Input](input-validation.md) section for more details on how to declare
validation rules.
Sometimes, you may want a rule to be applied only in certain [scenarios](#scenarios). To do so, you can
specify the `on` property of a rule, like the following:
```php
public function rules()
{
return [
// username, email and password are all required in "register" scenario
[['username', 'email', 'password'], 'required', 'on' => 'register'],
// username and password are required in "login" scenario
[['username', 'password'], 'required', 'on' => 'login'],
];
}
```
If you do not specify the `on` property, the rule would be applied in all scenarios. A rule is called
an *active rule* if it can be applied in the current [[yii\base\Model::scenario|scenario]].
An attribute will be validated if and only if it is an active attribute declared in `scenarios()` and
is associated with one or multiple active rules declared in `rules()`.
## Massive Assignment <a name="massive-assignment"></a>
Massive assignment is a convenient way of populating a model with user inputs using a single line of code.
It populates the attributes of a model by assigning the input data directly to the [[yii\base\Model::$attributes]]
property. The following two pieces of code are equivalent, both trying to assign the form data submitted by end users
to the attributes of the `ContactForm` model. Clearly, the former, which uses massive assignment, is much cleaner
and less error prone than the latter:
```php
$model = new \app\models\ContactForm;
$model->attributes = \Yii::$app->request->post('ContactForm');
```
```php
$model = new \app\models\ContactForm;
$data = \Yii::$app->request->post('ContactForm', []);
$model->name = isset($data['name']) ? $data['name'] : null;
$model->email = isset($data['email']) ? $data['email'] : null;
$model->subject = isset($data['subject']) ? $data['subject'] : null;
$model->body = isset($data['body']) ? $data['body'] : null;
```
### Safe Attributes <a name="safe-attributes"></a>
Massive assignment only applies to the so-called *safe attributes* which are the attributes listed in
[[yii\base\Model::scenarios()]] for the current [[yii\base\Model::scenario|scenario]] of a model.
For example, if the `User` model has the following scenario declaration, then when the current scenario
is `login`, only the `username` and `password` can be massively assigned. Any other attributes will
be kept untouched.
```php
public function scenarios()
{
return [
'login' => ['username', 'password'],
'register' => ['username', 'email', 'password'],
];
}
```
> Info: The reason that massive assignment only applies to safe attributes is because you want to
control which attributes can be modified by end user data. For example, if the `User` model
has a `permission` attribute which determines the permission assigned to the user, you would
like this attribute to be modifiable by administrators through a backend interface only.
Because the default implementation of [[yii\base\Model::scenarios()]] will return all scenarios and attributes
found in [[yii\base\Model::rules()]], if you do not override this method, it means an attribute is safe as long
as it appears in one of the active validation rules.
For this reason, a special validator aliased `safe` is provided so that you can declare an attribute
to be safe without actually validating it. For example, the following rules declare that both `title`
and `description` are safe attributes.
```php
public function rules()
{
return [
[['title', 'description'], 'safe'],
];
}
```
### Unsafe Attributes <a name="unsafe-attributes"></a>
As described above, the [[yii\base\Model::scenarios()]] method serves for two purposes: determining which attributes
should be validated, and determining which attributes are safe. In some rare cases, you may want to validate
an attribute but do not want to mark it safe. You can do so by prefixing an exclamation mark `!` to the attribute
name when declaring it in `scenarios()`, like the `secret` attribute in the following:
```php
public function scenarios()
{
return [
'login' => ['username', 'password', '!secret'],
];
}
```
When the model is in the `login` scenario, all three attributes will be validated. However, only the `username`
and `password` attributes can be massively assigned. To assign an input value to the `secret` attribute, you
have to do it explicitly as follows,
```php
$model->secret = $secret;
```
## Data Exporting <a name="data-exporting"></a>
Models often need to be exported in different formats. For example, you may want to convert a collection of
models into JSON or Excel format. The exporting process can be broken down into two independent steps.
In the first step, models are converted into arrays; in the second step, the arrays are converted into
target formats. You may just focus on the first step, because the second step can be achieved by generic
data formatters, such as [[yii\web\JsonResponseFormatter]].
The simplest way of converting a model into an array is to use the [[yii\base\Model::$attributes]] property.
For example,
```php
$post = \app\models\Post::findOne(100);
$array = $post->attributes;
```
By default, the [[yii\base\Model::$attributes]] property will return the values of *all* attributes
declared in [[yii\base\Model::attributes()]].
A more flexible and powerful way of converting a model into an array is to use the [[yii\base\Model::toArray()]]
method. Its default behavior is the same as that of [[yii\base\Model::$attributes]]. However, it allows you
to choose which data items, called *fields*, to be put in the resulting array and how they should be formatted.
In fact, it is the default way of exporting models in RESTful Web service development, as described in
the [Response Formatting](rest-response-formatting.md).
### Fields <a name="fields"></a>
A field is simply a named element in the array that is obtained by calling the [[yii\base\Model::toArray()]] method
of a model.
By default, field names are equivalent to attribute names. However, you can change this behavior by overriding
the [[yii\base\Model::fields()|fields()]] and/or [[yii\base\Model::extraFields()|extraFields()]] methods. Both methods
should return a list of field definitions. The fields defined by `fields()` are default fields, meaning that
`toArray()` will return these fields by default. The `extraFields()` method defines additionally available fields
which can also be returned by `toArray()` as long as you specify them via the `$expand` parameter. For example,
the following code will return all fields defined in `fields()` and the `prettyName` and `fullAddress` fields
if they are defined in `extraFields()`.
```php
$array = $model->toArray([], ['prettyName', 'fullAddress']);
```
You can override `fields()` to add, remove, rename or redefine fields. The return value of `fields()`
should be an array. The array keys are the field names, and the array values are the corresponding
field definitions which can be either property/attribute names or anonymous functions returning the
corresponding field values. In the special case when a field name is the same as its defining attribute
name, you can omit the array key. For example,
```php
// explicitly list every field, best used when you want to make sure the changes
// in your DB table or model attributes do not cause your field changes (to keep API backward compatibility).
public function fields()
{
return [
// field name is the same as the attribute name
'id',
// field name is "email", the corresponding attribute name is "email_address"
'email' => 'email_address',
// field name is "name", its value is defined by a PHP callback
'name' => function () {
return $this->first_name . ' ' . $this->last_name;
},
];
}
// filter out some fields, best used when you want to inherit the parent implementation
// and blacklist some sensitive fields.
public function fields()
{
$fields = parent::fields();
// remove fields that contain sensitive information
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);
return $fields;
}
```
> Warning: Because by default all attributes of a model will be included in the exported array, you should
> examine your data to make sure they do not contain sensitive information. If there is such information,
> you should override `fields()` to filter them out. In the above example, we choose
> to filter out `auth_key`, `password_hash` and `password_reset_token`.
## Best Practices <a name="best-practices"></a>
Models are the central places to represent business data, rules and logic. They often need to be reused
in different places. In a well-designed application, models are usually much fatter than
[controllers](structure-controllers.md).
In summary, models
* may contain attributes to represent business data;
* may contain validation rules to ensure the data validity and integrity;
* may contain methods implementing business logic;
* should NOT directly access request, session, or any other environmental data. These data should be injected
by [controllers](structure-controllers.md) into models;
* should avoid embedding HTML or other presentational code - this is better done in [views](structure-views.md);
* avoid having too many [scenarios](#scenarios) in a single model.
You may usually consider the last recommendation above when you are developing large complex systems.
In these systems, models could be very fat because they are used in many places and may thus contain many sets
of rules and business logic. This often ends up in a nightmare in maintaining the model code
because a single touch of the code could affect several different places. To make the mode code more maintainable,
you may take the following strategy:
* Define a set of base model classes that are shared by different [applications](structure-applications.md) or
[modules](structure-modules.md). These model classes should contain minimal sets of rules and logic that
are common among all their usages.
* In each [application](structure-applications.md) or [module](structure-modules.md) that uses a model,
define a concrete model class by extending from the corresponding base model class. The concrete model classes
should contain rules and logic that are specific for that application or module.
For example, in the [Advanced Application Template](tutorial-advanced-app.md), you may define a base model
class `common\models\Post`. Then for the front end application, you define and use a concrete model class
`frontend\models\Post` which extends from `common\models\Post`. And similarly for the back end application,
you define `backend\models\Post`. With this strategy, you will be sure that the code in `frontend\models\Post`
is only specific to the front end application, and if you make any change to it, you do not need to worry if
the change may break the back end application.
Views
=====
Views are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture.
They are code responsible for presenting data to end users. In a Web application, views are usually created
in terms of *view templates* which are PHP script files containing mainly HTML code and presentational PHP code.
They are managed by the [[yii\web\View|view]] application component which provides commonly used methods
to facilitate view composition and rendering. For simplicity, we often call view templates or view template files
as views.
## Creating Views <a name="creating-views"></a>
As aforementioned, a view is simply a PHP script mixed with HTML and PHP code. The following is the view
that presents a login form. As you can see, PHP code is used to generate the dynamic content, such as the
page title and the form, while HTML code organizes them into a presentable HTML page.
```php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $model app\models\LoginForm */
$this->title = 'Login';
?>
<h1><?= Html::encode($this->title) ?></h1>
<p>Please fill out the following fields to login:</p>
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'username') ?>
<?= $form->field($model, 'password')->passwordInput() ?>
<?= Html::submitButton('Login') ?>
<?php ActiveForm::end(); ?>
```
Within a view, you can access `$this` which refers to the [[yii\web\View|view component]] managing
and rendering this view template.
Besides `$this`, there may be other predefined variables in a view, such as `$model` in the above
example. These variables represent the data that are *pushed* into the view by [controllers](structure-controllers.md)
or other objects whose trigger the [view rendering](#rendering-views).
> Tip: The predefined variables are listed in a comment block at beginning of a view so that they can
be recognized by IDEs. It is also a good way of documenting your views.
### Security <a name="security"></a>
When creating views that generate HTML pages, it is important that you encode and/or filter the data coming
from end users before presenting them. Otherwise, your application may be subject to
[cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting) attacks.
To display a plain text, encode it first by calling [[yii\helpers\Html::encode()]]. For example, the following code
encodes the user name before displaying it:
```php
<?php
use yii\helpers\Html;
?>
<div class="username">
<?= Html::encode($user->name) ?>
</div>
```
To display HTML content, use [[yii\helpers\HtmlPurifier]] to filter the content first. For example, the following
code filters the post content before displaying it:
```php
<?php
use yii\helpers\HtmlPurifier;
?>
<div class="post">
<?= HtmlPurifier::process($post->text) ?>
</div>
```
> Tip: While HTMLPurifier does excellent job in making output safe, it is not fast. You should consider
[caching](caching-overview.md) the filtering result if your application requires high performance.
### Organizing Views <a name="organizing-views"></a>
Like [controllers](structure-controllers.md) and [models](structure-models.md), there are conventions to organize views.
* For views rendered by a controller, they should be put under the directory `@app/views/ControllerID` by default,
where `ControllerID` refers to the [controller ID](structure-controllers.md#routes). For example, if
the controller class is `PostController`, the directory would be `@app/views/post`; If it is `PostCommentController`,
the directory would be `@app/views/post-comment`. In case the controller belongs to a module, the directory
would be `views/ControllerID` under the [[yii\base\Module::basePath|module directory]].
* For views rendered in a [widget](structure-widgets.md), they should be put under the `WidgetPath/views` directory by
default, where `WidgetPath` stands for the directory containing the widget class file.
* For views rendered by other objects, it is recommended that you follow the similar convention as that for widgets.
You may customize these default view directories by overriding the [[yii\base\ViewContextInterface::getViewPath()]]
method of controllers or widgets.
## Rendering Views <a name="rendering-views"></a>
You can render views in [controllers](structure-controllers.md), [widgets](structure-widgets.md), or any
other places by calling view rendering methods. These methods share a similar signature shown as follows,
```
/**
* @param string $view view name or file path, depending on the actual rendering method
* @param array $params the data to be passed to the view
* @return string rendering result
*/
methodName($view, $params = [])
```
### Rendering in Controllers <a name="rendering-in-controllers"></a>
Within [controllers](structure-controllers.md), you may call the following controller methods to render views:
* [[yii\base\Controller::render()|render()]]: renders a [named view](#named-views) and applies a [layout](#layouts)
to the rendering result.
* [[yii\base\Controller::renderPartial()|renderPartial()]]: renders a [named view](#named-views) without any layout.
* [[yii\web\Controller::renderAjax()|renderAjax()]]: renders a [named view](#named-views) without any layout,
and injects all registered JS/CSS scripts and files. It is usually used in response to AJAX Web requests.
* [[yii\base\Controller::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or
[alias](concept-aliases.md).
For example,
```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;
}
// renders a view named "view" and applies a layout to it
return $this->render('view', [
'model' => $model,
]);
}
}
```
### Rendering in Widgets <a name="rendering-in-widgets"></a>
Within [widgets](structure-widgets.md), you may call the following widget methods to render views.
* [[yii\base\Widget::render()|render()]]: renders a [named view](#named-views).
* [[yii\base\Widget::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or
[alias](concept-aliases.md).
For example,
```php
namespace app\components;
use yii\base\Widget;
use yii\helpers\Html;
class ListWidget extends Widget
{
public $items = [];
public function run()
{
// renders a view named "list"
return $this->render('list', [
'items' => $this->items,
]);
}
}
```
### Rendering in Views <a name="rendering-in-views"></a>
You can render a view within another view by calling one of the following methods provided by the [[yii\base\View|view component]]:
* [[yii\base\View::render()|render()]]: renders a [named view](#named-views).
* [[yii\web\View::renderAjax()|renderAjax()]]: renders a [named view](#named-views) and injects all registered
JS/CSS scripts and files. It is usually used in response to AJAX Web requests.
* [[yii\base\View::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or
[alias](concept-aliases.md).
For example, the following code in a view renders the `_overview.php` view file which is in the same directory
as the view being currently rendered. Remember that `$this` in a view refers to the [[yii\base\View|view]] component:
```php
<?= $this->render('_overview') ?>
```
### Rendering in Other Places <a name="rendering-in-other-places"></a>
In any place, you can get access to the [[yii\base\View|view]] application component by the expression
`Yii::$app->view` and then call its aforementioned methods to render a view. For example,
```php
// displays the view file "@app/views/site/license.php"
echo \Yii::$app->view->renderFile('@app/views/site/license.php');
```
### Named Views <a name="named-views"></a>
When you render a view, you can specify the view using either a view name or a view file path/alias. In most cases,
you would use the former because it is more concise and flexible. We call views specified using names as *named views*.
A view name is resolved into the corresponding view file path according to the following rules:
* A view name may omit the file extension name. In this case, `.php` will be used as the extension. For example,
the view name `about` corresponds to the file name `about.php`.
* If the view name starts with double slashes `//`, the corresponding view file path would be `@app/views/ViewName`.
That is, the view is looked for under the [[yii\base\Application::viewPath|application's view path]].
For example, `//site/about` will be resolved into `@app/views/site/about.php`.
* If the view name starts with a single slash `/`, the view file path is formed by prefixing the view name
with the [[yii\base\Module::viewPath|view path]] of the currently active [module](structure-modules.md).
If there is no active module, `@app/views/ViewName` will be used. For example, `/user/create` will be resolved into
`@app/modules/user/views/user/create.php`, if the currently active module is `user`. If there is no active module,
the view file path would be `@app/views/user/create.php`.
* If the view is rendered with a [[yii\base\View::context|context]] and the context implements [[yii\base\ViewContextInterface]],
the view file path is formed by prefixing the [[yii\base\ViewContextInterface::getViewPath()|view path]] of the
context to the view name. This mainly applies to the views rendered within controllers and widgets. For example,
`site/about` will be resolved into `@app/views/site/about.php` if the context is the controller `SiteController`.
* If a view is rendered within another view, the directory containing the other view file will be prefixed to
the new view name to form the actual view file path. For example, `item` will be resolved into `@app/views/post/item`
if it is being rendered in the view `@app/views/post/index.php`.
According to the above rules, calling `$this->render('view')` in a controller `app\controllers\PostController` will
actually render the view file `@app/views/post/view.php`, while calling `$this->render('_overview')` in that view
will render the view file `@app/views/post/_overview.php`.
### Accessing Data in Views <a name="accessing-data-in-views"></a>
There are two approaches to access data within a view: push and pull.
By passing the data as the second parameter to the view rendering methods, you are using the push approach.
The data should be represented as an array of name-value pairs. When the view is being rendered, the PHP
`extract()` function will be called on this array so that the array is extracted into variables in the view.
For example, the following view rendering code in a controller will push two variables to the `report` view:
`$foo = 1` and `$bar = 2`.
```php
echo $this->render('report', [
'foo' => 1,
'bar' => 2,
]);
```
The pull approach actively retrieves data from the [[yii\base\View|view component]] or other objects accessible
in views (e.g. `Yii::$app`). Using the code below as an example, within the view you can get the controller object
by the expression `$this->context`. And as a result, it is possible for you to access any properties or methods
of the controller in the `report` view, such as the controller ID shown in the following:
```php
The controller ID is: <?= $this->context->id ?>
?>
```
The push approach is usually the preferred way of accessing data in views, because it makes views less dependent
on context objects. Its drawback is that you need to manually build the data array all the time, which could
become tedious and error prone if a view is shared and rendered in different places.
### Sharing Data among Views <a name="sharing-data-among-views"></a>
The [[yii\base\View|view component]] provides the [[yii\base\View::params|params]] property that you can use
to share data among views.
For example, in an `about` view, you can have the following code which specifies the current segment of the
breadcrumbs.
```php
$this->params['breadcrumbs'][] = 'About Us';
```
Then, in the [layout](#layouts) file, which is also a view, you can display the breadcrumbs using the data
passed along [[yii\base\View::params|params]]:
```php
<?= yii\widgets\Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]) ?>
```
## Layouts <a name="layouts"></a>
Layouts are a special type of views that represent the common parts of multiple views. For example, the pages
for most Web applications share the same page header and footer. While you can repeat the same page header and footer
in every view, a better way is to do this once in a layout and embed the rendering result of a content view at
an appropriate place in the layout.
### Creating Layouts <a name="creating-layouts"></a>
Because layouts are also views, they can be created in the similar way as normal views. By default, layouts
are stored in the directory `@app/views/layouts`. For layouts used within a [module](structure-modules.md),
they should be stored in the `views/layouts` directory under the [[yii\base\Module::basePath|module directory]].
You may customize the default layout directory by configuring the [[yii\base\Module::layoutPath]] property of
the application or modules.
The following example shows how a layout looks like. Note that for illustrative purpose, we have greatly simplified
the code in the layout. In practice, you may want to add more content to it, such as head tags, main menu, etc.
```php
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $content string */
?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<?= Html::csrfMetaTags() ?>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody() ?>
<header>My Company</header>
<?= $content ?>
<footer>&copy; 2014 by My Company</footer>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>
```
As you can see, the layout generates the HTML tags that are common to all pages. Within the `<body>` section,
the layout echoes the `$content` variable which represents the rendering result of content views and is pushed
into the layout when [[yii\base\Controller::render()]] is called.
Most layouts should call the following methods like shown in the above code. These methods mainly trigger events
about the rendering process so that scripts and tags registered in other places can be properly injected into
the places where these methods are called.
- [[yii\base\View::beginPage()|beginPage()]]: This method should be called at the very beginning of the layout.
It triggers the [[yii\base\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]] event which indicates the beginning of a page.
- [[yii\base\View::endPage()|endPage()]]: This method should be called at the end of the layout.
It triggers the [[yii\base\View::EVENT_END_PAGE|EVENT_END_PAGE]] event which indicates the end of a page.
- [[yii\web\View::head()|head()]]: This method should be called within the `<head>` section of an HTML page.
It generates a placeholder which will be replaced with the registered head HTML code (e.g. link tags, meta tags)
when a page finishes rendering.
- [[yii\web\View::beginBody()|beginBody()]]: This method should be called at the beginning of the `<body>` section.
It triggers the [[yii\web\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] event and generates a placeholder which will
be replaced by the registered HTML code (e.g. JavaScript) targeted at the body begin position.
- [[yii\web\View::endBody()|endBody()]]: This method should be called at the end of the `<body>` section.
It triggers the [[yii\web\View::EVENT_END_BODY|EVENT_END_BODY]] event and generates a placeholder which will
be replaced by the registered HTML code (e.g. JavaScript) targeted at the body end position.
### Accessing Data in Layouts <a name="accessing-data-in-layouts"></a>
Within a layout, you have access to two predefined variables: `$this` and `$content`. The former refers to
the [[yii\base\View|view]] component, like in normal views, while the latter contains the rendering result of a content
view which is rendered by calling the [[yii\base\Controller::render()|render()]] method in controllers.
If you want to access other data in layouts, you have to use the pull method as described in
the [Accessing Data in Views](#accessing-data-in-views) subsection. If you want to pass data from a content view
to a layout, you may use the method described in the [Sharing Data among Views](#sharing-data-among-views) subsection.
### Using Layouts <a name="using-layouts"></a>
As described in the [Rendering in Controllers](#rendering-in-controllers) subsection, when you render a view
by calling the [[yii\base\Controller::render()|render()]] method in a controller, a layout will be applied
to the rendering result. By default, the layout `@app/views/layouts/main.php` will be used.
You may use a different layout by configuring either [[yii\base\Application::layout]] or [[yii\base\Controller::layout]].
The former governs the layout used by all controllers, while the latter overrides the former for individual controllers.
For example, the following code makes the `post` controller to use `@app/views/layouts/post.php` as the layout
when rendering its views. Other controllers, assuming their `layout` property is untouched, will still use the default
`@app/views/layouts/main.php` as the layout.
```php
namespace app\controllers;
use yii\web\Controller;
class PostController extends Controller
{
public $layout = 'post';
// ...
}
```
For controllers belonging to a module, you may also configure the module's [[yii\base\Module::layout|layout]] property to
use a particular layout for these controllers.
Because the `layout` property may be configured at different levels (controllers, modules, application),
behind the scene Yii takes two steps to determine what is the actual layout file being used for a particular controller.
In the first step, it determines the layout value and the context module:
- If the [[yii\base\Controller::layout]] property of the controller is not null, use it as the layout value and
the [[yii\base\Controller::module|module]] of the controller as the context module.
- If [[yii\base\Controller::layout|layout]] is null, search through all ancestor modules (including the application itself) of the controller and
find the first module whose [[yii\base\Module::layout|layout]] property is not null. Use that module and
its [[yii\base\Module::layout|layout]] value as the context module and the chosen layout value.
If such a module cannot be found, it means no layout will be applied.
In the second step, it determines the actual layout file according to the layout value and the context module
determined in the first step. The layout value can be:
- a path alias (e.g. `@app/views/layouts/main`).
- an absolute path (e.g. `/main`): the layout value starts with a slash. The actual layout file will be
looked for under the application's [[yii\base\Application::layoutPath|layout path]] which defaults to
`@app/views/layouts`.
- a relative path (e.g. `main`): the actual layout file will be looked for under the context module's
[[yii\base\Module::layoutPath|layout path]] which defaults to the `views/layouts` directory under the
[[yii\base\Module::basePath|module directory]].
- the boolean value `false`: no layout will be applied.
If the layout value does not contain a file extension, it will use the default one `.php`.
### Nested Layouts <a name="nested-layouts"></a>
Sometimes you may want to nest one layout in another. For example, in different sections of a Web site, you
want to use different layouts, while all these layouts share the same basic layout that generates the overall
HTML5 page structure. You can achieve this goal by calling [[yii\base\View::beginContent()|beginContent()]] and
[[yii\base\View::endContent()|endContent()]] in the child layouts like the following:
```php
<?php $this->beginContent('@app/views/layouts/base.php'); ?>
...child layout content here...
<?php $this->endContent(); ?>
```
As shown above, the child layout content should be enclosed within [[yii\base\View::beginContent()|beginContent()]] and
[[yii\base\View::endContent()|endContent()]]. The parameter passed to [[yii\base\View::beginContent()|beginContent()]]
specifies what is the parent layout. It can be either a layout file or alias.
Using the above approach, you can nest layouts in more than one levels.
### Using Blocks <a name="using-blocks"></a>
Blocks allow you to specify the view content in one place while displaying it in another. They are often used together
with layouts. For example, you can define a block in a content view and display it in the layout.
You call [[yii\base\View::beginBlock()|beginBlock()]] and [[yii\base\View::endBlock()|endBlock()]] to define a block.
The block can then be accessed via `$view->blocks[$blockID]`, where `$blockID` stands for a unique ID that you assign
to the block when defining it.
The following example shows how you can use blocks to customize specific parts of a layout in a content view.
First, in a content view, define one or multiple blocks:
```php
...
<?php $this->beginBlock('block1'); ?>
...content of block1...
<?php $this->endBlock(); ?>
...
<?php $this->beginBlock('block3'); ?>
...content of block3...
<?php $this->endBlock(); ?>
```
Then, in the layout view, render the blocks if they are available, or display some default content if a block is
not defined.
```php
...
<?php if (isset($this->blocks['block1'])): ?>
<?= $this->blocks['block1'] ?>
<?php else: ?>
... default content for block1 ...
<?php endif; ?>
...
<?php if (isset($this->blocks['block2'])): ?>
<?= $this->blocks['block2'] ?>
<?php else: ?>
... default content for block2 ...
<?php endif; ?>
...
<?php if (isset($this->blocks['block3'])): ?>
<?= $this->blocks['block3'] ?>
<?php else: ?>
... default content for block3 ...
<?php endif; ?>
...
```
## Using View Components <a name="using-view-components"></a>
[[yii\base\View|View components]] provides many view-related features. While you can get view components
by creating individual instances of [[yii\base\View]] or its child class, in most cases you will mainly use
the `view` application component. You can configure this component in [application configurations](structure-applications.md#application-configurations)
like the following:
```php
[
// ...
'components' => [
'view' => [
'class' => 'app\components\View',
],
// ...
],
]
```
View components provide the following useful view-related features, each described in more details in a separate section:
* [theming](output-theming.md): allows you to develop and change the theme for your Web site.
* [fragment caching](caching-fragment.md): allows you to cache a fragment within a Web page.
* [client script handling](output-client-scripts.md): supports CSS and JavaScript registration and rendering.
* [asset bundle handling](structure-assets.md): supports registering and rendering of [asset bundles](structure-assets.md).
* [alternative template engines](tutorial-template-engines.md): allows you to use other template engines, such as
[Twig](http://twig.sensiolabs.org/), [Smarty](http://www.smarty.net/).
You may also frequently use the following minor yet useful features when you are developing Web pages.
### Setting Page Titles <a name="setting-page-titles"></a>
Every Web page should have a title. Normally the title tag is being displayed in a [layout](#layouts). However, in practice
the title is often determined in content views rather than layouts. To solve this problem, [[yii\web\View]] provides
the [[yii\web\View::title|title]] property for you to pass the title information from content views to layouts.
To make use of this feature, in each content view, you can set the page title like the following:
```php
<?php
$this->title = 'My page title';
?>
```
Then in the layout, make sure you have the following code in the `<head>` section:
```php
<title><?= Html::encode($this->title) ?></title>
```
### Registering Meta Tags <a name="registering-meta-tags"></a>
Web pages usually need to generate various meta tags needed by different parties. Like page titles, meta tags
appear in the `<head>` section and are usually generated in layouts.
If you want to specify what meta tags to generate in content views, you can call [[yii\web\View::registerMetaTag()]]
in a content view, like the following:
```php
<?php
$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);
?>
```
The above code will register a "keywords" meta tag with the view component. The registered meta tag is
rendered after the layout finishes rendering. By then, the following HTML code will be inserted
at the place where you call [[yii\web\View::head()]] in the layout and generate the following HTML code:
```php
<meta name="keywords" content="yii, framework, php">
```
Note that if you call [[yii\web\View::registerMetaTag()]] multiple times, it will register multiple meta tags,
regardless whether the meta tags are the same or not.
To make sure there is only a single instance of a meta tag type, you can specify a key as a second parameter when calling the method.
For example, the following code registers two "description" meta tags. However, only the second one will be rendered.
```html
$this->registerMetaTag(['name' => 'description', 'content' => 'This is my cool website made with Yii!'], 'description');
$this->registerMetaTag(['name' => 'description', 'content' => 'This website is about funny raccoons.'], 'description');
```
### Registering Link Tags <a name="registering-link-tags"></a>
Like [meta tags](#adding-meta-tags), link tags are useful in many cases, such as customizing favicon, pointing to
RSS feed or delegating OpenID to another server. You can work with link tags in the similar way as meta tags
by using [[yii\web\View::registerLinkTag()]]. For example, in a content view, you can register a link tag like follows,
```php
$this->registerLinkTag([
'title' => 'Live News for Yii',
'rel' => 'alternate',
'type' => 'application/rss+xml',
'href' => 'http://www.yiiframework.com/rss.xml/',
]);
```
The code above will result in
```html
<link title="Live News for Yii" rel="alternate" type="application/rss+xml" href="http://www.yiiframework.com/rss.xml/">
```
Similar as [[yii\web\View::registerMetaTag()|registerMetaTags()]], you can specify a key when calling
[[yii\web\View::registerLinkTag()|registerLinkTag()]] to avoid generated repeated link tags.
## View Events <a name="view-events"></a>
[[yii\base\View|View components]] trigger several events during the view rendering process. You may respond
to these events to inject content into views or process the rendering results before they are sent to end users.
- [[yii\base\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: triggered at the beginning of rendering a file
in a controller. Handlers of this event may set [[yii\base\ViewEvent::isValid]] to be false to cancel the rendering process.
- [[yii\base\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: triggered by the call of [[yii\base\View::beginPage()]] in layouts.
Handlers of this event may obtain the rendering result through [[yii\base\ViewEvent::output]] and may modify
this property to change the rendering result.
- [[yii\base\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: triggered by the call of [[yii\base\View::beginPage()]] in layouts.
- [[yii\base\View::EVENT_END_PAGE|EVENT_END_PAGE]]: triggered by the call of [[yii\base\View::endPage()]] in layouts.
- [[yii\web\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: triggered by the call of [[yii\web\View::beginBody()]] in layouts.
- [[yii\web\View::EVENT_END_BODY|EVENT_END_BODY]]: triggered by the call of [[yii\web\View::endBody()]] in layouts.
For example, the following code injects the current date at the end of the page body:
```php
\Yii::$app->view->on(View::EVENT_END_BODY, function () {
echo date('Y-m-d');
});
```
## Rendering Static Pages <a name="rendering-static-pages"></a>
Static pages refer to those Web pages whose main content are mostly static without the need of accessing
dynamic data pushed from controllers.
You can output static pages by putting their code in the view, and then using the code like the following in a controller:
```php
public function actionAbout()
{
return $this->render('about');
}
```
If a Web site contains many static pages, it would be very tedious repeating the similar code many times.
To solve this problem, you may introduce a [standalone action](structure-controllers.md#standalone-actions)
called [[yii\web\ViewAction]] in a controller. For example,
```php
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public function actions()
{
return [
'page' => [
'class' => 'yii\web\ViewAction',
],
];
}
}
```
Now if you create a view named `about` under the directory `@app/views/site/pages`, you will be able to
display this view by the following URL:
```
http://localhost/index.php?r=site/page&view=about
```
The `GET` parameter `view` tells [[yii\web\ViewAction]] which view is requested. The action will then look
for this view under the directory `@app/views/site/pages`. You may configure [[yii\web\ViewAction::viewPrefix]]
to change the directory for searching these views.
## Best Practices <a name="best-practices"></a>
Views are responsible for presenting models in the format that end users desire. In general, views
* should mainly contain presentational code, such as HTML, and simple PHP code to traverse, format and render data.
* should not contain code that performs DB queries. Such code should be done in models.
* should avoid direct access to request data, such as `$_GET`, `$_POST`. This belongs to controllers.
If request data is needed, they should be pushed into views by controllers.
* may read model properties, but should not modify them.
To make views more manageable, avoid creating views that are too complex or contain too much redundant code.
You may use the following techniques to achieve this goal:
* use [layouts](#layouts) to represent common presentational sections (e.g. page header, footer).
* divide a complicated view into several smaller ones. The smaller views can be rendered and assembled into a bigger
one using the rendering methods that we have described.
* create and use [widgets](structure-widgets.md) as building blocks of views.
* create and use helper classes to transform and format data in views.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment