diff --git a/docs/guide-zh-CN/structure-controllers.md b/docs/guide-zh-CN/structure-controllers.md index a76537d0c4..703f246b5a 100644 --- a/docs/guide-zh-CN/structure-controllers.md +++ b/docs/guide-zh-CN/structure-controllers.md @@ -1,27 +1,17 @@ 控制器 -Controllers =========== 控制器是 [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) 模式中的一部分, 是继承[[yii\base\Controller]]类的对象,负责处理请求和生成响应。 具体来说,控制器从[应用主体](structure-applications.md)接管控制后会分析请求数据并传送到[模型](structure-models.md), 传送模型结果到[视图](structure-views.md),最后生成输出响应信息。 -Controllers are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture. -They are objects of classes extending from [[yii\base\Controller]] and are responsible for processing requests and -generating responses. In particular, after taking over the control from [applications](structure-applications.md), -controllers will analyze incoming request data, pass them to [models](structure-models.md), inject model results -into [views](structure-views.md), and finally generate outgoing responses. ## 操作 -## Actions 控制器由 *操作* 组成,它是执行终端用户请求的最基础的单元,一个控制器可有一个或多个操作。 -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`: -The following example shows a `post` controller with two actions: `view` and `create`: ```php namespace app\controllers; @@ -62,41 +52,26 @@ class PostController extends Controller 在操作 `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`视图。 -In the `create` action (defined by the `actionCreate()` method), the code is similar. It first tries to populate -the [model](structure-models.md) using the request data and save the model. If both succeed it will redirect -the browser to the `view` action with the ID of the newly created model. Otherwise it will display -the `create` view through which users can provide the needed input. ## 路由 -## Routes -终端用户通过所谓的*路由*寻找到操作,路由是包含以下部分的字符串: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: 同控制器下唯一标识操作的字符串。 -* a module ID: this exists only if the controller belongs to a non-application [module](structure-modules.md); -* a controller ID: a string that uniquely identifies the controller among all controllers within the same application - (or the same module if the controller belongs to a module); -* an action ID: a string that uniquely identifies the action among all actions within the same controller. 路由使用如下格式: -Routes take the following format: ``` ControllerID/ActionID ``` 如果属于模块下的控制器,使用如下格式: -or the following format if the controller belongs to a module: ```php ModuleID/ControllerID/ActionID @@ -104,20 +79,13 @@ ModuleID/ControllerID/ActionID 如果用户的请求地址为 `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 -the [Routing](runtime-routing.md) section. ## 创建控制器 -## Creating Controllers 在[[yii\web\Application|Web applications]]网页应用中,控制器应继承[[yii\web\Controller]] 或它的子类。 同理在[[yii\console\Application|console applications]]控制台应用中,控制器继承[[yii\console\Controller]] 或它的子类。 如下代码定义一个 `site` 控制器: -In [[yii\web\Application|Web applications]], controllers should extend from [[yii\web\Controller]] or its -child classes. Similarly in [[yii\console\Application|console applications]], controllers should extend from -[[yii\console\Controller]] or its child classes. The following code defines a `site` controller: ```php namespace app\controllers; @@ -131,89 +99,50 @@ class SiteController extends Controller ### 控制器ID -### Controller IDs 通常情况下,控制器用来处理请求有关的资源类型,因此控制器ID通常为和资源有关的名词。 例如使用`article`作为处理文章的控制器ID。 -For example, you may use `article` as the ID of a controller that handles article data. -Usually, a controller is designed to handle the requests regarding a particular type of resource. -For this reason, controller IDs are often nouns referring to the types of the resources that they are handling. -For example, you may use `article` as the ID of a controller that handles article data. 控制器ID应仅包含英文小写字母、数字、下划线、中横杠和正斜杠, 例如 `article` 和 `post-comment` 是真是的控制器ID,`article?`, `PostComment`, `admin\post`不是控制器ID。 -By default, controller IDs should contain these characters only: English letters in lower case, digits, -underscores, dashes and forward slashes. For example, `article` and `post-comment` are both valid controller IDs, -while `article?`, `PostComment`, `admin\post` are not. 控制器Id可包含子目录前缀,例如 `admin/article` 代表 [[yii\base\Application::controllerNamespace|controller namespace]]控制器命名空间下 `admin`子目录中 `article` 控制器。 子目录前缀可为英文大小写字母、数字、下划线、正斜杠,其中正斜杠用来区分多级子目录(如 `panels/admin`)。 -A controller ID may also contain a subdirectory prefix. For example, `admin/article` stands for an `article` controller -in the `admin` subdirectory under the [[yii\base\Application::controllerNamespace|controller namespace]]. -Valid characters for subdirectory prefixes include: English letters in lower and upper cases, digits, underscores and -forward slashes, where forward slashes are used as separators for multi-level subdirectories (e.g. `panels/admin`). ### 控制器类命名 -### Controller Class Naming 控制器ID遵循以下规则衍生控制器类名: -Controller class names can be derived from controller IDs according to the following rules: * 将用正斜杠区分的每个单词第一个字母转为大写。注意如果控制器ID包含正斜杠,只将最后的正斜杠后的部分第一个字母转为大写; * 去掉中横杠,将正斜杠替换为反斜杠; * 增加`Controller`后缀; * 在前面增加[[yii\base\Application::controllerNamespace|controller namespace]]控制器命名空间. -* And prepend the [[yii\base\Application::controllerNamespace|controller namespace]]. -* Turn the first letter in each word separated by dashes into upper case. Note that if the controller ID - contains slashes, this rule only applies to the part after the last slash in the ID. -* Remove dashes and replace any forward slashes with backward slashes. -* Append the suffix `Controller`. -* And prepend the [[yii\base\Application::controllerNamespace|controller namespace]]. 下面为一些示例,假设[[yii\base\Application::controllerNamespace|controller namespace]]控制器命名空间为 `app\controllers`: -The followings are some examples, assuming the [[yii\base\Application::controllerNamespace|controller namespace]] -takes the default value `app\controllers`: * `article` 对应 `app\controllers\ArticleController`; * `post-comment` 对应 `app\controllers\PostCommentController`; * `admin/post-comment` 对应 `app\controllers\admin\PostCommentController`; * `adminPanels/post-comment` 对应 `app\controllers\adminPanels\PostCommentController`. -* `article` derives `app\controllers\ArticleController`; -* `post-comment` derives `app\controllers\PostCommentController`; -* `admin/post-comment` derives `app\controllers\admin\PostCommentController`; -* `adminPanels/post-comment` derives `app\controllers\adminPanels\PostCommentController`. 控制器类必须能被 [自动加载](concept-autoloading.md),所以在上面的例子中, 控制器`article` 类应在 [别名](concept-aliases.md) 为`@app/controllers/ArticleController.php`的文件中定义, 控制器`admin/post2-comment`应在`@app/controllers/admin/Post2CommentController.php`文件中。 -Controller classes must be [autoloadable](concept-autoloading.md). For this reason, in the above examples, -the `article` controller class should be saved in the file whose [alias](concept-aliases.md) -is `@app/controllers/ArticleController.php`; while the `admin/post2-comment` controller should be -in `@app/controllers/admin/Post2CommentController.php`. > 补充: 最后一个示例 `admin/post2-comment` 表示你可以将控制器放在 [[yii\base\Application::controllerNamespace|controller namespace]]控制器命名空间下的子目录中, 在你不想用 [模块](structure-modules.md) 的情况下给控制器分类,这种方式很有用。 -> Info: The last example `admin/post2-comment` shows how you can put a controller under a sub-directory - of the [[yii\base\Application::controllerNamespace|controller namespace]]. This is useful when you want - to organize your controllers into several categories and you do not want to use [modules](structure-modules.md). ### 控制器部署 -### Controller Map 可通过配置 [[yii\base\Application::controllerMap|controller map]] 来强制上述的控制器ID和类名对应, 通常用在使用第三方不能掌控类名的控制器上。 -You can configure [[yii\base\Application::controllerMap|controller map]] to overcome the constraints -of the controller IDs and class names described above. This is mainly useful when you are using some -third-party controllers which you do not control over their class names. 配置 [应用配置](structure-applications.md#application-configurations) 中的[application configuration](structure-applications.md#application-configurations),如下所示: -You may configure [[yii\base\Application::controllerMap|controller map]] in the -[application configuration](structure-applications.md#application-configurations) like the following: ```php [ @@ -232,20 +161,14 @@ You may configure [[yii\base\Application::controllerMap|controller map]] in the ### 默认控制器 -### Default Controller 每个应用有一个由[[yii\base\Application::defaultRoute]]属性指定的默认控制器; 当请求没有指定 [路由](#ids-routes),该属性值作为路由使用。 对于[[yii\web\Application|Web applications]]网页应用,它的值为 `'site'`, 对于 [[yii\console\Application|console applications]]控制台应用,它的值为 `help`, 所以URL为 `http://hostname/index.php` 表示由 `site` 控制器来处理。 -Each application has a default controller specified via the [[yii\base\Application::defaultRoute]] property. -When a request does not specify a [route](#ids-routes), the route specified by this property will be used. -For [[yii\web\Application|Web applications]], its value is `'site'`, while for [[yii\console\Application|console applications]], -it is `help`. Therefore, if a URL is `http://hostname/index.php`, it means the `site` controller will handle the request. 可以在 [应用配置](structure-applications.md#application-configurations) 中修改默认控制器,如下所示: -You may change the default controller with the following [application configuration](structure-applications.md#application-configurations): ```php [ @@ -255,13 +178,9 @@ You may change the default controller with the following [application configurat ## 创建操作 -## Creating Actions 创建操作可简单地在控制器类中定义所谓的 *操作方法* 来完成,操作方法必须是以`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`: ```php namespace app\controllers; @@ -284,75 +203,44 @@ class SiteController extends Controller ### 操作ID -### Action IDs -操作通常是用来执行资源的特定操作,因此,操作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通常为动词,如`view`, `update`等。 操作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)。 -You can create actions in two ways: inline actions and standalone actions. An inline action is -defined as a method in the controller class, while a standalone action is a class extending -[[yii\base\Action]] or its child class. Inline actions take less effort to create and are often preferred -if you have no intention to reuse these actions. Standalone actions, on the other hand, are mainly -created to be used in different controllers or be redistributed as [extensions](structure-extensions.md). ### 内联操作 -### Inline Actions 内联操作指的是根据我们刚描述的操作方法。 -Inline actions refer to the actions that are defined in terms of action methods as we just described. 操作方法的名字是根据操作ID遵循如下规则衍生: -The names of the action methods are derived from action IDs according to the following criteria: * 将每个单词的第一个字母转为大写; * 去掉中横杠; * 增加`action`前缀. -* Turn the first letter in each word of the action ID into upper case; -* Remove dashes; -* Prepend the prefix `action`. 例如`index` 转成 `actionIndex`, `hello-world` 转成 `actionHelloWorld`。 -For example, `index` becomes `actionIndex`, and `hello-world` becomes `actionHelloWorld`. > 注意: 操作方法的名字*大小写敏感*,如果方法名称为`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*. ### 独立操作 -### Standalone Actions 独立操作通过继承[[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*中申明,如下例所示: -To use a standalone action, you should declare it in the *action map* by overriding the -[[yii\base\Controller::actions()]] method in your controller classes like the following: ```php public function actions() @@ -372,15 +260,10 @@ public function 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()` 方法的角色和操作方法类似,例如: -To create a standalone action class, you should extend [[yii\base\Action]] or its child class, and implement -a public method named `run()`. The role of the `run()` method is similar to that of an action method. For example, ```php -### Action Results 操作方法或独立操作的`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. 返回值可为 [响应](runtime-responses.md) 对象,作为响应发送给终端用户。 -The return value can be a [response](runtime-responses.md) object which will be sent to as the response -to end users. * 对于[[yii\web\Application|Web applications]]网页应用,返回值可为任意数据, 它赋值给[[yii\web\Response::data]], 最终转换为字符串来展示响应内容。 * 对于[[yii\console\Application|console applications]]控制台应用,返回值可为整数, 表示命令行下执行的 [[yii\console\Response::exitStatus|exit status]] 退出状态。 -* For [[yii\web\Application|Web applications]], the return value can also be some arbitrary data which will - be assigned to [[yii\web\Response::data]] and be further converted into a string representing the response body. -* For [[yii\console\Application|console applications]], the return value can also be an integer representing - the [[yii\console\Response::exitStatus|exit status]] of the command execution. 在上面的例子中,操作结果都为字符串,作为响应数据发送给终端用户,下例显示一个操作通过 返回响应对象(因为[[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 -by returning a response object (because the [[yii\web\Controller::redirect()|redirect()]] method returns -a response object): ```php public function actionForward() @@ -435,19 +305,13 @@ public function actionForward() ### 操作参数 -### Action Parameters 内联操作的操作方法和独立操作的 `run()` 方法可以带参数,称为*操作参数*。 参数值从请求中获取,对于[[yii\web\Application|Web 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`。 -In the following example, the `view` action (an inline action) has declared two parameters: `$id` and `$version`. ```php namespace app\controllers; @@ -464,7 +328,6 @@ 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`请求参数; * `http://hostname/index.php?r=post/view&id=123&version=2`: $id` 和 `$version` 分别填入 `'123'` 和 `'2'`; @@ -472,17 +335,8 @@ The action parameters will be populated as follows for different requests: 因为请求没有提供参数给必须赋值参数`$id`; * `http://hostname/index.php?r=post/view&id[]=123`: 会抛出[[yii\web\BadRequestHttpException]] 异常 因为`$id` 参数收到数字值 `['123']`而不是字符串. -* `http://hostname/index.php?r=post/view&id=123`: the `$id` parameter will be filled with the value - `'123'`, while `$version` is still null because there is no `version` query parameter. -* `http://hostname/index.php?r=post/view&id=123&version=2`: the `$id` and `$version` parameters will - be filled with `'123'` and `'2'`, respectively. -* `http://hostname/index.php?r=post/view`: a [[yii\web\BadRequestHttpException]] exception will be thrown - because the required `$id` parameter is not provided in the request. -* `http://hostname/index.php?r=post/view&id[]=123`: a [[yii\web\BadRequestHttpException]] exception will be thrown - because `$id` parameter is receiving an unexpected array value `['123']`. 如果想让操作参数接收数组值,需要指定$id为`array`,如下所示: -If you want an action parameter to accept array values, you should type-hint it with `array`, like the following: ```php public function actionView(array $id, $version = null) @@ -496,22 +350,14 @@ public function actionView(array $id, $version = null) 参数 `$id` 会获取相同数组值,因为无类型的`'123'`会自动转成数组。 上述例子主要描述网页应用的操作参数,对于控制台应用,更多详情请参阅[控制台命令](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. ### 默认操作 -### Default Action 每个控制器都有一个由 [[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`,如果想修改默认操作,只需简单地在控制器类中覆盖这个属性,如下所示: -By default, the default action is set as `index`. If you want to change the default value, simply override -this property in the controller class, like the following: ```php namespace app\controllers; @@ -531,68 +377,36 @@ class SiteController extends Controller ## 控制器生命周期 -## Controller Lifecycle 处理一个请求时,[应用主体](structure-applications.md) 会根据请求[路由](#routes)创建一个控制器,will create a controller 控制器经过以下生命周期来完成请求: -When processing a request, an [application](structure-applications.md) will create a controller -based on the requested [route](#routes). The controller will then undergo the following lifecycle -to fulfill the request: 1. 在控制器创建和配置后,[[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对应操作方法,会创建一个内联操作; * 否则会抛出[[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. - * If the action ID is found in the [[yii\base\Controller::actions()|action map]], a standalone action - will be created; - * If the action ID is found to match an action method, an inline action will be created; - * Otherwise an [[yii\base\InvalidRouteException]] exception will be thrown. 3. 控制器按顺序调用应用主体、模块(如果控制器属于模块)、控制器的 `beforeAction()` 方法; * 如果任意一个调用返回false,后面未调用的`beforeAction()`会跳过并且操作执行会被取消; action execution will be cancelled. * 默认情况下每个 `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. The controller runs the action: - * The action parameters will be analyzed and populated from the request data; 5. 控制器按顺序调用控制器、模块(如果控制器属于模块)、应用主体的 `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. The application will take the action result and assign it to the [response](runtime-responses.md). ## 最佳实践 -## Best Practices 在设计良好的应用中,控制器很精练,包含的操作代码简短; 如果你的控制器很复杂,通常意味着需要重构,转移一些代码到其他类中。 -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 * 可访问 [请求](runtime-requests.md) 数据; * 可根据请求数据调用 [模型](structure-models.md) 的方法和其他服务组件; * 可使用 [视图](structure-views.md) 构造响应; * 不应处理应被[模型](structure-models.md)处理的请求数据; * 应避免嵌入HTML或其他展示代码,这些代码最好在 [视图](structure-views.md)中处理. -* may access the [request](runtime-requests.md) data; -* may call methods of [models](structure-models.md) and other service components with request data; -* may use [views](structure-views.md) to compose responses; -* should NOT process the request data - this should be done in [models](structure-models.md); -* should avoid embedding HTML or other presentational code - this is better done in [views](structure-views.md). diff --git a/docs/guide-zh-CN/structure-models.md b/docs/guide-zh-CN/structure-models.md index c6fc66e2e7..eaffbd4ec2 100644 --- a/docs/guide-zh-CN/structure-models.md +++ b/docs/guide-zh-CN/structure-models.md @@ -1,48 +1,29 @@ 模型 -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; -* [Massive assignment](#massive-assignment): supports populating multiple attributes in a single step; -* [Validation rules](#validation-rules): ensures input data based on the declared validation rules; -* [Data Exporting](#data-exporting): allows model data to be exported in terms of arrays with customizable formats. `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. ## 属性 -## Attributes 模型通过 *属性* 来代表业务数据,每个属性像是模型的公有可访问属性, [[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; @@ -54,9 +35,6 @@ 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]]: ```php $model = new \app\models\ContactForm; @@ -73,14 +51,10 @@ foreach ($model as $name => $value) { ### 定义属性 -### Defining Attributes 默认情况下你的模型类直接从[[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. ```php namespace app\models; @@ -100,24 +74,14 @@ 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 -override the magic methods such as `__get()`, `__set()` so that the attributes can be accessed like -normal object properties. ### 属性标签 -### Attribute Labels 当属性显示或获取输入时,经常要显示属性相关标签,例如假定一个属性名为`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; @@ -129,14 +93,8 @@ 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, ```php namespace app\models; @@ -164,8 +122,6 @@ 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: ```php public function attributeLabels() @@ -181,23 +137,14 @@ 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. ## 场景 -## Scenarios 模型可能在多个 *场景* 下使用,例如 `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. 模型使用 [[yii\base\Model::scenario]] 属性保持使用场景的跟踪, 默认情况下,模型支持一个名为 `default` 的场景,如下展示两种设置场景的方法: @@ -213,9 +160,6 @@ $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: ```php namespace app\models; @@ -236,23 +180,14 @@ 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: ```php namespace app\models; @@ -273,30 +208,17 @@ 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. ## 验证规则 -## Validation Rules 当模型接收到终端用户输入的数据,数据应当满足某种规则(称为 *验证规则*, 也称为 *业务规则*)。 例如假定`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 -and return false. For example, ```php $model = new \app\models\ContactForm; @@ -315,9 +237,6 @@ if ($model->validate()) { 通过覆盖 [[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: ```php public function rules() @@ -334,13 +253,8 @@ public function rules() 一条规则可用来验证一个或多个属性,一个属性可对应一条或多条规则。 更多关于如何申明验证规则的详情请参考 [验证输入](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: ```php public function rules() @@ -357,12 +271,8 @@ public function rules() 如果没有指定 `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()`. ## 块赋值 @@ -372,11 +282,6 @@ is associated with one or multiple active rules declared in `rules()`. 它直接将输入数据对应填充到 [[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 -to the attributes of the `ContactForm` model. Clearly, the former, which uses massive assignment, is much cleaner -and less error prone than the latter: ```php $model = new \app\models\ContactForm; @@ -394,16 +299,10 @@ $model->body = isset($data['body']) ? $data['body'] : null; ### 安全属性 -### Safe Attributes 块赋值只应用在模型当前[[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 -is `login`, only the `username` and `password` can be massively assigned. Any other attributes will -be kept untouched. ```php public function scenarios() @@ -418,22 +317,12 @@ 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. ```php public function rules() @@ -446,15 +335,10 @@ public function rules() ### 非安全属性 -### Unsafe Attributes 如上所述,[[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 -name when declaring it in `scenarios()`, like the `secret` attribute in the following: ```php public function scenarios() @@ -467,9 +351,6 @@ 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, ```php $model->secret = $secret; @@ -477,20 +358,12 @@ $model->secret = $secret; ## 数据导出 -## Data Exporting 模型通常要导出成不同格式,例如,你可能想将模型的一个集合转成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, ```php $post = \app\models\Post::findOne(100); @@ -498,39 +371,22 @@ $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. -In fact, it is the default way of exporting models in RESTful Web service development, as described in -the [Response Formatting](rest-response-formatting.md). ### 字段 -### Fields 字段是模型通过调用[[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 -`toArray()` will return these fields by default. The `extraFields()` method defines additionally available fields -which can also be returned by `toArray()` as long as you specify them via the `$expand` parameter. For example, -the following code will return all fields defined in `fields()` and the `prettyName` and `fullAddress` fields -if they are defined in `extraFields()`. ```php $array = $model->toArray([], ['prettyName', 'fullAddress']); @@ -539,11 +395,6 @@ $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 -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 // 明确列出每个字段,特别用于你想确保数据表或模型属性改变不会导致你的字段改变(保证后端的API兼容). @@ -578,23 +429,14 @@ public function 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`. ## 最佳实践 -## Best Practices 模型是代表业务数据、规则和逻辑的中心地方,通常在很多地方重用, 在一个设计良好的应用中,模型通常比[控制器](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 * 可包含属性来展示业务数据; * 可包含验证规则确保数据有效和完整; @@ -602,42 +444,18 @@ 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; -* should NOT directly access request, session, or any other environmental data. These data should be injected - by [controllers](structure-controllers.md) into models; -* should avoid embedding HTML or other presentational code - this is better done in [views](structure-views.md); -* avoid having too many [scenarios](#scenarios) in a single model. 在开发大型复杂系统时应经常考虑最后一条建议, 在这些系统中,模型会很大并在很多地方使用,因此会包含需要规则集和业务逻辑, 最后维护这些模型代码成为一个噩梦,因为一个简单修改会影响好多地方, 为确保模型好维护,最好使用以下策略: -You may usually consider the last recommendation above when you are developing large complex systems. -In these systems, models could be very fat because they are used in many places and may thus contain many sets -of rules and business logic. This often ends up in a nightmare in maintaining the model code -because a single touch of the code could affect several different places. To make the mode code more maintainable, -you may take the following strategy: * 定义可被多个 [应用主体](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. -* In each [application](structure-applications.md) or [module](structure-modules.md) that uses a model, - define a concrete model class by extending from the corresponding base model class. The concrete model classes - should contain rules and logic that are specific for that application or module. 例如,在[高级应用模板](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, -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.