Commit 08c19aba by funson86

for check

parent 101a30f7
......@@ -13,14 +13,14 @@ controllers will analyze incoming request data, pass them to [models](structure-
into [views](structure-views.md), and finally generate outgoing responses.
## 作 <a name="actions"></a>
## 作 <a name="actions"></a>
## Actions <a name="actions"></a>
控制器由 *动作* 组成,它是执行终端用户请求的最基础的单元,一个控制器可有一个或多个动作。
控制器由 *操作* 组成,它是执行终端用户请求的最基础的单元,一个控制器可有一个或多个操作。
Controllers are composed by *actions* which are the most basic units that end users can address and request for
execution. A controller can have one or multiple actions.
如下示例显示包含两个`view` and `create` 的控制器`post`
如下示例显示包含两个`view` and `create` 的控制器`post`
The following example shows a `post` controller with two actions: `view` and `create`:
```php
......@@ -60,14 +60,14 @@ class PostController extends Controller
}
```
`view` (定义为 `actionView()` 方法)中, 代码首先根据请求模型ID加载 [模型](structure-models.md)
`view` (定义为 `actionView()` 方法)中, 代码首先根据请求模型ID加载 [模型](structure-models.md)
如果加载成功,会渲染名称为`view`[视图](structure-views.md)并显示,否则会抛出一个异常。
the `view` action (defined by the `actionView()` method), the code first loads the [model](structure-models.md)
according to the requested model ID; If the model is loaded successfully, it will display it using
a [view](structure-views.md) named `view`. Otherwise, it will throw an exception.
`create` (定义为 `actionCreate()` 方法)中, 代码相似. 先将请求数据填入[模型](structure-models.md)
然后保存模型,如果两者都成功,会跳转到ID为新创建的模型的`view`作,否则显示提供用户输入的`create`视图。
`create` (定义为 `actionCreate()` 方法)中, 代码相似. 先将请求数据填入[模型](structure-models.md)
然后保存模型,如果两者都成功,会跳转到ID为新创建的模型的`view`作,否则显示提供用户输入的`create`视图。
In the `create` action (defined by the `actionCreate()` method), the code is similar. It first tries to populate
the [model](structure-models.md) using the request data and save the model. If both succeed it will redirect
the browser to the `view` action with the ID of the newly created model. Otherwise it will display
......@@ -77,12 +77,12 @@ the `create` view through which users can provide the needed input.
## 路由 <a name="routes"></a>
## Routes <a name="routes"></a>
终端用户通过所谓的*路由*寻找到作,路由是包含以下部分的字符串:End users address actions through the so-called *routes*. A route is a string that consists of the following parts:
终端用户通过所谓的*路由*寻找到作,路由是包含以下部分的字符串:End users address actions through the so-called *routes*. A route is a string that consists of the following parts:
End users address actions through the so-called *routes*. A route is a string that consists of the following parts:
* 模型ID: 仅存在于控制器属于非应用的[模块](structure-modules.md);
* 控制器ID: 同应用(或同模块如果为模块下的控制器)下唯一标识控制器的字符串;
* 动作ID: 同控制器下唯一标识动作的字符串。
* 操作ID: 同控制器下唯一标识操作的字符串。
* a module ID: this exists only if the controller belongs to a non-application [module](structure-modules.md);
* a controller ID: a string that uniquely identifies the controller among all controllers within the same application
(or the same module if the controller belongs to a module);
......@@ -102,7 +102,7 @@ or the following format if the controller belongs to a module:
ModuleID/ControllerID/ActionID
```
如果用户的请求地址为 `http://hostname/index.php?r=site/index`, 会执行`site` 控制器的`index` 作。
如果用户的请求地址为 `http://hostname/index.php?r=site/index`, 会执行`site` 控制器的`index` 作。
更多关于处理路由的详情请参阅 [路由](runtime-routing.md) 一节。
So if a user requests with the URL `http://hostname/index.php?r=site/index`, the `index` action in the `site` controller
will be executed. For more details how routes are resolved into actions, please refer to
......@@ -254,11 +254,11 @@ You may change the default controller with the following [application configurat
```
## 创建作 <a name="creating-actions"></a>
## 创建作 <a name="creating-actions"></a>
## Creating Actions <a name="creating-actions"></a>
创建动作可简单地在控制器类中定义所谓的 *动作方法* 来完成,动作方法必须是以`action`开头的公有方法。
动作方法的返回值会作为响应数据发送给终端用户,如下代码定义了两个动`index``hello-world`:
创建操作可简单地在控制器类中定义所谓的 *操作方法* 来完成,操作方法必须是以`action`开头的公有方法。
操作方法的返回值会作为响应数据发送给终端用户,如下代码定义了两个操`index``hello-world`:
Creating actions can be as simple as defining the so-called *action methods* in a controller class. An action method is
a *public* method whose name starts with the word `action`. The return value of an action method represents
the response data to be sent to end users. The following code defines two actions `index` and `hello-world`:
......@@ -283,23 +283,23 @@ class SiteController extends Controller
```
### 作ID <a name="action-ids"></a>
### 作ID <a name="action-ids"></a>
### Action IDs <a name="action-ids"></a>
动作通常是用来执行资源的特定操作,因此,动作ID通常为动词,如`view`, `update`等等。
操作通常是用来执行资源的特定操作,因此,操作ID通常为动词,如`view`, `update`等等。
An action is often designed to perform a particular manipulation about a resource. For this reason,
action IDs are usually verbs, such as `view`, `update`, etc.
动作ID应仅包含英文小写字母、数字、下划线和中横杠,动作ID中的中横杠用来分隔单词。
例如`view`, `update2`, `comment-post`是真实的动作ID,`view?`, `Update`不是动作ID.
操作ID应仅包含英文小写字母、数字、下划线和中横杠,操作ID中的中横杠用来分隔单词。
例如`view`, `update2`, `comment-post`是真实的操作ID,`view?`, `Update`不是操作ID.
By default, action IDs should contain these characters only: English letters in lower case, digits,
underscores and dashes. The dashes in an actionID are used to separate words. For example,
`view`, `update2`, `comment-post` are all valid action IDs, while `view?`, `Update` are not.
可通过两种方式创建动作ID,内联动作和独立动作. An inline action is
内联动作在控制器类中定义为方法;独立动作是继承[[yii\base\Action]]或它的子类的类。
内联作容易创建,在无需重用的情况下优先使用;
独立作相反,主要用于多个控制器重用,或重构为[扩展](structure-extensions.md)
可通过两种方式创建操作ID,内联操作和独立操作. An inline action is
内联操作在控制器类中定义为方法;独立操作是继承[[yii\base\Action]]或它的子类的类。
内联作容易创建,在无需重用的情况下优先使用;
独立作相反,主要用于多个控制器重用,或重构为[扩展](structure-extensions.md)
You can create actions in two ways: inline actions and standalone actions. An inline action is
defined as a method in the controller class, while a standalone action is a class extending
[[yii\base\Action]] or its child class. Inline actions take less effort to create and are often preferred
......@@ -307,13 +307,13 @@ if you have no intention to reuse these actions. Standalone actions, on the othe
created to be used in different controllers or be redistributed as [extensions](structure-extensions.md).
### 内联作 <a name="inline-actions"></a>
### 内联作 <a name="inline-actions"></a>
### Inline Actions <a name="inline-actions"></a>
内联动作指的是根据我们刚描述的动作方法。
内联操作指的是根据我们刚描述的操作方法。
Inline actions refer to the actions that are defined in terms of action methods as we just described.
动作方法的名字是根据动作ID遵循如下规则衍生:
操作方法的名字是根据操作ID遵循如下规则衍生:
The names of the action methods are derived from action IDs according to the following criteria:
* 将每个单词的第一个字母转为大写;
......@@ -326,31 +326,31 @@ The names of the action methods are derived from action IDs according to the fol
例如`index` 转成 `actionIndex`, `hello-world` 转成 `actionHelloWorld`
For example, `index` becomes `actionIndex`, and `hello-world` becomes `actionHelloWorld`.
> 注意: 动作方法的名字*大小写敏感*,如果方法名称为`ActionIndex`不会认为是动作方法,
所以请求`index`动作会返回一个异常,也要注意动作方法必须是公有的,私有或者受保护的方法不能定义成内联动作。
> 注意: 操作方法的名字*大小写敏感*,如果方法名称为`ActionIndex`不会认为是操作方法,
所以请求`index`操作会返回一个异常,也要注意操作方法必须是公有的,私有或者受保护的方法不能定义成内联操作。
> Note: The names of the action methods are *case-sensitive*. If you have a method named `ActionIndex`,
it will not be considered as an action method, and as a result, the request for the `index` action
will result in an exception. Also note that action methods must be public. A private or protected
method does NOT define an inline action.
因为容易创建,内联动作是最常用的动作,但是如果你计划在不同地方重用相同的动作,
或者你想重新分配一个动作,需要考虑定义它为*独立动作*
因为容易创建,内联操作是最常用的操作,但是如果你计划在不同地方重用相同的操作,
或者你想重新分配一个操作,需要考虑定义它为*独立操作*
Inline actions are the most commonly defined actions because they take little effort to create. However,
if you plan to reuse the same action in different places, or if you want to redistribute an action,
you should consider defining it as a *standalone action*.
### 独立作 <a name="standalone-actions"></a>
### 独立作 <a name="standalone-actions"></a>
### Standalone Actions <a name="standalone-actions"></a>
独立作通过继承[[yii\base\Action]]或它的子类来定义。
例如Yii发布的[[yii\web\ViewAction]]和[[yii\web\ErrorAction]]都是独立作。
独立作通过继承[[yii\base\Action]]或它的子类来定义。
例如Yii发布的[[yii\web\ViewAction]]和[[yii\web\ErrorAction]]都是独立作。
Standalone actions are defined in terms of action classes extending [[yii\base\Action]] or its child classes.
For example, in the Yii releases, there are [[yii\web\ViewAction]] and [[yii\web\ErrorAction]], both of which
are standalone actions.
要使用独立作,需要通过控制器中覆盖[[yii\base\Controller::actions()]]方法在*action map*中申明,如下例所示:
要使用独立作,需要通过控制器中覆盖[[yii\base\Controller::actions()]]方法在*action map*中申明,如下例所示:
To use a standalone action, you should declare it in the *action map* by overriding the
[[yii\base\Controller::actions()]] method in your controller classes like the following:
......@@ -358,10 +358,10 @@ To use a standalone action, you should declare it in the *action map* by overrid
public function actions()
{
return [
// 用类来申明"error"
// 用类来申明"error"
'error' => 'yii\web\ErrorAction',
// 用配置数组申明 "view"
// 用配置数组申明 "view"
'view' => [
'class' => 'yii\web\ViewAction',
'viewPrefix' => '',
......@@ -370,15 +370,15 @@ public function actions()
}
```
如上所示, `actions()` 方法返回键为动作ID、值为对应动作类名或数组[configurations](concept-configurations.md) 的数组。
和内联动作不同,独立动作ID可包含任意字符,只要在`actions()` 方法中申明.
如上所示, `actions()` 方法返回键为操作ID、值为对应操作类名或数组[configurations](concept-configurations.md) 的数组。
和内联操作不同,独立操作ID可包含任意字符,只要在`actions()` 方法中申明.
As you can see, the `actions()` method should return an array whose keys are action IDs and values the corresponding
action class names or [configurations](concept-configurations.md). Unlike inline actions, action IDs for standalone
actions can contain arbitrary characters, as long as they are declared in the `actions()` method.
为创建一个独立作类,需要继承[[yii\base\Action]] 或它的子类,并实现公有的名称为`run()`的方法,
`run()` 方法的角色和作方法类似,例如:
为创建一个独立作类,需要继承[[yii\base\Action]] 或它的子类,并实现公有的名称为`run()`的方法,
`run()` 方法的角色和作方法类似,例如:
To create a standalone action class, you should extend [[yii\base\Action]] or its child class, and implement
a public method named `run()`. The role of the `run()` method is similar to that of an action method. For example,
......@@ -398,10 +398,10 @@ class HelloWorldAction extends Action
```
### 作结果 <a name="action-results"></a>
### 作结果 <a name="action-results"></a>
### Action Results <a name="action-results"></a>
动作方法或独立动作的`run()`方法的返回值非常中药,它表示对应动作结果。
操作方法或独立操作的`run()`方法的返回值非常中药,它表示对应操作结果。
The return value of an action method or the `run()` method of a standalone action is significant. It stands
for the result of the corresponding action.
......@@ -418,7 +418,7 @@ to end users.
* For [[yii\console\Application|console applications]], the return value can also be an integer representing
the [[yii\console\Response::exitStatus|exit status]] of the command execution.
在上面的例子中,动作结果都为字符串,作为响应数据发送给终端用户,下例显示一个动作通过
在上面的例子中,操作结果都为字符串,作为响应数据发送给终端用户,下例显示一个操作通过
返回响应对象(因为[[yii\web\Controller::redirect()|redirect()]]方法返回一个响应对象)可将用户浏览器跳转到新的URL。
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
......@@ -434,19 +434,19 @@ public function actionForward()
```
### 作参数 <a name="action-parameters"></a>
### 作参数 <a name="action-parameters"></a>
### Action Parameters <a name="action-parameters"></a>
内联动作的动作方法和独立动作的 `run()` 方法可以带参数,称为*动作参数*
内联操作的操作方法和独立操作的 `run()` 方法可以带参数,称为*操作参数*
参数值从请求中获取,对于[[yii\web\Application|Web applications]]网页应用,
每个作参数的值从`$_GET`中获得,参数名作为键;
对于[[yii\console\Application|console applications]]控制台应用, 作参数对应命令行参数。
每个作参数的值从`$_GET`中获得,参数名作为键;
对于[[yii\console\Application|console applications]]控制台应用, 作参数对应命令行参数。
The action methods for inline actions and the `run()` methods for standalone actions can take parameters,
called *action parameters*. Their values are obtained from requests. For [[yii\web\Application|Web applications]],
the value of each action parameter is retrieved from `$_GET` using the parameter name as the key;
for [[yii\console\Application|console applications]], they correspond to the command line arguments.
如下例,动作`view` (内联动作) 申明了两个参数 `$id``$version`
如下例,操作`view` (内联操作) 申明了两个参数 `$id``$version`
In the following example, the `view` action (an inline action) has declared two parameters: `$id` and `$version`.
```php
......@@ -463,7 +463,7 @@ class PostController extends Controller
}
```
作参数会被不同的参数填入,如下所示:
作参数会被不同的参数填入,如下所示:
The action parameters will be populated as follows for different requests:
* `http://hostname/index.php?r=post/view&id=123`: `$id` 会填入`'123'``$version` 仍为 null 空因为没有`version`请求参数;
......@@ -481,7 +481,7 @@ The action parameters will be populated as follows for different requests:
* `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']`.
如果想让作参数接收数组值,需要指定$id为`array`,如下所示:
如果想让作参数接收数组值,需要指定$id为`array`,如下所示:
If you want an action parameter to accept array values, you should type-hint it with `array`, like the following:
```php
......@@ -495,21 +495,21 @@ public function actionView(array $id, $version = null)
如果请求为 `http://hostname/index.php?r=post/view&id=123`
参数 `$id` 会获取相同数组值,因为无类型的`'123'`会自动转成数组。
上述例子主要描述网页应用的作参数,对于控制台应用,更多详情请参阅[控制台命令](tutorial-console.md)
上述例子主要描述网页应用的作参数,对于控制台应用,更多详情请参阅[控制台命令](tutorial-console.md)
The above examples mainly show how action parameters work for Web applications. For console applications,
please refer to the [Console Commands](tutorial-console.md) section for more details.
### 默认作 <a name="default-action"></a>
### 默认作 <a name="default-action"></a>
### Default Action <a name="default-action"></a>
每个控制器都有一个由 [[yii\base\Controller::defaultAction]] 属性指定的默认作,
[路由](#ids-routes) 只包含控制器ID,会使用所请求的控制器的默认作。
每个控制器都有一个由 [[yii\base\Controller::defaultAction]] 属性指定的默认作,
[路由](#ids-routes) 只包含控制器ID,会使用所请求的控制器的默认作。
Each controller has a default action specified via the [[yii\base\Controller::defaultAction]] property.
When a [route](#ids-routes) contains the controller ID only, it implies that the default action of
the specified controller is requested.
默认动作默认为 `index`,如果想修改默认动作,只需简单地在控制器类中覆盖这个属性,如下所示:
默认操作默认为 `index`,如果想修改默认操作,只需简单地在控制器类中覆盖这个属性,如下所示:
By default, the default action is set as `index`. If you want to change the default value, simply override
this property in the controller class, like the following:
......@@ -541,10 +541,10 @@ to fulfill the request:
1. 在控制器创建和配置后,[[yii\base\Controller::init()]] 方法会被调用。
1. The [[yii\base\Controller::init()]] method is called after the controller is created and configured.
2. 控制器根据请求动作ID创建一个动作对象:
* 如果动作ID没有指定,会使用[[yii\base\Controller::defaultAction|default action ID]]默认动作ID;
* 如果在[[yii\base\Controller::actions()|action map]]找到动作ID,会创建一个独立动作;
* 如果动作ID对应动作方法,会创建一个内联动作;
2. 控制器根据请求操作ID创建一个操作对象:
* 如果操作ID没有指定,会使用[[yii\base\Controller::defaultAction|default action ID]]默认操作ID;
* 如果在[[yii\base\Controller::actions()|action map]]找到操作ID,会创建一个独立操作;
* 如果操作ID对应操作方法,会创建一个内联操作;
* 否则会抛出[[yii\base\InvalidRouteException]]异常。
2. The controller creates an action object based on the requested action ID:
* If the action ID is not specified, the [[yii\base\Controller::defaultAction|default action ID]] will be used.
......@@ -553,31 +553,31 @@ to fulfill the request:
* If the action ID is found to match an action method, an inline action will be created;
* Otherwise an [[yii\base\InvalidRouteException]] exception will be thrown.
3. 控制器按顺序调用应用主体、模块(如果控制器属于模块)、控制器的 `beforeAction()` 方法;
* 如果任意一个调用返回false,后面未调用的`beforeAction()`会跳过并且作执行会被取消;
* 如果任意一个调用返回false,后面未调用的`beforeAction()`会跳过并且作执行会被取消;
action execution will be cancelled.
* 默认情况下每个 `beforeAction()` 方法会触发一个 `beforeAction` 事件,在事件中你可以追加事件处理作;
* 默认情况下每个 `beforeAction()` 方法会触发一个 `beforeAction` 事件,在事件中你可以追加事件处理作;
3. The controller sequentially calls the `beforeAction()` method of the application, the module (if the controller
belongs to a module) and the controller.
* If one of the calls returns false, the rest of the uncalled `beforeAction()` will be skipped and the
action execution will be cancelled.
* By default, each `beforeAction()` method call will trigger a `beforeAction` event to which you can attach a handler.
4. 控制器执行作:
* 请求数据解析和填入到作参数;
4. 控制器执行作:
* 请求数据解析和填入到作参数;
4. The controller runs the action:
* The action parameters will be analyzed and populated from the request data;
5. 控制器按顺序调用控制器、模块(如果控制器属于模块)、应用主体的 `afterAction()` 方法;
* 默认情况下每个 `afterAction()` 方法会触发一个 `afterAction` 事件,在事件中你可以追加事件处理作;
* 默认情况下每个 `afterAction()` 方法会触发一个 `afterAction` 事件,在事件中你可以追加事件处理作;
5. The controller sequentially calls the `afterAction()` method of the controller, the module (if the controller
belongs to a module) and the application.
* By default, each `afterAction()` method call will trigger an `afterAction` event to which you can attach a handler.
6. 应用主体获取作结果并赋值给[响应](runtime-responses.md).
6. 应用主体获取作结果并赋值给[响应](runtime-responses.md).
6. The application will take the action result and assign it to the [response](runtime-responses.md).
## 最佳实践 <a name="best-practices"></a>
## Best Practices <a name="best-practices"></a>
在设计良好的应用中,控制器很精练,包含的作代码简短;
在设计良好的应用中,控制器很精练,包含的作代码简短;
如果你的控制器很复杂,通常意味着需要重构,转移一些代码到其他类中。
In a well-designed application, controllers are often very thin with each action containing only a few lines of code.
If your controller is rather complicated, it usually indicates that you should refactor it and move some code
......
模型
Models
======
模型是 [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) 模式中的一部分,
是代表业务数据、规则和逻辑的对象。
Models are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture.
They are objects representing business data, rules and logic.
可通过继承 [[yii\base\Model]] 或它的子类定义模型类,基类[[yii\base\Model]]支持许多实用的特性:
You can create model classes by extending [[yii\base\Model]] or its child classes. The base class
[[yii\base\Model]] supports many useful features:
* [属性](#attributes): 代表可像普通类属性或数组一样被访问的业务数据;
* [属性标签](#attribute-labels): 指定属性显示出来的标签;
* [块赋值](#massive-assignment): 支持一步给许多属性赋值;
* [验证规则](#validation-rules): 确保输入数据符合所申明的验证规则;
* [数据导出](#data-exporting): 允许模型数据导出为自定义格式的数组。
* [Attributes](#attributes): represent the business data and can be accessed like normal object properties
or array elements;
* [Attribute labels](#attribute-labels): specify the display labels for attributes;
......@@ -14,28 +23,37 @@ You can create model classes by extending [[yii\base\Model]] or its child classe
* [Validation rules](#validation-rules): ensures input data based on the declared validation rules;
* [Data Exporting](#data-exporting): allows model data to be exported in terms of arrays with customizable formats.
`Model` 类也是更多高级模型如[Active Record 活动记录](db-active-record.md)的基类,
更多关于这些高级模型的详情请参考相关手册。
The `Model` class is also the base class for more advanced models, such as [Active Record](db-active-record.md).
Please refer to the relevant documentation for more details about these advanced models.
> 补充:模型并不强制一定要继承[[yii\base\Model]],但是由于很多组件支持[[yii\base\Model]],最好使用它做为模型基类。
> Info: You are not required to base your model classes on [[yii\base\Model]]. However, because there are many Yii
components built to support [[yii\base\Model]], it is usually the preferable base class for a model.
## 属性 <a name="attributes"></a>
## Attributes <a name="attributes"></a>
模型通过 *属性* 来代表业务数据,每个属性像是模型的公有可访问属性,
[[yii\base\Model::attributes()]] 指定模型所拥有的属性。
Models represent business data in terms of *attributes*. Each attribute is like a publicly accessible property
of a model. The method [[yii\base\Model::attributes()]] specifies what attributes a model class has.
可像访问一个对象属性一样访问模型的属性:
You can access an attribute like accessing a normal object property:
```php
$model = new \app\models\ContactForm;
// "name" is an attribute of ContactForm
// "name" 是ContactForm模型的属性
$model->name = 'example';
echo $model->name;
```
也可像访问数组单元项一样访问属性,这要感谢[[yii\base\Model]]支持 [ArrayAccess 数组访问](http://php.net/manual/en/class.arrayaccess.php)
[ArrayIterator 数组迭代器](http://php.net/manual/en/class.arrayiterator.php):
You can also access attributes like accessing array elements, thanks to the support for
[ArrayAccess](http://php.net/manual/en/class.arrayaccess.php) and [ArrayIterator](http://php.net/manual/en/class.arrayiterator.php)
by [[yii\base\Model]]:
......@@ -43,19 +61,23 @@ by [[yii\base\Model]]:
```php
$model = new \app\models\ContactForm;
// accessing attributes like array elements
// 像访问数组单元项一样访问属性
$model['name'] = 'example';
echo $model['name'];
// iterate attributes
// 迭代器遍历模型
foreach ($model as $name => $value) {
echo "$name: $value\n";
}
```
### 定义属性 <a name="defining-attributes"></a>
### Defining Attributes <a name="defining-attributes"></a>
默认情况下你的模型类直接从[[yii\base\Model]]继承,所有 *non-static public非静态公有* 成员变量都是属性。
例如,下述`ContactForm` 模型类有四个属性`name`, `email`, `subject` and `body`
`ContactForm` 模型用来代表从HTML表单获取的输入数据。
By default, if your model class extends directly from [[yii\base\Model]], all its *non-static public* member
variables are attributes. For example, the `ContactForm` model class below has four attributes: `name`, `email`,
`subject` and `body`. The `ContactForm` model is used to represent the input data received from an HTML form.
......@@ -75,6 +97,9 @@ class ContactForm extends Model
```
另一种方式是可覆盖 [[yii\base\Model::attributes()]] 来定义属性,该方法返回模型的属性名。
例如 [[yii\db\ActiveRecord]] 返回对应数据表列名作为它的属性名,
注意可能需要覆盖魔术方法如`__get()`, `__set()`使属性像普通对象属性被访问。
You may override [[yii\base\Model::attributes()]] to define attributes in a different way. The method should
return the names of the attributes in a model. For example, [[yii\db\ActiveRecord]] does so by returning
the column names of the associated database table as its attribute names. Note that you may also need to
......@@ -82,26 +107,34 @@ override the magic methods such as `__get()`, `__set()` so that the attributes c
normal object properties.
### 属性标签 <a name="attribute-labels"></a>
### Attribute Labels <a name="attribute-labels"></a>
当属性显示或获取输入时,经常要显示属性相关标签,例如假定一个属性名为`firstName`
在某些地方如表单输入或错误信息处,你可能想显示对终端用户来说更友好的 `First Name` 标签。
When displaying values or getting input for attributes, you often need to display some labels associated
with attributes. For example, given an attribute named `firstName`, you may want to display a label `First Name`
which is more user-friendly when displayed to end users in places such as form inputs and error messages.
可以调用 [[yii\base\Model::getAttributeLabel()]] 获取属性的标签,例如:
You can get the label of an attribute by calling [[yii\base\Model::getAttributeLabel()]]. For example,
```php
$model = new \app\models\ContactForm;
// displays "Name"
// 显示为 "Name"
echo $model->getAttributeLabel('name');
```
默认情况下,属性标签通过[[yii\base\Model::generateAttributeLabel()]]方法自动从属性名生成.
它会自动将驼峰式大小写变量名转换为多个首字母大写的单词,例如 `username` 转换为 `Username`
`firstName` 转换为 `First Name`
By default, attribute labels are automatically generated from attribute names. The generation is done by
the method [[yii\base\Model::generateAttributeLabel()]]. It will turn camel-case variable names into
multiple words with the first letter in each word in upper case. For example, `username` becomes `Username`,
and `firstName` becomes `First Name`.
如果你不想用自动生成的标签,可以覆盖 [[yii\base\Model::attributeLabels()]] 方法明确指定属性标签,例如:
If you do not want to use automatically generated labels, you may override [[yii\base\Model::attributeLabels()]]
to explicitly declare attribute labels. For example,
......@@ -129,6 +162,8 @@ class ContactForm extends Model
}
```
应用支持多语言的情况下,可翻译属性标签,
可在 [[yii\base\Model::attributeLabels()|attributeLabels()]] 方法中定义,如下所示:
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:
......@@ -144,33 +179,40 @@ public function attributeLabels()
}
```
甚至可以根据条件定义标签,例如通过使用模型的 [scenario场景](#scenarios)
可对相同的属性返回不同的标签。
You may even conditionally define attribute labels. For example, based on the [scenario](#scenarios) the model
is being used in, you may return different labels for the same attribute.
> 补充:属性标签是 [视图](structure-views.md)一部分,但是在模型中申明标签通常非常方便,并可行程非常简洁可重用代码。
> Info: Strictly speaking, attribute labels are part of [views](structure-views.md). But declaring labels
in models is often very convenient and can result in very clean and reusable code.
## 场景 <a name="scenarios"></a>
## Scenarios <a name="scenarios"></a>
模型可能在多个 *场景* 下使用,例如 `User` 模块可能会在收集用户登录输入,也可能会在用户注册时使用。
在不同的场景下,模型可能会使用不同的业务规则和逻辑,例如 `email` 属性在注册时强制要求有,但在登陆时不需要。
A model may be used in different *scenarios*. For example, a `User` model may be used to collect user login inputs,
but it may also be used for the user registration purpose. In different scenarios, a model may use different
business rules and logic. For example, the `email` attribute may be required during user registration,
but not so during user login.
A model uses the [[yii\base\Model::scenario]] property to keep track of the scenario it is being used in.
By default, a model supports only a single scenario named `default`. The following code shows two ways of
setting the scenario of a model:
模型使用 [[yii\base\Model::scenario]] 属性保持使用场景的跟踪,
默认情况下,模型支持一个名为 `default` 的场景,如下展示两种设置场景的方法:
```php
// scenario is set as a property
// 场景作为属性来设置
$model = new User;
$model->scenario = 'login';
// scenario is set through configuration
// 场景通过构造初始化配置来设置
$model = new User(['scenario' => 'login']);
```
默认情况下,模型支持的场景由模型中申明的 [验证规则](#validation-rules) 来决定,
但你可以通过覆盖[[yii\base\Model::scenarios()]]方法来自定义行为,如下所示:
By default, the scenarios supported by a model are determined by the [validation rules](#validation-rules) declared
in the model. However, you can customize this behavior by overriding the [[yii\base\Model::scenarios()]] method,
like the following:
......@@ -192,14 +234,22 @@ class User extends ActiveRecord
}
```
> 补充:在上述和下述的例子中,模型类都是继承[[yii\db\ActiveRecord]],
因为多场景的使用通常发生在[Active Record](db-active-record.md) 类中.
> Info: In the above and following examples, the model classes are extending from [[yii\db\ActiveRecord]]
because the usage of multiple scenarios usually happens to [Active Record](db-active-record.md) classes.
`scenarios()` 方法返回一个数组,数组的键为场景名,值为对应的 *active attributes活动属性*
活动属性可被 [块赋值](#massive-assignment) 并遵循[验证规则](#validation-rules)
在上述例子中,`username``password``login`场景中启用,在 `register` 场景中,
除了 `username` and `password``email` 也被启用。
The `scenarios()` method returns an array whose keys are the scenario names and values the corresponding
*active attributes*. An active attribute can be [massively assigned](#massive-assignment) and is subject
to [validation](#validation-rules). In the above example, the `username` and `password` attributes are active
in the `login` scenario; while in the `register` scenario, `email` is also active besides `username` and `password`.
`scenarios()` 方法默认实现会返回所有[[yii\base\Model::rules()]]方法申明的验证规则中的场景,
当覆盖`scenarios()`时,如果你想在默认场景外使用新场景,可以编写类似如下代码:
The default implementation of `scenarios()` will return all scenarios found in the validation rule declaration
method [[yii\base\Model::rules()]]. When overriding `scenarios()`, if you want to introduce new scenarios
in addition to the default ones, you may write code like the following:
......@@ -221,19 +271,28 @@ class User extends ActiveRecord
}
```
场景特性主要在[验证](#validation-rules)[属性块赋值](#massive-assignment) 中使用。
你也可以用于其他目的,例如可基于不同的场景定义不同的 [属性标签](#attribute-labels)
The scenario feature is primarily used by [validation](#validation-rules) and [massive attribute assignment](#massive-assignment).
You can, however, use it for other purposes. For example, you may declare [attribute labels](#attribute-labels)
differently based on the current scenario.
## 验证规则 <a name="validation-rules"></a>
## Validation Rules <a name="validation-rules"></a>
当模型接收到终端用户输入的数据,数据应当满足某种规则(称为 *验证规则*, 也称为 *业务规则*)。
例如假定`ContactForm`模型,你可能想确保所有属性不为空且 `email` 属性包含一个有效的邮箱地址,
如果某个属性的值不满足对应的业务规则,相应的错误信息应显示,以帮助用户修正错误。
When the data for a model is received from end users, it should be validated to make sure it satisfies
certain rules (called *validation rules*, also known as *business rules*). For example, given a `ContactForm` model,
you may want to make sure all attributes are not empty and the `email` attribute contains a valid email address.
If the values for some attributes do not satisfy the corresponding business rules, appropriate error messages
should be displayed to help the user to fix the errors.
可调用 [[yii\base\Model::validate()]] 来验证接收到的数据,
该方法使用[[yii\base\Model::rules()]]申明的验证规则来验证每个相关属性,
如果没有找到错误,会返回 true,否则它会将错误保存在 [[yii\base\Model::errors]] 属性中并返回false,例如:
You may call [[yii\base\Model::validate()]] to validate the received data. The method will use
the validation rules declared in [[yii\base\Model::rules()]] to validate every relevant attribute. If no error
is found, it will return true. Otherwise, it will keep the errors in the [[yii\base\Model::errors]] property
......@@ -242,18 +301,20 @@ and return false. For example,
```php
$model = new \app\models\ContactForm;
// populate model attributes with user inputs
// 用户输入数据赋值到模型属性
$model->attributes = \Yii::$app->request->post('ContactForm');
if ($model->validate()) {
// all inputs are valid
// 所有输入数据都有效 all inputs are valid
} else {
// validation failed: $errors is an array containing error messages
// 验证失败:$errors 是一个包含错误信息的数组
$errors = $model->errors;
}
```
通过覆盖 [[yii\base\Model::rules()]] 方法指定模型属性应该满足的规则来申明模型相关验证规则。
下述例子显示`ContactForm`模型申明的验证规则:
To declare validation rules associated with a model, override the [[yii\base\Model::rules()]] method by returning
the rules that the model attributes should satisfy. The following example shows the validation rules declared
for the `ContactForm` model:
......@@ -262,19 +323,22 @@ for the `ContactForm` model:
public function rules()
{
return [
// the name, email, subject and body attributes are required
// name, email, subject 和 body 属性必须有值
[['name', 'email', 'subject', 'body'], 'required'],
// the email attribute should be a valid email address
// email 属性必须是一个有效的电子邮箱地址
['email', 'email'],
];
}
```
一条规则可用来验证一个或多个属性,一个属性可对应一条或多条规则。
更多关于如何申明验证规则的详情请参考 [验证输入](input-validation.md) 一节.
A rule can be used to validate one or multiple attributes, and an attribute may be validated by one or multiple rules.
Please refer to the [Validating Input](input-validation.md) section for more details on how to declare
validation rules.
有时你想一条规则只在某个 [场景](#scenarios) 下应用,为此你可以指定规则的 `on` 属性,如下所示:
Sometimes, you may want a rule to be applied only in certain [scenarios](#scenarios). To do so, you can
specify the `on` property of a rule, like the following:
......@@ -282,24 +346,32 @@ specify the `on` property of a rule, like the following:
public function rules()
{
return [
// username, email and password are all required in "register" scenario
// 在"register" 场景下 username, email 和 password 必须有值
[['username', 'email', 'password'], 'required', 'on' => 'register'],
// username and password are required in "login" scenario
// 在 "login" 场景下 username 和 password 必须有值
[['username', 'password'], 'required', 'on' => 'login'],
];
}
```
如果没有指定 `on` 属性,规则会在所有场景下应用, 在当前[[yii\base\Model::scenario|scenario]]
下应用的规则称之为 *active rule活动规则*
If you do not specify the `on` property, the rule would be applied in all scenarios. A rule is called
an *active rule* if it can be applied in the current [[yii\base\Model::scenario|scenario]].
一个属性只会属于`scenarios()`中定义的活动属性且在`rules()`申明对应一条或多条活动规则的情况下被验证。
An attribute will be validated if and only if it is an active attribute declared in `scenarios()` and
is associated with one or multiple active rules declared in `rules()`.
## 块赋值 <a name="massive-assignment"></a>
## Massive Assignment <a name="massive-assignment"></a>
块赋值只用一行代码将用户所有输入填充到一个模型,非常方便,
它直接将输入数据对应填充到 [[yii\base\Model::attributes]] 属性。
以下两段代码效果是相同的,都是将终端用户输入的表单数据赋值到 `ContactForm` 模型的属性,
明显地前一段块赋值的代码比后一段代码简洁且不易出错。
Massive assignment is a convenient way of populating a model with user inputs using a single line of code.
It populates the attributes of a model by assigning the input data directly to the [[yii\base\Model::attributes]]
property. The following two pieces of code are equivalent, both trying to assign the form data submitted by end users
......@@ -321,8 +393,12 @@ $model->body = isset($data['body']) ? $data['body'] : null;
```
### 安全属性 <a name="safe-attributes"></a>
### Safe Attributes <a name="safe-attributes"></a>
块赋值只应用在模型当前[[yii\base\Model::scenario|scenario]]场景[[yii\base\Model::scenarios()]]方法
列出的称之为 *安全属性* 的属性上,例如,如果`User`模型申明以下场景,
当当前场景为`login`时候,只有`username` and `password` 可被块赋值,其他属性不会被赋值。
Massive assignment only applies to the so-called *safe attributes* which are the attributes listed in
[[yii\base\Model::scenarios()]] for the current [[yii\base\Model::scenario|scenario]] of a model.
For example, if the `User` model has the following scenario declaration, then when the current scenario
......@@ -339,15 +415,22 @@ public function scenarios()
}
```
> 补充: 块赋值只应用在安全属性上,因为你想控制哪些属性会被终端用户输入数据所修改,
例如,如果 `User` 模型有一个`permission`属性对应用户的权限,
你可能只想让这个属性在后台界面被管理员修改。
> Info: The reason that massive assignment only applies to safe attributes is because you want to
control which attributes can be modified by end user data. For example, if the `User` model
has a `permission` attribute which determines the permission assigned to the user, you would
like this attribute to be modifiable by administrators through a backend interface only.
由于默认[[yii\base\Model::scenarios()]]的实现会返回[[yii\base\Model::rules()]]所有属性和数据,
如果不覆盖这个方法,表示所有只要出现在活动验证规则中的属性都是安全的。
Because the default implementation of [[yii\base\Model::scenarios()]] will return all scenarios and attributes
found in [[yii\base\Model::rules()]], if you do not override this method, it means an attribute is safe as long
as it appears in one of the active validation rules.
为此,提供一个特别的别名为 `safe` 的验证器来申明哪些属性是安全的不需要被验证,
如下示例的规则申明 `title``description` 都为安全属性。
For this reason, a special validator aliased `safe` is provided so that you can declare an attribute
to be safe without actually validating it. For example, the following rules declare that both `title`
and `description` are safe attributes.
......@@ -362,8 +445,12 @@ public function rules()
```
### 非安全属性 <a name="unsafe-attributes"></a>
### Unsafe Attributes <a name="unsafe-attributes"></a>
如上所述,[[yii\base\Model::scenarios()]] 方法提供两个用处:定义哪些属性应被验证,定义哪些属性安全。
在某些情况下,你可能想验证一个属性但不想让他是安全的,可在`scenarios()`方法中属性名加一个惊叹号 `!`
例如像如下的`secret`属性。
As described above, the [[yii\base\Model::scenarios()]] method serves for two purposes: determining which attributes
should be validated, and determining which attributes are safe. In some rare cases, you may want to validate
an attribute but do not want to mark it safe. You can do so by prefixing an exclamation mark `!` to the attribute
......@@ -378,6 +465,8 @@ public function scenarios()
}
```
当模型在 `login` 场景下,三个属性都会被验证,但只有 `username``password` 属性会被块赋值,
要对`secret`属性赋值,必须像如下例子明确对它赋值。
When the model is in the `login` scenario, all three attributes will be validated. However, only the `username`
and `password` attributes can be massively assigned. To assign an input value to the `secret` attribute, you
have to do it explicitly as follows,
......@@ -387,14 +476,19 @@ $model->secret = $secret;
```
## 数据导出 <a name="data-exporting"></a>
## Data Exporting <a name="data-exporting"></a>
模型通常要导出成不同格式,例如,你可能想将模型的一个集合转成JSON或Excel格式,
导出过程可分解为两个步骤,第一步,模型转换成数组;第二步,数组转换成所需要的格式。
你只需要关注第一步,因为第二步可被通用的数据转换器如[[yii\web\JsonResponseFormatter]]来完成。
Models often need to be exported in different formats. For example, you may want to convert a collection of
models into JSON or Excel format. The exporting process can be broken down into two independent steps.
In the first step, models are converted into arrays; in the second step, the arrays are converted into
target formats. You may just focus on the first step, because the second step can be achieved by generic
data formatters, such as [[yii\web\JsonResponseFormatter]].
将模型转换为数组最简单的方式是使用 [[yii\base\Model::attributes]] 属性,例如:
The simplest way of converting a model into an array is to use the [[yii\base\Model::attributes]] property.
For example,
......@@ -403,9 +497,14 @@ $post = \app\models\Post::findOne(100);
$array = $post->attributes;
```
[[yii\base\Model::attributes]] 属性会返回 *所有* [[yii\base\Model::attributes()]] 申明的属性的值。
By default, the [[yii\base\Model::attributes]] property will return the values of *all* attributes
declared in [[yii\base\Model::attributes()]].
更灵活和强大的将模型转换为数组的方式是使用 [[yii\base\Model::toArray()]] 方法,
它的行为默认和 [[yii\base\Model::attributes]] 相同,
但是它允许你选择哪些称之为*字段*的数据项放入到结果数组中并同时被格式化。
实际上,它是导出模型到 RESTful 网页服务开发的默认方法,详情请参阅[响应格式](rest-response-formatting.md).
A more flexible and powerful way of converting a model into an array is to use the [[yii\base\Model::toArray()]]
method. Its default behavior is the same as that of [[yii\base\Model::attributes]]. However, it allows you
to choose which data items, called *fields*, to be put in the resulting array and how they should be formatted.
......@@ -413,11 +512,18 @@ In fact, it is the default way of exporting models in RESTful Web service develo
the [Response Formatting](rest-response-formatting.md).
### 字段 <a name="fields"></a>
### Fields <a name="fields"></a>
字段是模型通过调用[[yii\base\Model::toArray()]]生成的数组的单元名。
A field is simply a named element in the array that is obtained by calling the [[yii\base\Model::toArray()]] method
of a model.
默认情况下,字段名对应属性名,但是你可以通过覆盖
[[yii\base\Model::fields()|fields()]] 和/或 [[yii\base\Model::extraFields()|extraFields()]] 方法来改变这种行为,
两个方法都返回一个字段定义列表,`fields()` 方法定义的字段是默认字段,表示`toArray()`方法默认会返回这些字段。
`extraFields()`方法定义额外可用字段,通过`toArray()`方法指定`$expand`参数来返回这些额外可用字段。
例如如下代码会返回`fields()`方法定义的所有字段和`extraFields()`方法定义的`prettyName` and `fullAddress`字段。
By default, field names are equivalent to attribute names. However, you can change this behavior by overriding
the [[yii\base\Model::fields()|fields()]] and/or [[yii\base\Model::extraFields()|extraFields()]] methods. Both methods
should return a list of field definitions. The fields defined by `fields()` are default fields, meaning that
......@@ -430,6 +536,9 @@ if they are defined in `extraFields()`.
$array = $model->toArray([], ['prettyName', 'fullAddress']);
```
可通过覆盖 `fields()` 来增加、删除、重命名和重定义字段,`fields()` 方法返回值应为数组,
数组的键为字段名,数组的值为对应的可为属性名或匿名函数返回的字段定义对应的值。
特使情况下,如果字段名和属性定义名相同,可以省略数组键,例如:
You can override `fields()` to add, remove, rename or redefine fields. The return value of `fields()`
should be an array. The array keys are the field names, and the array values are the corresponding
field definitions which can be either property/attribute names or anonymous functions returning the
......@@ -437,51 +546,62 @@ corresponding field values. In the special case when a field name is the same as
name, you can omit the array key. For example,
```php
// explicitly list every field, best used when you want to make sure the changes
// in your DB table or model attributes do not cause your field changes (to keep API backward compatibility).
// 明确列出每个字段,特别用于你想确保数据表或模型属性改变不会导致你的字段改变(保证后端的API兼容).
public function fields()
{
return [
// field name is the same as the attribute name
// 字段名和属性名相同
'id',
// field name is "email", the corresponding attribute name is "email_address"
// 字段名为 "email",对应属性名为 "email_address"
'email' => 'email_address',
// field name is "name", its value is defined by a PHP callback
// 字段名为 "name", 值通过PHP代码返回
'name' => function () {
return $this->first_name . ' ' . $this->last_name;
},
];
}
// filter out some fields, best used when you want to inherit the parent implementation
// and blacklist some sensitive fields.
// 过滤掉一些字段,特别用于你想继承父类实现并不想用一些敏感字段
public function fields()
{
$fields = parent::fields();
// remove fields that contain sensitive information
// 去掉一些包含敏感信息的字段
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);
return $fields;
}
```
> 警告:由于模型的所有属性会被包含在导出数组,最好检查数据确保没包含敏感数据,
> 如果有敏感数据,应覆盖 `fields()` 方法过滤掉,在上述列子中,我们选择过滤掉
> `auth_key`, `password_hash` and `password_reset_token`。
> Warning: Because by default all attributes of a model will be included in the exported array, you should
> examine your data to make sure they do not contain sensitive information. If there is such information,
> you should override `fields()` to filter them out. In the above example, we choose
> to filter out `auth_key`, `password_hash` and `password_reset_token`.
## 最佳实践 <a name="best-practices"></a>
## Best Practices <a name="best-practices"></a>
模型是代表业务数据、规则和逻辑的中心地方,通常在很多地方重用,
在一个设计良好的应用中,模型通常比[控制器](structure-controllers.md)代码多。
Models are the central places to represent business data, rules and logic. They often need to be reused
in different places. In a well-designed application, models are usually much fatter than
[controllers](structure-controllers.md).
归纳起来,模型
In summary, models
* 可包含属性来展示业务数据;
* 可包含验证规则确保数据有效和完整;
* 可包含方法实现业务逻辑;
* 不应直接访问请求,session和其他环境数据,这些数据应该由[控制器](structure-controllers.md)传入到模型;
* 应避免嵌入HTML或其他展示代码,这些代码最好在 [视图](structure-views.md)中处理;
* 单个模型中避免太多的 [场景](#scenarios).
* may contain attributes to represent business data;
* may contain validation rules to ensure the data validity and integrity;
* may contain methods implementing business logic;
......@@ -490,12 +610,20 @@ In summary, models
* should avoid embedding HTML or other presentational code - this is better done in [views](structure-views.md);
* avoid having too many [scenarios](#scenarios) in a single model.
在开发大型复杂系统时应经常考虑最后一条建议,
在这些系统中,模型会很大并在很多地方使用,因此会包含需要规则集和业务逻辑,
最后维护这些模型代码成为一个噩梦,因为一个简单修改会影响好多地方,
为确保模型好维护,最好使用以下策略:
You may usually consider the last recommendation above when you are developing large complex systems.
In these systems, models could be very fat because they are used in many places and may thus contain many sets
of rules and business logic. This often ends up in a nightmare in maintaining the model code
because a single touch of the code could affect several different places. To make the mode code more maintainable,
you may take the following strategy:
* 定义可被多个 [应用主体](structure-applications.md)[模块](structure-modules.md) 共享的模型基类集合。
这些模型类应包含通用的最小规则集合和逻辑。
* 在每个使用模型的 [应用主体](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
are common among all their usages.
......@@ -503,6 +631,10 @@ you may take the following strategy:
define a concrete model class by extending from the corresponding base model class. The concrete model classes
should contain rules and logic that are specific for that application or module.
例如,在[高级应用模板](tutorial-advanced-app.md),你可以定义一个模型基类`common\models\Post`
然后在前台应用中,定义并使用一个继承`common\models\Post`的具体模型类`frontend\models\Post`
在后台应用中可以类似地定义`backend\models\Post`
通过这种策略,你清楚`frontend\models\Post`只对应前台应用,如果你修改它,就无需担忧修改会影响后台应用。
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,
......
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