diff --git a/docs/guide-zh-CN/README.md b/docs/guide-zh-CN/README.md index d907542..58bfa0a 100644 --- a/docs/guide-zh-CN/README.md +++ b/docs/guide-zh-CN/README.md @@ -139,9 +139,9 @@ RESTful Web 服务 ----- * **编撰中** [概述](test-overview.md) -* **待定中** [单元测试](test-unit.md) -* **待定中** [功能测试](test-functional.md) -* **待定中** [验收测试](test-acceptance.md) +* **编撰中** [单元测试](test-unit.md) +* **编撰中** [功能测试](test-functional.md) +* **编撰中** [验收测试](test-acceptance.md) * **编撰中** [测试夹具](test-fixtures.md) diff --git a/docs/guide-zh-CN/caching-data.md b/docs/guide-zh-CN/caching-data.md index 988e4b4..ff9fdce 100644 --- a/docs/guide-zh-CN/caching-data.md +++ b/docs/guide-zh-CN/caching-data.md @@ -67,15 +67,15 @@ if ($data === false) { Yii 支持一系列缓存存储器,概况如下: -* [[yii\caching\ApcCache]]: 使用 PHP [APC](http://php.net/manual/en/book.apc.php) 扩展。这个选项可以认为是集中式应用程序环境中(例如:单一服务器,没有独立的负载均衡器等)最快的缓存方案。 -* [[yii\caching\DbCache]]: 使用一个数据库的表存储缓存数据。要使用这个缓存,你必须创建一个与 [[yii\caching\DbCache::cacheTable]] 对应的表。 +* [[yii\caching\ApcCache]]:使用 PHP [APC](http://php.net/manual/en/book.apc.php) 扩展。这个选项可以认为是集中式应用程序环境中(例如:单一服务器,没有独立的负载均衡器等)最快的缓存方案。 +* [[yii\caching\DbCache]]:使用一个数据库的表存储缓存数据。要使用这个缓存,你必须创建一个与 [[yii\caching\DbCache::cacheTable]] 对应的表。 * [[yii\caching\DummyCache]]: 仅作为一个缓存占位符,不实现任何真正的缓存功能。这个组件的目的是为了简化那些需要查询缓存有效性的代码。例如,在开发中如果服务器没有实际的缓存支持,用它配置一个缓存组件。一个真正的缓存服务启用后,可以再切换为使用相应的缓存组件。两种条件下你都可以使用同样的代码 `Yii::$app->cache->get($key)` 尝试从缓存中取回数据而不用担心 `Yii::$app->cache` 可能是 `null`。 -* [[yii\caching\FileCache]]: 使用标准文件存储缓存数据。这个特别适用于缓存大块数据,例如一个整页的内容。 -* [[yii\caching\MemCache]]: 使用 PHP [memcache](http://php.net/manual/en/book.memcache.php) 和 [memcached](http://php.net/manual/en/book.memcached.php) 扩展。这个选项被看作分布式应用环境中(例如:多台服务器,有负载均衡等)最快的缓存方案。 -* [[yii\redis\Cache]]: 实现了一个基于 [Redis](http://redis.io/) 键值对存储器的缓存组件(需要 redis 2.6.12 及以上版本的支持 )。 -* [[yii\caching\WinCache]]: 使用 PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension)([另可参考](http://php.net/manual/en/book.wincache.php))扩展. -* [[yii\caching\XCache]]: 使用 PHP [XCache](http://xcache.lighttpd.net/)扩展。 -* [[yii\caching\ZendDataCache]]: 使用 [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm) 作为底层缓存媒介。 +* [[yii\caching\FileCache]]:使用标准文件存储缓存数据。这个特别适用于缓存大块数据,例如一个整页的内容。 +* [[yii\caching\MemCache]]:使用 PHP [memcache](http://php.net/manual/en/book.memcache.php) 和 [memcached](http://php.net/manual/en/book.memcached.php) 扩展。这个选项被看作分布式应用环境中(例如:多台服务器,有负载均衡等)最快的缓存方案。 +* [[yii\redis\Cache]]:实现了一个基于 [Redis](http://redis.io/) 键值对存储器的缓存组件(需要 redis 2.6.12 及以上版本的支持 )。 +* [[yii\caching\WinCache]]:使用 PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension)([另可参考](http://php.net/manual/en/book.wincache.php))扩展. +* [[yii\caching\XCache]]:使用 PHP [XCache](http://xcache.lighttpd.net/)扩展。 +* [[yii\caching\ZendDataCache]]:使用 [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm) 作为底层缓存媒介。 > Tip: 你可以在同一个应用程序中使用不同的缓存存储器。一个常见的策略是使用基于内存的缓存存储器存储小而常用的数据(例如:统计数据),使用基于文件或数据库的缓存存储器存储大而不太常用的数据(例如:网页内容)。 @@ -85,15 +85,15 @@ Yii 支持一系列缓存存储器,概况如下: 所有缓存组件都有同样的基类 [[yii\caching\Cache]] ,因此都支持如下 API: -* [[yii\caching\Cache::get()|get()]]: 通过一个指定的键(key)从缓存中取回一项数据。如果该项数据不存在于缓存中或者已经过期/失效,则返回值 false。 -* [[yii\caching\Cache::set()|set()]]: 将一项数据指定一个键,存放到缓存中。 -* [[yii\caching\Cache::add()|add()]]: 如果缓存中未找到该键,则将指定数据存放到缓存中。 -* [[yii\caching\Cache::mget()|mget()]]: 通过指定的多个键从缓存中取回多项数据。 -* [[yii\caching\Cache::mset()|mset()]]: 将多项数据存储到缓存中,每项数据对应一个键。 -* [[yii\caching\Cache::madd()|madd()]]: 将多项数据存储到缓存中,每项数据对应一个键。如果某个键已经存在于缓存中,则该项数据会被跳过。 -* [[yii\caching\Cache::exists()|exists()]]: 返回一个值,指明某个键是否存在于缓存中。 -* [[yii\caching\Cache::delete()|delete()]]: 通过一个键,删除缓存中对应的值。 -* [[yii\caching\Cache::flush()|flush()]]: 删除缓存中的所有数据。 +* [[yii\caching\Cache::get()|get()]]:通过一个指定的键(key)从缓存中取回一项数据。如果该项数据不存在于缓存中或者已经过期/失效,则返回值 false。 +* [[yii\caching\Cache::set()|set()]]:将一项数据指定一个键,存放到缓存中。 +* [[yii\caching\Cache::add()|add()]]:如果缓存中未找到该键,则将指定数据存放到缓存中。 +* [[yii\caching\Cache::mget()|mget()]]:通过指定的多个键从缓存中取回多项数据。 +* [[yii\caching\Cache::mset()|mset()]]:将多项数据存储到缓存中,每项数据对应一个键。 +* [[yii\caching\Cache::madd()|madd()]]:将多项数据存储到缓存中,每项数据对应一个键。如果某个键已经存在于缓存中,则该项数据会被跳过。 +* [[yii\caching\Cache::exists()|exists()]]:返回一个值,指明某个键是否存在于缓存中。 +* [[yii\caching\Cache::delete()|delete()]]:通过一个键,删除缓存中对应的值。 +* [[yii\caching\Cache::flush()|flush()]]:删除缓存中的所有数据。 有些缓存存储器如 MemCache,APC 支持以批量模式取回缓存值,这样可以节省取回缓存数据的开支。 [[yii\caching\Cache::mget()|mget()]] 和 [[yii\caching\Cache::madd()|madd()]] API提供对该特性的支持。如果底层缓存存储器不支持该特性,Yii 也会模拟实现。 @@ -177,12 +177,11 @@ $data = $cache->get($key); 下面是可用的缓存依赖的概况: -- [[yii\caching\ChainedDependency]]: 如果依赖链上任何一个依赖产生变化,则依赖改变。 -- [[yii\caching\DbDependency]]: 如果指定 SQL 语句的查询结果发生了变化,则依赖改变。 -- [[yii\caching\ExpressionDependency]]: 如果指定的 PHP 表达式执行结果发生变化,则依赖改变。 -- [[yii\caching\FileDependency]]: 如果文件的最后修改时间发生变化,则依赖改变。 -- [[yii\caching\TagDependency]]: 为一项缓存数据添加一个或多个标签。你可以通过调用 [[yii\caching\TagDependency::invalidate()]] -一次性将具有指定标签的缓存数据全部置为失效状态。 +- [[yii\caching\ChainedDependency]]:如果依赖链上任何一个依赖产生变化,则依赖改变。 +- [[yii\caching\DbDependency]]:如果指定 SQL 语句的查询结果发生了变化,则依赖改变。 +- [[yii\caching\ExpressionDependency]]:如果指定的 PHP 表达式执行结果发生变化,则依赖改变。 +- [[yii\caching\FileDependency]]:如果文件的最后修改时间发生变化,则依赖改变。 +- [[yii\caching\GroupDependency]]:将一项缓存数据标记到一个组名,你可以通过调用 [[yii\caching\GroupDependency::invalidate()]] 一次性将相同组名的缓存全部置为失效状态。 ## 查询缓存 <a name="query-caching"></a> diff --git a/docs/guide-zh-CN/concept-aliases.md b/docs/guide-zh-CN/concept-aliases.md index e1045b3..a8b27a5 100644 --- a/docs/guide-zh-CN/concept-aliases.md +++ b/docs/guide-zh-CN/concept-aliases.md @@ -19,7 +19,7 @@ Yii::setAlias('@bar', 'http://www.example.com'); > 注意:别名所指向的文件路径或 URL 不一定是真实存在的文件或资源。 -可以通过在一个别名后面加斜杠 `/` 和一至多个路径分段生成新别名(无需调用 [[Yii::setAlias()]])。我们把通过 [[Yii::setAlias()]] 定义的别名称为**根别名**,而用他们衍生出去的别名成为**衍生别名**。例如,`@foo` 就是跟别名,而 `@foo/bar/file.php` 是一个衍生别名。 +可以通过在一个别名后面加斜杠 `/` 和一至多个路径分段生成新别名(无需调用 [[Yii::setAlias()]])。我们把通过 [[Yii::setAlias()]] 定义的别名称为**根别名**,而用他们衍生出去的别名成为**衍生别名**。例如,`@foo` 就是根别名,而 `@foo/bar/file.php` 是一个衍生别名。 你还可以用别名去定义新别名(根别名与衍生别名均可): @@ -86,7 +86,7 @@ $cache = new FileCache([ 预定义的别名 <a name="predefined-aliases"></a> ------------------ -Yii 预定义了一系列别名来简化常用路径和 URL的使用: +Yii 预定义了一系列别名来简化常用路径和 URL 的使用: - `@yii` - `BaseYii.php` 文件所在的目录(也被称为框架安装目录) - `@app` - 当前运行的应用 [[yii\base\Application::basePath|根路径(base path)]] diff --git a/docs/guide-zh-CN/input-validation.md b/docs/guide-zh-CN/input-validation.md index d6d6b34..e037326 100644 --- a/docs/guide-zh-CN/input-validation.md +++ b/docs/guide-zh-CN/input-validation.md @@ -47,61 +47,46 @@ public function rules() ```php [ - // required, specifies which attributes should be validated by this rule. - // For a single attribute, you can use the attribute name directly - // without having it in an array instead of an array + // 必须项,用于指定那些模型特性需要通过此规则的验证。 + // 对于只有一个特性的情况,可以直接写特性名,而不必用数组包裹。 ['attribute1', 'attribute2', ...], - // required, specifies the type of this rule. - // It can be a class name, validator alias, or a validation method name + // 必填项,用于指定规则的类型。 + // 它可以是类名,验证器昵称,或者是验证方法的名称。 'validator', - // optional, specifies in which scenario(s) this rule should be applied - // if not given, it means the rule applies to all scenarios - // You may also configure the "except" option if you want to apply the rule - // to all scenarios except the listed ones + // 可选项,用于指定在场景(scenario)中,需要启用该规则 + // 若不提供,则代表该规则适用于所有场景 + // 若你需要提供除了某些特定场景以外的所有其他场景,你也可以配置 "except" 选项 'on' => ['scenario1', 'scenario2', ...], - // optional, specifies additional configurations for the validator object + // 可选项,用于指定对该验证器对象的其他配置选项 'property1' => 'value1', 'property2' => 'value2', ... ] ``` -For each rule you must specify at least which attributes the rule applies to and what is the type of the rule. -You can specify the rule type in one of the following forms: +对于每个规则,你至少需要指定该规则适用于哪些特性,以及本规则的类型是什么。你可以指定以下的规则类型之一: -* the alias of a core validator, such as `required`, `in`, `date`, etc. Please refer to - the [Core Validators](tutorial-core-validators.md) for the complete list of core validators. -* the name of a validation method in the model class, or an anonymous function. Please refer to the - [Inline Validators](#inline-validators) subsection for more details. -* the name of a validator class. Please refer to the [Standalone Validators](#standalone-validators) - subsection for more details. +* 核心验证器的昵称,比如 `required`、`in`、`date`,等等。请参考[核心验证器](tutorial-core-validators.md)章节查看完整的核心验证器列表。 +* 模型类中的某个验证方法的名称,或者一个匿名方法。请参考[行内验证器](#inline-validators)小节了解更多。 +* 验证器类的名称。请参考[独立验证器](#standalone-validators)小节了解更多。 -A rule can be used to validate one or multiple attributes, and an attribute may be validated by one or multiple rules. -A rule may be applied in certain [scenarios](structure-models.md#scenarios) only by specifying the `on` option. -If you do not specify an `on` option, it means the rule will be applied to all scenarios. +一个规则可用于验证一个或多个模型特性,且一个特性可以被一个或多个规则所验证。一个规则可以施用于特定[场景(scenario)](structure-models.md#scenarios),只要指定 `on` 选项。如果你不指定 `on` 选项,那么该规则会适配于所有场景。 -When the `validate()` method is called, it does the following steps to perform validation: +当调用 `validate()` 方法时,它将运行以下几个具体的验证步骤: -1. Determine which attributes should be validated by checking the current [[yii\base\Model::scenario|scenario]] - against the scenarios declared in [[yii\base\Model::scenarios()]]. These attributes are the active attributes. -2. Determine which rules should be applied by checking the current [[yii\base\Model::scenario|scenario]] - against the rules declared in [[yii\base\Model::rules()]]. These rules are the active rules. -3. Use each active rule to validate each active attribute which is associated with the rule. +1. 检查从声明自 [[yii\base\Model::scenarios()]] 方法的场景中所挑选出的当前[[yii\base\Model::scenario|场景]]的信息,从而确定出那些特性需要被验证。这些特性被称为激活特性。 +2. 检查从声明自 [[yii\base\Model::rules()]] 方法的众多规则中所挑选出的适用于当前[[yii\base\Model::scenario|场景]]的规则,从而确定出需要验证哪些规则。这些规则被称为激活规则。 +3. 用每个激活规则去验证每个与之关联的激活特性。 -According to the above validation steps, 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()`. +基于以上验证步骤,有且仅有声明在 `scenarios()` 方法里的激活特性,且它还必须与一或多个声明自 `rules()` 里的激活规则相关联才会被验证。 ### 自定义错误信息 <a name="customizing-error-messages"></a> -Most validators have default error messages that will be added to the model being validated when its attributes -fail the validation. For example, the [[yii\validators\RequiredValidator|required]] validator will add -a message "Username cannot be blank." to a model when the `username` attribute fails the rule using this validator. +大多数的验证器都有默认的错误信息,当模型的某个特性验证失败的时候,该错误信息会被返回给模型。比如,用 [[yii\validators\RequiredValidator|required]] 验证器的规则检验 `username` 特性失败的话,会返还给模型 "Username cannot be blank." 信息。 -You can customize the error message of a rule by specifying the `message` property when declaring the rule, -like the following, +你可以通过在声明规则的时候同时指定 `message` 属性,来定制某个规则的错误信息,比如这样: ```php public function rules() @@ -112,32 +97,22 @@ public function rules() } ``` -Some validators may support additional error messages to more precisely describe different causes of -validation failures. For example, the [[yii\validators\NumberValidator|number]] validator supports -[[yii\validators\NumberValidator::tooBig|tooBig]] and [[yii\validators\NumberValidator::tooSmall|tooSmall]] -to describe the validation failure when the value being validated is too big and too small, respectively. -You may configure these error messages like configuring other properties of validators in a validation rule. +一些验证器还支持用于针对不同原因的验证失败返回更加准确的额外错误信息。比如,[[yii\validators\NumberValidator|number]] 验证器就支持 [[yii\validators\NumberValidator::tooBig|tooBig]] 和 [[yii\validators\NumberValidator::tooSmall|tooSmall]] 两种错误消息用于分别返回输入值是太大还是太小。 +你也可以像配置验证器的其他属性一样配置它们俩各自的错误信息。 ### 验证事件 <a name="validation-events"></a> -When [[yii\base\Model::validate()]] is called, it will call two methods that you may override to customize -the validation process: +当调用 [[yii\base\Model::validate()]] 方法的过程里,它同时会调用两个特殊的方法,把它们重写掉可以实现自定义验证过程的目的: -* [[yii\base\Model::beforeValidate()]]: the default implementation will trigger a [[yii\base\Model::EVENT_BEFORE_VALIDATE]] - event. You may either override this method or respond to this event to do some preprocessing work - (e.g. normalizing data inputs) before the validation occurs. The method should return a boolean value indicating - whether the validation should proceed or not. -* [[yii\base\Model::afterValidate()]]: the default implementation will trigger a [[yii\base\Model::EVENT_AFTER_VALIDATE]] - event. You may either override this method or respond to this event to do some postprocessing work after - the validation is completed. +* [[yii\base\Model::beforeValidate()]]:在默认的实现中会触发 [[yii\base\Model::EVENT_BEFORE_VALIDATE]] 事件。你可以重写该方法或者响应此事件,来在验证开始之前,先进行一些预处理的工作。(比如,标准化数据输入)该方法应该返回一个布尔值,用于标明验证是否通过。 +* [[yii\base\Model::afterValidate()]]:在默认的实现中会触发 [[yii\base\Model::EVENT_AFTER_VALIDATE]] 事件。你可以重写该方法或者响应此事件,来在验证结束之后,再进行一些收尾的工作。 ### 条件式验证 <a name="conditional-validation"></a> -To validate attributes only when certain conditions apply, e.g. the validation of one attribute depends -on the value of another attribute you can use the [[yii\validators\Validator::when|when]] property -to define such conditions. For example, +若要只在某些条件满足时,才验证相关特性,比如:是否验证某特性取决于另一特性的值,你可以通过 +[[yii\validators\Validator::when|when]] 属性来定义相关条件。举例而言, ```php [ @@ -147,20 +122,19 @@ to define such conditions. For example, ] ``` -The [[yii\validators\Validator::when|when]] property takes a PHP callable with the following signature: +[[yii\validators\Validator::when|when]] 属性会读入一个如下所示结构的 PHP callable 函数对象: ```php /** - * @param Model $model the model being validated - * @param string $attribute the attribute being validated - * @return boolean whether the rule should be applied + * @param Model $model 要验证的模型对象 + * @param string $attribute 待测特性名 + * @return boolean 返回是否启用该规则 */ function ($model, $attribute) ``` -If you also need to support client-side conditional validation, you should configure -the [[yii\validators\Validator::whenClient|whenClient]] property which takes a string representing a JavaScript -function whose return value determines whether to apply the rule or not. For example, +若你需要支持客户端的条件验证,你应该配置 +[[yii\validators\Validator::whenClient|whenClient]] 属性,它会读入一条包含有 JavaScript 函数的字符串。这个函数将被用于确定该客户端验证规则是否被启用。比如, ```php [ @@ -173,13 +147,11 @@ function whose return value determines whether to apply the rule or not. For exa ``` -### 数据过滤 <a name="data-filtering"></a> +### 数据预处理 <a name="data-filtering"></a> -User inputs often need to be filtered or preprocessed. For example, you may want to trim the spaces around the -`username` input. You may use validation rules to achieve this goal. +用户输入经常需要进行数据过滤,或者叫预处理。比如你可能会需要先去掉 `username` 输入的收尾空格。你可以通过使用验证规则来实现此目的。 -The following examples shows how to trim the spaces in the inputs and turn empty inputs into nulls by using -the [trim](tutorial-core-validators.md#trim) and [default](tutorial-core-validators.md#default) core validators: +下面的例子展示了如何去掉输入信息的首尾空格,并将空输入返回为 null。具体方法为通过调用 [trim](tutorial-core-validators.md#trim) 和 [default](tutorial-core-validators.md#default) 核心验证器: ```php [ @@ -188,31 +160,28 @@ the [trim](tutorial-core-validators.md#trim) and [default](tutorial-core-validat ] ``` -You may also use the more general [filter](tutorial-core-validators.md#filter) validator to perform more complex -data filtering. +也还可以用更加通用的 [filter(滤镜)](tutorial-core-validators.md#filter) 核心验证器来执行更加复杂的数据过滤。 -As you can see, these validation rules do not really validate the inputs. Instead, they will process the values -and save them back to the attributes being validated. +如你所见,这些验证规则并不真的对输入数据进行任何验证。而是,对输入数据进行一些处理,然后把它们存回当前被验证的模型特性。 ### 处理空输入 <a name="handling-empty-inputs"></a> -When input data are submitted from HTML forms, you often need to assign some default values to the inputs -if they are empty. You can do so by using the [default](tutorial-core-validators.md#default) validator. For example, +当输入数据是通过 HTML 表单,你经常会需要给空的输入项赋默认值。你可以通过调整 +[default](tutorial-core-validators.md#default) 验证器来实现这一点。举例来说, ```php [ - // set "username" and "email" as null if they are empty + // 若 "username" 和 "email" 为空,则设为 null [['username', 'email'], 'default'], - // set "level" to be 1 if it is empty + // 若 "level" 为空,则设其为 1 ['level', 'default', 'value' => 1], ] ``` -By default, an input is considered empty if its value is an empty string, an empty array or a null. -You may customize the default empty detection logic by configuring the the [[yii\validators\Validator::isEmpty]] property -with a PHP callable. For example, +默认情况下,当输入项为空字符串,空数组,或 null 时,会被视为“空值”。你也可以通过配置 +[[yii\validators\Validator::isEmpty]] 属性来自定义空值的判定规则。比如, ```php [ @@ -222,35 +191,33 @@ with a PHP callable. For example, ] ``` -> Note: Most validators do not handle empty inputs if their [[yii\base\Validator::skipOnEmpty]] property takes - the default value true. They will simply be skipped during validation if their associated attributes receive empty - inputs. Among the [core validators](tutorial-core-validators.md), only the `captcha`, `default`, `filter`, - `required`, and `trim` validators will handle empty inputs. +> 注意:对于绝大多数验证器而言,若其 [[yii\base\Validator::skipOnEmpty]] 属性为默认值 +true,则它们不会对空值进行任何处理。也就是当他们的关联特性接收到空值时,相关验证会被直接略过。在 +[核心验证器](tutorial-core-validators.md) 之中,只有 `captcha`(验证码),`default`(默认值),`filter`(滤镜),`required`(必填),以及 `trim`(去首尾空格),这几个验证器会处理空输入。 -## Ad Hoc Validation <a name="ad-hoc-validation"></a> +## 临时验证 <a name="ad-hoc-validation"></a> -Sometimes you need to do *ad hoc validation* for values that are not bound to any model. +有时,你需要对某些没有绑定任何模型类的值进行 **临时验证**。 -If you only need to perform one type of validation (e.g. validating email addresses), you may call -the [[yii\validators\Validator::validate()|validate()]] method of the desired validator, like the following: +若你只需要进行一种类型的验证 (e.g. 验证邮箱地址),你可以调用所需验证器的 +[[yii\validators\Validator::validate()|validate()]] 方法。像这样: ```php $email = 'test@example.com'; $validator = new yii\validators\EmailValidator(); if ($validator->validate($email, $error)) { - echo 'Email is valid.'; + echo '有效的 Email 地址。'; } else { echo $error; } ``` -> Note: Not all validators support such kind of validation. An example is the [unique](tutorial-core-validators.md#unique) - core validator which is designed to work with a model only. +> 注意:不是所有的验证器都支持这种形式的验证。比如 [unique(唯一性)](tutorial-core-validators.md#unique)核心验证器就就是一个例子,它的设计初衷就是只作用于模型类内部的。 -If you need to perform multiple validations against several values, you can use [[yii\base\DynamicModel]] -which supports declaring both attributes and rules on the fly. Its usage is like the following: +若你需要针对一系列值执行多项验证,你可以使用 [[yii\base\DynamicModel]] +。它支持即时添加特性和验证规则的定义。它的使用规则是这样的: ```php public function actionSearch($name, $email) @@ -261,18 +228,17 @@ public function actionSearch($name, $email) ]); if ($model->hasErrors()) { - // validation fails + // 验证失败 } else { - // validation succeeds + // 验证成功 } } ``` -The [[yii\base\DynamicModel::validateData()]] method creates an instance of `DynamicModel`, defines the attributes -using the given data (`name` and `email` in this example), and then calls [[yii\base\Model::validate()]] -with the given rules. +[[yii\base\DynamicModel::validateData()]] 方法会创建一个 `DynamicModel` 的实例对象,并通过给定数据定义模型特性(以 `name` 和 `email` 为例),之后用给定规则调用 +[[yii\base\Model::validate()]] 方法。 -Alternatively, you may use the following more "classic" syntax to perform ad hoc data validation: +除此之外呢,你也可以用如下的更加“传统”的语法来执行临时数据验证: ```php public function actionSearch($name, $email) @@ -283,43 +249,39 @@ public function actionSearch($name, $email) ->validate(); if ($model->hasErrors()) { - // validation fails + // 验证失败 } else { - // validation succeeds + // 验证成功 } } ``` -After validation, you can check if the validation succeeds or not by calling the -[[yii\base\DynamicModel::hasErrors()|hasErrors()]] method, and then get the validation errors from the -[[yii\base\DynamicModel::errors|errors]] property, like you do with a normal model. -You may also access the dynamic attributes defined through the model instance, e.g., -`$model->name` and `$model->email`. +验证之后你可以通过调用 [[yii\base\DynamicModel::hasErrors()|hasErrors()]] +方法来检查验证通过与否,并通过 [[yii\base\DynamicModel::errors|errors]] +属性获得验证的错误信息,过程与普通模型类一致。你也可以访问模型对象内定义的动态特性,就像: +`$model->name` 和 `$model->email`。 ## 创建验证器(Validators) <a name="creating-validators"></a> -Besides using the [core validators](tutorial-core-validators.md) included in the Yii releases, you may also -create your own validators. You may create inline validators or standalone validators. +除了使用 Yii 的发布版里所包含的[核心验证器](tutorial-core-validators.md)之外,你也可以创建你自己的验证器。自定义的验证器可以是**行内验证器**,也可以是**独立验证器**。 ### 行内验证器(Inline Validators) <a name="inline-validators"></a> -An inline validator is one defined in terms of a model method or an anonymous function. The signature of -the method/function is: +行内验证器是一种以模型方法或匿名函数的形式定义的验证器。这些方法/函数的结构如下: ```php /** - * @param string $attribute the attribute currently being validated - * @param array $params the additional name-value pairs given in the rule + * @param string $attribute 当前被验证的特性 + * @param array $params 以名-值对形式提供的额外参数 */ function ($attribute, $params) ``` -If an attribute fails the validation, the method/function should call [[yii\base\Model::addError()]] to save -the error message in the model so that it can be retrieved back later to present to end users. +若某特性的验证失败了,该方法/函数应该调用 [[yii\base\Model::addError()]] 保存错误信息到模型内。这样这些错误就能在之后的操作中,被读取并展现给终端用户。 -Below are some examples: +下面是一些例子: ```php use yii\base\Model; @@ -332,13 +294,13 @@ class MyForm extends Model public function rules() { return [ - // an inline validator defined as the model method validateCountry() + // 以模型方法 validateCountry() 形式定义的行内验证器 ['country', 'validateCountry'], - // an inline validator defined as an anonymous function + // 以匿名函数形式定义的行内验证器 ['token', function ($attribute, $params) { if (!ctype_alnum($this->$attribute)) { - $this->addError($attribute, 'The token must contain letters or digits.'); + $this->addError($attribute, '令牌本身必须包含字母或数字。'); } }], ]; @@ -346,17 +308,15 @@ class MyForm extends Model public function validateCountry($attribute, $params) { - if (!in_array($this->$attribute, ['USA', 'Web'])) { - $this->addError($attribute, 'The country must be either "USA" or "Web".'); + if (!in_array($this->$attribute, ['兲朝', '墙外'])) { + $this->addError($attribute, '国家必须为 "兲朝" 或 "墙外" 中的一个。'); } } } ``` -> Note: By default, inline validators will not be applied if their associated attributes receive empty inputs - or if they have already failed some validation rules. If you want to make sure a rule is always applied, - you may configure the [[yii\validators\Validator::skipOnEmpty|skipOnEmpty]] and/or [[yii\validators\Validator::skipOnError|skipOnError]] - properties to be false in the rule declarations. For example: +> 注意:缺省状态下,行内验证器不会在关联特性的输入值为空或该特性已经在其他验证中失败的情况下起效。若你想要确保该验证器始终启用的话,你可以在定义规则时,酌情将 [[yii\validators\Validator::skipOnEmpty|skipOnEmpty]] 以及 [[yii\validators\Validator::skipOnError|skipOnError]] + 属性设为 false,比如, > ```php [ ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false], @@ -366,10 +326,9 @@ class MyForm extends Model ### 独立验证器(Standalone Validators) <a name="standalone-validators"></a> -A standalone validator is a class extending [[yii\validators\Validator]] or its child class. You may implement -its validation logic by overriding the [[yii\validators\Validator::validateAttribute()]] method. If an attribute -fails the validation, call [[yii\base\Model::addError()]] to save the error message in the model, like you do -with [inline validators](#inline-validators). For example, +独立验证器是继承自 [[yii\validators\Validator]] 或其子类的类。你可以通过重写 +[[yii\validators\Validator::validateAttribute()]] 来实现它的验证规则。若特性验证失败,可以调用 +[[yii\base\Model::addError()]] 以保存错误信息到模型内,操作与 [inline validators](#inline-validators) 所需操作完全一样。比如, ```php namespace app\components; @@ -380,38 +339,30 @@ class CountryValidator extends Validator { public function validateAttribute($model, $attribute) { - if (!in_array($model->$attribute, ['USA', 'Web'])) { - $this->addError($attribute, 'The country must be either "USA" or "Web".'); + if (!in_array($model->$attribute, ['兲朝', '墙外'])) { + $this->addError($attribute, '国家必须为 "兲朝" 或 "墙外" 中的一个。'); } } } ``` -If you want your validator to support validating a value without a model, you should also override -[[yii\validators\Validator::validate()]]. You may also override [[yii\validators\Validator::validateValue()]] -instead of `validateAttribute()` and `validate()` because by default the latter two methods are implemented -by calling `validateValue()`. +若你想要验证器支持不使用 model 的数据验证,你还应该重写 +[[yii\validators\Validator::validate()]] 方法。你也可以通过重写 +[[yii\validators\Validator::validateValue()]] 方法替代 +`validateAttribute()` 和 `validate()`,因为默认状态下,后两者的实现使用过调用 +`validateValue()`实现的。 ## 客户端验证器(Client-Side Validation) <a name="client-side-validation"></a> -Client-side validation based on JavaScript is desirable when end users provide inputs via HTML forms, because -it allows users to find out input errors faster and thus provides better user experience. You may use or implement -a validator that supports client-side validation *in addition to* server-side validation. +当终端用户通过 HTML 表单提供相关输入信息时,我们可能会需要用到基于 JavaScript 的客户端验证。因为,它可以让用户更快速的得到错误信息,也因此可以提供更好的用户体验。你可以使用或自己实现除服务器端验证之外,**还能额外**客户端验证功能的验证器。 -> Info: While client-side validation is desirable, it is not a must. It main purpose is to provider users better - experience. Like input data coming from end users, you should never trust client-side validation. For this reason, - you should always perform server-side validation by calling [[yii\base\Model::validate()]], like - described in the previous subsections. +> 补充:尽管客户端验证为加分项,但它不是必须项。它存在的主要意义在于给用户提供更好的客户体验。正如“永远不要相信来自终端用户的输入信息”,也同样永远不要相信客户端验证。基于这个理由,你应该始终如前文所描述的那样,通过调用 [[yii\base\Model::validate()]] 方法执行服务器端验证。 ### 使用客户端验证 <a name="using-client-side-validation"></a> -Many [core validators](tutorial-core-validators.md) support client-side validation out-of-box. All you need to do -is just to use [[yii\widgets\ActiveForm]] to build your HTML forms. For example, `LoginForm` below declares two -rules: one uses the [required](tutorial-core-validators.md#required) core validator which is supported on both -client and server sides; the other uses the `validatePassword` inline validator which is only supported on the server -side. +许多[核心验证器](tutorial-core-validators.md)都支持开箱即用的客户端验证。你只需要用 [[yii\widgets\ActiveForm]] 的方式构建 HTML 表单即可。比如,下面的 `LoginForm`(登录表单)声明了两个规则:其一为 [required](tutorial-core-validators.md#required) 核心验证器,它同时支持客户端与服务器端的验证;另一个则采用 `validatePassword` 行内验证器,它只支持服务器端。 ```php namespace app\models; @@ -427,10 +378,10 @@ class LoginForm extends Model public function rules() { return [ - // username and password are both required + // username 和 password 都是必填项 [['username', 'password'], 'required'], - // password is validated by validatePassword() + // 用 validatePassword() 验证 password ['password', 'validatePassword'], ]; } @@ -446,9 +397,7 @@ class LoginForm extends Model } ``` -The HTML form built by the following code contains two input fields `username` and `password`. -If you submit the form without entering anything, you will find the error messages requiring you -to enter something appear right away without any communication with the server. +使用如下代码构建的 HTML 表单包含两个输入框 `username` 以及 `password`。如果你在没有输入任何东西之前提交表单,就会在没有任何与服务器端的通讯的情况下,立刻收到一个要求你填写空白项的错误信息。 ```php <?php $form = yii\widgets\ActiveForm::begin(); ?> @@ -458,29 +407,21 @@ to enter something appear right away without any communication with the server. <?php yii\widgets\ActiveForm::end(); ?> ``` -Behind the scene, [[yii\widgets\ActiveForm]] will read the validation rules declared in the model -and generate appropriate JavaScript code for validators that support client-side validation. When a user -changes the value of an input field or submit the form, the client-side validation JavaScript will be triggered. +幕后的运作过程是这样的:[[yii\widgets\ActiveForm]] 会读取声明在模型类中的验证规则,并生成那些支持支持客户端验证的验证器所需的 JavaScript 代码。当用户修改输入框的值,或者提交表单时,就会触发相应的客户端验证 JS 代码。 -If you want to turn off client-side validation completely, you may configure the -[[yii\widgets\ActiveForm::enableClientValidation]] property to be false. You may also turn off client-side -validation of individual input fields by configuring their [[yii\widgets\ActiveField::enableClientValidation]] -property to be false. +若你需要完全关闭客户端验证,你只需配置 [[yii\widgets\ActiveForm::enableClientValidation]] 属性为 false。你同样可以关闭各个输入框各自的客户端验证,只要把它们的 [[yii\widgets\ActiveField::enableClientValidation]] 属性设为 false。 -### 实现客户端验证 <a name="implementing-client-side-validation"></a> +### 自己实现客户端验证 <a name="implementing-client-side-validation"></a> -To create a validator that supports client-side validation, you should implement the -[[yii\validators\Validator::clientValidateAttribute()]] method which returns a piece of JavaScript code -that performs the validation on the client side. Within the JavaScript code, you may use the following -predefined variables: +要穿件一个支持客户端验证的验证器,你需要实现 +[[yii\validators\Validator::clientValidateAttribute()]] 方法,用于返回一段用于运行客户端验证的 JavaScript 代码。在这段 JavaScript 代码中,你可以使用以下预定义的变量: -- `attribute`: the name of the attribute being validated. -- `value`: the value being validated. -- `messages`: an array used to hold the validation error messages for the attribute. +- `attribute`:正在被验证的模型特性的名称。 +- `value`:进行验证的值。 +- `messages`:一个用于暂存模型特性的报错信息的数组。 -In the following example, we create a `StatusValidator` which validates if an input is a valid status input -against the existing status data. The validator supports both server side and client side validation. +在下面的例子里,我们会创建一个 `StatusValidator`,它会通过比对现有的状态数据,验证输入值是否为一个有效的状态。该验证器同时支持客户端以及服务器端验证。 ```php namespace app\components; @@ -493,7 +434,7 @@ class StatusValidator extends Validator public function init() { parent::init(); - $this->message = 'Invalid status input.'; + $this->message = '无效的状态输入。'; } public function validateAttribute($model, $attribute) @@ -517,9 +458,7 @@ JS; } ``` -> Tip: The above code is given mainly to demonstrate how to support client-side validation. In practice, - you may use the [in](tutorial-core-validators.md#in) core validator to achieve the same goal. You may - write the validation rule like the following: +> 技巧:上述代码主要是演示了如何支持客户端验证。在具体实践中,你可以使用 [in](tutorial-core-validators.md#in) 核心验证器来达到同样的目的。比如这样的验证规则: > ```php [ ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()], diff --git a/docs/guide-zh-CN/rest-error-handling.md b/docs/guide-zh-CN/rest-error-handling.md new file mode 100644 index 0000000..11f2b4c --- /dev/null +++ b/docs/guide-zh-CN/rest-error-handling.md @@ -0,0 +1,43 @@ +错误处理 +============== + +处理一个 RESTful API 请求时, 如果有一个用户请求错误或服务器发生意外时, 你可以简单地抛出一个异常来通知用户出错了。 +如果你能找出错误的原因 (例如,所请求的资源不存在),你应该 +考虑抛出一个适当的HTTP状态代码的异常 (例如, [[yii\web\NotFoundHttpException]] +意味着一个404 HTTP状态代码)。 Yii 将通过HTTP状态码和文本 +发送相应的响应。 它还将包括在响应主体异常的 +序列化表示形式。 例如, + +``` +HTTP/1.1 404 Not Found +Date: Sun, 02 Mar 2014 05:31:43 GMT +Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y +Transfer-Encoding: chunked +Content-Type: application/json; charset=UTF-8 + +{ + "type": "yii\\web\\NotFoundHttpException", + "name": "Not Found Exception", + "message": "The requested resource was not found.", + "code": 0, + "status": 404 +} +``` + +下面的列表总结了Yii的REST框架的HTTP状态代码: + +* `200`: OK。一切正常。 +* `201`: 响应 `POST` 请求时成功创建一个资源。`Location` header + 包含的URL指向新创建的资源。 +* `204`: 该请求被成功处理,响应不包含正文内容 (类似 `DELETE` 请求)。 +* `304`: 资源没有被修改。可以使用缓存的版本。 +* `400`: 错误的请求。可能通过用户方面的多种原因引起的,例如在请求体内有无效的JSON + 数据,无效的操作参数,等等。 +* `401`: 验证失败。 +* `403`: 已经经过身份验证的用户不允许访问指定的 API 末端。 +* `404`: 所请求的资源不存在。 +* `405`: 不被允许的方法。 请检查 `Allow` header 允许的HTTP方法。 +* `415`: 不支持的媒体类型。 所请求的内容类型或版本号是无效的。 +* `422`: 数据验证失败 (例如,响应一个 `POST` 请求)。 请检查响应体内详细的错误消息。 +* `429`: 请求过多。 由于限速请求被拒绝。 +* `500`: 内部服务器错误。 这可能是由于内部程序错误引起的。 diff --git a/docs/guide-zh-CN/rest-quick-start.md b/docs/guide-zh-CN/rest-quick-start.md new file mode 100644 index 0000000..16d7820 --- /dev/null +++ b/docs/guide-zh-CN/rest-quick-start.md @@ -0,0 +1,174 @@ +快速入门 +=========== + +Yii 提供了一整套用来简化实现RESTful风格的Web Service服务的API。 +特别是,Yii支持以下关于RESTful风格的API: + +* 支持 [Active Record](db-active-record.md) 类的通用API的快速原型; +* 涉及的响应格式(在默认情况下支持JSON 和 XML); +* 支持可选输出字段的 可定制对象序列化; +* 适当的格式的数据采集和验证错误; +* 支持 [HATEOAS](http://en.wikipedia.org/wiki/HATEOAS); +* 有适当HTTP动词检查的高效的路由; +* 内置`OPTIONS`和`HEAD`动词的支持; +* 认证和授权; +* 数据缓存和HTTP缓存; +* 速率限制; + + +如下, 我们用一个例子来说明如何用最少的编码来建立一套RESTful风格的API。 + +假设你想通过RESTful风格的API来展示用户数据。用户数据被存储在用户DB表, +你已经创建了 [[yii\db\ActiveRecord|ActiveRecord]] 类 `app\models\User` 来访问该用户数据. + + +## 创建一个控制器 <a name="creating-controller"></a> + +首先,创建一个控制器类 `app\controllers\UserController` 如下, + +```php +namespace app\controllers; + +use yii\rest\ActiveController; + +class UserController extends ActiveController +{ + public $modelClass = 'app\models\User'; +} +``` + +控制器类扩展自 [[yii\rest\ActiveController]]。通过指定 [[yii\rest\ActiveController::modelClass|modelClass]] +作为 `app\models\User`, 控制器就能知道使用哪个模型去获取和处理数据。 + + +## 配置URL规则 <a name="configuring-url-rules"></a> + +然后,修改有关在应用程序配置的`urlManager`组件的配置: + +```php +'urlManager' => [ + 'enablePrettyUrl' => true, + 'enableStrictParsing' => true, + 'showScriptName' => false, + 'rules' => [ + ['class' => 'yii\rest\UrlRule', 'controller' => 'user'], + ], +] +``` + +上面的配置主要是为`user`控制器增加一个URL规则。这样, +用户的数据就能通过美化的URL和有意义的http动词进行访问和操作。 + + +## 尝试 <a name="trying-it-out"></a> + +随着以上所做的最小的努力,你已经完成了创建用于访问用户数据 +的RESTful风格的API。您所创建的API包括: + +* `GET /users`: 逐页列出所有用户; +* `HEAD /users`: 显示用户列表的概要信息; +* `POST /users`: 创建一个新用户; +* `GET /users/123`: 返回用户为123的详细信息; +* `HEAD /users/123`: 显示用户 123 的概述信息; +* `PATCH /users/123` and `PUT /users/123`: 更新用户123; +* `DELETE /users/123`: 删除用户123; +* `OPTIONS /users`: 显示关于末端 `/users` 支持的动词; +* `OPTIONS /users/123`: 显示有关末端 `/users/123` 支持的动词。 + +> Info: Yii将在末端使用的控制器的名称自动变为复数。 + +你可以访问你的API用`curl`命令如下, + +``` +$ curl -i -H "Accept:application/json" "http://localhost/users" + +HTTP/1.1 200 OK +Date: Sun, 02 Mar 2014 05:31:43 GMT +Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y +X-Powered-By: PHP/5.4.20 +X-Pagination-Total-Count: 1000 +X-Pagination-Page-Count: 50 +X-Pagination-Current-Page: 1 +X-Pagination-Per-Page: 20 +Link: <http://localhost/users?page=1>; rel=self, + <http://localhost/users?page=2>; rel=next, + <http://localhost/users?page=50>; rel=last +Transfer-Encoding: chunked +Content-Type: application/json; charset=UTF-8 + +[ + { + "id": 1, + ... + }, + { + "id": 2, + ... + }, + ... +] +``` + +试着改变可接受的内容类型为`application/xml`,你会看到结果以XML格式返回: + +``` +$ curl -i -H "Accept:application/xml" "http://localhost/users" + +HTTP/1.1 200 OK +Date: Sun, 02 Mar 2014 05:31:43 GMT +Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y +X-Powered-By: PHP/5.4.20 +X-Pagination-Total-Count: 1000 +X-Pagination-Page-Count: 50 +X-Pagination-Current-Page: 1 +X-Pagination-Per-Page: 20 +Link: <http://localhost/users?page=1>; rel=self, + <http://localhost/users?page=2>; rel=next, + <http://localhost/users?page=50>; rel=last +Transfer-Encoding: chunked +Content-Type: application/xml + +<?xml version="1.0" encoding="UTF-8"?> +<response> + <item> + <id>1</id> + ... + </item> + <item> + <id>2</id> + ... + </item> + ... +</response> +``` + +> Tip: 您还可以通过Web浏览器中输入URL `http://localhost/users` 来访问你的API。 + 尽管如此,你可能需要一些浏览器插件来发送特定的headers请求。 + +如你所见, 在headers响应, 有关于总数,页数的信息,等等。 +还有一些链接,让您导航到其他页面的数据. 例如, `http://localhost/users?page=2` +会给你的用户数据的下一个页面。 + +使用 `fields` 和 `expand` 参数, 您也可以指定哪些字段应该包含在结果内。 +例如, URL `http://localhost/users?fields=id,email` 将只返回 `id` 和 `email` 字段。 + + +> Info: 您可能已经注意到了 `http://localhost/users` 的结果包括一些敏感字段, +> 例如 `password_hash`, `auth_key`。 你肯定不希望这些出现在你的API结果中。 +> 你应该在 [Response Formatting](rest-response-formatting.md) 部分中过滤掉这些字段。 + + +## 总结 <a name="summary"></a> + +使用Yii框架的RESTful风格的API, 在控制器的操作中实现API末端, 使用 +控制器来组织末端接口为一个单一的资源类型。 + +从 [[yii\base\Model]] 类扩展的资源被表示为数据模型。 +如果你在使用(关系或非关系)数据库,推荐您使用 [[yii\db\ActiveRecord|ActiveRecord]] +来表示资源。 + +你可以使用 [[yii\rest\UrlRule]] 简化路由到你的API末端。 + +虽然不是必须的,为了方便维护您的WEB前端和后端, +建议您开发接口作为一个单独的应用程序。 + diff --git a/docs/guide-zh-CN/rest-rate-limiting.md b/docs/guide-zh-CN/rest-rate-limiting.md new file mode 100644 index 0000000..de29f39 --- /dev/null +++ b/docs/guide-zh-CN/rest-rate-limiting.md @@ -0,0 +1,43 @@ +速率限制 +============= + +为防止滥用,你应该考虑增加速率限制到您的API。 +例如,您可以限制每个用户的API的使用是在10分钟内最多100次的API调用。 +如果一个用户同一个时间段内太多的请求被接收, 将返回响应状态代码 429 (这意味着过多的请求)。 + +要启用速率限制, [[yii\web\User::identityClass|user identity class]] 应该实现 [[yii\filters\RateLimitInterface]]. +这个接口需要实现以下三个方法: + +* `getRateLimit()`: 返回允许的请求的最大数目及时间,例如,`[100, 600]` 表示在600秒内最多100次的API调用。 +* `loadAllowance()`: 返回剩余的允许的请求和相应的UNIX时间戳数 + 当最后一次速率限制检查时。 +* `saveAllowance()`: 保存允许剩余的请求数和当前的UNIX时间戳。 + +你可以在user表中使用两列来记录容差和时间戳信息。 +`loadAllowance()` 和 `saveAllowance()` 可以通过实现对符合当前身份验证的用户 +的这两列值的读和保存。为了提高性能,你也可以 +考虑使用缓存或NoSQL存储这些信息。 + +一旦 identity 实现所需的接口, Yii 会自动使用 [[yii\filters\RateLimiter]] +为 [[yii\rest\Controller]] 配置一个行为过滤器来执行速率限制检查。 如果速度超出限制 +该速率限制器将抛出一个 [[yii\web\TooManyRequestsHttpException]]。 你可以在你的 REST +控制器类里配置速率限制, + +```php +public function behaviors() +{ + $behaviors = parent::behaviors(); + $behaviors['rateLimiter']['enableRateLimitHeaders'] = false; + return $behaviors; +} +``` + +当速率限制被激活,默认情况下每个响应将包含以下HTTP头发送 +目前的速率限制信息: + +* `X-Rate-Limit-Limit`: 同一个时间段所允许的请求的最大数目; +* `X-Rate-Limit-Remaining`: 在当前时间段内剩余的请求的数量; +* `X-Rate-Limit-Reset`: 为了得到最大请求数所等待的秒数。 + +你可以禁用这些头信息通过配置 [[yii\filters\RateLimiter::enableRateLimitHeaders]] 为false, +就像在上面的代码示例所示。 diff --git a/docs/guide-zh-CN/rest-response-formatting.md b/docs/guide-zh-CN/rest-response-formatting.md new file mode 100644 index 0000000..125f0a0 --- /dev/null +++ b/docs/guide-zh-CN/rest-response-formatting.md @@ -0,0 +1,150 @@ +响应格式 +=================== + +当处理一个 RESTful API 请求时, 一个应用程序通常需要如下步骤 +来处理响应格式: + +1. 确定可能影响响应格式的各种因素, 例如媒介类型, 语言, 版本, 等等。 + 这个过程也被称为 [content negotiation](http://en.wikipedia.org/wiki/Content_negotiation)。 +2. 资源对象转换为数组, 如在 [Resources](rest-resources.md) 部分中所描述的。 + 通过 [[yii\rest\Serializer]] 来完成。 +3. 通过内容协商步骤将数组转换成字符串。 + [yii\web\ResponseFormatterInterface|response formatters]] 通过 + [yii\web\Response::formatters|response]] 应用程序组件来注册完成。 + + +## 内容协商 <a name="content-negotiation"></a> + +Yii 提供了通过 [[yii\filters\ContentNegotiator]] 过滤器支持内容协商。RESTful API 基于 +控制器类 [[yii\rest\Controller]] 在 `contentNegotiator` 下配备这个过滤器。 +文件管理器提供了涉及的响应格式和语言。 例如, 如果一个 RESTful +API 请求中包含以下 header, + +``` +Accept: application/json; q=1.0, */*; q=0.1 +``` + +将会得到JSON格式的响应,如下: + +``` +$ curl -i -H "Accept: application/json; q=1.0, */*; q=0.1" "http://localhost/users" + +HTTP/1.1 200 OK +Date: Sun, 02 Mar 2014 05:31:43 GMT +Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y +X-Powered-By: PHP/5.4.20 +X-Pagination-Total-Count: 1000 +X-Pagination-Page-Count: 50 +X-Pagination-Current-Page: 1 +X-Pagination-Per-Page: 20 +Link: <http://localhost/users?page=1>; rel=self, + <http://localhost/users?page=2>; rel=next, + <http://localhost/users?page=50>; rel=last +Transfer-Encoding: chunked +Content-Type: application/json; charset=UTF-8 + +[ + { + "id": 1, + ... + }, + { + "id": 2, + ... + }, + ... +] +``` + +幕后,执行一个 RESTful API 控制器动作之前,[[yii\filters\ContentNegotiator]] +filter 将检查 `Accept` HTTP header 在请求时和配置 [[yii\web\Response::format|response format]] +为 `'json'`。 之后的动作被执行并返回得到的资源对象或集合, +[[yii\rest\Serializer]] 将结果转换成一个数组。最后,[[yii\web\JsonResponseFormatter]] +该数组将序列化为JSON字符串,并将其包括在响应主体。 + +默认, RESTful APIs 同时支持JSON和XML格式。为了支持新的格式,你应该 +在 `contentNegotiator` 过滤器中配置 [[yii\filters\ContentNegotiator::formats|formats]] 属性, +类似如下 API 控制器类: + +```php +use yii\web\Response; + +public function behaviors() +{ + $behaviors = parent::behaviors(); + $behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML; + return $behaviors; +} +``` + +`formats` 属性的keys支持 MIME 类型,而 values 必须在 [[yii\web\Response::formatters]] +中支持被响应格式名称。 + + +## 数据序列化 <a name="data-serializing"></a> + +正如我们上面所描述的,[[yii\rest\Serializer]] 负责转换资源的中间件 +对象或集合到数组。它将对象 [[yii\base\ArrayableInterface]] 作为 +[[yii\data\DataProviderInterface]]。 前者主要由资源对象实现, 而 +后者是资源集合。 + +你可以通过设置 [[yii\rest\Controller::serializer]] 属性和一个配置数组。 +例如,有时你可能想通过直接在响应主体内包含分页信息来 +简化客户端的开发工作。这样做,按照如下规则配置 [[yii\rest\Serializer::collectionEnvelope]] +属性: + +```php +use yii\rest\ActiveController; + +class UserController extends ActiveController +{ + public $modelClass = 'app\models\User'; + public $serializer = [ + 'class' => 'yii\rest\Serializer', + 'collectionEnvelope' => 'items', + ]; +} +``` + +那么你的请求可能会得到的响应如下 `http://localhost/users`: + +``` +HTTP/1.1 200 OK +Date: Sun, 02 Mar 2014 05:31:43 GMT +Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y +X-Powered-By: PHP/5.4.20 +X-Pagination-Total-Count: 1000 +X-Pagination-Page-Count: 50 +X-Pagination-Current-Page: 1 +X-Pagination-Per-Page: 20 +Link: <http://localhost/users?page=1>; rel=self, + <http://localhost/users?page=2>; rel=next, + <http://localhost/users?page=50>; rel=last +Transfer-Encoding: chunked +Content-Type: application/json; charset=UTF-8 + +{ + "items": [ + { + "id": 1, + ... + }, + { + "id": 2, + ... + }, + ... + ], + "_links": { + "self": "http://localhost/users?page=1", + "next": "http://localhost/users?page=2", + "last": "http://localhost/users?page=50" + }, + "_meta": { + "totalCount": 1000, + "pageCount": 50, + "currentPage": 1, + "perPage": 20 + } +} +``` diff --git a/docs/guide-zh-CN/rest-routing.md b/docs/guide-zh-CN/rest-routing.md new file mode 100644 index 0000000..d635c95 --- /dev/null +++ b/docs/guide-zh-CN/rest-routing.md @@ -0,0 +1,78 @@ +路由 +======= + +随着资源和控制器类准备,您可以使用URL如 +`http://localhost/index.php?r=user/create`访问资源,类似于你可以用正常的Web应用程序做法。 + +在实践中,你通常要用美观的URL并采取有优势的HTTP动词。 +例如,请求`POST /users`意味着访问`user/create`动作。 +这可以很容易地通过配置`urlManager`应用程序组件来完成 +如下所示: + +```php +'urlManager' => [ + 'enablePrettyUrl' => true, + 'enableStrictParsing' => true, + 'showScriptName' => false, + 'rules' => [ + ['class' => 'yii\rest\UrlRule', 'controller' => 'user'], + ], +] +``` + +相比于URL管理的Web应用程序,上述主要的新东西是通过RESTful API +请求[[yii\rest\UrlRule]]。这个特殊的URL规则类将会 +建立一整套子URL规则来支持路由和URL创建的指定的控制器。 +例如, 上面的代码中是大致按照下面的规则: + +```php +[ + 'PUT,PATCH users/<id>' => 'user/update', + 'DELETE users/<id>' => 'user/delete', + 'GET,HEAD users/<id>' => 'user/view', + 'POST users' => 'user/create', + 'GET,HEAD users' => 'user/index', + 'users/<id>' => 'user/options', + 'users' => 'user/options', +] +``` + +该规则支持下面的API末端: + +* `GET /users`: 逐页列出所有用户; +* `HEAD /users`: 显示用户列表的概要信息; +* `POST /users`: 创建一个新用户; +* `GET /users/123`: 返回用户为123的详细信息; +* `HEAD /users/123`: 显示用户 123 的概述信息; +* `PATCH /users/123` and `PUT /users/123`: 更新用户123; +* `DELETE /users/123`: 删除用户123; +* `OPTIONS /users`: 显示关于末端 `/users` 支持的动词; +* `OPTIONS /users/123`: 显示有关末端 `/users/123` 支持的动词。 + +您可以通过配置 `only` 和 `except` 选项来明确列出哪些行为支持, +哪些行为禁用。例如, + +```php +[ + 'class' => 'yii\rest\UrlRule', + 'controller' => 'user', + 'except' => ['delete', 'create', 'update'], +], +``` + +您也可以通过配置 `patterns` 或 `extraPatterns` 重新定义现有的模式或添加此规则支持的新模式。 +例如,通过末端 `GET /users/search` 可以支持新行为 `search`, 按照如下配置 `extraPatterns` 选项, + +```php +[ + 'class' => 'yii\rest\UrlRule', + 'controller' => 'user', + 'extraPatterns' => [ + 'GET search' => 'search', + ], +``` + +您可能已经注意到控制器ID`user`以复数形式出现在`users`末端。 +这是因为 [[yii\rest\UrlRule]] 能够为他们使用的末端全自动复数化控制器ID。 +您可以通过设置 [[yii\rest\UrlRule::pluralize]] 为false 来禁用此行为,如果您想 +使用一些特殊的名字您可以通过配置 [[yii\rest\UrlRule::controller]] 属性。 diff --git a/docs/guide-zh-CN/rest-versioning.md b/docs/guide-zh-CN/rest-versioning.md new file mode 100644 index 0000000..9e2f4a4 --- /dev/null +++ b/docs/guide-zh-CN/rest-versioning.md @@ -0,0 +1,105 @@ +版本 +========== + +你的API应该是版本化的。不像你完全控制在客户端和服务器端Web应用程序代码, 对于API,您通常没有对API的客户端代码的控制权。 +因此,应该尽可能的保持向后兼容性(BC),如果一些不能向后兼容的变化必须引入 +APIs,你应该增加版本号。你可以参考[Semantic Versioning](http://semver.org/) +有关设计的API的版本号的详细信息。 + +关于如何实现API版本,一个常见的做法是在API的URL中嵌入版本号。 +例如,`http://example.com/v1/users`代表`/users`版本1的API. 另一种API版本化的方法最近用的非常多的是把版本号放入HTTP请求头,通常是通过`Accept`头, +如下: + +``` +// 通过参数 +Accept: application/json; version=v1 +// 通过vendor的内容类型 +Accept: application/vnd.company.myapp-v1+json +``` + +这两种方法都有优点和缺点, 而且关于他们也有很多争论。 +下面我们描述在一种API版本混合了这两种方法的一个实用的策略: + +* 把每个主要版本的API实现在一个单独的模块ID的主版本号 (例如 `v1`, `v2`)。 + 自然,API的url将包含主要的版本号。 +* 在每一个主要版本 (在相应的模块),使用 `Accept` HTTP 请求头 + 确定小版本号编写条件代码来响应相应的次要版本. + +为每个模块提供一个主要版本, 它应该包括资源类和控制器类 +为特定服务版本。 更好的分离代码, 你可以保存一组通用的 +基础资源和控制器类, 并用在每个子类版本模块。 在子类中, +实现具体的代码例如 `Model::fields()`。 + +你的代码可以类似于如下的方法组织起来: + +``` +api/ + common/ + controllers/ + UserController.php + PostController.php + models/ + User.php + Post.php + modules/ + v1/ + controllers/ + UserController.php + PostController.php + models/ + User.php + Post.php + v2/ + controllers/ + UserController.php + PostController.php + models/ + User.php + Post.php +``` + +你的应用程序配置应该这样: + +```php +return [ + 'modules' => [ + 'v1' => [ + 'basePath' => '@app/modules/v1', + ], + 'v2' => [ + 'basePath' => '@app/modules/v2', + ], + ], + 'components' => [ + 'urlManager' => [ + 'enablePrettyUrl' => true, + 'enableStrictParsing' => true, + 'showScriptName' => false, + 'rules' => [ + ['class' => 'yii\rest\UrlRule', 'controller' => ['v1/user', 'v1/post']], + ['class' => 'yii\rest\UrlRule', 'controller' => ['v2/user', 'v2/post']], + ], + ], + ], +]; +``` + +因此,`http://example.com/v1/users`将返回版本1的用户列表,而 +`http://example.com/v2/users`将返回版本2的用户。 + +使用模块, 将不同版本的代码隔离。 通过共用基类和其他类 +跨模块重用代码也是有可能的。 + +为了处理次要版本号, 可以利用内容协商 +功能通过 [[yii\filters\ContentNegotiator|contentNegotiator]] 提供的行为。`contentNegotiator` +行为可设置 [[yii\web\Response::acceptParams]] 属性当它确定 +支持哪些内容类型时。 + +例如, 如果一个请求通过 `Accept: application/json; version=v1`被发送, +内容交涉后,[[yii\web\Response::acceptParams]]将包含值`['version' => 'v1']`. + +基于 `acceptParams` 的版本信息,你可以写条件代码 +如 actions,resource classes,serializers等等。 + +由于次要版本需要保持向后兼容性,希望你的代码不会有 +太多的版本检查。否则,有机会你可能需要创建一个新的主要版本。 diff --git a/docs/guide-zh-CN/start-databases.md b/docs/guide-zh-CN/start-databases.md index c43f4b2..178de25 100644 --- a/docs/guide-zh-CN/start-databases.md +++ b/docs/guide-zh-CN/start-databases.md @@ -27,16 +27,16 @@ CREATE TABLE `country` ( `population` INT(11) NOT NULL DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `Country` VALUES ('AU','Australia',18886000); -INSERT INTO `Country` VALUES ('BR','Brazil',170115000); -INSERT INTO `Country` VALUES ('CA','Canada',1147000); -INSERT INTO `Country` VALUES ('CN','China',1277558000); -INSERT INTO `Country` VALUES ('DE','Germany',82164700); -INSERT INTO `Country` VALUES ('FR','France',59225700); -INSERT INTO `Country` VALUES ('GB','United Kingdom',59623400); -INSERT INTO `Country` VALUES ('IN','India',1013662000); -INSERT INTO `Country` VALUES ('RU','Russia',146934000); -INSERT INTO `Country` VALUES ('US','United States',278357000); +INSERT INTO `country` VALUES ('AU','Australia',18886000); +INSERT INTO `country` VALUES ('BR','Brazil',170115000); +INSERT INTO `country` VALUES ('CA','Canada',1147000); +INSERT INTO `country` VALUES ('CN','China',1277558000); +INSERT INTO `country` VALUES ('DE','Germany',82164700); +INSERT INTO `country` VALUES ('FR','France',59225700); +INSERT INTO `country` VALUES ('GB','United Kingdom',59623400); +INSERT INTO `country` VALUES ('IN','India',1013662000); +INSERT INTO `country` VALUES ('RU','Russia',146934000); +INSERT INTO `country` VALUES ('US','United States',278357000); ``` 于是便有了一个名为 `yii2basic` 的数据库,在这个数据库中有一个包含三个字段的数据表 `country`,表中有十行数据。 diff --git a/docs/guide-zh-CN/start-workflow.md b/docs/guide-zh-CN/start-workflow.md index 539ba1e..d918ad3 100644 --- a/docs/guide-zh-CN/start-workflow.md +++ b/docs/guide-zh-CN/start-workflow.md @@ -1,7 +1,7 @@ 运行应用 ==================== -安装 Yii 后,就有了一个运行中的 Yii 应用,根据配置的不同,可以通过 `http://hostname/basic/web/index.php` 或 `http://hostname/index.php` 访问。本章节将介绍应用的内建功能,如何组织代码,以及一般情况下应用如何处理请求。 +安装 Yii 后,就有了一个可运行的 Yii 应用,根据配置的不同,可以通过 `http://hostname/basic/web/index.php` 或 `http://hostname/index.php` 访问。本章节将介绍应用的内建功能,如何组织代码,以及一般情况下应用如何处理请求。 > 补充:为简单起见,在整个“入门”板块都假定你已经把 `basic/web` 设为 Web 服务器根目录并配置完毕,你访问应用的地址会是 `http://lostname/index.php` 或类似的。请按需调整 URL。 @@ -24,6 +24,7 @@ --------------------- 应用中最重要的目录和文件(假设应用根目录是 `basic`): + ``` basic/ 应用根目录 composer.json Composer 配置文件, 描述包信息 diff --git a/docs/guide-zh-CN/tutorial-core-validators.md b/docs/guide-zh-CN/tutorial-core-validators.md index 4551017..993c8ab 100644 --- a/docs/guide-zh-CN/tutorial-core-validators.md +++ b/docs/guide-zh-CN/tutorial-core-validators.md @@ -121,7 +121,7 @@ public function rules() ```php function foo($model, $attribute) { - // ... compute $value ... + // ... 计算 $value ... return $value; } ``` diff --git a/docs/guide-zh-CN/tutorial-yii-integration.md b/docs/guide-zh-CN/tutorial-yii-integration.md new file mode 100644 index 0000000..0a37eb3 --- /dev/null +++ b/docs/guide-zh-CN/tutorial-yii-integration.md @@ -0,0 +1,144 @@ +引入第三方代码 +============================= + +有时,你可能会需要在 Yii 应用中使用第三方的代码。又或者是你想要在第三方系统中把 Yii 作为类库引用。在下面这个板块中,我们向你展示如何实现这些目标。 + + +## 在 Yii 中使用第三方类库 <a name="using-libs-in-yii"></a> + +要想在 Yii 应用中使用第三方类库,你主要需要确保这些库中的类文件都可以被正常导入或可以被自动加载。 + + +### 使用 Composer 包 <a name="using-composer-packages"></a> + +目前很多第三方的类库都以 [Composer](https://getcomposer.org/) 包的形式发布。你只需要以下两个简单的步骤即可安装他们: + +1. 修改你应用的 `composer.json` 文件,并注明需要安装哪些 Composer 包。 +2. 运行 `php composer.phar install` 安装这些包。 + +这些Composer 包内的类库,可以通过 Composer 的自动加载器实现自动加载。不过请确保你应用的 +[入口脚本](structure-entry-scripts.md)包含以下几行用于加载 Composer 自动加载器的代码: + +```php +// install Composer autoloader (安装 Composer 自动加载器) +require(__DIR__ . '/../vendor/autoload.php'); + +// include Yii class file (加载 Yii 的类文件) +require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); +``` + + +### 使用下载的类库 <a name="using-downloaded-libs"></a> + +若你的类库并未发布为一个 Composer 包,你可以参考以下安装说明来安装它。在大多数情况下,你需要预先下载一个发布文件,并把它解压缩到 +`BasePath/vendor` 目录,这里的 `BasePath` 代指你应用程序自身的 [base path(主目录)](structure-applications.md#basePath)。 + +若该类库包含他自己的类自动加载器,你可以把它安装到你应用的[入口脚本](structure-entry-scripts.md)里。我们推荐你把它的安装代码置于 +`Yii.php` 的导入之前,这样 Yii 的官方自动加载器可以拥有更高的优先级。 + +若一个类库并没有提供自动加载器,但是他的类库命名方式符合 [PSR-4](http://www.php-fig.org/psr/psr-4/) 标准,你可以使用 Yii 官方的自动加载器来自动加载这些类。你只需给他们的每个根命名空间声明一下[根路径别名](concept-aliases.md#defining-aliases)。比如,假设说你已经在目录 `vendor/foo/bar` 里安装了一个类库,且这些类库的根命名空间为 `xyz`。你可以把以下代码放入你的应用配置文件中: + +```php +[ + 'aliases' => [ + '@xyz' => '@vendor/foo/bar', + ], +] +``` + +若以上情形都不符合,最可能是这些类库需要依赖于 PHP 的 include_path 配置,来正确定位并导入类文件。只需参考它的安装说明简单地配置一下 PHP 导入路径即可。 + +最悲催的情形是,该类库需要显式导入每个类文件,你可以使用以下方法按需导入相关类文件: + +* 找出该库内包含哪些类。 +* 在应用的[入口脚本](structure-entry-scripts.md)里的 `Yii::$classMap` 数组中列出这些类,和他们各自对应的文件路径。 + +举例来说, + +```php +Yii::$classMap['Class1'] = 'path/to/Class1.php'; +Yii::$classMap['Class2'] = 'path/to/Class2.php'; +``` + + +## 在第三方系统内使用 Yii <a name="using-yii-in-others"></a> + +因为 Yii 提供了很多牛逼的功能,有时,你可能会想要使用它们中的一些功能用来支持开发或完善某些第三方的系统,比如:WordPress,Joomla,或是用其他 PHP 框架开发的应用程序。举两个例子吧,你可能会想念方便的 [[yii\helpers\ArrayHelper]] 类,或在第三方系统中使用 +[Active Record](db-active-record.md) 活动记录功能。要实现这些目标,你只需两个步骤:安装 Yii,启动 Yii。 + +若这个第三方系统支持 Composer 管理他的依赖文件,你可以直接运行一下命令来安装 Yii: + +``` +php composer.phar require yiisoft/yii2-framework:* +php composer.phar install +``` + +不然的话,你可以[下载](http://www.yiiframework.com/download/) Yii 的发布包,并把它解压到对应系统的 `BasePath/vendor` 目录内。 + +之后,你需要修改该第三方应用的入口脚本,在开头位置添加 Yii 的引入代码: + +```php +require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); + +$yiiConfig = require(__DIR__ . '/../config/yii/web.php'); +new yii\web\Application($yiiConfig); // 千万别在这调用 run() 方法。(笑) +``` + +如你所见,这段代码与典型的 Yii 应用的[入口脚本](structure-entry-scripts.md)非常相似。唯一的不同之处在于在 Yii 应用创建成功之后,并不会紧接着调用 `run()` 方法。因为,`run()` 方法的调用会接管 HTTP 请求的处理流程。(译者注:换言之,这就不是第三方系统而是 Yii 系统了,URL 规则也会跟着换成 Yii 的规则了) + +与 Yii 应用中一样,你可以依据运行该第三方系统的环境,针对性地配置 Yii 应用实例。比如,为了使用[活动记录](db-active-record.md)功能,你需要先用该第三方系统的 DB 连接信息,配置 Yii 的 `db` 应用组件。 + +现在,你就可以使用 Yii 提供的绝大多数功能了。比如,创建 AR 类,并用它们来操作数据库。 + + +## 配合使用 Yii 2 和 Yii 1 <a name="using-both-yii2-yii1"></a> + +如果你之前使用 Yii 1,大概你也有正在运行的 Yii 1 应用吧。不必用 Yii 2 重写整个应用,你也可以通过增添对哪些 +Yii 2 独占功能的支持来增强这个系统。下面我们就来详细描述一下具体的实现过程。 + +> 注意:Yii 2 需要 PHP 5.4+ 的版本。你需要确保你的服务器以及现有应用都可以支持 PHP 5.4。 + +首先,参考前文板块中给出的方法,在已有的应用中安装 Yii 2。 + +之后,如下修改 Yii 1 应用的入口脚步: + +```php +// 导入下面会详细说明的定制 Yii 类文件。 +require(__DIR__ . '/../components/Yii.php'); + +// Yii 2 应用的配置文件 +$yii2Config = require(__DIR__ . '/../config/yii2/web.php'); +new yii\web\Application($yii2Config); // Do NOT call run() + +// Yii 1 应用的配置文件 +$yii1Config = require(__DIR__ . '/../config/yii1/main.php'); +Yii::createWebApplication($yii1Config)->run(); +``` + +因为,Yii 1 和 Yii 2 都包含有 `Yii` 这个类,你应该创建一个定制版的 Yii 来把他们组合起来。上面的代码里包含了的这个定制版的 `Yii` 类,可以用以下代码创建出来: + +```php +$yii2path = '/path/to/yii2'; +require($yii2path . '/BaseYii.php'); // Yii 2.x + +$yii1path = '/path/to/yii1'; +require($yii1path . '/YiiBase.php'); // Yii 1.x + +class Yii extends \yii\BaseYii +{ + // 复制粘贴 YiiBase (1.x) 文件中的代码于此 +} + +Yii::$classMap = include($yii2path . '/classes.php'); + +// 通过 Yii 1 注册 Yii2 的类自动加载器 +Yii::registerAutoloader(['Yii', 'autoload']); +``` + +大功告成!此时,你可以在你代码的任意位置,调用 `Yii::$app` 以访问 Yii 2 的应用实例,而用 +`Yii::app()` 则会返回 Yii 1 的应用实例: + +```php +echo get_class(Yii::app()); // 输出 'CWebApplication' +echo get_class(Yii::$app); // 输出 'yii\web\Application' +```