diff --git a/docs/guide-zh-CN/structure-application-components.md b/docs/guide-zh-CN/structure-application-components.md new file mode 100644 index 0000000..1155695 --- /dev/null +++ b/docs/guide-zh-CN/structure-application-components.md @@ -0,0 +1,119 @@ +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. diff --git a/docs/guide-zh-CN/structure-applications.md b/docs/guide-zh-CN/structure-applications.md new file mode 100644 index 0000000..2a1c137 --- /dev/null +++ b/docs/guide-zh-CN/structure-applications.md @@ -0,0 +1,609 @@ +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. diff --git a/docs/guide-zh-CN/structure-controllers.md b/docs/guide-zh-CN/structure-controllers.md new file mode 100644 index 0000000..79737be --- /dev/null +++ b/docs/guide-zh-CN/structure-controllers.md @@ -0,0 +1,449 @@ +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). diff --git a/docs/guide-zh-CN/structure-models.md b/docs/guide-zh-CN/structure-models.md new file mode 100644 index 0000000..e483d73 --- /dev/null +++ b/docs/guide-zh-CN/structure-models.md @@ -0,0 +1,511 @@ +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. diff --git a/docs/guide-zh-CN/structure-views.md b/docs/guide-zh-CN/structure-views.md new file mode 100644 index 0000000..1b13333 --- /dev/null +++ b/docs/guide-zh-CN/structure-views.md @@ -0,0 +1,722 @@ +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>© 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. +