Commit f03e6f0a by Qiang Xue

Merge pull request #5739 from yii2-chinesization/master

New Chinese translations
parents 26e185d3 ac0ef360
启动引导(Bootstrapping)
=============
启动引导是指:在应用开始解析并处理新接受请求之前,一个预先准备环境的过程。启动引导会在两个地方具体进行:[入口脚本(Entry Script)](structure-entry-scripts.md)
[应用主体(application)](structure-applications.md)
[入口脚本](structure-entry-scripts.md)里,需注册各个类库的类文件自动加载器(Class Autoloader,简称自动加载器)。这主要包括通过其 `autoload.php` 文件加载的
Composer 自动加载器,以及通过 `Yii` 类加载的 Yii 自动加载器。之后,入口脚本会加载应用的
[配置(configuration)](concept-configurations.md)
并创建一个 [应用主体](structure-applications.md) 的实例。
在应用主体的构造函数中,会执行以下引导工作:
1. 调用 [[yii\base\Application::preInit()|preInit()]](预初始化)方法,配置一些高优先级的应用属性,比如 [[yii\base\Application::basePath|basePath]] 属性。
2. 注册[[yii\base\Application::errorHandler|错误处理器(ErrorHandler)]]。
3. 通过给定的应用配置初始化应用的各属性。
4. 通过调用 [[yii\base\Application::init()|init()]](初始化)方法,它会顺次调用
[[yii\base\Application::bootstrap()|bootstrap()]] 从而运行引导组件。
- 加载扩展清单文件(extension manifest file) `vendor/yiisoft/extensions.php`
- 创建并运行各个扩展声明的 [引导组件(bootstrap components)](structure-extensions.md#bootstrapping-classes)
- 创建并运行各个 [应用组件](structure-application-components.md) 以及在应用的 [Bootstrap 属性](structure-applications.md#bootstrap)中声明的各个
[模块(modules)组件](structure-modules.md)(如果有)。
因为引导工作必须在处理**每一次**请求之前都进行一遍,因此让该过程尽可能轻量化就异常重要,请尽可能地优化这一步骤。
请尽量不要注册太多引导组件。只有他需要在 HTTP 请求处理的全部生命周期中都作用时才需要使用它。举一个用到它的范例:一个模块需要注册额外的 URL 解析规则,就应该把它列在应用的
[bootstrap 属性](structure-applications.md#bootstrap)之中,这样该 URL 解析规则才能在解析请求之前生效。(译者注:换言之,为了性能需要,除了 URL
解析等少量操作之外,绝大多数组件都应该按需加载,而不是都放在引导过程中。)
在生产环境中,可以开启字节码缓存,比如 APC,来进一步最小化加载和解析 PHP 文件所需的时间。
一些大型应用都包含有非常复杂的应用[配置](concept-configurations.md),它们会被分割到许多更小的配置文件中。此时,可以考虑将整个配置数组缓存起来,并在入口脚本创建应用实例之前直接从缓存中加载。
Controllers 控制器
=========== ===========
Controllers are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture. 控制器是 [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) 模式中的一部分,
They are objects of classes extending from [[yii\base\Controller]] and are responsible for processing requests and 是继承[[yii\base\Controller]]类的对象,负责处理请求和生成响应。
generating responses. In particular, after taking over the control from [applications](structure-applications.md), 具体来说,控制器从[应用主体](structure-applications.md)接管控制后会分析请求数据并传送到[模型](structure-models.md)
controllers will analyze incoming request data, pass them to [models](structure-models.md), inject model results 传送模型结果到[视图](structure-views.md),最后生成输出响应信息。
into [views](structure-views.md), and finally generate outgoing responses.
## Actions <a name="actions"></a> ## 操作 <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`: 如下示例显示包含两个操作`view` and `create` 的控制器`post`
```php ```php
namespace app\controllers; namespace app\controllers;
...@@ -52,47 +50,42 @@ class PostController extends Controller ...@@ -52,47 +50,42 @@ class PostController extends Controller
} }
``` ```
In the `view` action (defined by the `actionView()` method), the code first loads the [model](structure-models.md) 在操作 `view` (定义为 `actionView()` 方法)中, 代码首先根据请求模型ID加载 [模型](structure-models.md)
according to the requested model ID; If the model is loaded successfully, it will display it using 如果加载成功,会渲染名称为`view`[视图](structure-views.md)并显示,否则会抛出一个异常。
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 在操作 `create` (定义为 `actionCreate()` 方法)中, 代码相似. 先将请求数据填入[模型](structure-models.md)
the [model](structure-models.md) using the request data and save the model. If both succeed it will redirect 然后保存模型,如果两者都成功,会跳转到ID为新创建的模型的`view`操作,否则显示提供用户输入的`create`视图。
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> ## 路由 <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); * 模型ID: 仅存在于控制器属于非应用的[模块](structure-modules.md);
* a controller ID: a string that uniquely identifies the controller among all controllers within the same application * 控制器ID: 同应用(或同模块如果为模块下的控制器)下唯一标识控制器的字符串;
(or the same module if the controller belongs to a module); * 操作ID: 同控制器下唯一标识操作的字符串。
* an action ID: a string that uniquely identifies the action among all actions within the same controller.
Routes take the following format: 路由使用如下格式:
``` ```
ControllerID/ActionID ControllerID/ActionID
``` ```
or the following format if the controller belongs to a module: 如果属于模块下的控制器,使用如下格式:
```php ```php
ModuleID/ControllerID/ActionID 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 如果用户的请求地址为 `http://hostname/index.php?r=site/index`, 会执行`site` 控制器的`index` 操作。
will be executed. For more details how routes are resolved into actions, please refer to 更多关于处理路由的详情请参阅 [路由](runtime-routing.md) 一节。
the [Routing](runtime-routing.md) section.
## Creating Controllers <a name="creating-controllers"></a> ## 创建控制器 <a name="creating-controllers"></a>
In [[yii\web\Application|Web applications]], controllers should extend from [[yii\web\Controller]] or its [[yii\web\Application|Web applications]]网页应用中,控制器应继承[[yii\web\Controller]] 或它的子类。
child classes. Similarly in [[yii\console\Application|console applications]], controllers should extend from 同理在[[yii\console\Application|console applications]]控制台应用中,控制器继承[[yii\console\Controller]] 或它的子类。
[[yii\console\Controller]] or its child classes. The following code defines a `site` controller: 如下代码定义一个 `site` 控制器:
```php ```php
namespace app\controllers; namespace app\controllers;
...@@ -105,66 +98,59 @@ class SiteController extends Controller ...@@ -105,66 +98,59 @@ class SiteController extends Controller
``` ```
### Controller IDs <a name="controller-ids"></a> ### 控制器ID <a name="controller-ids"></a>
Usually, a controller is designed to handle the requests regarding a particular type of resource. 通常情况下,控制器用来处理请求有关的资源类型,因此控制器ID通常为和资源有关的名词。
For this reason, controller IDs are often nouns referring to the types of the resources that they are handling. 例如使用`article`作为处理文章的控制器ID。
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, 控制器ID应仅包含英文小写字母、数字、下划线、中横杠和正斜杠,
underscores, dashes and forward slashes. For example, `article` and `post-comment` are both valid controller IDs, 例如 `article``post-comment` 是真是的控制器ID,`article?`, `PostComment`, `admin\post`不是控制器ID。
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 控制器Id可包含子目录前缀,例如 `admin/article` 代表
in the `admin` subdirectory under the [[yii\base\Application::controllerNamespace|controller namespace]]. [[yii\base\Application::controllerNamespace|controller namespace]]控制器命名空间下 `admin`子目录中 `article` 控制器。
Valid characters for subdirectory prefixes include: English letters in lower and upper cases, digits, underscores and 子目录前缀可为英文大小写字母、数字、下划线、正斜杠,其中正斜杠用来区分多级子目录(如 `panels/admin`)。
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> ### 控制器类命名 <a name="controller-class-naming"></a>
Controller class names can be derived from controller IDs according to the following rules: 控制器ID遵循以下规则衍生控制器类名:
* Turn the first letter in each word separated by dashes into upper case. Note that if the controller ID * 将用正斜杠区分的每个单词第一个字母转为大写。注意如果控制器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. * 增加`Controller`后缀;
* Append the suffix `Controller`. * 在前面增加[[yii\base\Application::controllerNamespace|controller namespace]]控制器命名空间.
* And prepend the [[yii\base\Application::controllerNamespace|controller namespace]].
The followings are some examples, assuming the [[yii\base\Application::controllerNamespace|controller namespace]] 下面为一些示例,假设[[yii\base\Application::controllerNamespace|controller namespace]]控制器命名空间为 `app\controllers`:
takes the default value `app\controllers`:
* `article` derives `app\controllers\ArticleController`; * `article` 对应 `app\controllers\ArticleController`;
* `post-comment` derives `app\controllers\PostCommentController`; * `post-comment` 对应 `app\controllers\PostCommentController`;
* `admin/post-comment` derives `app\controllers\admin\PostCommentController`; * `admin/post-comment` 对应 `app\controllers\admin\PostCommentController`;
* `adminPanels/post-comment` derives `app\controllers\adminPanels\PostCommentController`. * `adminPanels/post-comment` 对应 `app\controllers\adminPanels\PostCommentController`.
Controller classes must be [autoloadable](concept-autoloading.md). For this reason, in the above examples, 控制器类必须能被 [自动加载](concept-autoloading.md),所以在上面的例子中,
the `article` controller class should be saved in the file whose [alias](concept-aliases.md) 控制器`article` 类应在 [别名](concept-aliases.md)`@app/controllers/ArticleController.php`的文件中定义,
is `@app/controllers/ArticleController.php`; while the `admin/post2-comment` controller should be 控制器`admin/post2-comment`应在`@app/controllers/admin/Post2CommentController.php`文件中。
in `@app/controllers/admin/Post2CommentController.php`.
> Info: The last example `admin/post2-comment` shows how you can put a controller under a sub-directory > 补充: 最后一个示例 `admin/post2-comment` 表示你可以将控制器放在
of the [[yii\base\Application::controllerNamespace|controller namespace]]. This is useful when you want [[yii\base\Application::controllerNamespace|controller namespace]]控制器命名空间下的子目录中,
to organize your controllers into several categories and you do not want to use [modules](structure-modules.md). 在你不想用 [模块](structure-modules.md) 的情况下给控制器分类,这种方式很有用。
### Controller Map <a name="controller-map"></a> ### 控制器部署 <a name="controller-map"></a>
You can configure [[yii\base\Application::controllerMap|controller map]] to overcome the constraints 可通过配置 [[yii\base\Application::controllerMap|controller map]] 来强制上述的控制器ID和类名对应,
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 配置 [应用配置](structure-applications.md#application-configurations)
[application configuration](structure-applications.md#application-configurations) like the following: 中的[application configuration](structure-applications.md#application-configurations),如下所示:
```php ```php
[ [
'controllerMap' => [ 'controllerMap' => [
// declares "account" controller using a class name // 用类名申明 "account" 控制器
'account' => 'app\controllers\UserController', 'account' => 'app\controllers\UserController',
// declares "article" controller using a configuration array // 用配置数组申明 "article" 控制器
'article' => [ 'article' => [
'class' => 'app\controllers\PostController', 'class' => 'app\controllers\PostController',
'enableCsrfValidation' => false, 'enableCsrfValidation' => false,
...@@ -174,14 +160,15 @@ You may configure [[yii\base\Application::controllerMap|controller map]] in the ...@@ -174,14 +160,15 @@ You may configure [[yii\base\Application::controllerMap|controller map]] in the
``` ```
### Default Controller <a name="default-controller"></a> ### 默认控制器 <a name="default-controller"></a>
Each application has a default controller specified via the [[yii\base\Application::defaultRoute]] property. 每个应用有一个由[[yii\base\Application::defaultRoute]]属性指定的默认控制器;
When a request does not specify a [route](#ids-routes), the route specified by this property will be used. 当请求没有指定 [路由](#ids-routes),该属性值作为路由使用。
For [[yii\web\Application|Web applications]], its value is `'site'`, while for [[yii\console\Application|console applications]], 对于[[yii\web\Application|Web applications]]网页应用,它的值为 `'site'`
it is `help`. Therefore, if a URL is `http://hostname/index.php`, it means the `site` controller will handle the request. 对于 [[yii\console\Application|console applications]]控制台应用,它的值为 `help`
所以URL为 `http://hostname/index.php` 表示由 `site` 控制器来处理。
You may change the default controller with the following [application configuration](structure-applications.md#application-configurations): 可以在 [应用配置](structure-applications.md#application-configurations) 中修改默认控制器,如下所示:
```php ```php
[ [
...@@ -190,11 +177,10 @@ You may change the default controller with the following [application configurat ...@@ -190,11 +177,10 @@ You may change the default controller with the following [application configurat
``` ```
## Creating Actions <a name="creating-actions"></a> ## 创建操作 <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 创建操作可简单地在控制器类中定义所谓的 *操作方法* 来完成,操作方法必须是以`action`开头的公有方法。
a *public* method whose name starts with the word `action`. The return value of an action method represents 操作方法的返回值会作为响应数据发送给终端用户,如下代码定义了两个操作 `index``hello-world`:
the response data to be sent to end users. The following code defines two actions `index` and `hello-world`:
```php ```php
namespace app\controllers; namespace app\controllers;
...@@ -216,62 +202,54 @@ class SiteController extends Controller ...@@ -216,62 +202,54 @@ class SiteController extends Controller
``` ```
### Action IDs <a name="action-ids"></a> ### 操作ID <a name="action-ids"></a>
An action is often designed to perform a particular manipulation about a resource. For this reason, 操作通常是用来执行资源的特定操作,因此,操作ID通常为动词,如`view`, `update`等。
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, 操作ID应仅包含英文小写字母、数字、下划线和中横杠,操作ID中的中横杠用来分隔单词。
underscores and dashes. The dashes in an actionID are used to separate words. For example, 例如`view`, `update2`, `comment-post`是真实的操作ID,`view?`, `Update`不是操作ID.
`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 可通过两种方式创建操作ID,内联操作和独立操作. An inline action is
defined as a method in the controller class, while a standalone action is a class extending 内联操作在控制器类中定义为方法;独立操作是继承[[yii\base\Action]]或它的子类的类。
[[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 独立操作相反,主要用于多个控制器重用,或重构为[扩展](structure-extensions.md)
created to be used in different controllers or be redistributed as [extensions](structure-extensions.md).
### Inline Actions <a name="inline-actions"></a> ### 内联操作 <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: 操作方法的名字是根据操作ID遵循如下规则衍生:
* Turn the first letter in each word of the action ID into upper case; * 将每个单词的第一个字母转为大写;
* Remove dashes; * 去掉中横杠;
* Prepend the prefix `action`. * 增加`action`前缀.
For example, `index` becomes `actionIndex`, and `hello-world` becomes `actionHelloWorld`. 例如`index` 转成 `actionIndex`, `hello-world` 转成 `actionHelloWorld`
> Note: The names of the action methods are *case-sensitive*. If you have a method named `ActionIndex`, > 注意: 操作方法的名字*大小写敏感*,如果方法名称为`ActionIndex`不会认为是操作方法,
it will not be considered as an action method, and as a result, the request for the `index` action 所以请求`index`操作会返回一个异常,也要注意操作方法必须是公有的,私有或者受保护的方法不能定义成内联操作。
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> ### 独立操作 <a name="standalone-actions"></a>
Standalone actions are defined in terms of action classes extending [[yii\base\Action]] or its child classes. 独立操作通过继承[[yii\base\Action]]或它的子类来定义。
For example, in the Yii releases, there are [[yii\web\ViewAction]] and [[yii\web\ErrorAction]], both of which 例如Yii发布的[[yii\web\ViewAction]]和[[yii\web\ErrorAction]]都是独立操作。
are standalone actions.
To use a standalone action, you should declare it in the *action map* by overriding the 要使用独立操作,需要通过控制器中覆盖[[yii\base\Controller::actions()]]方法在*action map*中申明,如下例所示:
[[yii\base\Controller::actions()]] method in your controller classes like the following:
```php ```php
public function actions() public function actions()
{ {
return [ return [
// declares "error" action using a class name // 用类来申明"error" 操作
'error' => 'yii\web\ErrorAction', 'error' => 'yii\web\ErrorAction',
// declares "view" action using a configuration array // 用配置数组申明 "view" 操作
'view' => [ 'view' => [
'class' => 'yii\web\ViewAction', 'class' => 'yii\web\ViewAction',
'viewPrefix' => '', 'viewPrefix' => '',
...@@ -280,13 +258,12 @@ public function actions() ...@@ -280,13 +258,12 @@ public function actions()
} }
``` ```
As you can see, the `actions()` method should return an array whose keys are action IDs and values the corresponding 如上所示, `actions()` 方法返回键为操作ID、值为对应操作类名或数组[configurations](concept-configurations.md) 的数组。
action class names or [configurations](concept-configurations.md). Unlike inline actions, action IDs for standalone 和内联操作不同,独立操作ID可包含任意字符,只要在`actions()` 方法中申明.
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 为创建一个独立操作类,需要继承[[yii\base\Action]] 或它的子类,并实现公有的名称为`run()`的方法,
a public method named `run()`. The role of the `run()` method is similar to that of an action method. For example, `run()` 方法的角色和操作方法类似,例如:
```php ```php
<?php <?php
...@@ -304,41 +281,37 @@ class HelloWorldAction extends Action ...@@ -304,41 +281,37 @@ class HelloWorldAction extends Action
``` ```
### Action Results <a name="action-results"></a> ### 操作结果 <a name="action-results"></a>
The return value of an action method or the `run()` method of a standalone action is significant. It stands 操作方法或独立操作的`run()`方法的返回值非常中药,它表示对应操作结果。
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 返回值可为 [响应](runtime-responses.md) 对象,作为响应发送给终端用户。
to end users.
* For [[yii\web\Application|Web applications]], the return value can also be some arbitrary data which will * 对于[[yii\web\Application|Web applications]]网页应用,返回值可为任意数据, 它赋值给[[yii\web\Response::data]],
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 * 对于[[yii\console\Application|console applications]]控制台应用,返回值可为整数,
the [[yii\console\Response::exitStatus|exit status]] of the command execution. 表示命令行下执行的 [[yii\console\Response::exitStatus|exit status]] 退出状态。
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 返回响应对象(因为[[yii\web\Controller::redirect()|redirect()]]方法返回一个响应对象)可将用户浏览器跳转到新的URL。
by returning a response object (because the [[yii\web\Controller::redirect()|redirect()]] method returns
a response object):
```php ```php
public function actionForward() public function actionForward()
{ {
// redirect the user browser to http://example.com // 用户浏览器跳转到 http://example.com
return $this->redirect('http://example.com'); return $this->redirect('http://example.com');
} }
``` ```
### Action Parameters <a name="action-parameters"></a> ### 操作参数 <a name="action-parameters"></a>
The action methods for inline actions and the `run()` methods for standalone actions can take parameters, 内联操作的操作方法和独立操作的 `run()` 方法可以带参数,称为*操作参数*
called *action parameters*. Their values are obtained from requests. For [[yii\web\Application|Web applications]], 参数值从请求中获取,对于[[yii\web\Application|Web applications]]网页应用,
the value of each action parameter is retrieved from `$_GET` using the parameter name as the key; 每个操作参数的值从`$_GET`中获得,参数名作为键;
for [[yii\console\Application|console applications]], they correspond to the command line arguments. 对于[[yii\console\Application|console applications]]控制台应用, 操作参数对应命令行参数。
In the following example, the `view` action (an inline action) has declared two parameters: `$id` and `$version`. 如下例,操作`view` (内联操作) 申明了两个参数 `$id``$version`
```php ```php
namespace app\controllers; namespace app\controllers;
...@@ -354,18 +327,16 @@ class PostController extends Controller ...@@ -354,18 +327,16 @@ class PostController extends Controller
} }
``` ```
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 * `http://hostname/index.php?r=post/view&id=123`: `$id` 会填入`'123'``$version` 仍为 null 空因为没有`version`请求参数;
`'123'`, while `$version` is still null because there is no `version` query parameter. * `http://hostname/index.php?r=post/view&id=123&version=2`: $id` 和 `$version` 分别填入 `'123'` 和 `'2'`;
* `http://hostname/index.php?r=post/view&id=123&version=2`: the `$id` and `$version` parameters will * `http://hostname/index.php?r=post/view`: 会抛出[[yii\web\BadRequestHttpException]] 异常
be filled with `'123'` and `'2'`, respectively. 因为请求没有提供参数给必须赋值参数`$id`;
* `http://hostname/index.php?r=post/view`: a [[yii\web\BadRequestHttpException]] exception will be thrown * `http://hostname/index.php?r=post/view&id[]=123`: 会抛出[[yii\web\BadRequestHttpException]] 异常
because the required `$id` parameter is not provided in the request. 因为`$id` 参数收到数字值 `['123']`而不是字符串.
* `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: 如果想让操作参数接收数组值,需要指定$id为`array`,如下所示:
```php ```php
public function actionView(array $id, $version = null) public function actionView(array $id, $version = null)
...@@ -374,22 +345,19 @@ public function actionView(array $id, $version = null) ...@@ -374,22 +345,19 @@ 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 现在如果请求为 `http://hostname/index.php?r=post/view&id[]=123`, 参数 `$id` 会使用数组值`['123']`
of `['123']`. If the request is `http://hostname/index.php?r=post/view&id=123`, the `$id` parameter will still 如果请求为 `http://hostname/index.php?r=post/view&id=123`
receive the same array value because the scalar value `'123'` will be automatically turned into an array. 参数 `$id` 会获取相同数组值,因为无类型的`'123'`会自动转成数组。
The above examples mainly show how action parameters work for Web applications. For console applications, 上述例子主要描述网页应用的操作参数,对于控制台应用,更多详情请参阅[控制台命令](tutorial-console.md)
please refer to the [Console Commands](tutorial-console.md) section for more details.
### Default Action <a name="default-action"></a> ### 默认操作 <a name="default-action"></a>
Each controller has a default action specified via the [[yii\base\Controller::defaultAction]] property. 每个控制器都有一个由 [[yii\base\Controller::defaultAction]] 属性指定的默认操作,
When a [route](#ids-routes) contains the controller ID only, it implies that the default action of [路由](#ids-routes) 只包含控制器ID,会使用所请求的控制器的默认操作。
the specified controller is requested.
By default, the default action is set as `index`. If you want to change the default value, simply override 默认操作默认为 `index`,如果想修改默认操作,只需简单地在控制器类中覆盖这个属性,如下所示:
this property in the controller class, like the following:
```php ```php
namespace app\controllers; namespace app\controllers;
...@@ -408,42 +376,37 @@ class SiteController extends Controller ...@@ -408,42 +376,37 @@ class SiteController extends Controller
``` ```
## Controller Lifecycle <a name="controller-lifecycle"></a> ## 控制器生命周期 <a name="controller-lifecycle"></a>
When processing a request, an [application](structure-applications.md) will create a controller 处理一个请求时,[应用主体](structure-applications.md) 会根据请求[路由](#routes)创建一个控制器,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. 1. 在控制器创建和配置后,[[yii\base\Controller::init()]] 方法会被调用。
2. The controller creates an action object based on the requested action ID: 2. 控制器根据请求操作ID创建一个操作对象:
* If the action ID is not specified, the [[yii\base\Controller::defaultAction|default action ID]] will be used. * 如果操作ID没有指定,会使用[[yii\base\Controller::defaultAction|default action ID]]默认操作ID;
* If the action ID is found in the [[yii\base\Controller::actions()|action map]], a standalone action * 如果在[[yii\base\Controller::actions()|action map]]找到操作ID,会创建一个独立操作;
will be created; * 如果操作ID对应操作方法,会创建一个内联操作;
* If the action ID is found to match an action method, an inline action will be created; * 否则会抛出[[yii\base\InvalidRouteException]]异常。
* Otherwise an [[yii\base\InvalidRouteException]] exception will be thrown. 3. 控制器按顺序调用应用主体、模块(如果控制器属于模块)、控制器的 `beforeAction()` 方法;
3. The controller sequentially calls the `beforeAction()` method of the application, the module (if the controller * 如果任意一个调用返回false,后面未调用的`beforeAction()`会跳过并且操作执行会被取消;
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. action execution will be cancelled.
* By default, each `beforeAction()` method call will trigger a `beforeAction` event to which you can attach a handler. * 默认情况下每个 `beforeAction()` 方法会触发一个 `beforeAction` 事件,在事件中你可以追加事件处理操作;
4. The controller runs the action: 4. 控制器执行操作:
* 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 5. 控制器按顺序调用控制器、模块(如果控制器属于模块)、应用主体的 `afterAction()` 方法;
belongs to a module) and the application. * 默认情况下每个 `afterAction()` 方法会触发一个 `afterAction` 事件,在事件中你可以追加事件处理操作;
* By default, each `afterAction()` method call will trigger an `afterAction` event to which you can attach a handler. 6. 应用主体获取操作结果并赋值给[响应](runtime-responses.md).
6. The application will take the action result and assign it to the [response](runtime-responses.md).
## Best Practices <a name="best-practices"></a> ## 最佳实践 <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; * 可访问 [请求](runtime-requests.md) 数据;
* may call methods of [models](structure-models.md) and other service components with request data; * 可根据请求数据调用 [模型](structure-models.md) 的方法和其他服务组件;
* may use [views](structure-views.md) to compose responses; * 可使用 [视图](structure-views.md) 构造响应;
* should NOT process the request data - this should be done in [models](structure-models.md); * 不应处理应被[模型](structure-models.md)处理的请求数据;
* should avoid embedding HTML or other presentational code - this is better done in [views](structure-views.md). * 应避免嵌入HTML或其他展示代码,这些代码最好在 [视图](structure-views.md)中处理.
Models 模型
====== ======
Models are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture. 模型是 [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) 模式中的一部分,
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]] 或它的子类定义模型类,基类[[yii\base\Model]]支持许多实用的特性:
[[yii\base\Model]] supports many useful features:
* [Attributes](#attributes): represent the business data and can be accessed like normal object properties * [属性](#attributes): 代表可像普通类属性或数组一样被访问的业务数据;
or array elements; * [属性标签](#attribute-labels): 指定属性显示出来的标签;
* [Attribute labels](#attribute-labels): specify the display labels for attributes; * [块赋值](#massive-assignment): 支持一步给许多属性赋值;
* [Massive assignment](#massive-assignment): supports populating multiple attributes in a single step; * [验证规则](#validation-rules): 确保输入数据符合所申明的验证规则;
* [Validation rules](#validation-rules): ensures input data based on the declared validation rules; * [数据导出](#data-exporting): 允许模型数据导出为自定义格式的数组。
* [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). `Model` 类也是更多高级模型如[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 > 补充:模型并不强制一定要继承[[yii\base\Model]],但是由于很多组件支持[[yii\base\Model]],最好使用它做为模型基类。
components built to support [[yii\base\Model]], it is usually the preferable base class for a model.
## Attributes <a name="attributes"></a> ## 属性 <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. [[yii\base\Model::attributes()]] 指定模型所拥有的属性。
You can access an attribute like accessing a normal object property: 可像访问一个对象属性一样访问模型的属性:
```php ```php
$model = new \app\models\ContactForm; $model = new \app\models\ContactForm;
// "name" is an attribute of ContactForm // "name" 是ContactForm模型的属性
$model->name = 'example'; $model->name = 'example';
echo $model->name; echo $model->name;
``` ```
You can also access attributes like accessing array elements, thanks to the support for 也可像访问数组单元项一样访问属性,这要感谢[[yii\base\Model]]支持 [ArrayAccess 数组访问](http://php.net/manual/en/class.arrayaccess.php)
[ArrayAccess](http://php.net/manual/en/class.arrayaccess.php) and [ArrayIterator](http://php.net/manual/en/class.arrayiterator.php) [ArrayIterator 数组迭代器](http://php.net/manual/en/class.arrayiterator.php):
by [[yii\base\Model]]:
```php ```php
$model = new \app\models\ContactForm; $model = new \app\models\ContactForm;
// accessing attributes like array elements // 像访问数组单元项一样访问属性
$model['name'] = 'example'; $model['name'] = 'example';
echo $model['name']; echo $model['name'];
// iterate attributes // 迭代器遍历模型
foreach ($model as $name => $value) { foreach ($model as $name => $value) {
echo "$name: $value\n"; echo "$name: $value\n";
} }
``` ```
### Defining Attributes <a name="defining-attributes"></a> ### 定义属性 <a name="defining-attributes"></a>
By default, if your model class extends directly from [[yii\base\Model]], all its *non-static public* member 默认情况下你的模型类直接从[[yii\base\Model]]继承,所有 *non-static public非静态公有* 成员变量都是属性。
variables are attributes. For example, the `ContactForm` model class below has four attributes: `name`, `email`, 例如,下述`ContactForm` 模型类有四个属性`name`, `email`, `subject` and `body`
`subject` and `body`. The `ContactForm` model is used to represent the input data received from an HTML form. `ContactForm` 模型用来代表从HTML表单获取的输入数据。
```php ```php
namespace app\models; namespace app\models;
...@@ -75,35 +71,30 @@ class ContactForm extends Model ...@@ -75,35 +71,30 @@ class ContactForm extends Model
``` ```
You may override [[yii\base\Model::attributes()]] to define attributes in a different way. The method should 另一种方式是可覆盖 [[yii\base\Model::attributes()]] 来定义属性,该方法返回模型的属性名。
return the names of the attributes in a model. For example, [[yii\db\ActiveRecord]] does so by returning 例如 [[yii\db\ActiveRecord]] 返回对应数据表列名作为它的属性名,
the column names of the associated database table as its attribute names. Note that you may also need to 注意可能需要覆盖魔术方法如`__get()`, `__set()`使属性像普通对象属性被访问。
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> ### 属性标签 <a name="attribute-labels"></a>
When displaying values or getting input for attributes, you often need to display some labels associated 当属性显示或获取输入时,经常要显示属性相关标签,例如假定一个属性名为`firstName`
with attributes. For example, given an attribute named `firstName`, you may want to display a label `First Name` 在某些地方如表单输入或错误信息处,你可能想显示对终端用户来说更友好的 `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, 可以调用 [[yii\base\Model::getAttributeLabel()]] 获取属性的标签,例如:
```php ```php
$model = new \app\models\ContactForm; $model = new \app\models\ContactForm;
// displays "Name" // 显示为 "Name"
echo $model->getAttributeLabel('name'); echo $model->getAttributeLabel('name');
``` ```
By default, attribute labels are automatically generated from attribute names. The generation is done by 默认情况下,属性标签通过[[yii\base\Model::generateAttributeLabel()]]方法自动从属性名生成.
the method [[yii\base\Model::generateAttributeLabel()]]. It will turn camel-case variable names into 它会自动将驼峰式大小写变量名转换为多个首字母大写的单词,例如 `username` 转换为 `Username`
multiple words with the first letter in each word in upper case. For example, `username` becomes `Username`, `firstName` 转换为 `First Name`
and `firstName` becomes `First Name`.
If you do not want to use automatically generated labels, you may override [[yii\base\Model::attributeLabels()]] 如果你不想用自动生成的标签,可以覆盖 [[yii\base\Model::attributeLabels()]] 方法明确指定属性标签,例如:
to explicitly declare attribute labels. For example,
```php ```php
namespace app\models; namespace app\models;
...@@ -129,8 +120,8 @@ class ContactForm extends Model ...@@ -129,8 +120,8 @@ class ContactForm extends Model
} }
``` ```
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: 可在 [[yii\base\Model::attributeLabels()|attributeLabels()]] 方法中定义,如下所示:
```php ```php
public function attributeLabels() public function attributeLabels()
...@@ -144,36 +135,31 @@ public function attributeLabels() ...@@ -144,36 +135,31 @@ public function attributeLabels()
} }
``` ```
You may even conditionally define attribute labels. For example, based on the [scenario](#scenarios) the model 甚至可以根据条件定义标签,例如通过使用模型的 [scenario场景](#scenarios)
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 > 补充:属性标签是 [视图](structure-views.md)一部分,但是在模型中申明标签通常非常方便,并可行程非常简洁可重用代码。
in models is often very convenient and can result in very clean and reusable code.
## Scenarios <a name="scenarios"></a> ## 场景 <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, 模型可能在多个 *场景* 下使用,例如 `User` 模块可能会在收集用户登录输入,也可能会在用户注册时使用。
but it may also be used for the user registration purpose. In different scenarios, a model may use different 在不同的场景下,模型可能会使用不同的业务规则和逻辑,例如 `email` 属性在注册时强制要求有,但在登陆时不需要。
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. 模型使用 [[yii\base\Model::scenario]] 属性保持使用场景的跟踪,
By default, a model supports only a single scenario named `default`. The following code shows two ways of 默认情况下,模型支持一个名为 `default` 的场景,如下展示两种设置场景的方法:
setting the scenario of a model:
```php ```php
// scenario is set as a property // 场景作为属性来设置
$model = new User; $model = new User;
$model->scenario = 'login'; $model->scenario = 'login';
// scenario is set through configuration // 场景通过构造初始化配置来设置
$model = new User(['scenario' => 'login']); $model = new User(['scenario' => 'login']);
``` ```
By default, the scenarios supported by a model are determined by the [validation rules](#validation-rules) declared 默认情况下,模型支持的场景由模型中申明的 [验证规则](#validation-rules) 来决定,
in the model. However, you can customize this behavior by overriding the [[yii\base\Model::scenarios()]] method, 但你可以通过覆盖[[yii\base\Model::scenarios()]]方法来自定义行为,如下所示:
like the following:
```php ```php
namespace app\models; namespace app\models;
...@@ -192,17 +178,16 @@ class User extends ActiveRecord ...@@ -192,17 +178,16 @@ class User extends ActiveRecord
} }
``` ```
> Info: In the above and following examples, the model classes are extending from [[yii\db\ActiveRecord]] > 补充:在上述和下述的例子中,模型类都是继承[[yii\db\ActiveRecord]],
because the usage of multiple scenarios usually happens to [Active Record](db-active-record.md) classes. 因为多场景的使用通常发生在[Active Record](db-active-record.md) 类中.
The `scenarios()` method returns an array whose keys are the scenario names and values the corresponding `scenarios()` 方法返回一个数组,数组的键为场景名,值为对应的 *active attributes活动属性*
*active attributes*. An active attribute can be [massively assigned](#massive-assignment) and is subject 活动属性可被 [块赋值](#massive-assignment) 并遵循[验证规则](#validation-rules)
to [validation](#validation-rules). In the above example, the `username` and `password` attributes are active 在上述例子中,`username``password``login`场景中启用,在 `register` 场景中,
in the `login` scenario; while in the `register` scenario, `email` is also active besides `username` and `password`. 除了 `username` and `password``email` 也被启用。
The default implementation of `scenarios()` will return all scenarios found in the validation rule declaration `scenarios()` 方法默认实现会返回所有[[yii\base\Model::rules()]]方法申明的验证规则中的场景,
method [[yii\base\Model::rules()]]. When overriding `scenarios()`, if you want to introduce new scenarios 当覆盖`scenarios()`时,如果你想在默认场景外使用新场景,可以编写类似如下代码:
in addition to the default ones, you may write code like the following:
```php ```php
namespace app\models; namespace app\models;
...@@ -221,90 +206,82 @@ class User extends ActiveRecord ...@@ -221,90 +206,82 @@ class User extends ActiveRecord
} }
``` ```
The scenario feature is primarily used by [validation](#validation-rules) and [massive attribute assignment](#massive-assignment). 场景特性主要在[验证](#validation-rules)[属性块赋值](#massive-assignment) 中使用。
You can, however, use it for other purposes. For example, you may declare [attribute labels](#attribute-labels) 你也可以用于其他目的,例如可基于不同的场景定义不同的 [属性标签](#attribute-labels)
differently based on the current scenario.
## Validation Rules <a name="validation-rules"></a> ## 验证规则 <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, 例如假定`ContactForm`模型,你可能想确保所有属性不为空且 `email` 属性包含一个有效的邮箱地址,
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 可调用 [[yii\base\Model::validate()]] 来验证接收到的数据,
the validation rules declared in [[yii\base\Model::rules()]] to validate every relevant attribute. If no error 该方法使用[[yii\base\Model::rules()]]申明的验证规则来验证每个相关属性,
is found, it will return true. Otherwise, it will keep the errors in the [[yii\base\Model::errors]] property 如果没有找到错误,会返回 true,否则它会将错误保存在 [[yii\base\Model::errors]] 属性中并返回false,例如:
and return false. For example,
```php ```php
$model = new \app\models\ContactForm; $model = new \app\models\ContactForm;
// populate model attributes with user inputs // 用户输入数据赋值到模型属性
$model->attributes = \Yii::$app->request->post('ContactForm'); $model->attributes = \Yii::$app->request->post('ContactForm');
if ($model->validate()) { if ($model->validate()) {
// all inputs are valid // 所有输入数据都有效 all inputs are valid
} else { } else {
// validation failed: $errors is an array containing error messages // 验证失败:$errors 是一个包含错误信息的数组
$errors = $model->errors; $errors = $model->errors;
} }
``` ```
To declare validation rules associated with a model, override the [[yii\base\Model::rules()]] method by returning 通过覆盖 [[yii\base\Model::rules()]] 方法指定模型属性应该满足的规则来申明模型相关验证规则。
the rules that the model attributes should satisfy. The following example shows the validation rules declared 下述例子显示`ContactForm`模型申明的验证规则:
for the `ContactForm` model:
```php ```php
public function rules() public function rules()
{ {
return [ return [
// the name, email, subject and body attributes are required // name, email, subject 和 body 属性必须有值
[['name', 'email', 'subject', 'body'], 'required'], [['name', 'email', 'subject', 'body'], 'required'],
// the email attribute should be a valid email address // email 属性必须是一个有效的电子邮箱地址
['email', 'email'], ['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 更多关于如何申明验证规则的详情请参考 [验证输入](input-validation.md) 一节.
validation rules.
Sometimes, you may want a rule to be applied only in certain [scenarios](#scenarios). To do so, you can 有时你想一条规则只在某个 [场景](#scenarios) 下应用,为此你可以指定规则的 `on` 属性,如下所示:
specify the `on` property of a rule, like the following:
```php ```php
public function rules() public function rules()
{ {
return [ return [
// username, email and password are all required in "register" scenario // 在"register" 场景下 username, email 和 password 必须有值
[['username', 'email', 'password'], 'required', 'on' => 'register'], [['username', 'email', 'password'], 'required', 'on' => 'register'],
// username and password are required in "login" scenario // 在 "login" 场景下 username 和 password 必须有值
[['username', 'password'], 'required', 'on' => 'login'], [['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 如果没有指定 `on` 属性,规则会在所有场景下应用, 在当前[[yii\base\Model::scenario|scenario]]
an *active rule* if it can be applied in the current [[yii\base\Model::scenario|scenario]]. 下应用的规则称之为 *active rule活动规则*
An attribute will be validated if and only if it is an active attribute declared in `scenarios()` and 一个属性只会属于`scenarios()`中定义的活动属性且在`rules()`申明对应一条或多条活动规则的情况下被验证。
is associated with one or multiple active rules declared in `rules()`.
## 块赋值 <a name="massive-assignment"></a>
## Massive Assignment <a name="massive-assignment"></a> ## 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]] 它直接将输入数据对应填充到 [[yii\base\Model::attributes]] 属性。
property. The following two pieces of code are equivalent, both trying to assign the form data submitted by end users 以下两段代码效果是相同的,都是将终端用户输入的表单数据赋值到 `ContactForm` 模型的属性,
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 ```php
$model = new \app\models\ContactForm; $model = new \app\models\ContactForm;
...@@ -321,13 +298,11 @@ $model->body = isset($data['body']) ? $data['body'] : null; ...@@ -321,13 +298,11 @@ $model->body = isset($data['body']) ? $data['body'] : null;
``` ```
### Safe Attributes <a name="safe-attributes"></a> ### 安全属性 <a name="safe-attributes"></a>
Massive assignment only applies to the so-called *safe attributes* which are the attributes listed in 块赋值只应用在模型当前[[yii\base\Model::scenario|scenario]]场景[[yii\base\Model::scenarios()]]方法
[[yii\base\Model::scenarios()]] for the current [[yii\base\Model::scenario|scenario]] of a model. 列出的称之为 *安全属性* 的属性上,例如,如果`User`模型申明以下场景,
For example, if the `User` model has the following scenario declaration, then when the current scenario 当当前场景为`login`时候,只有`username` and `password` 可被块赋值,其他属性不会被赋值。
is `login`, only the `username` and `password` can be massively assigned. Any other attributes will
be kept untouched.
```php ```php
public function scenarios() public function scenarios()
...@@ -339,18 +314,15 @@ public function scenarios() ...@@ -339,18 +314,15 @@ public function scenarios()
} }
``` ```
> 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 例如,如果 `User` 模型有一个`permission`属性对应用户的权限,
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 由于默认[[yii\base\Model::scenarios()]]的实现会返回[[yii\base\Model::rules()]]所有属性和数据,
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 为此,提供一个特别的别名为 `safe` 的验证器来申明哪些属性是安全的不需要被验证,
to be safe without actually validating it. For example, the following rules declare that both `title` 如下示例的规则申明 `title``description` 都为安全属性。
and `description` are safe attributes.
```php ```php
public function rules() public function rules()
...@@ -362,12 +334,11 @@ public function rules() ...@@ -362,12 +334,11 @@ public function rules()
``` ```
### Unsafe Attributes <a name="unsafe-attributes"></a> ### 非安全属性 <a name="unsafe-attributes"></a>
As described above, the [[yii\base\Model::scenarios()]] method serves for two purposes: determining which attributes 如上所述,[[yii\base\Model::scenarios()]] 方法提供两个用处:定义哪些属性应被验证,定义哪些属性安全。
should be validated, and determining which attributes are safe. In some rare cases, you may want to validate 在某些情况下,你可能想验证一个属性但不想让他是安全的,可在`scenarios()`方法中属性名加一个惊叹号 `!`
an attribute but do not want to mark it safe. You can do so by prefixing an exclamation mark `!` to the attribute 例如像如下的`secret`属性。
name when declaring it in `scenarios()`, like the `secret` attribute in the following:
```php ```php
public function scenarios() public function scenarios()
...@@ -378,134 +349,113 @@ public function scenarios() ...@@ -378,134 +349,113 @@ public function scenarios()
} }
``` ```
When the model is in the `login` scenario, all three attributes will be validated. However, only the `username` 当模型在 `login` 场景下,三个属性都会被验证,但只有 `username``password` 属性会被块赋值,
and `password` attributes can be massively assigned. To assign an input value to the `secret` attribute, you 要对`secret`属性赋值,必须像如下例子明确对它赋值。
have to do it explicitly as follows,
```php ```php
$model->secret = $secret; $model->secret = $secret;
``` ```
## Data Exporting <a name="data-exporting"></a> ## 数据导出 <a name="data-exporting"></a>
Models often need to be exported in different formats. For example, you may want to convert a collection of 模型通常要导出成不同格式,例如,你可能想将模型的一个集合转成JSON或Excel格式,
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 你只需要关注第一步,因为第二步可被通用的数据转换器如[[yii\web\JsonResponseFormatter]]来完成。
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. 将模型转换为数组最简单的方式是使用 [[yii\base\Model::attributes]] 属性,例如:
For example,
```php ```php
$post = \app\models\Post::findOne(100); $post = \app\models\Post::findOne(100);
$array = $post->attributes; $array = $post->attributes;
``` ```
By default, the [[yii\base\Model::$attributes]] property will return the values of *all* attributes [[yii\base\Model::attributes]] 属性会返回 *所有* [[yii\base\Model::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()]] 更灵活和强大的将模型转换为数组的方式是使用 [[yii\base\Model::toArray()]] 方法,
method. Its default behavior is the same as that of [[yii\base\Model::$attributes]]. However, it allows you 它的行为默认和 [[yii\base\Model::attributes]] 相同,
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 实际上,它是导出模型到 RESTful 网页服务开发的默认方法,详情请参阅[响应格式](rest-response-formatting.md).
the [Response Formatting](rest-response-formatting.md).
### Fields <a name="fields"></a> ### 字段 <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 字段是模型通过调用[[yii\base\Model::toArray()]]生成的数组的单元名。
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 [[yii\base\Model::fields()|fields()]] 和/或 [[yii\base\Model::extraFields()|extraFields()]] 方法来改变这种行为,
should return a list of field definitions. The fields defined by `fields()` are default fields, meaning that 两个方法都返回一个字段定义列表,`fields()` 方法定义的字段是默认字段,表示`toArray()`方法默认会返回这些字段。
`toArray()` will return these fields by default. The `extraFields()` method defines additionally available fields `extraFields()`方法定义额外可用字段,通过`toArray()`方法指定`$expand`参数来返回这些额外可用字段。
which can also be returned by `toArray()` as long as you specify them via the `$expand` parameter. For example, 例如如下代码会返回`fields()`方法定义的所有字段和`extraFields()`方法定义的`prettyName` and `fullAddress`字段。
the following code will return all fields defined in `fields()` and the `prettyName` and `fullAddress` fields
if they are defined in `extraFields()`.
```php ```php
$array = $model->toArray([], ['prettyName', 'fullAddress']); $array = $model->toArray([], ['prettyName', 'fullAddress']);
``` ```
You can override `fields()` to add, remove, rename or redefine fields. The return value of `fields()` 可通过覆盖 `fields()` 来增加、删除、重命名和重定义字段,`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 ```php
// explicitly list every field, best used when you want to make sure the changes // 明确列出每个字段,特别用于你想确保数据表或模型属性改变不会导致你的字段改变(保证后端的API兼容).
// in your DB table or model attributes do not cause your field changes (to keep API backward compatibility).
public function fields() public function fields()
{ {
return [ return [
// field name is the same as the attribute name // 字段名和属性名相同
'id', 'id',
// field name is "email", the corresponding attribute name is "email_address" // 字段名为 "email",对应属性名为 "email_address"
'email' => 'email_address', 'email' => 'email_address',
// field name is "name", its value is defined by a PHP callback // 字段名为 "name", 值通过PHP代码返回
'name' => function () { 'name' => function () {
return $this->first_name . ' ' . $this->last_name; 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() public function fields()
{ {
$fields = parent::fields(); $fields = parent::fields();
// remove fields that contain sensitive information // 去掉一些包含敏感信息的字段
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']); unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);
return $fields; 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, > 如果有敏感数据,应覆盖 `fields()` 方法过滤掉,在上述列子中,我们选择过滤掉
> you should override `fields()` to filter them out. In the above example, we choose > `auth_key`, `password_hash` and `password_reset_token`。
> to filter out `auth_key`, `password_hash` and `password_reset_token`.
## 最佳实践 <a name="best-practices"></a>
## 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 在一个设计良好的应用中,模型通常比[控制器](structure-controllers.md)代码多。
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; * 不应直接访问请求,session和其他环境数据,这些数据应该由[控制器](structure-controllers.md)传入到模型;
* may contain methods implementing business logic; * 应避免嵌入HTML或其他展示代码,这些代码最好在 [视图](structure-views.md)中处理;
* should NOT directly access request, session, or any other environmental data. These data should be injected * 单个模型中避免太多的 [场景](#scenarios).
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 * 定义可被多个 [应用主体](structure-applications.md)[模块](structure-modules.md) 共享的模型基类集合。
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: * 在每个使用模型的 [应用主体](structure-applications.md)[模块](structure-modules.md)中,
通过继承对应的模型基类来定义具体的模型类,具体模型类包含应用主体或模块指定的规则和逻辑。
* 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 例如,在[高级应用模板](tutorial-advanced-app.md),你可以定义一个模型基类`common\models\Post`
are common among all their usages. 然后在前台应用中,定义并使用一个继承`common\models\Post`的具体模型类`frontend\models\Post`
* In each [application](structure-applications.md) or [module](structure-modules.md) that uses a model, 在后台应用中可以类似地定义`backend\models\Post`
define a concrete model class by extending from the corresponding base model class. The concrete model classes 通过这种策略,你清楚`frontend\models\Post`只对应前台应用,如果你修改它,就无需担忧修改会影响后台应用。
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.
模块
=======
模块是独立的软件单元,由[模型](structure-models.md), [视图](structure-views.md),
[控制器](structure-controllers.md)和其他支持组件组成,
终端用户可以访问在[应用主体](structure-applications.md)中已安装的模块的控制器,
模块被当成小应用主体来看待,和[应用主体](structure-applications.md)不同的是,
模块不能单独部署,必须属于某个应用主体。
## 创建模块 <a name="creating-modules"></a>
模块被组织成一个称为[[yii\base\Module::basePath|base path]]的目录,
在该目录中有子目录如`controllers`, `models`, `views` 分别为对应控制器,模型,视图和其他代码,和应用非常类似。
如下例子显示一个模型的目录结构:
```
forum/
Module.php 模块类文件
controllers/ 包含控制器类文件
DefaultController.php default 控制器类文件
models/ 包含模型类文件
views/ 包含控制器视图文件和布局文件
layouts/ 包含布局文件
default/ 包含DefaultController控制器视图文件
index.php index视图文件
```
### 模块类 <a name="module-classes"></a>
每个模块都有一个继承[[yii\base\Module]]的模块类,该类文件直接放在模块的[[yii\base\Module::basePath|base path]]目录下,
并且能被 [自动加载](concept-autoloading.md)。当一个模块被访问,和 [application instances](structure-applications.md)
类似会创建该模块类唯一实例,模块实例用来帮模块内代码共享数据和组件。
以下示例一个模块类大致定义:
```php
namespace app\modules\forum;
class Module extends \yii\base\Module
{
public function init()
{
parent::init();
$this->params['foo'] = 'bar';
// ... 其他初始化代码 ...
}
}
```
如果 `init()` 方法包含很多初始化模块属性代码,
可将他们保存在[配置](concept-configurations.md) 并在`init()`中使用以下代码加载:
```php
public function init()
{
parent::init();
// 从config.php加载配置来初始化模块
\Yii::configure($this, require(__DIR__ . '/config.php'));
}
```
`config.php`配置文件可能包含以下内容,类似[应用主体配置](structure-applications.md#application-configurations).
```php
<?php
return [
'components' => [
// list of component configurations
],
'params' => [
// list of parameters
],
];
```
### 模块中的控制器 <a name="controllers-in-modules"></a>
创建模块的控制器时,惯例是将控制器类放在模块类命名空间的`controllers`子命名空间中,
也意味着要将控制器类文件放在模块[[yii\base\Module::basePath|base path]]目录中的`controllers`子目录中。
例如,上小节中要在`forum`模块中创建`post`控制器,应像如下申明控制器类:
```php
namespace app\modules\forum\controllers;
use yii\web\Controller;
class PostController extends Controller
{
// ...
}
```
可配置[[yii\base\Module::controllerNamespace]]属性来自定义控制器类的命名空间,
如果一些控制器不再该命名空间下,可配置[[yii\base\Module::controllerMap]]属性让它们能被访问,
这类似于 [应用主体配置](structure-applications.md#controller-map) 所做的。
### 模块中的视图 <a name="views-in-modules"></a>
视图应放在模块的[[yii\base\Module::basePath|base path]]对应目录下的 `views` 目录,
对于模块中控制器对应的视图文件应放在 `views/ControllerID` 目录下,
其中`ControllerID`对应 [控制器 ID](structure-controllers.md#routes). For example, if
例如,假定控制器类为`PostController`,目录对应模块[[yii\base\Module::basePath|base path]]目录下的 `views/post` 目录。
模块可指定 [布局](structure-views.md#layouts),它用在模块的控制器视图渲染。
布局文件默认放在 `views/layouts` 目录下,可配置[[yii\base\Module::layout]]属性指定布局名,
如果没有配置 `layout` 属性名,默认会使用应用的布局。
## 使用模块 <a name="using-modules"></a>
要在应用中使用模块,只需要将模块加入到应用主体配置的[[yii\base\Application::modules|modules]]属性的列表中,
如下代码的[应用主体配置](structure-applications.md#application-configurations) 使用 `forum` 模块:
```php
[
'modules' => [
'forum' => [
'class' => 'app\modules\forum\Module',
// ... 模块其他配置 ...
],
],
]
```
[[yii\base\Application::modules|modules]] 属性使用模块配置数组,每个数组键为*模块 ID*
它标识该应用中唯一的模块,数组的值为用来创建模块的 [配置](concept-configurations.md)
### 路由 <a name="routes"></a>
和访问应用的控制器类似,[路由](structure-controllers.md#routes) 也用在模块中控制器的寻址,
模块中控制器的路由必须以模块ID开始,接下来为控制器ID和操作ID。
例如,假定应用使用一个名为 `forum` 模块,路由`forum/post/index` 代表模块中 `post` 控制器的 `index` 操作,
如果路由只包含模块ID,默认为 `default`[[yii\base\Module::defaultRoute]] 属性来决定使用哪个控制器/操作,
也就是说路由 `forum` 可能代表 `forum` 模块的 `default` 控制器。
### 访问模块 <a name="accessing-modules"></a>
在模块中,可能经常需要获取[模块类](#module-classes)的实例来访问模块ID,模块参数,模块组件等,
可以使用如下语句来获取:
```php
$module = MyModuleClass::getInstance();
```
其中 `MyModuleClass` 对应你想要的模块类,`getInstance()` 方法返回当前请求的模块类实例,
如果模块没有被请求,该方法会返回空,注意不需要手动创建一个模块类,因为手动创建的和Yii处理请求时自动创建的不同。
> 补充: 当开发模块时,你不能假定模块使用固定的ID,因为在应用或其他没模块中,模块可能会对应到任意的ID,
为了获取模块ID,应使用上述代码获取模块实例,然后通过`$module->id`获取模块ID。
也可以使用如下方式访问模块实例:
```php
// 获取ID为 "forum" 的模块
$module = \Yii::$app->getModule('forum');
// 获取处理当前请求控制器所属的模块
$module = \Yii::$app->controller->module;
```
第一种方式仅在你知道模块ID的情况下有效,第二种方式在你知道处理请求的控制器下使用。
一旦获取到模块实例,可访问注册到模块的参数和组件,例如:
```php
$maxPostCount = $module->params['maxPostCount'];
```
### 引导启动模块 <a name="bootstrapping-modules"></a>
有些模块在每个请求下都有运行, [[yii\debug\Module|debug]] 模块就是这种,
为此将这种模块加入到应用主体的 [[yii\base\Application::bootstrap|bootstrap]] 属性中。
例如,如下示例的应用主体配置会确保`debug`模块每次都被加载:
```php
[
'bootstrap' => [
'debug',
],
'modules' => [
'debug' => 'yii\debug\Module',
],
]
```
## 模块嵌套 <a name="nested-modules"></a>
模块可无限级嵌套,也就是说,模块可以包含另一个包含模块的模块,我们称前者为*父模块*,后者为*子模块*
子模块必须在父模块的[[yii\base\Module::modules|modules]]属性中申明,例如:
```php
namespace app\modules\forum;
class Module extends \yii\base\Module
{
public function init()
{
parent::init();
$this->modules = [
'admin' => [
// 此处应考虑使用一个更短的命名空间
'class' => 'app\modules\forum\modules\admin\Module',
],
];
}
}
```
在嵌套模块中的控制器,它的路由应包含它所有祖先模块的ID,例如`forum/admin/dashboard/index` 代表
在模块`forum`中子模块`admin``dashboard`控制器的`index`操作。
## 最佳实践 <a name="best-practices"></a>
模块在大型项目中常备使用,这些项目的特性可分组,每个组包含一些强相关的特性,
每个特性组可以做成一个模块由特定的开发人员和开发组来开发和维护。
在特性组上,使用模块也是重用代码的好方式,一些常用特性,如用户管理,评论管理,可以开发成模块,
这样在相关项目中非常容易被重用。
Views 视图
===== =====
Views are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture. 视图是 [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) 模式中的一部分。
They are code responsible for presenting data to end users. In a Web application, views are usually created 它是展示数据到终端用户的代码,在网页应用中,根据*视图模板*来创建视图,视图模板为PHP脚本文件,
in terms of *view templates* which are PHP script files containing mainly HTML code and presentational PHP code. 主要包含HTML代码和展示类PHP代码,通过[[yii\web\View|view]]应用组件来管理,
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> ## 创建视图 <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 如前所述,视图为包含HTML和PHP代码的PHP脚本,如下代码为一个登录表单的视图,
that presents a login form. As you can see, PHP code is used to generate the dynamic content, such as the 可看到PHP代码用来生成动态内容如页面标题和表单,HTML代码把它组织成一个漂亮的HTML页面。
page title and the form, while HTML code organizes them into a presentable HTML page.
```php ```php
<?php <?php
...@@ -37,25 +34,20 @@ $this->title = 'Login'; ...@@ -37,25 +34,20 @@ $this->title = 'Login';
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>
``` ```
Within a view, you can access `$this` which refers to the [[yii\web\View|view component]] managing 在视图中,可访问 `$this` 指向 [[yii\web\View|view component]] 来管理和渲染这个视图文件。
and rendering this view template.
Besides `$this`, there may be other predefined variables in a view, such as `$model` in the above 除了 `$this`之外,上述示例中的视图有其他预定义变量如 `$model`
example. These variables represent the data that are *pushed* into the view by [controllers](structure-controllers.md) 这些变量代表从[控制器](structure-controllers.md)或其他触发[视图渲染](#rendering-views)的对象 *传入* 到视图的数据。
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 > 技巧: 将预定义变量列到视图文件头部注释处,这样可被IDE编辑器识别,也是生成视图文档的好方法。
be recognized by IDEs. It is also a good way of documenting your views.
### Security <a name="security"></a> ### 安全 <a name="security"></a>
When creating views that generate HTML pages, it is important that you encode and/or filter the data coming 当创建生成HTML页面的视图时,在显示之前将用户输入数据进行转码和过滤非常重要,
from end users before presenting them. Otherwise, your application may be subject to 否则,你的应用可能会被[跨站脚本](http://en.wikipedia.org/wiki/Cross-site_scripting) 攻击。
[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 要显示纯文本,先调用 [[yii\helpers\Html::encode()]] 进行转码,例如如下代码将用户名在显示前先转码:
encodes the user name before displaying it:
```php ```php
<?php <?php
...@@ -67,8 +59,7 @@ use yii\helpers\Html; ...@@ -67,8 +59,7 @@ use yii\helpers\Html;
</div> </div>
``` ```
To display HTML content, use [[yii\helpers\HtmlPurifier]] to filter the content first. For example, the following 要显示HTML内容,先调用 [[yii\helpers\HtmlPurifier]] 过滤内容,例如如下代码将提交内容在显示前先过滤:
code filters the post content before displaying it:
```php ```php
<?php <?php
...@@ -80,55 +71,53 @@ use yii\helpers\HtmlPurifier; ...@@ -80,55 +71,53 @@ use yii\helpers\HtmlPurifier;
</div> </div>
``` ```
> Tip: While HTMLPurifier does excellent job in making output safe, it is not fast. You should consider > 技巧:HTMLPurifier在保证输出数据安全上做的不错,但性能不佳,如果你的应用需要高性能可考虑
[caching](caching-overview.md) the filtering result if your application requires high performance. [缓存](caching-overview.md) 过滤后的结果。
### Organizing Views <a name="organizing-views"></a> ### 组织视图 <a name="organizing-views"></a>
Like [controllers](structure-controllers.md) and [models](structure-models.md), there are conventions to organize views. [控制器](structure-controllers.md)[模型](structure-models.md) 类似,在组织视图上有一些约定:
* For views rendered by a controller, they should be put under the directory `@app/views/ControllerID` by default, * 控制器渲染的视图文件默认放在 `@app/views/ControllerID` 目录下,
where `ControllerID` refers to the [controller ID](structure-controllers.md#routes). For example, if 其中 `ControllerID` 对应 [控制器 ID](structure-controllers.md#routes),
the controller class is `PostController`, the directory would be `@app/views/post`; If it is `PostCommentController`, 例如控制器类为 `PostController`,视图文件目录应为 `@app/views/post`
the directory would be `@app/views/post-comment`. In case the controller belongs to a module, the directory 控制器类 `PostCommentController`对应的目录为 `@app/views/post-comment`
would be `views/ControllerID` under the [[yii\base\Module::basePath|module directory]]. 如果是模块中的控制器,目录应为 [[yii\base\Module::basePath|module directory]] 模块目录下的 `views/ControllerID` 目录;
* For views rendered in a [widget](structure-widgets.md), they should be put under the `WidgetPath/views` directory by * 对于 [小部件](structure-widgets.md) 渲染的视图文件默认放在 `WidgetPath/views` 目录,
default, where `WidgetPath` stands for the directory containing the widget class file. 其中 `WidgetPath` 代表小部件类文件所在的目录;
* 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()]] 可覆盖控制器或小部件的 [[yii\base\ViewContextInterface::getViewPath()]] 方法来自定义视图文件默认目录。
method of controllers or widgets.
## Rendering Views <a name="rendering-views"></a> ## 渲染视图 <a name="rendering-views"></a>
You can render views in [controllers](structure-controllers.md), [widgets](structure-widgets.md), or any 可在 [控制器](structure-controllers.md), [小部件](structure-widgets.md), 或其他地方调用渲染视图方法来渲染视图,
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 string $view 视图名或文件路径,由实际的渲染方法决定
* @param array $params the data to be passed to the view * @param array $params 传递给视图的数据
* @return string rendering result * @return string 渲染结果
*/ */
methodName($view, $params = []) methodName($view, $params = [])
``` ```
### Rendering in Controllers <a name="rendering-in-controllers"></a> ### 控制器中渲染 <a name="rendering-in-controllers"></a>
Within [controllers](structure-controllers.md), you may call the following controller methods to render views: [控制器](structure-controllers.md) 中,可调用以下控制器方法来渲染视图:
* [[yii\base\Controller::render()|render()]]: renders a [named view](#named-views) and applies a [layout](#layouts) * [[yii\base\Controller::render()|render()]]: 渲染一个 [视图名](#named-views) 并使用一个 [布局](#layouts)
to the rendering result. 返回到渲染结果。
* [[yii\base\Controller::renderPartial()|renderPartial()]]: renders a [named view](#named-views) without any layout. * [[yii\base\Controller::renderPartial()|renderPartial()]]: 渲染一个 [视图名](#named-views) 并且不使用布局。
* [[yii\web\Controller::renderAjax()|renderAjax()]]: renders a [named view](#named-views) without any layout, * [[yii\web\Controller::renderAjax()|renderAjax()]]: 渲染一个 [视图名](#named-views) 并且不使用布局,
and injects all registered JS/CSS scripts and files. It is usually used in response to AJAX Web requests. 并注入所有注册的JS/CSS脚本和文件,通常使用在响应AJAX网页请求的情况下。
* [[yii\base\Controller::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or * [[yii\base\Controller::renderFile()|renderFile()]]: 渲染一个视图文件目录或[别名](concept-aliases.md)下的视图文件。
[alias](concept-aliases.md).
For example, 例如:
```php ```php
namespace app\controllers; namespace app\controllers;
...@@ -147,7 +136,7 @@ class PostController extends Controller ...@@ -147,7 +136,7 @@ class PostController extends Controller
throw new NotFoundHttpException; throw new NotFoundHttpException;
} }
// renders a view named "view" and applies a layout to it // 渲染一个名称为"view"的视图并使用布局
return $this->render('view', [ return $this->render('view', [
'model' => $model, 'model' => $model,
]); ]);
...@@ -156,15 +145,15 @@ class PostController extends Controller ...@@ -156,15 +145,15 @@ class PostController extends Controller
``` ```
### Rendering in Widgets <a name="rendering-in-widgets"></a> ### 小部件中渲染 <a name="rendering-in-widgets"></a>
[小部件](structure-widgets.md) 中,可调用以下小部件方法来渲染视图:
Within [widgets](structure-widgets.md), you may call the following widget methods to render views. 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::render()|render()]]: 渲染一个 [视图名](#named-views).
* [[yii\base\Widget::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or * [[yii\base\Widget::renderFile()|renderFile()]]: 渲染一个视图文件目录或[别名](concept-aliases.md)下的视图文件。
[alias](concept-aliases.md).
For example, 例如:
```php ```php
namespace app\components; namespace app\components;
...@@ -178,7 +167,7 @@ class ListWidget extends Widget ...@@ -178,7 +167,7 @@ class ListWidget extends Widget
public function run() public function run()
{ {
// renders a view named "list" // 渲染一个名为 "list" 的视图
return $this->render('list', [ return $this->render('list', [
'items' => $this->items, 'items' => $this->items,
]); ]);
...@@ -187,74 +176,68 @@ class ListWidget extends Widget ...@@ -187,74 +176,68 @@ class ListWidget extends Widget
``` ```
### Rendering in Views <a name="rendering-in-views"></a> ### 视图中渲染 <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|view component]]视图组件提供的以下方法:
* [[yii\base\View::render()|render()]]: renders a [named view](#named-views). * [[yii\base\View::render()|render()]]: 渲染一个 [视图名](#named-views).
* [[yii\web\View::renderAjax()|renderAjax()]]: renders a [named view](#named-views) and injects all registered * [[yii\web\View::renderAjax()|renderAjax()]]: 渲染一个 [视图名](#named-views)
JS/CSS scripts and files. It is usually used in response to AJAX Web requests. 并注入所有注册的JS/CSS脚本和文件,通常使用在响应AJAX网页请求的情况下。
* [[yii\base\View::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or * [[yii\base\View::renderFile()|renderFile()]]: 渲染一个视图文件目录或[别名](concept-aliases.md)下的视图文件。
[alias](concept-aliases.md).
For example, the following code in a view renders the `_overview.php` view file which is in the same directory 例如,视图中的如下代码会渲染该视图所在目录下的 `_overview.php` 视图文件,
as the view being currently rendered. Remember that `$this` in a view refers to the [[yii\base\View|view]] component: 记住视图中 `$this` 对应 [[yii\base\View|view]] 组件:
```php ```php
<?= $this->render('_overview') ?> <?= $this->render('_overview') ?>
``` ```
### Rendering in Other Places <a name="rendering-in-other-places"></a> ### 其他地方渲染 <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` 访问 [[yii\base\View|view]] 应用组件,
`Yii::$app->view` and then call its aforementioned methods to render a view. For example, 调用它的如前所述的方法渲染视图,例如:
```php ```php
// displays the view file "@app/views/site/license.php" // 显示视图文件 "@app/views/site/license.php"
echo \Yii::$app->view->renderFile('@app/views/site/license.php'); echo \Yii::$app->view->renderFile('@app/views/site/license.php');
``` ```
### Named Views <a name="named-views"></a> ### 视图名 <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, * 视图名可省略文件扩展名,这种情况下使用 `.php` 作为扩展,
the view name `about` corresponds to the file name `about.php`. 视图名 `about` 对应到 `about.php` 文件名;
* If the view name starts with double slashes `//`, the corresponding view file path would be `@app/views/ViewName`. * 视图名以双斜杠 `//` 开头,对应的视图文件路径为 `@app/views/ViewName`
That is, the view is looked for under the [[yii\base\Application::viewPath|application's view path]]. 也就是说视图文件在 [[yii\base\Application::viewPath|application's view path]] 路径下找,
For example, `//site/about` will be resolved into `@app/views/site/about.php`. 例如 `//site/about` 对应到 `@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 * 视图名以单斜杠`/`开始,视图文件路径以当前使用[模块](structure-modules.md)[[yii\base\Module::viewPath|view path]]开始,
with the [[yii\base\Module::viewPath|view path]] of the currently active [module](structure-modules.md). 如果不存在模块,使用`@app/views/ViewName`开始,例如,如果当前模块为`user``/user/create` 对应成
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`, 如果在模块中,`/user/create`对应`@app/views/user/create.php`
`@app/modules/user/views/user/create.php`, if the currently active module is `user`. If there is no active module, * 如果 [[yii\base\View::context|context]] 渲染视图 并且上下文实现了 [[yii\base\ViewContextInterface]],
the view file path would be `@app/views/user/create.php`. 视图文件路径由上下文的 [[yii\base\ViewContextInterface::getViewPath()|view path]] 开始,
* 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 如果上下文为控制器`SiteController``site/about` 对应到 `@app/views/site/about.php`
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`. 例如被视图`@app/views/post/index.php` 渲染的 `item` 对应到 `@app/views/post/item`
* 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 根据以上规则,在控制器中 `app\controllers\PostController` 调用 `$this->render('view')`
actually render the view file `@app/views/post/view.php`, while calling `$this->render('_overview')` in that view 实际上渲染 `@app/views/post/view.php` 视图文件,当在该视图文件中调用 `$this->render('_overview')`
will render the view file `@app/views/post/_overview.php`. 会渲染 `@app/views/post/_overview.php` 视图文件。
### Accessing Data in Views <a name="accessing-data-in-views"></a> ### 视图中访问数据 <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 视图渲染时,调用PHP `extract()` 方法将该数组转换为视图可访问的变量。
`extract()` function will be called on this array so that the array is extracted into variables in the view. 例如,如下控制器的渲染视图代码推送2个变量到 `report` 视图:`$foo = 1``$bar = 2`
For example, the following view rendering code in a controller will push two variables to the `report` view:
`$foo = 1` and `$bar = 2`.
```php ```php
echo $this->render('report', [ echo $this->render('report', [
...@@ -263,35 +246,31 @@ echo $this->render('report', [ ...@@ -263,35 +246,31 @@ echo $this->render('report', [
]); ]);
``` ```
The pull approach actively retrieves data from the [[yii\base\View|view component]] or other objects accessible 拉取方式可让视图从[[yii\base\View|view component]]视图组件或其他对象中主动获得数据(如`Yii::$app`),
in views (e.g. `Yii::$app`). Using the code below as an example, within the view you can get the controller object 在视图中使用如下表达式`$this->context`可获取到控制器ID,
by the expression `$this->context`. And as a result, it is possible for you to access any properties or methods 可让你在`report`视图中获取控制器的任意属性或方法,如以下代码获取控制器ID。
of the controller in the `report` view, such as the controller ID shown in the following:
```php ```php
The controller ID is: <?= $this->context->id ?> 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> ### 视图间共享数据 <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 [[yii\base\View|view component]]视图组件提供[[yii\base\View::params|params]]参数属性来让不同视图共享数据。
to share data among views.
For example, in an `about` view, you can have the following code which specifies the current segment of the 例如在`about`视图中,可使用如下代码指定当前breadcrumbs的当前部分。
breadcrumbs.
```php ```php
$this->params['breadcrumbs'][] = 'About Us'; $this->params['breadcrumbs'][] = 'About Us';
``` ```
Then, in the [layout](#layouts) file, which is also a view, you can display the breadcrumbs using the data [布局](#layouts)文件(也是一个视图)中,可使用依次加入到[[yii\base\View::params|params]]数组的值来
passed along [[yii\base\View::params|params]]: 生成显示breadcrumbs:
```php ```php
<?= yii\widgets\Breadcrumbs::widget([ <?= yii\widgets\Breadcrumbs::widget([
...@@ -300,31 +279,28 @@ passed along [[yii\base\View::params|params]]: ...@@ -300,31 +279,28 @@ passed along [[yii\base\View::params|params]]:
``` ```
## Layouts <a name="layouts"></a> ## 布局 <a name="layouts"></a>
Layouts are a special type of views that represent the common parts of multiple views. For example, the pages 布局是一种特殊的视图,代表多个视图的公共部分,例如,大多数Web应用共享相同的页头和页尾,
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> ### 创建布局 <a name="creating-layouts"></a>
Because layouts are also views, they can be created in the similar way as normal views. By default, layouts 由于布局也是视图,它可像普通视图一样创建,布局默认存储在`@app/views/layouts`路径下,
are stored in the directory `@app/views/layouts`. For layouts used within a [module](structure-modules.md), [模块](structure-modules.md)中使用的布局应存储在[[yii\base\Module::basePath|module directory]]模块目录
they should be stored in the `views/layouts` directory under the [[yii\base\Module::basePath|module directory]]. 下的`views/layouts`路径下,可配置[[yii\base\Module::layoutPath]]来自定义应用或模块的布局默认路径。
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
<?php <?php
use yii\helpers\Html; use yii\helpers\Html;
/* @var $this yii\web\View */ /* @var $this yii\web\View */
/* @var $content string */ /* @var $content string 字符串 */
?> ?>
<?php $this->beginPage() ?> <?php $this->beginPage() ?>
<!DOCTYPE html> <!DOCTYPE html>
...@@ -346,51 +322,44 @@ use yii\helpers\Html; ...@@ -346,51 +322,44 @@ use yii\helpers\Html;
<?php $this->endPage() ?> <?php $this->endPage() ?>
``` ```
As you can see, the layout generates the HTML tags that are common to all pages. Within the `<body>` section, 如上所示,布局生成每个页面通用的HTML标签,在`<body>`标签中,打印`$content`变量,
the layout echoes the `$content` variable which represents the rendering result of content views and is pushed `$content`变量代表当[[yii\base\Controller::render()]]控制器渲染方法调用时传递到布局的内容视图渲染结果。
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. - [[yii\base\View::beginPage()|beginPage()]]: 该方法应在布局的开始处调用,
It triggers the [[yii\base\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]] event which indicates the beginning of a page. 它触发表明页面开始的 [[yii\base\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]] 事件。
- [[yii\base\View::endPage()|endPage()]]: This method should be called at the end of the layout. - [[yii\base\View::endPage()|endPage()]]: 该方法应在布局的结尾处调用,
It triggers the [[yii\base\View::EVENT_END_PAGE|EVENT_END_PAGE]] event which indicates the end of a page. 它触发表明页面结尾的 [[yii\base\View::EVENT_END_PAGE|EVENT_END_PAGE]] 时间。
- [[yii\web\View::head()|head()]]: This method should be called within the `<head>` section of an HTML page. - [[yii\web\View::head()|head()]]: 该方法应在HTML页面的`<head>`标签中调用,
It generates a placeholder which will be replaced with the registered head HTML code (e.g. link tags, meta tags) 它生成一个占位符,在页面渲染结束时会被注册的头部HTML代码(如,link标签, meta标签)替换。
when a page finishes rendering. - [[yii\web\View::beginBody()|beginBody()]]: 该方法应在`<body>`标签的开始处调用,
- [[yii\web\View::beginBody()|beginBody()]]: This method should be called at the beginning of the `<body>` section. 它触发 [[yii\web\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] 事件并生成一个占位符,
It triggers the [[yii\web\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] event and generates a placeholder which will 会被注册的HTML代码(如JavaScript)在页面主体开始处替换。
be replaced by the registered HTML code (e.g. JavaScript) targeted at the body begin position. - [[yii\web\View::endBody()|endBody()]]: 该方法应在`<body>`标签的结尾处调用,
- [[yii\web\View::endBody()|endBody()]]: This method should be called at the end of the `<body>` section. 它触发 [[yii\web\View::EVENT_END_BODY|EVENT_END_BODY]] 事件并生成一个占位符,
It triggers the [[yii\web\View::EVENT_END_BODY|EVENT_END_BODY]] event and generates a placeholder which will 会被注册的HTML代码(如JavaScript)在页面主体结尾处替换。
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> ### 布局中访问数据 <a name="accessing-data-in-layouts"></a>
Within a layout, you have access to two predefined variables: `$this` and `$content`. The former refers to 在布局中可访问两个预定义变量:`$this``$content`,前者对应和普通视图类似的[[yii\base\View|view]] 视图组件
the [[yii\base\View|view]] component, like in normal views, while the latter contains the rendering result of a content 后者包含调用[[yii\base\Controller::render()|render()]]方法渲染内容视图的结果。
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 如果想在布局中访问其他数据,必须使用[视图中访问数据](#accessing-data-in-views)一节介绍的拉取方式,
the [Accessing Data in Views](#accessing-data-in-views) subsection. If you want to pass data from a content view 如果想从内容视图中传递数据到布局,可使用[视图间共享数据](#sharing-data-among-views)一节中的方法。
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> ### 使用布局 <a name="using-layouts"></a>
As described in the [Rendering in Controllers](#rendering-in-controllers) subsection, when you render a view [控制器中渲染](#rendering-in-controllers)一节描述,当控制器调用[[yii\base\Controller::render()|render()]]
by calling the [[yii\base\Controller::render()|render()]] method in a controller, a layout will be applied 方法渲染视图时,会同时使用布局到渲染结果中,默认会使用`@app/views/layouts/main.php`布局文件。
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]]. 可配置[[yii\base\Application::layout]] 或 [[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 例如,如下代码使 `post` 控制器渲染视图时使用 `@app/views/layouts/post.php` 作为布局文件,
when rendering its views. Other controllers, assuming their `layout` property is untouched, will still use the default 假如 `layout` 属性没改变,控制器默认使用 `@app/views/layouts/main.php` 作为布局文件。
`@app/views/layouts/main.php` as the layout.
```php ```php
namespace app\controllers; namespace app\controllers;
...@@ -405,42 +374,37 @@ class PostController extends Controller ...@@ -405,42 +374,37 @@ class PostController extends Controller
} }
``` ```
For controllers belonging to a module, you may also configure the module's [[yii\base\Module::layout|layout]] property to 对于模块中的控制器,可配置模块的 [[yii\base\Module::layout|layout]] 属性指定布局文件应用到模块的所有控制器。
use a particular layout for these controllers.
Because the `layout` property may be configured at different levels (controllers, modules, application), 由于`layout` 可在不同层级(控制器、模块,应用)配置,在幕后Yii使用两步来决定控制器实际使用的布局。
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 - 如果控制器的 [[yii\base\Controller::layout]] 属性不为空null,使用它作为布局的值,
the [[yii\base\Controller::module|module]] of the controller as the context module. 控制器的 [[yii\base\Controller::module|module]]模块 作为上下文模块。
- If [[yii\base\Controller::layout|layout]] is null, search through all ancestor modules (including the application itself) of the controller and - 如果 [[yii\base\Controller::layout|layout]] 为空,从控制器的祖先模块(包括应用) 开始找
find the first module whose [[yii\base\Module::layout|layout]] property is not null. Use that module and 第一个[[yii\base\Module::layout|layout]] 属性不为空的模块,使用该模块作为上下文模块,
its [[yii\base\Module::layout|layout]] value as the context module and the chosen layout value. 并将它的[[yii\base\Module::layout|layout]] 的值作为布局的值,
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`). - 路径别名 (如 `@app/views/layouts/main`).
- an absolute path (e.g. `/main`): the layout value starts with a slash. The actual layout file will be - 绝对路径 (如 `/main`): 布局的值以斜杠开始,在应用的[[yii\base\Application::layoutPath|layout path] 布局路径
looked for under the application's [[yii\base\Application::layoutPath|layout path]] which defaults to 中查找实际的布局文件,布局路径默认为 `@app/views/layouts`
`@app/views/layouts`. - 相对路径 (如 `main`): 在上下文模块的[[yii\base\Module::layoutPath|layout path]]布局路径中查找实际的布局文件,
- a relative path (e.g. `main`): the actual layout file will be looked for under the context module's 布局路径默认为[[yii\base\Module::basePath|module directory]]模块目录下的`views/layouts` 目录。
[[yii\base\Module::layoutPath|layout path]] which defaults to the `views/layouts` directory under the - 布尔值 `false`: 不使用布局。
[[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`. 布局的值没有包含文件扩展名,默认使用 `.php`作为扩展名。
### Nested Layouts <a name="nested-layouts"></a> ### 嵌套布局 <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 有时候你想嵌套一个布局到另一个,例如,在Web站点不同地方,想使用不同的布局,
want to use different layouts, while all these layouts share the same basic layout that generates the overall 同时这些布局共享相同的生成全局HTML5页面结构的基本布局,可以在子布局中调用
HTML5 page structure. You can achieve this goal by calling [[yii\base\View::beginContent()|beginContent()]] and [[yii\base\View::beginContent()|beginContent()]] 和[[yii\base\View::endContent()|endContent()]]
[[yii\base\View::endContent()|endContent()]] in the child layouts like the following: 方法,如下所示:
```php ```php
<?php $this->beginContent('@app/views/layouts/base.php'); ?> <?php $this->beginContent('@app/views/layouts/base.php'); ?>
...@@ -450,25 +414,24 @@ HTML5 page structure. You can achieve this goal by calling [[yii\base\View::begi ...@@ -450,25 +414,24 @@ HTML5 page structure. You can achieve this goal by calling [[yii\base\View::begi
<?php $this->endContent(); ?> <?php $this->endContent(); ?>
``` ```
As shown above, the child layout content should be enclosed within [[yii\base\View::beginContent()|beginContent()]] and 如上所示,子布局内容应在 [[yii\base\View::beginContent()|beginContent()]] 和
[[yii\base\View::endContent()|endContent()]]. The parameter passed to [[yii\base\View::beginContent()|beginContent()]] [[yii\base\View::endContent()|endContent()]] 方法之间,传给 [[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> ### 使用数据块 <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. 调用 [[yii\base\View::beginBlock()|beginBlock()]] 和 [[yii\base\View::endBlock()|endBlock()]] 来定义数据块,
The block can then be accessed via `$view->blocks[$blockID]`, where `$blockID` stands for a unique ID that you assign 使用 `$view->blocks[$blockID]` 访问该数据块,其中 `$blockID` 为定义数据块时指定的唯一标识ID。
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
... ...
...@@ -488,8 +451,7 @@ First, in a content view, define one or multiple blocks: ...@@ -488,8 +451,7 @@ First, in a content view, define one or multiple blocks:
<?php $this->endBlock(); ?> <?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
... ...
...@@ -518,12 +480,11 @@ not defined. ...@@ -518,12 +480,11 @@ not defined.
``` ```
## Using View Components <a name="using-view-components"></a> ## 使用视图组件 <a name="using-view-components"></a>
[[yii\base\View|View components]] provides many view-related features. While you can get view components [[yii\base\View|View components]]视图组件提供许多视图相关特性,可创建[[yii\base\View]]或它的子类实例来获取视图组件,
by creating individual instances of [[yii\base\View]] or its child class, in most cases you will mainly use 大多数情况下主要使用 `view` 应用组件,可在[应用配置](structure-applications.md#application-configurations)中配置该组件,
the `view` application component. You can configure this component in [application configurations](structure-applications.md#application-configurations) 如下所示:
like the following:
```php ```php
[ [
...@@ -537,25 +498,25 @@ like the following: ...@@ -537,25 +498,25 @@ like the following:
] ]
``` ```
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. * [主题](output-theming.md): 允许为你的Web站点开发和修改主题;
* [fragment caching](caching-fragment.md): allows you to cache a fragment within a Web page. * [片段缓存](caching-fragment.md): 允许你在Web页面中缓存片段;
* [client script handling](output-client-scripts.md): supports CSS and JavaScript registration and rendering. * [客户脚本处理](output-client-scripts.md): 支持CSS 和 JavaScript 注册和渲染;
* [asset bundle handling](structure-assets.md): supports registering and rendering of [asset bundles](structure-assets.md). * [资源包处理](structure-assets.md): 支持 [资源包](structure-assets.md)的注册和渲染;
* [alternative template engines](tutorial-template-engines.md): allows you to use other template engines, such as * [模板引擎](tutorial-template-engines.md): 允许你使用其他模板引擎,如
[Twig](http://twig.sensiolabs.org/), [Smarty](http://www.smarty.net/). [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. 开发Web页面时,也可能频繁使用以下实用的小特性。
### Setting Page Titles <a name="setting-page-titles"></a> ### 设置页面标题 <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 每个Web页面应有一个标题,正常情况下标题的标签显示在 [布局](#layouts)中,
the title is often determined in content views rather than layouts. To solve this problem, [[yii\web\View]] provides 但是实际上标题大多由内容视图而不是布局来决定,为解决这个问题, [[yii\web\View]] 提供
the [[yii\web\View::title|title]] property for you to pass the title information from content views to layouts. [[yii\web\View::title|title]] 标题属性可让标题信息从内容视图传递到布局中。
To make use of this feature, in each content view, you can set the page title like the following: 为利用这个特性,在每个内容视图中设置页面标题,如下所示:
```php ```php
<?php <?php
...@@ -563,20 +524,18 @@ $this->title = 'My page title'; ...@@ -563,20 +524,18 @@ $this->title = 'My page title';
?> ?>
``` ```
Then in the layout, make sure you have the following code in the `<head>` section: 然后在视图中,确保在 `<head>` 段中有如下代码:
```php ```php
<title><?= Html::encode($this->title) ?></title> <title><?= Html::encode($this->title) ?></title>
``` ```
### Registering Meta Tags <a name="registering-meta-tags"></a> ### 注册Meta元标签 <a name="registering-meta-tags"></a>
Web pages usually need to generate various meta tags needed by different parties. Like page titles, meta tags Web页面通常需要生成各种元标签提供给不同的浏览器,如`<head>`中的页面标题,元标签通常在布局中生成。
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()]] 如果想在内容视图中生成元标签,可在内容视图中调用[[yii\web\View::registerMetaTag()]]方法,如下所示:
in a content view, like the following:
```php ```php
<?php <?php
...@@ -584,19 +543,17 @@ $this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php' ...@@ -584,19 +543,17 @@ $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 以上代码会在视图组件中注册一个 "keywords" 元标签,在布局渲染后会渲染该注册的元标签,
rendered after the layout finishes rendering. By then, the following HTML code will be inserted 然后,如下HTML代码会插入到布局中调用[[yii\web\View::head()]]方法处:
at the place where you call [[yii\web\View::head()]] in the layout and generate the following HTML code:
```php ```php
<meta name="keywords" content="yii, framework, 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, 注意如果多次调用 [[yii\web\View::registerMetaTag()]] 方法,它会注册多个元标签,注册时不会检查是否重复。
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. 例如,如下代码注册两次 "description" 元标签,但是只会渲染第二个。
```html ```html
$this->registerMetaTag(['name' => 'description', 'content' => 'This is my cool website made with Yii!'], 'description'); $this->registerMetaTag(['name' => 'description', 'content' => 'This is my cool website made with Yii!'], 'description');
...@@ -604,11 +561,10 @@ $this->registerMetaTag(['name' => 'description', 'content' => 'This website is a ...@@ -604,11 +561,10 @@ $this->registerMetaTag(['name' => 'description', 'content' => 'This website is a
``` ```
### Registering Link Tags <a name="registering-link-tags"></a> ### 注册链接标签 <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 [Meta标签](#adding-meta-tags) 类似,链接标签有时很使用,如自定义网站图标,指定Rss订阅,或授权OpenID到其他服务器。
RSS feed or delegating OpenID to another server. You can work with link tags in the similar way as meta tags 可以和元标签相似的方式调用[[yii\web\View::registerLinkTag()]],例如,在内容视图中注册链接标签如下所示:
by using [[yii\web\View::registerLinkTag()]]. For example, in a content view, you can register a link tag like follows,
```php ```php
$this->registerLinkTag([ $this->registerLinkTag([
...@@ -619,32 +575,31 @@ $this->registerLinkTag([ ...@@ -619,32 +575,31 @@ $this->registerLinkTag([
]); ]);
``` ```
The code above will result in 上述代码会转换成
```html ```html
<link title="Live News for Yii" rel="alternate" type="application/rss+xml" href="http://www.yiiframework.com/rss.xml/"> <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::registerMetaTag()|registerMetaTags()]] 类似,
[[yii\web\View::registerLinkTag()|registerLinkTag()]] to avoid generated repeated link tags. 调用[[yii\web\View::registerLinkTag()|registerLinkTag()]] 指定键来避免生成重复链接标签。
## View Events <a name="view-events"></a> ## 视图事件 <a name="view-events"></a>
[[yii\base\View|View components]] trigger several events during the view rendering process. You may respond [[yii\base\View|View components]] 视图组件会在视图渲染过程中触发几个事件,
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 - [[yii\base\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: 在控制器渲染文件开始时触发,
in a controller. Handlers of this event may set [[yii\base\ViewEvent::isValid]] to be false to cancel the rendering process. 该事件可设置 [[yii\base\ViewEvent::isValid]] 为 false 取消视图渲染。
- [[yii\base\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: triggered by the call of [[yii\base\View::beginPage()]] in layouts. - [[yii\base\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: 在布局中调用 [[yii\base\View::beginPage()]] 时触发,
Handlers of this event may obtain the rendering result through [[yii\base\ViewEvent::output]] and may modify 该事件可获取[[yii\base\ViewEvent::output]]的渲染结果,可修改该属性来修改渲染结果。
this property to change the rendering result. - [[yii\base\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: 在布局调用 [[yii\base\View::beginPage()]] 时触发;
- [[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]]: 在布局调用 [[yii\base\View::endPage()]] 是触发;
- [[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]]: 在布局调用 [[yii\web\View::beginBody()]] 时触发;
- [[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]]: 在布局调用 [[yii\web\View::endBody()]] 时触发。
- [[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 ```php
\Yii::$app->view->on(View::EVENT_END_BODY, function () { \Yii::$app->view->on(View::EVENT_END_BODY, function () {
...@@ -653,12 +608,11 @@ For example, the following code injects the current date at the end of the page ...@@ -653,12 +608,11 @@ For example, the following code injects the current date at the end of the page
``` ```
## Rendering Static Pages <a name="rendering-static-pages"></a> ## 渲染静态页面 <a name="rendering-static-pages"></a>
Static pages refer to those Web pages whose main content are mostly static without the need of accessing 静态页面值得是大部分内容为静态的不需要控制器传递动态数据的Web页面。
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: 可将HTML代码放置在视图中,在控制器中使用以下代码输出静态页面:
```php ```php
public function actionAbout() public function actionAbout()
...@@ -667,9 +621,9 @@ public function actionAbout() ...@@ -667,9 +621,9 @@ public function actionAbout()
} }
``` ```
If a Web site contains many static pages, it would be very tedious repeating the similar code many times. 如果Web站点包含很多静态页面,多次重复相似的代码显得很繁琐,
To solve this problem, you may introduce a [standalone action](structure-controllers.md#standalone-actions) 为解决这个问题,可以使用一个在控制器中称为 [[yii\web\ViewAction]] 的[独立操作](structure-controllers.md#standalone-actions)
called [[yii\web\ViewAction]] in a controller. For example, 例如:
```php ```php
namespace app\controllers; namespace app\controllers;
...@@ -689,34 +643,31 @@ class SiteController extends Controller ...@@ -689,34 +643,31 @@ class SiteController extends Controller
} }
``` ```
Now if you create a view named `about` under the directory `@app/views/site/pages`, you will be able to 现在如果你在`@app/views/site/pages`目录下创建名为 `about` 的视图,
display this view by the following URL: 可通过如下rul显示该视图:
``` ```
http://localhost/index.php?r=site/page&view=about 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 `GET``view` 参数告知 [[yii\web\ViewAction]] 操作请求哪个视图,然后操作在
for this view under the directory `@app/views/site/pages`. You may configure [[yii\web\ViewAction::viewPrefix]] `@app/views/site/pages`目录下寻找该视图,可配置 [[yii\web\ViewAction::viewPrefix]]
to change the directory for searching these views. 修改搜索视图的目录。
## Best Practices <a name="best-practices"></a> ## 最佳实践 <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. * 应主要包含展示代码,如HTML, 和简单的PHP代码来控制、格式化和渲染数据;
* 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. * 应避免直接访问请求数据,如 `$_GET`, `$_POST`,这种应在控制器中执行,
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.
* 使用 [布局](#layouts) 来展示公共代码(如,页面头部、尾部);
* 将复杂的视图分成几个小视图,可使用上面描述的渲染方法将这些小视图渲染并组装成大视图;
* 创建并使用 [小部件](structure-widgets.md) 作为视图的数据块;
* 创建并使用助手类在视图中转换和格式化数据。
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment