diff --git a/docs/guide-zh-CN/blocktypes.json b/docs/guide-zh-CN/blocktypes.json deleted file mode 100644 index 67d0c6e..0000000 --- a/docs/guide-zh-CN/blocktypes.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Warning:": "警告:", - "Note:": "注意:", - "Info:": "补充:", - "Tip:": "提示:" -} \ No newline at end of file diff --git a/docs/guide-zh-CN/caching-data.md b/docs/guide-zh-CN/caching-data.md index a1d98ca..45fcfa8 100644 --- a/docs/guide-zh-CN/caching-data.md +++ b/docs/guide-zh-CN/caching-data.md @@ -1,9 +1,12 @@ 数据缓存 ============ -数据缓存是指将一些 PHP 变量存储到缓存中,使用时再从缓存中取回。它也是更高级缓存特性的基础,例如[查询缓存](#query-caching)和[内容缓存](caching-content.md)。 +数据缓存是指将一些 PHP 变量存储到缓存中,使用时再从缓存中取回。 +它也是更高级缓存特性的基础,例如[查询缓存](#query-caching) +和[内容缓存](caching-content.md)。 -如下代码是一个典型的数据缓存使用模式。其中 `$cache` 指向[缓存组件](#cache-components): +如下代码是一个典型的数据缓存使用模式。其中 `$cache` 指向 +[缓存组件](#cache-components): ```php // 尝试从缓存中取回 $data @@ -23,9 +26,13 @@ if ($data === false) { ## 缓存组件 -数据缓存需要**缓存组件**提供支持,它代表各种缓存存储器,例如内存,文件,数据库。 +数据缓存需要**缓存组件**提供支持,它代表各种缓存存储器, +例如内存,文件,数据库。 -缓存组件通常注册为应用程序组件,这样它们就可以在全局进行配置与访问。如下代码演示了如何配置应用程序组件 `cache` 使用两个 [memcached](http://memcached.org/) 服务器: +缓存组件通常注册为应用程序组件,这样 +它们就可以在全局进行配置与访问。 +如下代码演示了如何配置应用程序组件 `cache` 使用 +两个 [memcached](http://memcached.org/) 服务器: ```php 'components' => [ @@ -49,7 +56,9 @@ if ($data === false) { 然后就可以通过 `Yii::$app->cache` 访问上面的缓存组件了。 -由于所有缓存组件都支持同样的一系列 API ,并不需要修改使用缓存的业务代码就能直接替换为其他底层缓存组件,只需在应用配置中重新配置一下就可以。例如,你可以将上述配置修改为使用 [[yii\caching\ApcCache|APC cache]]: +由于所有缓存组件都支持同样的一系列 API ,并不需要修改使用缓存的 +业务代码就能直接替换为其他底层缓存组件,只需在应用配置中重新配置一下就可以。 +例如,你可以将上述配置修改为使用 [[yii\caching\ApcCache|APC cache]]: ```php @@ -60,44 +69,74 @@ if ($data === false) { ], ``` -> Tip: 你可以注册多个缓存组件,很多依赖缓存的类默认调用名为 `cache` 的组件(例如 [[yii\web\UrlManager]])。 +> Tip: 你可以注册多个缓存组件,很多依赖缓存的类默认调用 + 名为 `cache` 的组件(例如 [[yii\web\UrlManager]])。 ### 支持的缓存存储器 Yii 支持一系列缓存存储器,概况如下: -* [[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\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\ZendDataCache]]:使用 + [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm) + 作为底层缓存媒介。 -> Tip: 你可以在同一个应用程序中使用不同的缓存存储器。一个常见的策略是使用基于内存的缓存存储器存储小而常用的数据(例如:统计数据),使用基于文件或数据库的缓存存储器存储大而不太常用的数据(例如:网页内容)。 +> Tip: 你可以在同一个应用程序中使用不同的缓存存储器。一个常见的策略是使用基于内存的缓存存储器 + 存储小而常用的数据(例如:统计数据),使用基于文件 + 或数据库的缓存存储器存储大而不太常用的数据(例如:网页内容)。 ## 缓存 API 所有缓存组件都有同样的基类 [[yii\caching\Cache]] ,因此都支持如下 API: -* [[yii\caching\Cache::get()|get()]]:通过一个指定的键(key)从缓存中取回一项数据。如果该项数据不存在于缓存中或者已经过期/失效,则返回值 false。 +* [[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::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 也会模拟实现。 +> Note: Do not cache a `false` boolean value directly because the [[yii\caching\Cache::get()|get()]] method uses +`false` return value to indicate the data item is not found in the cache. You may put `false` in an array and cache +this array instead to avoid this problem. -由于 [[yii\caching\Cache]] 实现了 PHP `ArrayAccess` 接口,缓存组件也可以像数组那样使用,下面是几个例子: +有些缓存存储器如 MemCache,APC 支持以批量模式取回缓存值,这样可以节省取回 +缓存数据的开支。[[yii\caching\Cache::mget()|mget()]] 和 +[[yii\caching\Cache::madd()|madd()]] API提供对该特性的支持。 +如果底层缓存存储器不支持该特性,Yii 也会模拟实现。 + +由于 [[yii\caching\Cache]] 实现了 PHP `ArrayAccess` 接口,缓存组件也可以像数组那样使用, +下面是几个例子: ```php $cache['var1'] = $value1; // 等价于: $cache->set('var1', $value1); @@ -107,11 +146,15 @@ $value2 = $cache['var2']; // 等价于: $value2 = $cache->get('var2'); ### 缓存键 -存储在缓存中的每项数据都通过键作唯一识别。当你在缓存中存储一项数据时,必须为它指定一个键,稍后从缓存中取回数据时,也需要提供相应的键。 +存储在缓存中的每项数据都通过键作唯一识别。当你在缓存中存储一项 +数据时,必须为它指定一个键,稍后从缓存中取回数据时,也需要提供 +相应的键。 -你可以使用一个字符串或者任意值作为一个缓存键。当键不是一个字符串时,它将会自动被序列化为一个字符串。 +你可以使用一个字符串或者任意值作为一个缓存键。当键不是一个字符串时,它将会自动 +被序列化为一个字符串。 -定义一个缓存键常见的一个策略就是在一个数组中包含所有的决定性因素。例如,[[yii\db\Schema]] 使用如下键存储一个数据表的结构信息。 +定义一个缓存键常见的一个策略就是在一个数组中包含所有的决定性因素。 +例如,[[yii\db\Schema]] 使用如下键存储一个数据表的结构信息。 ```php [ @@ -124,7 +167,9 @@ $value2 = $cache['var2']; // 等价于: $value2 = $cache->get('var2'); 如你所见,该键包含了可唯一指定一个数据库表所需的所有必要信息。 -当同一个缓存存储器被用于多个不同的应用时,应该为每个应用指定一个唯一的缓存键前缀以避免缓存键冲突。可以通过配置 [[yii\caching\Cache::keyPrefix]] 属性实现。例如,在应用配置中可以编写如下代码: +当同一个缓存存储器被用于多个不同的应用时,应该为每个应用指定一个唯一的缓存键前缀 +以避免缓存键冲突。可以通过配置 [[yii\caching\Cache::keyPrefix]] +属性实现。例如,在应用配置中可以编写如下代码: ```php 'components' => [ @@ -140,7 +185,12 @@ $value2 = $cache['var2']; // 等价于: $value2 = $cache->get('var2'); ### 缓存过期 -默认情况下,缓存中的数据会永久存留,除非它被某些缓存策略强制移除(例如:缓存空间已满,最老的数据会被移除)。要改变此特性,你可以在调用 [[yii\caching\Cache::set()|set()]] 存储一项数据时提供一个过期时间参数。该参数代表这项数据在缓存中可保持有效多少秒。当你调用 [[yii\caching\Cache::get()|get()]] 取回数据时,如果它已经过了超时时间,该方法将返回 false,表明在缓存中找不到这项数据。例如: +默认情况下,缓存中的数据会永久存留,除非它被某些缓存策略强制移除(例如: +缓存空间已满,最老的数据会被移除)。要改变此特性,你可以在调用 +[[yii\caching\Cache::set()|set()]] 存储一项数据时提供一个过期时间参数。 +该参数代表这项数据在缓存中可保持有效多少秒。当你调用 +[[yii\caching\Cache::get()|get()]] 取回数据时,如果它已经过了超时时间, +该方法将返回 false,表明在缓存中找不到这项数据。例如: ```php // 将数据在缓存中保留 45 秒 @@ -157,9 +207,15 @@ if ($data === false) { ### 缓存依赖 -除了超时设置,缓存数据还可能受到**缓存依赖**的影响而失效。例如,[[yii\caching\FileDependency]] 代表对一个文件修改时间的依赖。这个依赖条件发生变化也就意味着相应的文件已经被修改。因此,缓存中任何过期的文件内容都应该被置为失效状态,对 [[yii\caching\Cache::get()|get()]] 的调用都应该返回 false。 +除了超时设置,缓存数据还可能受到**缓存依赖**的影响而失效。 +例如,[[yii\caching\FileDependency]] 代表对一个文件修改时间的依赖。 +这个依赖条件发生变化也就意味着相应的文件已经被修改。 +因此,缓存中任何过期的文件内容都应该被置为失效状态, +对 [[yii\caching\Cache::get()|get()]] 的调用都应该返回 false。 -缓存依赖用 [[yii\caching\Dependency]] 的派生类所表示。当调用 [[yii\caching\Cache::set()|set()]] 在缓存中存储一项数据时,可以同时传递一个关联的缓存依赖对象。例如: +缓存依赖用 [[yii\caching\Dependency]] 的派生类所表示。当调用 +[[yii\caching\Cache::set()|set()]] 在缓存中存储一项数据时, +可以同时传递一个关联的缓存依赖对象。例如: ```php // 创建一个对 example.txt 文件修改时间的缓存依赖 @@ -181,44 +237,150 @@ $data = $cache->get($key); - [[yii\caching\DbDependency]]:如果指定 SQL 语句的查询结果发生了变化,则依赖改变。 - [[yii\caching\ExpressionDependency]]:如果指定的 PHP 表达式执行结果发生变化,则依赖改变。 - [[yii\caching\FileDependency]]:如果文件的最后修改时间发生变化,则依赖改变。 -- [[yii\caching\GroupDependency]]:将一项缓存数据标记到一个组名,你可以通过调用 [[yii\caching\GroupDependency::invalidate()]] 一次性将相同组名的缓存全部置为失效状态。 +- [[yii\caching\GroupDependency]]:将一项缓存数据标记到一个组名,你可以通过调用 + [[yii\caching\GroupDependency::invalidate()]] 一次性将相同组名的缓存全部置为失效状态。 + +> Note: Avoid using [[yii\caching\Cache::exists()|exists()]] method along with dependencies. It does not check whether + the dependency associated with the cached data, if there is any, has changed. So a call to + [[yii\caching\Cache::get()|get()]] may return `false` while [[yii\caching\Cache::exists()|exists()]] returns `true`. ## 查询缓存 -查询缓存是一个建立在数据缓存之上的特殊缓存特性。它用于缓存数据库查询的结果。 +查询缓存是一个建立在数据缓存之上的特殊缓存特性。 +它用于缓存数据库查询的结果。 -查询缓存需要一个 [[yii\db\Connection|数据库连接]] 和一个有效的 `cache` 应用组件。查询缓存的基本用法如下,假设 `$db` 是一个 [[yii\db\Connection]] 实例: +查询缓存需要一个 [[yii\db\Connection|数据库连接]] 和一个有效的 `cache` 应用组件。 +查询缓存的基本用法如下,假设 `$db` 是一个 [[yii\db\Connection]] 实例: ```php -$duration = 60; // 缓存查询结果60秒 -$dependency = ...; // 可选的缓存依赖 - -$db->beginCache($duration, $dependency); +$result = $db->cache(function ($db) { -// ...这儿执行数据库查询... + // the result of the SQL query will be served from the cache + // if query caching is enabled and the query result is found in the cache + return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne(); -$db->endCache(); +}); ``` -如你所见,`beginCache()` 和 `endCache()` 中间的任何查询结果都会被缓存起来。如果缓存中找到了同样查询的结果,则查询会被跳过,直接从缓存中提取结果。 - 查询缓存可以用于 [ActiveRecord](db-active-record.md) 和 [DAO](db-dao.md)。 -> Info: 有些 DBMS (例如:[MySQL](http://dev.mysql.com/doc/refman/5.1/en/query-cache.html))也支持数据库服务器端的查询缓存。你可以选择使用任一查询缓存机制。上文所述的查询缓存的好处在于你可以指定更灵活的缓存依赖因此可能更加高效。 +```php +$result = Customer::getDb()->cache(function ($db) { + return Customer::find()->where(['id' => 1])->one(); +}); + + +> Info: 有些 DBMS (例如:[MySQL](http://dev.mysql.com/doc/refman/5.1/en/query-cache.html) ) + 也支持数据库服务器端的查询缓存。你可以选择使用任一查询缓存机制。 + 上文所述的查询缓存的好处在于你可以指定更灵活的缓存 + 依赖因此可能更加高效。 + + +### Cache Flushing + +When you need to invalidate all the stored cache data, you can call [[yii\caching\Cache::flush()]]. + +You can flush the cache from the console by calling `yii cache/flush` as well. + - `yii cache`: lists the available caches in application + - `yii cache/flush cache1 cache2`: flushes the cache components `cache1`, `cache2` (you can pass multiple component + names separated with space) + - `yii cache/flush-all`: flushes all cache components in the application + +> Info: Console application uses a separate configuration file by default. Ensure, that you have the same caching +components in your web and console application configs to reach the proper effect. ### 配置 -查询缓存有两个通过 [[yii\db\Connection]] 设置的配置项: +Query caching has three global configurable options through [[yii\db\Connection]]: + +* [[yii\db\Connection::enableQueryCache|enableQueryCache]]: whether to turn on or off query caching. + It defaults to true. Note that to effectively turn on query caching, you also need to have a valid + cache, as specified by [[yii\db\Connection::queryCache|queryCache]]. +* [[yii\db\Connection::queryCacheDuration|queryCacheDuration]]: this represents the number of seconds + that a query result can remain valid in the cache. You can use 0 to indicate a query result should + remain in the cache forever. This property is the default value used when [[yii\db\Connection::cache()]] + is called without specifying a duration. +* [[yii\db\Connection::queryCache|queryCache]]: 缓存应用组件的 ID。默认为 `'cache'`。 + 只有在设置了一个有效的缓存应用组件时,查询缓存才会有效。 + + +### Usages + +You can use [[yii\db\Connection::cache()]] if you have multiple SQL queries that need to take advantage of +query caching. The usage is as follows, + +```php +$duration = 60; // cache query results for 60 seconds. +$dependency = ...; // optional dependency + +$result = $db->cache(function ($db) { + + // ... perform SQL queries here ... + + return $result; + +}, $duration, $dependency); +``` -* [[yii\db\Connection::queryCacheDuration|queryCacheDuration]]: 查询结果在缓存中的有效期,以秒表示。如果在调用 [[yii\db\Connection::beginCache()]] 时传递了一个显式的时值参数,则配置中的有效期时值会被覆盖。 -* [[yii\db\Connection::queryCache|queryCache]]: 缓存应用组件的 ID。默认为 `'cache'`。只有在设置了一个有效的缓存应用组件时,查询缓存才会有效。 +Any SQL queries in the anonymous function will be cached for the specified duration with the specified dependency. +If the result of a query is found valid in the cache, the query will be skipped and the result will be served +from the cache instead. If you do not specify the `$duration` parameter, the value of +[[yii\db\Connection::queryCacheDuration|queryCacheDuration]] will be used instead. + +Sometimes within `cache()`, you may want to disable query caching for some particular queries. You can use +[[yii\db\Connection::noCache()]] in this case. + +```php +$result = $db->cache(function ($db) { + + // SQL queries that use query caching + + $db->noCache(function ($db) { + + // SQL queries that do not use query caching + + }); + + // ... + + return $result; +}); +``` + +If you just want to use query caching for a single query, you can call [[yii\db\Command::cache()]] when building +the command. For example, + +```php +// use query caching and set query cache duration to be 60 seconds +$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne(); +``` + +You can also use [[yii\db\Command::noCache()]] to disable query caching for a single command. For example, + +```php +$result = $db->cache(function ($db) { + + // SQL queries that use query caching + + // do not use query caching for this command + $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne(); + + // ... + + return $result; +}); +``` ### 限制条件 -当查询结果中含有资源句柄时,查询缓存无法使用。例如,在有些 DBMS 中使用了 `BLOB` 列的时候,缓存结果会为该数据列返回一个资源句柄。 +当查询结果中含有资源句柄时,查询缓存无法使用。例如, +在有些 DBMS 中使用了 `BLOB` 列的时候,缓存结果会为 +该数据列返回一个资源句柄。 -有些缓存存储器有大小限制。例如,memcache 限制每条数据最大为 1MB。因此,如果查询结果的大小超出了该限制,则会导致缓存失败。 +有些缓存存储器有大小限制。例如,memcache 限制每条数据 +最大为 1MB。因此,如果查询结果的大小超出了该限制, +则会导致缓存失败。 diff --git a/docs/guide-zh-CN/caching-fragment.md b/docs/guide-zh-CN/caching-fragment.md index 88698f6..4b9727e 100644 --- a/docs/guide-zh-CN/caching-fragment.md +++ b/docs/guide-zh-CN/caching-fragment.md @@ -1,7 +1,9 @@ 片段缓存 ================ -片段缓存指的是缓存页面内容中的某个片段。例如,一个页面显示了逐年销售额的摘要表格,可以把表格缓存下来,以消除每次请求都要重新生成表格的耗时。片段缓存是基于[数据缓存](caching-data.md)实现的。 +片段缓存指的是缓存页面内容中的某个片段。例如,一个页面显示了逐年销售额的摘要表格, +可以把表格缓存下来,以消除每次请求都要重新生成表格 +的耗时。片段缓存是基于[数据缓存](caching-data.md)实现的。 在[视图](structure-views.md)中使用以下结构启用片段缓存: @@ -14,18 +16,27 @@ if ($this->beginCache($id)) { } ``` -调用 [[yii\base\View::beginCache()|beginCache()]] 和 [[yii\base\View::endCache()|endcache()]] 方法包裹内容生成逻辑。如果缓存中存在该内容,[[yii\base\View::beginCache()|beginCache()]] 方法将渲染内容并返回 false,因此将跳过内容生成逻辑。否则,内容生成逻辑被执行,一直执行到 [[yii\base\View::endCache()|endCache()]] 时,生成的内容将被捕获并存储在缓存中。 +调用 [[yii\base\View::beginCache()|beginCache()]] 和 [[yii\base\View::endCache()|endcache()]] +方法包裹内容生成逻辑。如果缓存中存在该内容,[[yii\base\View::beginCache()|beginCache()]] +方法将渲染内容并返回 false,因此将跳过内容生成逻辑。否则, +内容生成逻辑被执行,一直执行到 [[yii\base\View::endCache()|endCache()]] +时,生成的内容将被捕获并存储在缓存中。 和[[数据缓存]](caching-data.md)一样,每个片段缓存也需要全局唯一的 `$id` 标记。 ## 缓存选项 -如果要为片段缓存指定额外配置项,请通过向 [[yii\base\View::beginCache()|beginCache()]] 方法第二个参数传递配置数组。在框架内部,该数组将被用来配置一个 [[yii\widget\FragmentCache]] 小部件用以实现片段缓存功能。 +如果要为片段缓存指定额外配置项,请通过向 [[yii\base\View::beginCache()|beginCache()]] +方法第二个参数传递配置数组。在框架内部, +该数组将被用来配置一个 [[yii\widget\FragmentCache]] +小部件用以实现片段缓存功能。 ### 过期时间(duration) -或许片段缓存中最常用的一个配置选项就是 [[yii\widgets\FragmentCache::duration|duration]] 了。它指定了内容被缓存的秒数。以下代码缓存内容最多一小时: +或许片段缓存中最常用的一个配置选项就是 +[[yii\widgets\FragmentCache::duration|duration]] 了。 +它指定了内容被缓存的秒数。以下代码缓存内容最多一小时: ```php if ($this->beginCache($id, ['duration' => 3600])) { @@ -36,14 +47,17 @@ if ($this->beginCache($id, ['duration' => 3600])) { } ``` -如果该选项未设置,则默认为 0,永不过期。 +如果该选项未设置,it will take the default value 60, which means the cached content will expire in 60 seconds. ### 依赖 -和[[数据缓存]](caching-data.md)一样,片段缓存的内容一样可以设置缓存依赖。例如一段被缓存的文章,是否重新缓存取决于它是否被修改过。 +和[[数据缓存]](caching-data.md)一样,片段缓存的内容一样可以设置缓存依赖。例如 +一段被缓存的文章,是否重新缓存取决于它是否被修改过。 -通过设置 [[yii\widgets\FragmentCache::dependency|dependency]] 选项来指定依赖,该选项的值可以是一个 [[yii\caching\Dependency]] 类的派生类,也可以是创建缓存对象的配置数组。以下代码指定了一个片段缓存,它依赖于 `update_at` 字段是否被更改过的。 +通过设置 [[yii\widgets\FragmentCache::dependency|dependency]] 选项来指定依赖, +该选项的值可以是一个 [[yii\caching\Dependency]] 类的派生类,也可以是创建缓存对象的配置数组。 +以下代码指定了一个片段缓存,它依赖于 `update_at` 字段是否被更改过的。 ```php $dependency = [ @@ -62,9 +76,13 @@ if ($this->beginCache($id, ['dependency' => $dependency])) { ### 变化 -缓存的内容可能需要根据一些参数的更改而变化。例如一个 Web 应用支持多语言,同一段视图代码也许需要生成多个语言的内容。因此可以设置缓存根据应用当前语言而变化。 +缓存的内容可能需要根据一些参数的更改而变化。例如一个 Web 应用 +支持多语言,同一段视图代码也许需要生成多个语言的内容。 +因此可以设置缓存根据应用当前语言而变化。 -通过设置 [[yii\widgets\FragmentCache::variations|variations]] 选项来指定变化,该选项的值应该是一个标量,每个标量代表不同的变化系数。例如设置缓存根据当前语言而变化可以用以下代码: +通过设置 [[yii\widgets\FragmentCache::variations|variations]] 选项来指定变化, +该选项的值应该是一个标量,每个标量代表不同的变化系数。 +例如设置缓存根据当前语言而变化可以用以下代码: ```php if ($this->beginCache($id, ['variations' => [Yii::$app->language]])) { @@ -78,7 +96,10 @@ if ($this->beginCache($id, ['variations' => [Yii::$app->language]])) { ### 开关 -有时你可能只想在特定条件下开启片段缓存。例如,一个显示表单的页面,可能只需要在初次请求时缓存表单(通过 GET 请求)。随后请求所显示(通过 POST 请求)的表单不该使用缓存,因为此时表单中可能包含用户输入内容。鉴于此种情况,可以使用 [[yii\widgets\FragmentCache::enabled|enabled]] 选项来指定缓存开关,如下所示: +有时你可能只想在特定条件下开启片段缓存。例如,一个显示表单的页面,可能只 +需要在初次请求时缓存表单(通过 GET 请求)。随后请求所显示(通过 POST 请求) +的表单不该使用缓存,因为此时表单中可能包含用户输入内容。鉴于此种情况,可以使用 +[[yii\widgets\FragmentCache::enabled|enabled]] 选项来指定缓存开关,如下所示: ```php if ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) { @@ -92,7 +113,9 @@ if ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) { ## 缓存嵌套 -片段缓存可以被嵌套使用。一个片段缓存可以被另一个包裹。例如,评论被缓存在里层,同时整个评论的片段又被缓存在外层的文章中。以下代码展示了片段缓存的嵌套使用: +片段缓存可以被嵌套使用。一个片段缓存可以被另一个包裹。 +例如,评论被缓存在里层,同时整个评论的片段又被缓存在 +外层的文章中。以下代码展示了片段缓存的嵌套使用: ```php if ($this->beginCache($id1)) { @@ -112,18 +135,28 @@ if ($this->beginCache($id1)) { } ``` -可以为嵌套的缓存设置不同的配置项。例如,内层缓存和外层缓存使用不同的过期时间。甚至当外层缓存的数据过期失效了,内层缓存仍然可能提供有效的片段缓存数据。但是,反之则不然。如果外层片段缓存没有过期而被视为有效,此时即使内层片段缓存已经失效,它也将继续提供同样的缓存副本。因此,你必须谨慎处理缓存嵌套中的过期时间和依赖,否则外层的片段很有可能返回的是不符合你预期的失效数据。 - +可以为嵌套的缓存设置不同的配置项。例如,内层缓存和外层缓存使用 +不同的过期时间。甚至当外层缓存的数据过期失效了,内层缓存仍然 +可能提供有效的片段缓存数据。但是,反之则不然。如果外层片段缓存 +没有过期而被视为有效,此时即使内层片段缓存已经失效,它也将继续 +提供同样的缓存副本。因此,你必须谨慎处理缓存嵌套中的过期时间和 +依赖,否则外层的片段很有可能返回的是不符合你预期的失效数据。 > 译注:外层的失效时间应该短于内层,外层的依赖条件应该低于内层,以确保最小的片段,返回的是最新的数据。 - ## 动态内容 -使用片段缓存时,可能会遇到一大段较为静态的内容中有少许动态内容的情况。例如,一个显示着菜单栏和当前用户名的页面头部。还有一种可能是缓存的内容可能包含每次请求都需要执行的 PHP 代码(例如注册资源包的代码)。这两个问题都可以使用**动态内容**功能解决。 +使用片段缓存时,可能会遇到一大段较为静态的内容中有少许动态内 +容的情况。例如,一个显示着菜单栏和当前用户名的页面头部。还有 +一种可能是缓存的内容可能包含每次请求都需要执行的 PHP 代码 +(例如注册资源包的代码)。这两个问题都可以 +使用 **动态内容** 功能解决。 -动态内容的意思是这部分输出的内容不该被缓存,即便是它被包裹在片段缓存中。为了使内容保持动态,每次请求都执行 PHP 代码生成,即使这些代码已经被缓存了。 +动态内容的意思是这部分输出的内容不该被缓存,即便是它被包裹 +在片段缓存中。为了使内容保持动态,每次请求都执行 PHP 代码 +生成,即使这些代码已经被缓存了。 -可以在片段缓存中调用 [[yii\base\View::renderDynamic()]] 去插入动态内容,如下所示: +可以在片段缓存中调用 [[yii\base\View::renderDynamic()]] 去 +插入动态内容,如下所示: ```php if ($this->beginCache($id1)) { @@ -138,4 +171,6 @@ if ($this->beginCache($id1)) { } ``` -[[yii\base\View::renderDynamic()|renderDynamic()]] 方法接受一段 PHP 代码作为参数。代码的返回值被看作是动态内容。这段代码将在每次请求时都执行,无论其外层的片段缓存是否被存储。 +[[yii\base\View::renderDynamic()|renderDynamic()]] 方法接受 +一段 PHP 代码作为参数。代码的返回值被看作是动态内容。这段代 +码将在每次请求时都执行,无论其外层的片段缓存是否被存储。 diff --git a/docs/guide-zh-CN/caching-http.md b/docs/guide-zh-CN/caching-http.md index 2284197..000ec3a 100644 --- a/docs/guide-zh-CN/caching-http.md +++ b/docs/guide-zh-CN/caching-http.md @@ -1,9 +1,12 @@ HTTP 缓存 ============ -除了前面章节讲到的服务器端缓存外, Web 应用还可以利用客户端缓存去节省相同页面内容的生成和传输时间。 +除了前面章节讲到的服务器端缓存外, Web 应用还可以利用客户端 +缓存去节省相同页面内容的生成和传输时间。 -通过配置 [[yii\filters\HttpCache]] 过滤器,控制器操作渲染的内容就能缓存在客户端。[[yii\filters\HttpCache|HttpCache]] 过滤器仅对 `GET` 和 `HEAD` 请求生效,它能为这些请求设置三种与缓存有关的 HTTP 头。 +通过配置 [[yii\filters\HttpCache]] 过滤器,控制器操作渲染的内容就能 +缓存在客户端。[[yii\filters\HttpCache|HttpCache]] 过滤器仅对 `GET` +和 `HEAD` 请求生效,它能为这些请求设置三种与缓存有关的 HTTP 头。 * [[yii\filters\HttpCache::lastModified|Last-Modified]] * [[yii\filters\HttpCache::etagSeed|Etag]] @@ -14,7 +17,9 @@ HTTP 缓存 `Last-Modified` 头使用时间戳标明页面自上次客户端缓存后是否被修改过。 -通过配置 [[yii\filters\HttpCache::lastModified]] 属性向客户端发送 `Last-Modified` 头。该属性的值应该为 PHP callable 类型,返回的是页面修改时的 Unix 时间戳。该 callable 的参数和返回值应该如下: +通过配置 [[yii\filters\HttpCache::lastModified]] 属性向客户端发送 +`Last-Modified` 头。该属性的值应该为 PHP callable 类型,返回的是页 +面修改时的 Unix 时间戳。该 callable 的参数和返回值应该如下: ```php /** @@ -43,14 +48,23 @@ public function behaviors() } ``` -上述代码表明 HTTP 缓存只在 `index` 操作时启用。它会基于页面最后修改时间生成一个 `Last-Modified` HTTP 头。当浏览器第一次访问 `index` 页时,服务器将会生成页面并发送至客户端浏览器。之后客户端浏览器在页面没被修改期间访问该页,服务器将不会重新生成页面,浏览器会使用之前客户端缓存下来的内容。因此服务端渲染和内容传输都将省去。 +上述代码表明 HTTP 缓存只在 `index` 操作时启用。它会基于页面 +最后修改时间生成一个 `Last-Modified` HTTP 头。当浏览器第一 +次访问 `index` 页时,服务器将会生成页面并发送至客户端浏览器。 +之后客户端浏览器在页面没被修改期间访问该页,服务器将不会重新 +生成页面,浏览器会使用之前客户端缓存下来的内容。因此服务端 +渲染和内容传输都将省去。 ## `ETag` 头 -“Entity Tag”(实体标签,简称 ETag)使用一个哈希值表示页面内容。如果页面被修改过,哈希值也会随之改变。通过对比客户端的哈希值和服务器端生成的哈希值,浏览器就能判断页面是否被修改过,进而决定是否应该重新传输内容。 +“Entity Tag”(实体标签,简称 ETag)使用一个哈希值表示页面内容。如果页面 +被修改过,哈希值也会随之改变。通过对比客户端的哈希值和服务器端生成的哈 +希值,浏览器就能判断页面是否被修改过,进而决定是否应该重新传输内容。 -通过配置 [[yii\filters\HttpCache::etagSeed]] 属性向客户端发送 `ETag` 头。该属性的值应该为 PHP callable 类型,返回的是一段种子字符用来生成 ETag 哈希值。该 callable 的参数和返回值应该如下: +通过配置 [[yii\filters\HttpCache::etagSeed]] 属性向客户端发送 +`ETag` 头。该属性的值应该为 PHP callable 类型,返回的是一段种 +子字符用来生成 ETag 哈希值。该 callable 的参数和返回值应该如下: ```php /** @@ -79,24 +93,31 @@ public function behaviors() } ``` -上述代码表明 HTTP 缓存只在 `view` 操作时启用。它会基于用户请求的标题和内容生成一个 `ETag` HTTP 头。当浏览器第一次访问 `view` 页时,服务器将会生成页面并发送至客户端浏览器。之后客户端浏览器标题和内容没被修改在期间访问该页,服务器将不会重新生成页面,浏览器会使用之前客户端缓存下来的内容。因此服务端渲染和内容传输都将省去。 +上述代码表明 HTTP 缓存只在 `view` 操作时启用。它会基于用户 +请求的标题和内容生成一个 `ETag` HTTP 头。当浏览器第一次访问 +`view` 页时,服务器将会生成页面并发送至客户端浏览器。之后 +客户端浏览器标题和内容没被修改在期间访问该页,服务器将不会 +重新生成页面,浏览器会使用之前客户端缓存下来的内容。 +因此服务端渲染和内容传输都将省去。 -ETag 相比 `Last-Modified` 能实现更复杂和更精确的缓存策略。例如,当站点切换到另一个主题时可以使 ETag 失效。 +ETag 相比 `Last-Modified` 能实现更复杂和更精确的缓存策略。 +例如,当站点切换到另一个主题时可以使 ETag 失效。 -复杂的 Etag 生成种子可能会违背使用 `HttpCache` 的初衷而引起不必要的性能开销,因为响应每一次请求都需要重新计算 Etag。请试着找出一个最简单的表达式去触发 Etag 失效。 - - -<<<<<<< .merge_file_a06996 -> 注意:为了遵循 [RFC 7232(HTTP 1.1 协议)](http://tools.ietf.org/html/rfc7232#section-2.4),如果同时配置了 `ETag` 和 `Last-Modified` 头,`HttpCache` 将会同时发送它们。并且如果客户端同时发送 `If-None-Match` 头和 `If-Modified-Since` 头,则只有前者会被接受。 -======= -> Note: 为了遵循 [RFC 7232(HTTP 1.1 协议)](http://tools.ietf.org/html/rfc7232#section-2.4),如果同时配置了 `ETag` 和 `Last-Modified` 头,`HttpCache` 将会同时发送它们。并且如果客户端同时发送 `If-None-Match` 头和 `If-Modified-Since` 头,则只有前者会被接受。 ->>>>>>> .merge_file_a01948 +复杂的 Etag 生成种子可能会违背使用 `HttpCache` 的初衷而引起 +不必要的性能开销,因为响应每一次请求都需要重新计算 Etag。 +请试着找出一个最简单的表达式去触发 Etag 失效。 +> 注意:为了遵循 [RFC 7232(HTTP 1.1 协议)](http://tools.ietf.org/html/rfc7232#section-2.4) , + 如果同时配置了 `ETag` 和 `Last-Modified` 头,`HttpCache` 将会同时 + 发送它们。并且如果客户端同时发送 `If-None-Match` 头 + 和 `If-Modified-Since` 头,则只有前者会被接受。 ## `Cache-Control` 头 -`Cache-Control` 头指定了页面的常规缓存策略。可以通过配置 [[yii\filters\HttpCache::cacheControlHeader]] 属性发送相应的头信息。默认发送以下头: +`Cache-Control` 头指定了页面的常规缓存策略。可以通过配置 +[[yii\filters\HttpCache::cacheControlHeader]] 属性发送相 +应的头信息。默认发送以下头: ``` Cache-Control: public, max-age=3600 @@ -104,10 +125,19 @@ Cache-Control: public, max-age=3600 ## 会话缓存限制器 -当页面使 session 时,PHP 将会按照 PHP.INI 中所设置的 `session.cache_limiter` 值自动发送一些缓存相关的 HTTP 头。这些 HTTP 头有可能会干扰你原本设置的 `HttpCache` 或让其失效。为了避免此问题,默认情况下 `HttpCache` 禁止自动发送这些头。想改变这一行为,可以配置 [[yii\filters\HttpCache::sessionCacheLimiter]] 属性。该属性接受一个字符串值,包括 `public`,`private`,`private_no_expire`,和 `nocache`。请参考 PHP 手册中的[缓存限制器](http://www.php.net/manual/en/function.session-cache-limiter.php)了解这些值的含义。 +当页面使 session 时,PHP 将会按照 PHP.INI 中所设置的`session.cache_limiter` +值自动发送一些缓存相关的 HTTP 头。这些 HTTP 头有可能会干扰你原本设置的 +`HttpCache` 或让其失效。为了避免此问题,默认情况下 `HttpCache` 禁止 +自动发送这些头。想改变这一行为,可以配置 +[[yii\filters\HttpCache::sessionCacheLimiter]] 属性。该属性接受一个 +字符串值,包括 `public`,`private`,`private_no_expire`, +和 `nocache`。请参考 PHP 手册中的[缓存限制器](http://www.php.net/manual/en/function.session-cache-limiter.php) +了解这些值的含义。 ## SEO 影响 -搜索引擎趋向于遵循站点的缓存头。因为一些爬虫的抓取频率有限制,启用缓存头可以可以减少重复请求数量,增加爬虫抓取效率(译者:大意如此,但搜索引擎的排名规则不了解,好的缓存策略应该是可以为用户体验加分的)。 +搜索引擎趋向于遵循站点的缓存头。因为一些爬虫的 +抓取频率有限制,启用缓存头可以可以减少重复请求 +数量,增加爬虫抓取效率(译者:大意如此,但搜索引擎的排名规则不了解,好的缓存策略应该是可以为用户体验加分的)。 diff --git a/docs/guide-zh-CN/caching-overview.md b/docs/guide-zh-CN/caching-overview.md index b9345cd..1a06d70 100644 --- a/docs/guide-zh-CN/caching-overview.md +++ b/docs/guide-zh-CN/caching-overview.md @@ -1,15 +1,17 @@ 缓存 ======= -缓存是提升 Web 应用性能简便有效的方式。通过将相对静态的数据存储到缓存并在收到请求时取回缓存,应用程序便节省了每次重新生成这些数据所需的时间。 +缓存是提升 Web 应用性能简便有效的方式。通过将相对静态的数据 +存储到缓存并在收到请求时取回缓存,应用程序便节省了每次重新生 +成这些数据所需的时间。 -缓存可以应用在 Web 应用程序的任何层级任何位置。在服务器端,在较的低层面,缓存可能用于存储基础数据,例如从数据库中取出的最新文章列表;在较高的层面,缓存可能用于存储一段或整个 Web 页面,例如最新文章的渲染结果。在客户端,HTTP 缓存可能用于将最近访问的页面内容存储到浏览器缓存中。 +缓存可以应用在 Web 应用程序的任何层级任何位置。在服务器端, +在较的低层面,缓存可能用于存储基础数据,例如从数据库中取 +出的最新文章列表;在较高的层面,缓存可能用于存储一段或整 +个 Web 页面,例如最新文章的渲染结果。在客户端,HTTP 缓存 +可能用于将最近访问的页面内容存储到浏览器缓存中。 -<<<<<<< .merge_file_a06272 Yii 支持如上所有缓存机制: -======= -Yii 支持如下所有缓存机制: ->>>>>>> .merge_file_a04468 * [数据缓存](caching-data.md) * [片段缓存](caching-fragment.md) diff --git a/docs/guide-zh-CN/caching-page.md b/docs/guide-zh-CN/caching-page.md index a7275a2..f395274 100644 --- a/docs/guide-zh-CN/caching-page.md +++ b/docs/guide-zh-CN/caching-page.md @@ -1,9 +1,11 @@ 页面缓存 ============ -页面缓存指的是在服务器端缓存整个页面的内容。随后当同一个页面被请求时,内容将从缓存中取出,而不是重新生成。 +页面缓存指的是在服务器端缓存整个页面的内容。随后当同一个页面 +被请求时,内容将从缓存中取出,而不是重新生成。 -页面缓存由 [[yii\filters\PageCache]] 类提供支持,该类是一个[过滤器](structure-filters.md)。它可以像这样在控制器类中使用: +页面缓存由 [[yii\filters\PageCache]] 类提供支持,该类是一个 +[过滤器](structure-filters.md)。它可以像这样在控制器类中使用: ```php public function behaviors() @@ -25,16 +27,14 @@ public function behaviors() } ``` -上述代码表示页面缓存只在 `index` 操作时启用,页面内容最多被缓存 60 秒,会随着当前应用的语言更改而变化。如果文章总数发生变化则缓存的页面会失效。 - -如你所见,页面缓存和[片段缓存](caching-fragment.md)极其相似。它们都支持 `duration`,`dependencies`,`variations` 和 `enabled` 配置选项。它们的主要区别是页面缓存是由[过滤器](structure-filters.md)实现,而片段缓存则是一个[小部件](structure-widgets.md)。 - -你可以在使用页面缓存的同时,使用[片段缓存](caching-fragment.md)和[动态内容](caching-fragment.md#dynamic-content)。 - - - - - +上述代码表示页面缓存只在 `index` 操作时启用,页面内容最多被缓存 +60 秒,会随着当前应用的语言更改而变化。如果文章总数发生变化则缓 +存的页面会失效。 +如你所见,页面缓存和[片段缓存](caching-fragment.md)极其相似。它们都支持 `duration`,`dependencies`, +`variations` 和 `enabled` 配置选项。它们的主要区别是页面缓存是由[过滤器](structure-filters.md)实现, +而片段缓存则是一个[小部件](structure-widgets.md)。 +你可以在使用页面缓存的同时,使用[片段缓存](caching-fragment.md)和 +[动态内容](caching-fragment.md#dynamic-content)。 diff --git a/docs/guide-zh-CN/concept-aliases.md b/docs/guide-zh-CN/concept-aliases.md index f2f05ba..31ce278 100644 --- a/docs/guide-zh-CN/concept-aliases.md +++ b/docs/guide-zh-CN/concept-aliases.md @@ -1,8 +1,12 @@ 别名(Aliases) ======= -别名用来表示文件路径和 URL,这样就避免了在代码中硬编码一些绝对路径和 URL。一个别名必须以 `@` 字符开头,以区别于传统的文件路径和 URL。Yii 预定义了大量可用的别名。例如,别名 `@yii` 指的是 Yii 框架本身的安装目录,而 `@web` 表示的是当前运行应用的根 URL。 +别名用来表示文件路径和 URL,这样就避免了在代码中硬编码一些绝对路径 +和 URL。一个别名必须以 `@` 字符开头,以区别于传统的文件路径和 URL。 +Alias defined without leading `@` will be prefixed with `@` character. +Yii 预定义了大量可用的别名。例如,别名 `@yii` 指的是 Yii 框架本身的安装 +目录,而 `@web` 表示的是当前运行应用的根 URL。 定义别名 ---------------- @@ -17,13 +21,12 @@ Yii::setAlias('@foo', '/path/to/foo'); Yii::setAlias('@bar', 'http://www.example.com'); ``` -<<<<<<< .merge_file_a06852 > 注意:别名所指向的文件路径或 URL 不一定是真实存在的文件或资源。 -======= -> Note: 别名所指向的文件路径或 URL 不一定是真实存在的文件或资源。 ->>>>>>> .merge_file_a04208 -可以通过在一个别名后面加斜杠 `/` 和一至多个路径分段生成新别名(无需调用 [[Yii::setAlias()]])。我们把通过 [[Yii::setAlias()]] 定义的别名称为**根别名**,而用他们衍生出去的别名成为**衍生别名**。例如,`@foo` 就是根别名,而 `@foo/bar/file.php` 是一个衍生别名。 +可以通过在一个别名后面加斜杠 `/` 和一至多个路径分段生成新别名 +(无需调用 [[Yii::setAlias()]])。我们把通过 [[Yii::setAlias()]] +定义的别名称为**根别名**,而用他们衍生出去的别名成为**衍生别名**。 +例如,`@foo` 就是根别名,而 `@foo/bar/file.php` 是一个衍生别名。 你还可以用别名去定义新别名(根别名与衍生别名均可): @@ -31,7 +34,10 @@ Yii::setAlias('@bar', 'http://www.example.com'); Yii::setAlias('@foobar', '@foo/bar'); ``` -根别名通常在[引导](runtime-bootstrapping.md)阶段定义。比如你可以在[入口脚本](structure-entry-scripts.md)里调用 [[Yii::setAlias()]]。为了方便起见,[应用](structure-applications.md)提供了一个名为 `aliases` 的可写属性,你可以在应用[配置](concept-configurations.md)中设置它,就像这样: +根别名通常在[引导](runtime-bootstrapping.md)阶段定义。比如你可以在 +[入口脚本](structure-entry-scripts.md)里调用 [[Yii::setAlias()]]。为 +了方便起见,[应用](structure-applications.md)提供了一个名为 `aliases`的 +可写属性,你可以在应用[配置](concept-configurations.md)中设置它,就像这样: ```php return [ @@ -47,7 +53,8 @@ return [ 解析别名 ----------------- -你可以调用 [[Yii::getAlias()]] 命令来解析根别名到对应的文件路径或 URL。同样的页面也可以用于解析衍生别名。例如: +你可以调用 [[Yii::getAlias()]] 命令来解析根别名到对应的文件路径或 URL。 +同样的页面也可以用于解析衍生别名。例如: ```php echo Yii::getAlias('@foo'); // 输出:/path/to/foo @@ -55,15 +62,15 @@ echo Yii::getAlias('@bar'); // 输出:http://www.example.com echo Yii::getAlias('@foo/bar/file.php'); // 输出:/path/to/foo/bar/file.php ``` -由衍生别名所解析出的文件路径和 URL 是通过替换掉衍生别名中的根别名部分得到的。 +由衍生别名所解析出的文件路径和 URL 是通过替换掉衍生别名 +中的根别名部分得到的。 -<<<<<<< .merge_file_a06852 > 注意:[[Yii::getAlias()]] 并不检查结果路径/URL 所指向的资源是否真实存在。 -======= -> Note: [[Yii::getAlias()]] 并不检查结果路径/URL 所指向的资源是否真实存在。 ->>>>>>> .merge_file_a04208 -根别名可能也会包含斜杠 `/`。[[Yii::getAlias()]] 足够智能到判断一个别名中的哪部分是根别名,因此能正确解析文件路径/URL。例如: + +根别名可能也会包含斜杠 `/`。[[Yii::getAlias()]] +足够智能到判断一个别名中的哪部分是根别名,因此 +能正确解析文件路径/URL。例如: ```php Yii::setAlias('@foo', '/path/to/foo'); @@ -78,7 +85,10 @@ echo Yii::getAlias('@foo/bar/file.php'); // 输出:/path2/bar/file.php 使用别名 ------------- -别名在 Yii 的很多地方都会被正确识别,无需调用 [[Yii::getAlias()]] 来把它们转换为路径/URL。例如,[[yii\caching\FileCache::cachePath]] 能同时接受文件路径或是指向文件路径的别名,因为通过 `@` 前缀能区分它们。 +别名在 Yii 的很多地方都会被正确识别,无需调用 [[Yii::getAlias()]] +来把它们转换为路径/URL。例如,[[yii\caching\FileCache::cachePath]] +能同时接受文件路径或是指向文件路径的别名, +因为通过 `@` 前缀能区分它们。 ```php use yii\caching\FileCache; @@ -96,21 +106,29 @@ $cache = new FileCache([ Yii 预定义了一系列别名来简化常用路径和 URL 的使用: -- `@yii` - `BaseYii.php` 文件所在的目录(也被称为框架安装目录) -- `@app` - 当前运行的应用 [[yii\base\Application::basePath|根路径(base path)]] -- `@runtime` - 当前运行的应用的 [[yii\base\Application::runtimePath|运行环境(runtime)路径]] -- `@vendor` - [[yii\base\Application::vendorPath|Composer 供应商目录]] -- `@webroot` - 当前运行应用的 Web 入口目录 -- `@web` - 当前运行应用的根 URL +- `@yii`, `BaseYii.php` 文件所在的目录(也被称为框架安装目录) +- `@app`, 当前运行的应用 [[yii\base\Application::basePath|根路径(base path)]] +- `@runtime`, 当前运行的应用的 [[yii\base\Application::runtimePath|运行环境(runtime)路径]]. Defaults to `@app/runtime`. +- `@webroot`, the Web root directory of the currently running Web application. It is determined based on the directory + containing the [entry script](structure-entry-scripts.md). +- `@web`,the base URL of the currently running Web application. It has the same value as [[yii\web\Request::baseUrl]]. +- `@vendor`,[[yii\base\Application::vendorPath|Composer 供应商目录]]. Defaults to `@app/vendor`. +- `@bower`, the root directory that contains [bower packages](http://bower.io/). Defaults to `@vendor/bower`. +- `@npm`, the root directory that contains [npm packages](https://www.npmjs.org/). Defaults to `@vendor/npm`. -`@yii` 别名是在[入口脚本](structure-entry-scripts.md)里包含 `Yii.php` 文件时定义的,其他的别名都是在[配置应用](concept-configurations.md)的时候,于应用的构造方法内定义的。 +`@yii` 别名是在[入口脚本](structure-entry-scripts.md)里包含 `Yii.php` 文件时定义的, +其他的别名都是在[配置应用](concept-configurations.md)的时候, +于应用的构造方法内定义的。 扩展的别名 ----------------- -每一个通过 Composer 安装的 [扩展](structure-extensions.md) 都自动添加了一个别名。该别名会以该扩展在 `composer.json` 文件中所声明的根命名空间为名,且他直接代指该包的根目录。例如,如果你安装有 `yiisoft/yii2-jui` 扩展,会自动得到 `@yii/jui` 别名,它定义于[引导启动](runtime-bootstrapping.md)阶段: +每一个通过 Composer 安装的 [扩展](structure-extensions.md) 都自动添加了一个别名。 +该别名会以该扩展在 `composer.json` 文件中所声明的根命名空间为名,且他直接代指该包的根目录。 +例如,如果你安装有 `yiisoft/yii2-jui` 扩展, +会自动得到 `@yii/jui` 别名,它定义于[引导启动](runtime-bootstrapping.md)阶段: ```php Yii::setAlias('@yii/jui', 'VendorPath/yiisoft/yii2-jui'); -``` \ No newline at end of file +``` diff --git a/docs/guide-zh-CN/concept-autoloading.md b/docs/guide-zh-CN/concept-autoloading.md index 6d88e41..df9864d 100644 --- a/docs/guide-zh-CN/concept-autoloading.md +++ b/docs/guide-zh-CN/concept-autoloading.md @@ -1,13 +1,13 @@ 类自动加载(Autoloading) ================= -Yii 依靠[类自动加载机制](http://www.php.net/manual/en/language.oop5.autoload.php)来定位和包含所需的类文件。它提供一个高性能且完美支持[PSR-4 标准](https://github.com/php-fig/fig-standards/blob/master/proposed/psr-4-autoloader/psr-4-autoloader.md)([中文汉化](https://github.com/hfcorriez/fig-standards/blob/zh_CN/%E6%8E%A5%E5%8F%97/PSR-4-autoloader.md))的自动加载器。该自动加载器会在引入框架文件 `Yii.php` 时安装好。 +Yii 依靠[类自动加载机制](http://www.php.net/manual/en/language.oop5.autoload.php)来定位和包含所需的类文件。 +它提供一个高性能且完美支持[PSR-4 标准](https://github.com/php-fig/fig-standards/blob/master/proposed/psr-4-autoloader/psr-4-autoloader.md) +([中文汉化](https://github.com/hfcorriez/fig-standards/blob/zh_CN/%E6%8E%A5%E5%8F%97/PSR-4-autoloader.md))的自动加载器。 +该自动加载器会在引入框架文件 `Yii.php` 时安装好。 -<<<<<<< .merge_file_a06860 -> 注意:为了简化叙述,本篇文档中我们只会提及类的自动加载。不过,要记得文中的描述同样也适用于接口和Trait(特质)的自动加载哦。 -======= -> Note: 为了简化叙述,本篇文档中我们只会提及类的自动加载。不过,要记得文中的描述同样也适用于接口和Trait(特质)的自动加载哦。 ->>>>>>> .merge_file_a06468 +> 注意:为了简化叙述,本篇文档中我们只会提及类的自动加载。 +不过,要记得文中的描述同样也适用于接口和Trait(特质)的自动加载哦。 使用 Yii 自动加载器 @@ -23,18 +23,28 @@ Yii 依靠[类自动加载机制](http://www.php.net/manual/en/language.oop5.aut $classFile = Yii::getAlias('@' . str_replace('\\', '/', $className) . '.php'); ``` -举例来说,若某个类名为 `foo\bar\MyClass`,对应类的文件路径[别名](concept-aliases.md)会是 `@foo/bar/MyClass.php`。为了让该别名能被正确解析为文件路径,`@foo` 或 `@foo/bar` +举例来说,若某个类名为 `foo\bar\MyClass`,对应类的文件路径[别名](concept-aliases.md)会是 `@foo/bar/MyClass.php`。 +为了让该别名能被正确解析为文件路径,`@foo` 或 `@foo/bar` 中的一个必须是[根别名](concept-aliases.md#defining-aliases)。 -当我们使用[基本应用模版](start-installation.md)时,可以把你的类放置在顶级命名空间 `app` 下,这样它们就可以被 Yii 自动加载,而无需定义一个新的别名。这是因为 `@app` 本身是一个[预定义别名](concept-aliases.md#predefined-aliases),且类似于 `app\components\MyClass` 这样的类名,基于我们刚才所提到的算法,可以正确解析出 `AppBasePath/components/MyClass.php` 路径。 +当我们使用[基本应用模版](start-installation.md)时,可以把你的类放置在顶级命名空间 `app` 下, +这样它们就可以被 Yii 自动加载,而无需定义一个新的别名。 +这是因为 `@app` 本身是一个[预定义别名](concept-aliases.md#predefined-aliases),且类似于 `app\components\MyClass` 这样的类名, +基于我们刚才所提到的算法,可以正确解析出 `AppBasePath/components/MyClass.php` 路径。 -在[高级应用模版](tutorial-advanced-app.md)里,每一逻辑层级会使用他自己的根别名。比如,前端层会使用 `@frontend` 而后端层会使用 `@backend`。因此,你可以把前端的类放在 `frontend` 命名空间,而后端的类放在 `backend`。 这样这些类就可以被 Yii 自动加载了。 +在[高级应用模版](tutorial-advanced-app.md)里,每一逻辑层级会使用他自己的根别名。 +比如,前端层会使用 `@frontend` 而后端层会使用 `@backend`。 +因此,你可以把前端的类放在 `frontend` 命名空间,而后端的类放在 `backend`。 +这样这些类就可以被 Yii 自动加载了。 类映射表(Class Map) --------- -Yii 类自动加载器支持**类映射表**功能,该功能会建立一个从类的名字到类文件路径的映射。当自动加载器加载一个文件时,他首先检查映射表里有没有该类。如果有,对应的文件路径就直接加载了,省掉了进一步的检查。这让类的自动加载变得超级快。事实上所有的 Yii 核心类都是这样加载的。 +Yii 类自动加载器支持**类映射表**功能,该功能会建立一个从类的名字到类文件路径的映射。 +当自动加载器加载一个文件时,他首先检查映射表里有没有该类。 +如果有,对应的文件路径就直接加载了,省掉了进一步的检查。 +这让类的自动加载变得超级快。事实上所有的 Yii 核心类都是这样加载的。 你可以用 `Yii::$classMap` 方法向映射表中添加类, @@ -42,34 +52,41 @@ Yii 类自动加载器支持**类映射表**功能,该功能会建立一个从 Yii::$classMap['foo\bar\MyClass'] = 'path/to/MyClass.php'; ``` -[别名](concept-aliases.md)可以被用于指定类文件的路径。你应该在[引导启动](runtime-bootstrapping.md)的过程中设置类映射表,这样映射表就可以在你使用具体类之前就准备好。 +[别名](concept-aliases.md)可以被用于指定类文件的路径。 +你应该在[引导启动](runtime-bootstrapping.md)的过程中设置类映射表,这样映射表就可以在你使用具体类之前就准备好。 用其他自动加载器 ----------------------- -因为 Yii 完全支持 Composer 管理依赖包,所以推荐你也同时安装 Composer 的自动加载器,如果你用了一些自带自动加载器的第三方类库,你应该也安装下它们。 +因为 Yii 完全支持 Composer 管理依赖包, +所以推荐你也同时安装 Composer 的自动加载器,如果你用了一些自带自动加载器的第三方类库, +你应该也安装下它们。 -当你同时使用其他自动加载器和 Yii 自动加载器时,应该在其他自动加载器安装成功**之后**,再包含 `Yii.php` 文件。这将使 Yii 成为第一个响应任何类自动加载请求的自动加载器。举例来说,以下代码提取自[基本应用模版](start-installation.md)的[入口脚本](structure-entry-scripts.md) 。第一行安装了 Composer 的自动加载器,第二行才是 Yii 的自动加载器: +当你同时使用其他自动加载器和 Yii 自动加载器时,应该在其他自动加载器安装成功**之后**,再包含 `Yii.php` 文件。 +这将使 Yii 成为第一个响应任何类自动加载请求的自动加载器。 +举例来说,以下代码提取自[基本应用模版](start-installation.md)的[入口脚本](structure-entry-scripts.md) 。 +第一行安装了 Composer 的自动加载器, +第二行才是 Yii 的自动加载器: ```php require(__DIR__ . '/../vendor/autoload.php'); require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); ``` -你也可以只使用 Composer 的自动加载,而不用 Yii 的自动加载。不过这样做的话,类的加载效率会下降,且你必须遵循 Composer 所设定的规则,从而让你的类满足可以被自动加载的要求。 +你也可以只使用 Composer 的自动加载,而不用 Yii 的自动加载。 +不过这样做的话,类的加载效率会下降,且你必须遵循 Composer 所设定的规则, +从而让你的类满足可以被自动加载的要求。 -<<<<<<< .merge_file_a06860 -> 补充:若你不想要使用 Yii 的自动加载器,你必须创建一个你自己版本的 `Yii.php` 文件,并把它包含进你的[入口脚本](structure-entry-scripts.md)里。 -======= -> Info: 若你不想要使用 Yii 的自动加载器,你必须创建一个你自己版本的 `Yii.php` 文件,并把它包含进你的[入口脚本](structure-entry-scripts.md)里。 ->>>>>>> .merge_file_a06468 +> 补充:若你不想要使用 Yii 的自动加载器,你必须创建一个你自己版本的 `Yii.php` 文件, + 并把它包含进你的[入口脚本](structure-entry-scripts.md)里。 自动加载扩展类 ----------------------------- Yii 自动加载器支持自动加载[扩展](structure-extensions.md)的类。唯一的要求是它需要在 `composer.json` -文件里正确地定义 `autoload` 部分。请参考 [Composer 文档(英文)](https://getcomposer.org/doc/04-schema.md#autoload)([中文汉化](https://github.com/5-say/composer-doc-cn/blob/master/cn-introduction/04-schema.md#autoload)),来了解如何正确描述 `autoload` 的更多细节。 +文件里正确地定义 `autoload` 部分。请参考 [Composer 文档(英文)](https://getcomposer.org/doc/04-schema.md#autoload) +([中文汉化](https://github.com/5-say/composer-doc-cn/blob/master/cn-introduction/04-schema.md#autoload)),来了解如何正确描述 `autoload` 的更多细节。 在你不使用 Yii 的自动加载器时,Composer 的自动加载器仍然可以帮你自动加载扩展内的类。 diff --git a/docs/guide-zh-CN/concept-behaviors.md b/docs/guide-zh-CN/concept-behaviors.md index 5b4ed00..03ddccb 100644 --- a/docs/guide-zh-CN/concept-behaviors.md +++ b/docs/guide-zh-CN/concept-behaviors.md @@ -1,7 +1,12 @@ 行为 ========= -行为是 [[yii\base\Behavior]] 或其子类的实例。行为,也称为 [mixins](http://en.wikipedia.org/wiki/Mixin),可以无须改变类继承关系即可增强一个已有的 [[yii\base\Component|组件]] 类功能。当行为附加到组件后,它将“注入”它的方法和属性到组件,然后可以像访问组件内定义的方法和属性一样访问它们。此外,行为通过组件能响应被触发的[事件](basic-events.md),从而自定义或调整组件正常执行的代码。 +行为是 [[yii\base\Behavior]] 或其子类的实例。 +行为,也称为 [mixins](http://en.wikipedia.org/wiki/Mixin), +可以无须改变类继承关系即可增强一个已有的 [[yii\base\Component|组件]] 类功能。 +当行为附加到组件后,它将“注入”它的方法和属性到组件,然后可以像访问组件内定义的方法和属性一样访问它们。 +此外,行为通过组件能响应被触发的[事件](basic-events.md), +从而自定义或调整组件正常执行的代码。 定义行为 @@ -37,19 +42,22 @@ class MyBehavior extends Behavior } ``` -以上代码定义了行为类 `app\components\MyBehavior` 并为要附加行为的组件提供了两个属性 `prop1` 、 `prop2` 和一个方法 `foo()` 。注意属性 `prop2` 是通过 getter `getProp2()` 和 setter `setProp2()` 定义的。能这样用是因为 [[yii\base\Object]] 是 [[yii\base\Behavior]] 的祖先类,此祖先类支持用 getter 和 setter 方法定义[属性](basic-properties.md) +以上代码定义了行为类 `app\components\MyBehavior` 并为要附加行为的组件提供了两个属性 `prop1` 、 `prop2` 和一个方法 `foo()` 。 +注意属性 `prop2` 是通过 getter `getProp2()` 和 setter `setProp2()` 定义的。 +能这样用是因为 [[yii\base\Object]] 是 [[yii\base\Behavior]] 的祖先类,此祖先类支持用 getter 和 setter 方法定义[属性](basic-properties.md) + +Because this class is a behavior, when it is attached to a component, that component will then also have the `prop1` and `prop2` properties and the `foo()` method. -<<<<<<< .merge_file_a05016 > 提示:在行为内部可以通过 [[yii\base\Behavior::owner]] 属性访问行为已附加的组件。 -======= -> Tip: 在行为内部可以通过 [[yii\base\Behavior::owner]] 属性访问行为已附加的组件。 ->>>>>>> .merge_file_a00824 +> Note: In case [[yii\base\Behavior::__get()]] and/or [[yii\base\Behavior::__set()]] method of behavior is overridden you +need to override [[yii\base\Behavior::canGetProperty()]] and/or [[yii\base\Behavior::canSetProperty()]] as well. 处理事件 ------------- -如果要让行为响应对应组件的事件触发,就应覆写 [[yii\base\Behavior::events()]] 方法,如: +如果要让行为响应对应组件的事件触发, +就应覆写 [[yii\base\Behavior::events()]] 方法,如: ```php namespace app\components; @@ -75,26 +83,30 @@ class MyBehavior extends Behavior } ``` -[[yii\base\Behavior::events()|events()]] 方法返回事件列表和相应的处理器。上例声明了 [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] 事件和它的处理器 `beforeValidate()` 。当指定一个事件处理器时,要使用以下格式之一: +[[yii\base\Behavior::events()|events()]] 方法返回事件列表和相应的处理器。 +上例声明了 [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] 事件和它的处理器 `beforeValidate()` 。 +当指定一个事件处理器时,要使用以下格式之一: * 指向行为类的方法名的字符串,如上例所示; * 对象或类名和方法名的数组,如 `[$object, 'methodName']`; * 匿名方法。 -处理器的格式如下,其中 `$event` 指向事件参数。关于事件的更多细节请参考[事件](basic-events.md): +处理器的格式如下,其中 `$event` 指向事件参数。 +关于事件的更多细节请参考[事件](basic-events.md): ```php function ($event) { } ``` - -附加行为 +附加行为 ---------- 可以静态或动态地附加行为到[[yii\base\Component|组件]]。前者在实践中更常见。 -要静态附加行为,覆写行为要附加的组件类的 [[yii\base\Component::behaviors()|behaviors()]] 方法即可。[[yii\base\Component::behaviors()|behaviors()]] 方法应该返回行为[配置](basic-configs.md)列表。每个行为配置可以是行为类名也可以是配置数组。如: +要静态附加行为,覆写行为要附加的组件类的 [[yii\base\Component::behaviors()|behaviors()]] 方法即可。 +[[yii\base\Component::behaviors()|behaviors()]] 方法应该返回行为[配置](basic-configs.md)列表。 +每个行为配置可以是行为类名也可以是配置数组。如: ```php namespace app\models; @@ -131,7 +143,9 @@ class User extends ActiveRecord } ``` -通过指定行为配置数组相应的键可以给行为关联一个名称。这种行为称为**命名行为**。上例中,有两个命名行为:`myBehavior2` 和 `myBehavior4` 。如果行为没有指定名称就是**匿名行为**。 +通过指定行为配置数组相应的键可以给行为关联一个名称。这种行为称为**命名行为**。 +上例中,有两个命名行为:`myBehavior2` 和 `myBehavior4` 。如果行为没有指定名称就是**匿名行为**。 + 要动态附加行为,在对应组件里调用 [[yii\base\Component::attachBehavior()]] 方法即可,如: @@ -175,15 +189,16 @@ $component->attachBehaviors([ ] ``` -详情请参考[配置](concept-configurations.md#configuration-format)章节。 - +详情请参考 +[配置](concept-configurations.md#configuration-format)章节。 使用行为 --------------- 使用行为,必须像前文描述的一样先把它附加到 [[yii\base\Component|component]] 类或其子类。一旦行为附加到组件,就可以直接使用它。 -行为附加到组件后,可以通过组件访问一个行为的**公共**成员变量或 getter 和 setter 方法定义的[属性](concept-properties.md): +行为附加到组件后,可以通过组件访问一个行为的**公共**成员变量或 getter 和 setter 方法定义的 +[属性](concept-properties.md): ```php // "prop1" 是定义在行为类的属性 @@ -198,11 +213,14 @@ $component->prop1 = $value; $component->foo(); ``` -如你所见,尽管 `$component` 未定义 `prop1` 和 `foo()` ,它们用起来也像组件自己定义的一样。 +如你所见,尽管 `$component` 未定义 `prop1` 和 `foo()` , +它们用起来也像组件自己定义的一样。 -如果两个行为都定义了一样的属性或方法,并且它们都附加到同一个组件,那么**首先**附加上的行为在属性或方法被访问时有优先权。 +如果两个行为都定义了一样的属性或方法,并且它们都附加到同一个组件, +那么**首先**附加上的行为在属性或方法被访问时有优先权。 -附加行为到组件时的命名行为,可以使用这个名称来访问行为对象,如下所示: +附加行为到组件时的命名行为, +可以使用这个名称来访问行为对象,如下所示: ```php $behavior = $component->getBehavior('myBehavior'); @@ -234,7 +252,9 @@ $component->detachBehaviors(); 使用 `TimestampBehavior` ---------------------------- -最后以 [[yii\behaviors\TimestampBehavior]] 的讲解来结尾,这个行为支持在 [[yii\db\ActiveRecord|Active Record]] 存储时自动更新它的时间戳属性。 +最后以 [[yii\behaviors\TimestampBehavior]] 的讲解来结尾, +这个行为支持在 [[yii\db\ActiveRecord|Active Record]] +存储时自动更新它的时间戳属性。 首先,附加这个行为到计划使用该行为的 [[yii\db\ActiveRecord|Active Record]] 类: @@ -257,6 +277,8 @@ class User extends ActiveRecord ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'], ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'], ], + // if you're using datetime instead of UNIX timestamp: + // 'value' => new Expression('NOW()'), ], ]; } @@ -265,11 +287,14 @@ class User extends ActiveRecord 以上指定的行为数组: -* 当记录插入时,行为将当前时间戳赋值给 `created_at` 和 `updated_at` 属性; -* 当记录更新时,行为将当前时间戳赋值给 `updated_at` 属性。 +* 当记录插入时, + 行为将当前的 UNIX 时间戳赋值给 `created_at` 和 `updated_at` 属性; +* 当记录更新时,行为将当前的 UNIX 时间戳赋值给 `updated_at` 属性。 +> Note: For the above implementation to work with MySQL database, please declare the columns(`created_at`, `updated_at`) as int(11) for being UNIX timestamp. -保存 `User` 对象,将会发现它的 `created_at` 和 `updated_at` 属性自动填充了当前时间戳: +保存 `User` 对象, +将会发现它的 `created_at` 和 `updated_at` 属性自动填充了当前时间戳: ```php $user = new User; @@ -278,36 +303,58 @@ $user->save(); echo $user->created_at; // 显示当前时间戳 ``` -[[yii\behaviors\TimestampBehavior|TimestampBehavior]] 行为还提供了一个有用的方法 [[yii\behaviors\TimestampBehavior::touch()|touch()]],这个方法能将当前时间戳赋值给指定属性并保存到数据库: +[[yii\behaviors\TimestampBehavior|TimestampBehavior]] 行为还提供了一个有用的方法 +[[yii\behaviors\TimestampBehavior::touch()|touch()]], +这个方法能将当前时间戳赋值给指定属性并保存到数据库: ```php $user->touch('login_time'); ``` +Other behaviors +--------------- + +There are several built-in and external behaviors available: -与 PHP traits 的比较 -------------------- +- [[yii\behaviors\BlameableBehavior]] - automatically fills the specified attributes with the current user ID. +- [[yii\behaviors\SluggableBehavior]] - automatically fills the specified attribute with a value that can be used + as a slug in a URL. +- [[yii\behaviors\AttributeBehavior]] - automatically assigns a specified value to one or multiple attributes of + an ActiveRecord object when certain events happen. +- [yii2tech\ar\softdelete\SoftDeleteBehavior](https://github.com/yii2tech/ar-softdelete) - provides methods to soft-delete + and soft-restore ActiveRecord i.e. set flag or status which marks record as deleted. +- [yii2tech\ar\position\PositionBehavior](https://github.com/yii2tech/ar-position) - allows managing records order in an + integer field by providing reordering methods. -尽管行为在 "注入" 属性和方法到主类方面类似于 [traits](http://www.php.net/traits) ,它们在很多方面却不相同。如上所述,它们各有利弊。它们更像是互补的而不是相互替代。 +Comparing Behaviors with Traits +---------------------- +While behaviors are similar to [traits](http://www.php.net/traits) in that they both "inject" their +properties and methods to the primary class, they differ in many aspects. As explained below, they +both have pros and cons. They are more like complements to each other rather than alternatives. -### 行为的优势 -行为类像普通类支持继承。另一方面,traits 可以视为 PHP 语言支持的复制粘贴功能,它不支持继承。 +### Reasons to Use Behaviors -行为无须修改组件类就可动态附加到组件或移除。要使用 traits,必须修改使用它的类。 +Behavior classes, like normal classes, support inheritance. Traits, on the other hand, +can be considered as language-supported copy and paste. They do not support inheritance. -行为是可配置的而 traits 不能。 +Behaviors can be attached and detached to a component dynamically without requiring modification of the component class. +To use a trait, you must modify the code of the class using it. -行为以响应事件来自定义组件的代码执行。 +Behaviors are configurable while traits are not. -当不同行为附加到同一组件产生命名冲突时,这个冲突通过先附加行为的优先权自动解决。而由不同 traits 引发的命名冲突需要通过手工重命名冲突属性或方法来解决。 +Behaviors can customize the code execution of a component by responding to its events. +When there can be name conflicts among different behaviors attached to the same component, the conflicts are +automatically resolved by prioritizing the behavior attached to the component first. +Name conflicts caused by different traits requires manual resolution by renaming the affected +properties or methods. -### traits 的优势 -traits 比起行为更高效,因为行为是对象,消耗时间和内存。 +### Reasons to Use Traits -IDE 对 traits 更友好,因为它们是语言结构。 +Traits are much more efficient than behaviors as behaviors are objects that take both time and memory. +IDEs are more friendly to traits as they are a native language construct. diff --git a/docs/guide-zh-CN/concept-components.md b/docs/guide-zh-CN/concept-components.md index b9feaa3..4044c9e 100644 --- a/docs/guide-zh-CN/concept-components.md +++ b/docs/guide-zh-CN/concept-components.md @@ -1,13 +1,16 @@ 组件(Component) ========== -组件是 Yii 应用的主要基石。是 [[yii\base\Component]] 类或其子类的实例。三个用以区分它和其它类的主要功能有: +组件是 Yii 应用的主要基石。是 [[yii\base\Component]] 类或其子类的实例。 +三个用以区分它和其它类的主要功能有: * [属性(Property)](concept-properties.md) * [事件(Event)](concept-events.md) * [行为(Behavior)](concept-behaviors.md) -或单独使用,或彼此配合,这些功能的应用让 Yii 的类变得更加灵活和易用。以小部件 [[yii\jui\DatePicker|日期选择器]] 来举例,这是个方便你在 [视图](structure-view.md) 中生成一个交互式日期选择器的 UI 组件: +或单独使用,或彼此配合,这些功能的应用让 Yii 的类变得更加灵活和易用。 +以小部件 [[yii\jui\DatePicker|日期选择器]] 来举例, +这是个方便你在 [视图](structure-view.md) 中生成一个交互式日期选择器的 UI 组件: ```php use yii\jui\DatePicker; @@ -23,11 +26,17 @@ echo DatePicker::widget([ 这个小部件继承自 [[yii\base\Component]],它的各项属性改写起来会很容易。 -正是因为组件功能的强大,他们比常规的对象(Object)稍微重量级一点,因为他们要使用额外的内存和 CPU 时间来处理 [事件](concept-events.md) 和 [行为](concept-behaviors.md) 。如果你不需要这两项功能,可以继承 [[yii\base\Object]] 而不是 [[yii\base\Component]]。这样组件可以像普通 PHP 对象一样高效,同时还支持[属性(Property)](concept-properties.md)功能。 +正是因为组件功能的强大,他们比常规的对象(Object)稍微重量级一点, +因为他们要使用额外的内存和 CPU 时间来处理 [事件](concept-events.md) 和 [行为](concept-behaviors.md) 。 +如果你不需要这两项功能,可以继承 [[yii\base\Object]] 而不是 [[yii\base\Component]]。 +这样组件可以像普通 PHP 对象一样高效, +同时还支持[属性(Property)](concept-properties.md)功能。 -当继承 [[yii\base\Component]] 或 [[yii\base\Object]] 时,推荐你使用如下的编码风格: +当继承 [[yii\base\Component]] 或 [[yii\base\Object]] 时, +推荐你使用如下的编码风格: -- 若你需要重写构造方法(Constructor),传入 `$config` 作为构造器方法**最后一个**参数,然后把它传递给父类的构造方法。 +- 若你需要重写构造方法(Constructor),传入 `$config` 作为构造器方法**最后一个**参数, + 然后把它传递给父类的构造方法。 - 永远在你重写的构造方法**结尾处**调用一下父类的构造方法。 - 如果你重写了 [[yii\base\Object::init()]] 方法,请确保你在 `init` 方法的**开头处**调用了父类的 `init` 方法。 @@ -71,11 +80,8 @@ $component = \Yii::createObject([ ], [1, 2]); ``` -<<<<<<< .merge_file_a01348 -> 补充:尽管调用 [[Yii::createObject()]] 的方法看起来更加复杂,但这主要因为它更加灵活强大,它是基于[依赖注入容器](concept-di-container.md)实现的。 -======= -> Info: 尽管调用 [[Yii::createObject()]] 的方法看起来更加复杂,但这主要因为它更加灵活强大,它是基于[依赖注入容器](concept-di-container.md)实现的。 ->>>>>>> .merge_file_a06700 +> 补充:尽管调用 [[Yii::createObject()]] 的方法看起来更加复杂, +> 但这主要因为它更加灵活强大,它是基于[依赖注入容器](concept-di-container.md)实现的。 [[yii\base\Object]] 类执行时的生命周期如下: @@ -85,4 +91,5 @@ $component = \Yii::createObject([ 3. 在 [[yii\base\Object::init()|init()]] 方法内进行初始化后的收尾工作。你可以通过重写此方法,进行一些良品检验,属性的初始化之类的工作。 4. 对象方法调用。 -前三步都是在对象的构造方法内发生的。这意味着一旦你获得了一个对象实例,那么它就已经初始化就绪可供使用。 +前三步都是在对象的构造方法内发生的。 +这意味着一旦你获得了一个对象实例,那么它就已经初始化就绪可供使用。 diff --git a/docs/guide-zh-CN/concept-properties.md b/docs/guide-zh-CN/concept-properties.md index eab95c2..7da3ce5 100644 --- a/docs/guide-zh-CN/concept-properties.md +++ b/docs/guide-zh-CN/concept-properties.md @@ -1,23 +1,31 @@ 属性(Property) ========== -在 PHP 中,类的成员变量也被称为**属性(properties)**。它们是类定义的一部分,用来表现一个实例的状态(也就是区分类的不同实例)。在具体实践中,常常会想用一个稍微特殊些的方法实现属性的读写。例如,如果有需求每次都要对 `label` 属性执行 trim 操作,就可以用以下代码实现: +在 PHP 中,类的成员变量也被称为**属性(properties)**。 +它们是类定义的一部分,用来表现一个实例的状态(也就是区分类的不同实例)。 +在具体实践中,常常会想用一个稍微特殊些的方法实现属性的读写。 +例如,如果有需求每次都要对 `label` 属性执行 trim 操作, +就可以用以下代码实现: ```php $object->label = trim($label); ``` -上述代码的缺点是只要修改 `label` 属性就必须再次调用 `trim()` 函数。若将来需要用其它方式处理 `label` 属性,比如首字母大写,就不得不修改所有给 `label` 属性赋值的代码。这种代码的重复会导致 bug,这种实践显然需要尽可能避免。 +上述代码的缺点是只要修改 `label` 属性就必须再次调用 `trim()` 函数。 +若将来需要用其它方式处理 `label` 属性,比如首字母大写, +就不得不修改所有给 `label` 属性赋值的代码。 +这种代码的重复会导致 bug,这种实践显然需要尽可能避免。 -为解决该问题,Yii 引入了一个名为 [[yii\base\Object]] 的基类,它支持基于类内的 **getter** 和 **setter**(读取器和设定器)方法来定义属性。如果某类需要支持这个特性,只需要继承 [[yii\base\Object]] 或其子类即可。 +为解决该问题,Yii 引入了一个名为 [[yii\base\Object]] 的基类, +它支持基于类内的 **getter** 和 **setter**(读取器和设定器)方法来定义属性。 +如果某类需要支持这个特性,只需要继承 [[yii\base\Object]] 或其子类即可。 -<<<<<<< .merge_file_a06884 -> 补充:几乎每个 Yii 框架的核心类都继承自 [[yii\base\Object]] 或其子类。这意味着只要在核心类中见到 getter 或 setter 方法,就可以像调用属性一样调用它。 -======= -> Info: 几乎每个 Yii 框架的核心类都继承自 [[yii\base\Object]] 或其子类。这意味着只要在核心类中见到 getter 或 setter 方法,就可以像调用属性一样调用它。 ->>>>>>> .merge_file_a04208 +> 补充:几乎每个 Yii 框架的核心类都继承自 [[yii\base\Object]] 或其子类。 + 这意味着只要在核心类中见到 getter 或 setter 方法,就可以像调用属性一样调用它。 -getter 方法是名称以 `get` 开头的方法,而 setter 方法名以 `set` 开头。方法名中 `get` 或 `set` 后面的部分就定义了该属性的名字。如下面代码所示,getter 方法 `getLabel()` 和 setter 方法 `setLabel()` 操作的是 `label` 属性,: +getter 方法是名称以 `get` 开头的方法,而 setter 方法名以 `set` 开头。 +方法名中 `get` 或 `set` 后面的部分就定义了该属性的名字。 +如下面代码所示,getter 方法 `getLabel()` 和 setter 方法 `setLabel()` 操作的是 `label` 属性,: ```php namespace app\components; @@ -40,9 +48,12 @@ class Foo extend Object } ``` -(详细解释:getter 和 setter 方法创建了一个名为 `label` 的属性,在这个例子里,它指向一个私有的内部属性 `_label`。) +详细解释:getter 和 setter 方法创建了一个名为 `label` 的属性, +在这个例子里,它指向一个私有的内部属性 `_label`。 -getter/setter 定义的属性用法与类成员变量一样。两者主要的区别是:当这种属性被读取时,对应的 getter 方法将被调用;而当属性被赋值时,对应的 setter 方法就调用。如: +getter/setter 定义的属性用法与类成员变量一样。 +两者主要的区别是:当这种属性被读取时,对应的 getter 方法将被调用; +而当属性被赋值时,对应的 setter 方法就调用。如: ```php // 等效于 $label = $object->getLabel(); @@ -52,13 +63,24 @@ $label = $object->label; $object->label = 'abc'; ``` -只定义了 getter 没有 setter 的属性是**只读属性**。尝试赋值给这样的属性将导致 [[yii\base\InvalidCallException|InvalidCallException]] (无效调用)异常。类似的,只有 setter 方法而没有 getter 方法定义的属性是**只写属性**,尝试读取这种属性也会触发异常。使用只写属性的情况几乎没有。 +只定义了 getter 没有 setter 的属性是**只读属性**。 +尝试赋值给这样的属性将导致 [[yii\base\InvalidCallException|InvalidCallException]] (无效调用)异常。 +类似的,只有 setter 方法而没有 getter 方法定义的属性是**只写属性**,尝试读取这种属性也会触发异常。 +使用只写属性的情况几乎没有。 通过 getter 和 setter 定义的属性也有一些特殊规则和限制: -* 这类属性的名字是**不区分大小写**的。如,`$object->label` 和 `$object->Label` 是同一个属性。因为 PHP 方法名是不区分大小写的。 -* 如果此类属性名和类成员变量相同,以后者为准。例如,假设以上 `Foo` 类有个 `label` 成员变量,然后给 `$object->label = 'abc'` 赋值,将赋给成员变量而不是 setter `setLabel()` 方法。 +* 这类属性的名字是**不区分大小写**的。如,`$object->label` 和 `$object->Label` 是同一个属性。 + 因为 PHP 方法名是不区分大小写的。 +* 如果此类属性名和类成员变量相同,以后者为准。 + 例如,假设以上 `Foo` 类有个 `label` 成员变量, + 然后给 `$object->label = 'abc'` 赋值,将赋给成员变量而不是 setter `setLabel()` 方法。 * 这类属性不支持可见性(访问限制)。定义属性的 getter 和 setter 方法是 public、protected 还是 private 对属性的可见性没有任何影响。 * 这类属性的 getter 和 setter 方法只能定义为**非静态**的,若定义为静态方法(static)则不会以相同方式处理。 +* A normal call to `property_exists()` does not work to determine magic properties. You should call [[yii\base\Object::canGetProperty()|canGetProperty()]] + or [[yii\base\Object::canSetProperty()|canSetProperty()]] respectively. -回到开头提到的问题,与其处处要调用 `trim()` 函数,现在我们只需在 setter `setLabel()` 方法内调用一次。如果 label 首字母变成大写的新要求来了,我们只需要修改`setLabel()` 方法,而无须接触任何其它代码。 \ No newline at end of file +回到开头提到的问题,与其处处要调用 `trim()` 函数, +现在我们只需在 setter `setLabel()` 方法内调用一次。 +如果 label 首字母变成大写的新要求来了,我们只需要修改`setLabel()` 方法, +而无须接触任何其它代码。 diff --git a/docs/guide-zh-CN/concept-service-locator.md b/docs/guide-zh-CN/concept-service-locator.md index c9f278d..242dd5c 100644 --- a/docs/guide-zh-CN/concept-service-locator.md +++ b/docs/guide-zh-CN/concept-service-locator.md @@ -1,15 +1,21 @@ 服务定位器 =============== -服务定位器是一个了解如何提供各种应用所需的服务(或组件)的对象。在服务定位器中,每个组件都只有一个单独的实例,并通过ID 唯一地标识。用这个 ID 就能从服务定位器中得到这个组件。 +服务定位器是一个了解如何提供各种应用所需的服务(或组件)的对象。 +在服务定位器中,每个组件都只有一个单独的实例,并通过ID 唯一地标识。 +用这个 ID 就能从服务定位器中得到这个组件。 在 Yii 中,服务定位器是 [[yii\di\ServiceLocator]] 或其子类的一个实例。 -最常用的服务定位器是**application(应用)**对象,可以通过 `\Yii::$app` 访问。它所提供的服务被称为**application components(应用组件)**,比如:`request`、`response`、`urlManager` 组件。可以通过服务定位器所提供的功能,非常容易地配置这些组件,或甚至是用你自己的实现替换掉他们。 +最常用的服务定位器是**application(应用)**对象,可以通过 `\Yii::$app` 访问。 +它所提供的服务被称为**application components(应用组件)**,比如:`request`、`response`、`urlManager` 组件。 +可以通过服务定位器所提供的功能,非常容易地配置这些组件, +或甚至是用你自己的实现替换掉他们。 除了 application 对象,每个模块对象本身也是一个服务定位器。 -要使用服务定位器,第一步是要注册相关组件。组件可以通过 [[yii\di\ServiceLocator::set()]] 方法进行注册。以下的方法展示了注册组件的不同方法: +要使用服务定位器,第一步是要注册相关组件。 +组件可以通过 [[yii\di\ServiceLocator::set()]] 方法进行注册。以下的方法展示了注册组件的不同方法: ```php use yii\di\ServiceLocator; @@ -45,11 +51,21 @@ $cache = $locator->get('cache'); $cache = $locator->cache; ``` -如上所示, [[yii\di\ServiceLocator]] 允许通过组件 ID 像访问一个属性值那样访问一个组件。当你第一次访问某组件时,[[yii\di\ServiceLocator]] 会通过该组件的注册信息创建一个该组件的实例,并返回它。之后,如果再次访问,则服务定位器会返回同一个实例。 +如上所示, [[yii\di\ServiceLocator]] 允许通过组件 ID 像访问一个属性值那样访问一个组件。 +当你第一次访问某组件时, +[[yii\di\ServiceLocator]] 会通过该组件的注册信息创建一个该组件的实例,并返回它。 +之后,如果再次访问,则服务定位器会返回同一个实例。 -你可以通过 [[yii\di\ServiceLocator::has()]] 检查某组件 ID 是否被注册。若你用一个无效的 ID 调用 [[yii\di\ServiceLocator::get()]],则会抛出一个异常。 +你可以通过 [[yii\di\ServiceLocator::has()]] 检查某组件 ID 是否被注册。 +若你用一个无效的 ID 调用 [[yii\di\ServiceLocator::get()]],则会抛出一个异常。 + + +因为服务定位器,经常会在创建时附带[配置信息](concept-configurations.md), +因此我们提供了一个可写的属性, +名为 [[yii\di\ServiceLocator::setComponents()|components]], +这样就可以配置该属性,或一次性注册多个组件。 +下面的代码展示了如何用一个配置数组,配置一个应用并注册"db","cache" 和 "search" 三个组件: -因为服务定位器,经常会在创建时附带[配置信息](concept-configurations.md),因此我们提供了一个可写的属性,名为 [[yii\di\ServiceLocator::setComponents()|components]],这样就可以配置该属性,或一次性注册多个组件。下面的代码展示了如何用一个配置数组,配置一个应用并注册"db","cache" 和 "search" 三个组件: ```php return [ // ... @@ -62,8 +78,40 @@ return [ ], 'cache' => 'yii\caching\ApcCache', 'search' => function () { - return new app\components\SolrService; + $solr = new app\components\SolrService('127.0.0.1'); + // ... other initializations ... + return $solr; }, ], ]; ``` + +In the above, there is an alternative way to configure the "search" component. Instead of directly writing a PHP +callback which builds a `SolrService` instance, you can use a static class method to return such a callback, like +shown as below: + +```php +class SolrServiceBuilder +{ + public static function build($ip) + { + return function () use ($ip) { + $solr = new app\components\SolrService($ip); + // ... other initializations ... + return $solr; + }; + } +} + +return [ + // ... + 'components' => [ + // ... + 'search' => SolrServiceBuilder::build('127.0.0.1'), + ], +]; +``` + +This alternative approach is most preferable when you are releasing a Yii component which encapsulates some non-Yii +3rd-party library. You use the static method like shown above to represent the complex logic of building the +3rd-party object, and the user of your component only needs to call the static method to configure the component. diff --git a/docs/guide-zh-CN/helper-array.md b/docs/guide-zh-CN/helper-array.md index a2a69ef..bef0999 100755 --- a/docs/guide-zh-CN/helper-array.md +++ b/docs/guide-zh-CN/helper-array.md @@ -113,32 +113,96 @@ $result = ArrayHelper::getColumn($array, function ($element) { 键名(译者注:第二个参数)可以是子数组的键名、对象的属性名, 也可以是一个返回给定元素数组键值的匿名函数。 -如果一个键值(译者注:第二个参数对应的值)是 null,相应的数组元素将被丢弃并且不会放入到结果中,例如, +The `$groups` attribute is an array of keys, that will be used to group the input array into one or more sub-arrays +based on keys specified. + +If the `$key` attribute or its value for the particular element is null and `$groups` is not defined, the array +element will be discarded. Otherwise, if `$groups` is specified, array element will be added to the result array +without any key. + +For example: ```php $array = [ - ['id' => '123', 'data' => 'abc'], - ['id' => '345', 'data' => 'def'], + ['id' => '123', 'data' => 'abc', 'device' => 'laptop'], + ['id' => '345', 'data' => 'def', 'device' => 'tablet'], + ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'], ]; $result = ArrayHelper::index($array, 'id'); -// the result is: -// [ -// '123' => ['id' => '123', 'data' => 'abc'], -// '345' => ['id' => '345', 'data' => 'def'], -// ] +``` + +The result will be an associative array, where the key is the value of `id` attribute + +```php +[ + '123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'], + '345' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'] + // The second element of an original array is overwritten by the last element because of the same id +] +``` + +Anonymous function, passed as a `$key`, gives the same result. -// using anonymous function +```php $result = ArrayHelper::index($array, function ($element) { return $element['id']; }); ``` +Passing `id` as a third argument will group `$array` by `id`: + +```php +$result = ArrayHelper::index($array, null, 'id'); +``` + +The result will be a multidimensional array grouped by `id` on the first level and not indexed on the second level: + +```php +[ + '123' => [ + ['id' => '123', 'data' => 'abc', 'device' => 'laptop'] + ], + '345' => [ // all elements with this index are present in the result array + ['id' => '345', 'data' => 'def', 'device' => 'tablet'], + ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'], + ] +] +``` + +An anonymous function can be used in the grouping array as well: + +```php +$result = ArrayHelper::index($array, 'data', [function ($element) { + return $element['id']; +}, 'device']); +``` + +The result will be a multidimensional array grouped by `id` on the first level, by `device` on the second level and +indexed by `data` on the third level: + +```php +[ + '123' => [ + 'laptop' => [ + 'abc' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'] + ] + ], + '345' => [ + 'tablet' => [ + 'def' => ['id' => '345', 'data' => 'def', 'device' => 'tablet'] + ], + 'smartphone' => [ + 'hgi' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'] + ] + ] +] +``` ## 建立哈希表 为了从一个多维数组或者一个对象数组中建立一个映射表(键值对),你可以使用 `map`方法.`$from` 和 `$to` 参数分别指定了欲构建的映射表的键名和属性名。 -根据需要,你可以按照一个分组字段 `$group` 将映射表进行分组,例如。 +根据需要,你可以按照一个分组字段 `$group` 将映射表进行分组,例如, ```php $array = [ @@ -201,9 +265,9 @@ ArrayHelper::multisort($data, function($item) { }); ``` -第三个参数表示增降顺序。单键排序时,它可以是 `SORT_ASC` 或者 `SORT_DESC` 之一。 -如果是按多个键名排序,你可以用一个数组为各个键指定不同的顺序。 - +第三个参数表示增降顺序。单键排序时,它可以是 `SORT_ASC` 或者 +`SORT_DESC` 之一。如果是按多个键名排序,你可以用一个数组为 +各个键指定不同的顺序。 最后一个参数(译者注:第四个参数)是PHP的排序标识(sort flag),可使用的值和调用PHP [sort()](http://php.net/manual/zh/function.sort.php) 函数时传递的值一样。 @@ -301,3 +365,22 @@ $data = ArrayHelper::toArray($posts, [ 也可以在一个特定的类中实现[[yii\base\Arrayable|Arrayable]]接口, 从而为其对象提供默认的转换成数组的方法。 + +## Testing against Arrays + +Often you need to check if an element is in an array or a set of elements is a subset of another. +While PHP offers `in_array()`, this does not support subsets or `\Traversable` objects. + +To aid these kinds of tests, [[yii\base\ArrayHelper]] provides [[yii\base\ArrayHelper::isIn()|isIn()]] +and [[yii\base\ArrayHelper::isSubset()|isSubset()]] with the same signature as [[in_array()]]. + +```php +// true +ArrayHelper::isIn('a', ['a']); +// true +ArrayHelper::isIn('a', new(ArrayObject['a'])); + +// true +ArrayHelper::isSubset(new(ArrayObject['a', 'c']), new(ArrayObject['a', 'b', 'c']) + +``` diff --git a/docs/guide-zh-CN/helper-html.md b/docs/guide-zh-CN/helper-html.md index 45dd66f..66e50b9 100644 --- a/docs/guide-zh-CN/helper-html.md +++ b/docs/guide-zh-CN/helper-html.md @@ -78,6 +78,56 @@ echo Html::tag('div', 'Pwede na', $options); 基于同样的目的,针对 `style` 属性: ```php +$options = ['class' => ['btn', 'btn-default']]; + +echo Html::tag('div', 'Save', $options); +// renders '
Save
' +``` + +While adding or removing classes you may use the array format as well: + +```php +$options = ['class' => 'btn']; + +if ($type === 'success') { + Html::addCssClass($options, ['btn-success', 'btn-lg']); +} + +echo Html::tag('div', 'Save', $options); +// renders '
Save
' +``` + +`Html::addCssClass()` prevents duplicating classes, so you don't need to worry that the same class may appear twice: + +```php +$options = ['class' => 'btn btn-default']; + +Html::addCssClass($options, 'btn-default'); // class 'btn-default' is already present + +echo Html::tag('div', 'Save', $options); +// renders '
Save
' +``` + +If the CSS class option is specified via the array format, you may use a named key to mark the logical purpose of the class. +In this case, a class with the same key in the array format will be ignored in `Html::addCssClass()`: + +```php +$options = [ + 'class' => [ + 'btn', + 'theme' => 'btn-default', + ] +]; + +Html::addCssClass($options, ['theme' => 'btn-success']); // 'theme' key is already taken + +echo Html::tag('div', 'Save', $options); +// renders '
Save
' +``` + +CSS styles can be setup in similar way using `style` attribute: + +```php $options = ['style' => ['width' => '100px', 'height' => '100px']]; // gives style="width: 100px; height: 200px; position: absolute;" @@ -149,15 +199,15 @@ Yii 也提供了一系列的方法来辅助处理表单标签。 'reset']) ?> ``` -上述三个方法的第一个参数为按钮的标题,第二个是标签属性。标题默认没有进行转码,如果标题是由终端用输入的,那么请自行用 [[yii\helpers\Html::encode()|Html::encode()]] 方法进行转码。 - +上述三个方法的第一个参数为按钮的标题,第二个是标签属性。标题默认没有进行转码,如果标题是由终端用输入的, +那么请自行用 [[yii\helpers\Html::encode()|Html::encode()]] 方法进行转码。 ### 输入栏 -input 相关的方法有两组:以 `active` 开头的被称为 active inputs,另一组则不以其开头。 -active inputs 依据指定的模型和属性获取数据,而普通 input 则是直接指定数据。 - +input 相关的方法有两组:以 `active` 开头的被称为 active inputs, +另一组则不以其开头。active inputs 依据指定的模型和属性获取数据, +而普通 input 则是直接指定数据。 一般用法如下: diff --git a/docs/guide-zh-CN/helper-overview.md b/docs/guide-zh-CN/helper-overview.md index 5098128..331594b 100644 --- a/docs/guide-zh-CN/helper-overview.md +++ b/docs/guide-zh-CN/helper-overview.md @@ -1,11 +1,7 @@ 助手类 ======= -<<<<<<< .merge_file_a01044 > 注意:这部分正在开发中。 -======= -> Note: 这部分正在开发中。 ->>>>>>> .merge_file_a06768 Yii 提供许多类来简化常见编码,如对字条串或数组的操作, HTML 代码生成,等等。这些助手类被编写在命名空间 `yii\helpers` 下,并且 @@ -19,12 +15,8 @@ use yii\helpers\Html; echo Html::encode('Test > test'); ``` -<<<<<<< .merge_file_a01044 > 注意:为了支持 [自定义助手类](#customizing-helper-classes),Yii 将每一个助手类 -======= -> Note: 为了支持 [自定义助手类](#customizing-helper-classes),Yii 将每一个助手类 ->>>>>>> .merge_file_a06768 - 分隔成两个类:一个基类 (例如 `BaseArrayHelper`) 和一个具体的类 (例如 `ArrayHelper`). + 分隔成两个类:一个基类 (例如 `BaseArrayHelper`) 和一个具体的类 (例如 `ArrayHelper`)。 当使用助手类时,应该仅使用具体的类版本而不使用基类。 diff --git a/docs/guide-zh-CN/input-file-upload.md b/docs/guide-zh-CN/input-file-upload.md index eabbed5..5d5895a 100755 --- a/docs/guide-zh-CN/input-file-upload.md +++ b/docs/guide-zh-CN/input-file-upload.md @@ -77,6 +77,8 @@ use yii\widgets\ActiveForm; 需要注意的是要记得在表单选项里加入 `enctype` 属性以确保文件能被正常上传。 `fileInput()` 方法会渲染一个 `` 标签,让用户可以选择一个文件上传。 +> Tip: since version 2.0.8, [[yii\web\widgets\ActiveField::fileInput|fileInput]] adds `enctype` option to the form + automatically when file input field is used. ## 视图和模型的连接 @@ -119,8 +121,10 @@ class SiteController extends Controller 将前面所述的代码做一些调整,也可以一次性上传多个文件。 首先你得调整模型类,在 `file` 验证规则里增加一个 `maxFiles` 选项,用以限制一次上传文件的最大数量。 -`upload()`方法也得修改, -以便一个一个地保存上传的文件。 +`upload()`方法也得修改,以便一个一个地保存上传的文件。Setting `maxFiles` to `0` means there is no limit on the number of files +that can be uploaded simultaneously. The maximum number of files allowed to be uploaded simultaneously is also limited +with PHP directive [`max_file_uploads`](http://php.net/manual/en/ini.core.php#ini.max-file-uploads), +which defaults to 20. The `upload()` method should also be updated to save the uploaded files one by one. ```php namespace app\models; diff --git a/docs/guide-zh-CN/input-forms.md b/docs/guide-zh-CN/input-forms.md index 64ce58b..98f9e1b 100644 --- a/docs/guide-zh-CN/input-forms.md +++ b/docs/guide-zh-CN/input-forms.md @@ -1,14 +1,16 @@ 创建表单 ======== -在 Yii 中使用表单的主要方式是通过 [[yii\widgets\ActiveForm]]。如果是基于模型的表单应首选这种方式。此外,在 [[yii\helpers\Html]] -中也有一些实用的方法用于添加按钮和帮助文本。 +在 Yii 中使用表单的主要方式是通过 [[yii\widgets\ActiveForm]]。如果是基于 +模型的表单应首选这种方式。此外,在 [[yii\helpers\Html]]中也有一些实用的 +方法用于添加按钮和帮助文本。 在客户端上显示的表单,大多数情况下有一个相应的[模型](structure-models.md),用来验证其输入的服务器数据 (可在 [输入验证](input-validation.md) 一节获取关于验证的细节)。 -当创建基于模型的表单时,第一步是定义模型本身。该模式可以是一个基于[活动记录](db-active-record.md)的类,表示数据库中的数据, -也可以是一个基于通用模型的类(继承自 [[yii\base\Model]] ),来获取任意的输入数据,如登录表单。 -在下面的例子中,我们展示了一个用来做登录表单的通用模型: +当创建基于模型的表单时,第一步是定义模型本身。该模式可以是一个基于[活动记录](db-active-record.md)的类, +表示数据库中的数据,也可以是一个基于通用模型的类(继承自 [[yii\base\Model]] ), +来获取任意的输入数据,如登录表单。在下面的例子中,我们展示了一个用来做 +登录表单的通用模型: ```php ` 标签中。 与任何小部件一样,你可以指定一些选项,通过传递数组到 `begin` 方法中来配置该小部件。在这种情况下, -一个额外的 CSS 类和 ID 会在 `
` 标签中使用。要查看所有可用的选项,请参阅 API 文档的 [[yii\widgets\ActiveForm]]。 +一个额外的 CSS 类和 ID 会在 `` 标签中使用。要查看所有可用的选项, +请参阅 API 文档的 [[yii\widgets\ActiveForm]]。 为了在表单中创建表单元素与元素的标签,以及任何适用的 JavaScript 验证,[[yii\widgets\ActiveForm::field()|ActiveForm::field()]] 方法在调用时,会返回一个 [[yii\widgets\ActiveField]] 的实例。 -直接输出该方法时,结果是一个普通的(文本)输入。要自定义输出,可以附加上 [[yii\widgets\ActiveField|ActiveField]] 的其它方法来一起调用: +直接输出该方法时,结果是一个普通的(文本)输入。要自定义输出,可以附加上 [[yii\widgets\ActiveField|ActiveField]] +的其它方法来一起调用: ```php // 一个密码输入框 @@ -73,8 +78,13 @@ input 输入框的 name 属性会自动地根据 [[yii\base\Model::formName()|fo 例如,对于在上面的例子中 `username` 输入字段的 name 属性将是 `LoginForm[username]`。 这种命名规则使所有属性的数组的登录表单在服务器端的 `$_POST['LoginForm']` 数组中是可用的。 -指定模型的属性可以以更复杂的方式来完成。例如,当上传时,多个文件或选择多个项目的属性,可能需要一个数组值, -你可以通过附加 `[]` 来指定它的属性名称: +> Tip: If you have only one model in a form and want to simplify the input names you may skip the array part by +> overriding the [[yii\base\Model::formName()|formName()]] method of the model to return an empty string. +> This can be useful for filter models used in the [GridView](output-data-widgets.md#grid-view) to create nicer URLs. + +指定模型的属性可以以更复杂的方式来完成。例如,当上传时,多个文件 +或选择多个项目的属性,可能需要一个数组值,你可以通过附加 `[]` 来 +指定它的属性名称: ```php // 允许多个文件被上传: @@ -84,16 +94,19 @@ echo $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']); echo $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']); ``` -命名表单元素,如提交按钮时要小心。在 [jQuery 文档](https://api.jquery.com/submit/) 中有一些保留的名称,可能会导致冲突: +命名表单元素,如提交按钮时要小心。在 [jQuery 文档](https://api.jquery.com/submit/) +中有一些保留的名称,可能会导致冲突: -> 表单和它们的子元素不应该使用与表单的属性冲突的 input name 或 id,例如 `submit`,`length`,或者 `method`。 +> 表单和它们的子元素不应该使用与表单的属性冲突的 input name 或 id, +> 例如 `submit`,`length`,或者 `method`。 > 要检查你的标签是否存在这些问题,一个完整的规则列表详见 [DOMLint](http://kangax.github.io/domlint/)。 额外的 HTML 标签可以使用纯 HTML 或者 [[yii\helpers\Html|Html]]-辅助类中的方法来添加到表单中,就如上面例子中的 [[yii\helpers\Html::submitButton()|Html::submitButton()]]。 -> 提示: 如果你正在你的应用程序中使用 Twitter Bootstrap CSS 你可以使用[[yii\bootstrap\ActiveForm]] 来代替 [[yii\widgets\ActiveForm]]。 +> 提示: 如果你正在你的应用程序中使用 Twitter Bootstrap CSS 你可以使用[[yii\bootstrap\ActiveForm]] +> 来代替 [[yii\widgets\ActiveForm]]。 > 前者继承自后者并在生成表单字段时使用 Bootstrap 特有的样式。 @@ -114,7 +127,6 @@ echo $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Ite ```php use app\models\ProductCategory; -use yii\helpers\ArrayHelper; /* @var $this yii\web\View */ /* @var $form yii\widgets\ActiveForm */ @@ -128,10 +140,56 @@ echo $form->field($model, 'product_category')->dropdownList( 模型字段的值将被自动预先选定。 +Working with Pjax +----------------------- + +The [[yii\widgets\Pjax|Pjax]] widget allows you to update a certain section of a +page instead of reloading the entire page. You can use it to update only the form +and replace its contents after the submission. + +You can configure [[yii\widgets\Pjax::$formSelector|$formSelector]] to specify +which form submission may trigger pjax. If not set, all forms with `data-pjax` +attribute within the enclosed content of Pjax will trigger pjax requests. + +```php +use yii\widgets\Pjax; +use yii\widgets\ActiveForm; + +Pjax::begin([ + // Pjax options +]); + $form = ActiveForm::begin([ + 'options' => ['data' => ['pjax' => true]], + // more ActiveForm options + ]); + + // ActiveForm content + + ActiveForm::end(); +Pjax::end(); +``` +> Tip: Be careful with the links inside the [[yii\widgets\Pjax|Pjax]] widget since +> the response will also be rendered inside the widget. To prevent this, use the +> `data-pjax="0"` HTML attribute. + +#### Values in Submit Buttons and File Upload + +There are known issues using `jQuery.serializeArray()` when dealing with +[[https://github.com/jquery/jquery/issues/2321|files]] and +[[https://github.com/jquery/jquery/issues/2321|submit button values]] which +won't be solved and are instead deprecated in favor of the `FormData` class +introduced in HTML5. + +That means the only official support for files and submit button values with +ajax or using the [[yii\widgets\Pjax|Pjax]] widget depends on the +[[https://developer.mozilla.org/en-US/docs/Web/API/FormData#Browser_compatibility|browser support]] +for the `FormData` class. + 延伸阅读 --------- +--------------- -下一节 [输入验证](input-validation.md) 处理提交的表单数据的服务器端验证,以及 ajax- 和客户端验证。 +下一节 [输入验证](input-validation.md) 处理提交的表单数据的服务器端验证, +以及 ajax- 和客户端验证。 要学会有关表格的更复杂的用法,你可以查看以下几节: diff --git a/docs/guide-zh-CN/input-multiple-models.md b/docs/guide-zh-CN/input-multiple-models.md index 108d162..ee45de8 100644 --- a/docs/guide-zh-CN/input-multiple-models.md +++ b/docs/guide-zh-CN/input-multiple-models.md @@ -1,35 +1,80 @@ 多模型的复合表单 ================================== -在复杂的用户界面可能会发生,用户在表单中填写数据 -后将其保存在数据库的不同表中。yii形式的表单与单模型表单相比 -可以让你用更加简单的方法来创建。 +When dealing with some complex data, it is possible that you may need to use multiple different models to collect +the user input. For example, assuming the user login information is stored in the `user` table while the user profile +information is stored in the `profile` table, you may want to collect the input data about a user through a `User` model +and a `Profile` model. With the Yii model and form support, you can solve this problem in a way that is not much +different from handling a single model. -与一个模型一样,你遵循以下模式用于服务端验证: +In the following, we will show how you can create a form that would allow you to collect data for both `User` and `Profile` +models. -1. 实例化模型类 -2. 用输入数据填充模型属性 -3. 验证所有模型 -4. 如果所有模型验证通过,则保存它们 -5. 如果验证失败或没有提交数据,传递所有模型对象到视图显示表单 +First, the controller action for collecting the user and profile data can be written as follows, -在下文中我们展示了一个在表单中使用多模型的例子... TBD +```php +namespace app\controllers; -多模型实例 ---------------- +use Yii; +use yii\base\Model; +use yii\web\Controller; +use yii\web\NotFoundHttpException; +use app\models\User; +use app\models\Profile; -> Note: This section is under development. -> -> It has no content yet. +class UserController extends Controller +{ + public function actionUpdate($id) + { + $user = User::findOne($id); + $profile = Profile::findOne($id); + + if (!isset($user, $profile)) { + throw new NotFoundHttpException("The user was not found."); + } + + $user->scenario = 'update'; + $profile->scenario = 'update'; + + if ($user->load(Yii::$app->request->post()) && $profile->load(Yii::$app->request->post())) { + $isValid = $user->validate(); + $isValid = $profile->validate() && $isValid; + if ($isValid) { + $user->save(false); + $profile->save(false); + return $this->redirect(['user/view', 'id' => $id]); + } + } + + return $this->render('update', [ + 'user' => $user, + 'profile' => $profile, + ]); + } +} +``` -TBD +In the `update` action, we first load the `$user` and `$profile` models to be updated from the database. We then call +[[yii\base\Model::load()]] to populate these two models with the user input. If successful we will validate +the two models and save them. Otherwise we will render the `update` view which has the following content: -Dependend models ----------------- +```php + 'user-update-form', + 'options' => ['class' => 'form-horizontal'], +]) ?> + field($user, 'username') ?> -> Note: This section is under development. -> -> It has no content yet. + ...other input fields... + + field($profile, 'website') ?> -TBD + 'btn btn-primary']) ?> + +``` + +As you can see, in the `update` view you would render input fields using two models `$user` and `$profile`. diff --git a/docs/guide-zh-CN/input-tabular-input.md b/docs/guide-zh-CN/input-tabular-input.md index 9b0e678..2cc2b36 100644 --- a/docs/guide-zh-CN/input-tabular-input.md +++ b/docs/guide-zh-CN/input-tabular-input.md @@ -1,8 +1,11 @@ 收集列表输入 ============= -有时你需要在一个表单中以单一的形式处理多个模型。例如,有多个设置,每个设置存储为一个 name-value,并通过 `Setting` [活动记录](db-active-record.md)模型来表示。 -这种形式也常被称为“列表输入”。与此相反,处理不同模型的不同类型,在[多模型同时输入](input-multiple-models.md)章节中介绍。 +有时你需要在一个表单中以单一的形式处理多个模型。例如,有多个设置, +每个设置存储为一个 name-value,并通过 `Setting` [活动记录](db-active-record.md) +模型来表示。这种形式也常被称为“列表输入”。与此相反, +处理不同模型的不同类型,在 +[多模型同时输入](input-multiple-models.md)章节中介绍。 下面展示了如何在 Yii 中收集列表输入。 @@ -11,7 +14,8 @@ - 创建一个动态的新记录集 - 更新、创建和删除一页记录 -与之前介绍的单一模型表单相反,我们现在用的是一个数组类的模型。这个数组将每个模型传递到视图并以一种类似于表格的方式来显示表单字段。 +与之前介绍的单一模型表单相反,我们现在用的是一个数组类的模型。这个数组将 +每个模型传递到视图并以一种类似于表格的方式来显示表单字段。 我们使用 [[yii\base\Model]] 助手类方法来一次性地加载和验证多模型数据: - [[yii\base\Model::loadMultiple()|Model::loadMultiple()]] 将数据加载到一个数组中。 @@ -51,11 +55,12 @@ class SettingsController extends Controller } ``` -在上面的代码中,当用模型来从数据库获取数据时,我们使用 [[yii\db\ActiveQuery::indexBy()|indexBy()]] 来让模型的主键成为一个数组的索引。 -其中 [[yii\base\Model::loadMultiple()|Model::loadMultiple()]] 用于接收以 POST 方式提交的表单数据并填充多个模型, +在上面的代码中,当用模型来从数据库获取数据时,我们使用 [[yii\db\ActiveQuery::indexBy()|indexBy()]] +来让模型的主键成为一个数组的索引。其中 [[yii\base\Model::loadMultiple()|Model::loadMultiple()]] +用于接收以 POST 方式提交的表单数据并填充多个模型, [[yii\base\Model::validateMultiple()|Model::validateMultiple()]] 一次验证多个模型。 -正如我们之前验证的模型,使用了 `validateMultiple()`,现在通过传递 `false` 作为 [[yii\db\ActiveRecord::save()|save()]] -的一个参数使其不会重复验证两次。 +正如我们之前验证的模型,使用了 `validateMultiple()`,现在通过传递 `false` +作为 [[yii\db\ActiveRecord::save()|save()]]的一个参数使其不会重复验证两次。 现在在 `update` 视图的表单: diff --git a/docs/guide-zh-CN/input-validation.md b/docs/guide-zh-CN/input-validation.md index 58bf6af..796a1c6 100644 --- a/docs/guide-zh-CN/input-validation.md +++ b/docs/guide-zh-CN/input-validation.md @@ -1,34 +1,35 @@ 输入验证 ================ -一般说来,程序猿永远不应该信任从最终用户直接接收到的数据,并且使用它们之前应始终先验证其可靠性。 +一般说来,程序猿永远不应该信任从最终用户直接接收到的数据, +并且使用它们之前应始终先验证其可靠性。 -要给 [model](structure-models.md) 填充其所需的用户输入数据,你可以调用 [[yii\base\Model::validate()]] 方法验证它们。该方法会返回一个布尔值,指明是否通过验证。若没有通过,你能通过 [[yii\base\Model::errors]] 属性获取相应的报错信息。比如, +要给 [model](structure-models.md) 填充其所需的用户输入数据,你可以调用 [[yii\base\Model::validate()]] +方法验证它们。该方法会返回一个布尔值,指明是否通过验证。若没有通过,你能通过 [[yii\base\Model::errors]] +属性获取相应的报错信息。比如, ```php -$model = new \app\models\ContactForm; +$model = new \app\models\ContactForm(); -// 用用户输入来填充模型的特性 -$model->attributes = \Yii::$app->request->post('ContactForm'); +// populate model attributes with user inputs +$model->load(\Yii::$app->request->post()); +// which is equivalent to the following: +// $model->attributes = \Yii::$app->request->post('ContactForm'); if ($model->validate()) { - // 若所有输入都是有效的 + // all inputs are valid } else { - // 有效性验证失败:$errors 属性就是存储错误信息的数组 + // validation failed: $errors is an array containing error messages $errors = $model->errors; } ``` -`validate()` 方法,在幕后为执行验证操作,进行了以下步骤: - -1. 通过从 [[yii\base\Model::scenarios()]] 方法返回基于当前 [[yii\base\Model::scenario|场景(scenario)]] 的特性属性列表,算出哪些特性应该进行有效性验证。这些属性被称作 *active attributes*(激活特性)。 -2. 通过从 [[yii\base\Model::rules()]] 方法返回基于当前 [[yii\base\Model::scenario|场景(scenario)]] 的验证规则列表,这些规则被称作 *active rules*(激活规则)。 -3. 用每个激活规则去验证每个与之关联的激活特性。若失败,则记录下对应模型特性的错误信息。 - ## 声明规则(Rules) -要让 `validate()` 方法起作用,你需要声明与需验证模型特性相关的验证规则。为此,需要重写 [[yii\base\Model::rules()]] 方法。下面的例子展示了如何声明用于验证 `ContactForm` 模型的相关验证规则: +要让 `validate()` 方法起作用,你需要声明与需验证模型特性相关的验证规则。 +为此,需要重写 [[yii\base\Model::rules()]] 方法。下面的例子展示了如何 +声明用于验证 `ContactForm` 模型的相关验证规则: ```php public function rules() @@ -43,50 +44,88 @@ public function rules() } ``` -[[yii\base\Model::rules()|rules()]] 方法应返回一个由规则所组成的数组,每一个规则都呈现为以下这类格式的小数组: +[[yii\base\Model::rules()|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 ['attribute1', 'attribute2', ...], - // 必填项,用于指定规则的类型。 - // 它可以是类名,验证器昵称,或者是验证方法的名称。 + // required, specifies the type of this rule. + // It can be a class name, validator alias, or a validation method name 'validator', - // 可选项,用于指定在场景(scenario)中,需要启用该规则 - // 若不提供,则代表该规则适用于所有场景 - // 若你需要提供除了某些特定场景以外的所有其他场景,你也可以配置 "except" 选项 + // 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 'on' => ['scenario1', 'scenario2', ...], - // 可选项,用于指定对该验证器对象的其他配置选项 + // optional, specifies additional configurations for the validator object 'property1' => 'value1', 'property2' => 'value2', ... ] ``` -对于每个规则,你至少需要指定该规则适用于哪些特性,以及本规则的类型是什么。你可以指定以下的规则类型之一: +对于每个规则,你至少需要指定该规则适用于哪些特性,以及本规则的类型是什么。 +你可以指定以下的规则类型之一: -* 核心验证器的昵称,比如 `required`、`in`、`date`,等等。请参考[核心验证器](tutorial-core-validators.md)章节查看完整的核心验证器列表。 -* 模型类中的某个验证方法的名称,或者一个匿名方法。请参考[行内验证器](#inline-validators)小节了解更多。 -* 验证器类的名称。请参考[独立验证器](#standalone-validators)小节了解更多。 +* 核心验证器的昵称,比如 `required`、`in`、`date`,等等。请参考 + [核心验证器](tutorial-core-validators.md)章节查看完整的核心验证器列表。 +* 模型类中的某个验证方法的名称,或者一个匿名方法。 + 请参考[行内验证器](#inline-validators)小节了解更多。 +* 验证器类的名称。 + 请参考[独立验证器](#standalone-validators)小节了解更多。 -一个规则可用于验证一个或多个模型特性,且一个特性可以被一个或多个规则所验证。一个规则可以施用于特定[场景(scenario)](structure-models.md#scenarios),只要指定 `on` 选项。如果你不指定 `on` 选项,那么该规则会适配于所有场景。 +一个规则可用于验证一个或多个模型特性,且一个特性可以被一个或多个规则所验证。 +一个规则可以施用于特定[场景(scenario)](structure-models.md#scenarios),只 +要指定 `on` 选项。如果你不指定 `on` 选项,那么该规则会适配于所有场景。 当调用 `validate()` 方法时,它将运行以下几个具体的验证步骤: -1. 检查从声明自 [[yii\base\Model::scenarios()]] 方法的场景中所挑选出的当前[[yii\base\Model::scenario|场景]]的信息,从而确定出那些特性需要被验证。这些特性被称为激活特性。 -2. 检查从声明自 [[yii\base\Model::rules()]] 方法的众多规则中所挑选出的适用于当前[[yii\base\Model::scenario|场景]]的规则,从而确定出需要验证哪些规则。这些规则被称为激活规则。 -3. 用每个激活规则去验证每个与之关联的激活特性。 +1. 检查从声明自 [[yii\base\Model::scenarios()]] 方法的场景中所挑选出的当前[[yii\base\Model::scenario|场景]]的信息, + 从而确定出那些特性需要被验证。这些特性被称为激活特性。 +2. 检查从声明自 [[yii\base\Model::rules()]] 方法的众多规则中所挑选出的适用于当前[[yii\base\Model::scenario|场景]]的规则, + 从而确定出需要验证哪些规则。这些规则被称为激活规则。 +3. 用每个激活规则去验证每个 + 与之关联的激活特性。 + +基于以上验证步骤,有且仅有声明在 `scenarios()` +方法里的激活特性,且它还必须与一或多个声明自 +`rules()` 里的激活规则相关联才会被验证。 -基于以上验证步骤,有且仅有声明在 `scenarios()` 方法里的激活特性,且它还必须与一或多个声明自 `rules()` 里的激活规则相关联才会被验证。 +> Note: It is handy to give names to rules i.e. +> ```php +> public function rules() +> { +> return [ +> // ... +> 'password' => [['password'], 'string', 'max' => 60], +> ]; +> } +> ``` +> +> You can use it in a child model: +> +> ```php +> public function rules() +> { +> $rules = parent::rules(); +> unset($rules['password']); +> return $rules; +> } ### 自定义错误信息 -大多数的验证器都有默认的错误信息,当模型的某个特性验证失败的时候,该错误信息会被返回给模型。比如,用 [[yii\validators\RequiredValidator|required]] 验证器的规则检验 `username` 特性失败的话,会返还给模型 "Username cannot be blank." 信息。 +大多数的验证器都有默认的错误信息,当模型的某个特性验证失败的时候,该错误信息会被返回给模型。 +比如,用 [[yii\validators\RequiredValidator|required]] 验证器的规则检验 `username` +特性失败的话,会返还给模型 "Username cannot be blank." 信息。 -你可以通过在声明规则的时候同时指定 `message` 属性,来定制某个规则的错误信息,比如这样: +你可以通过在声明规则的时候同时指定 `message` 属性, +来定制某个规则的错误信息,比如这样: ```php public function rules() @@ -97,22 +136,32 @@ public function rules() } ``` -一些验证器还支持用于针对不同原因的验证失败返回更加准确的额外错误信息。比如,[[yii\validators\NumberValidator|number]] 验证器就支持 [[yii\validators\NumberValidator::tooBig|tooBig]] 和 [[yii\validators\NumberValidator::tooSmall|tooSmall]] 两种错误消息用于分别返回输入值是太大还是太小。 -你也可以像配置验证器的其他属性一样配置它们俩各自的错误信息。 +一些验证器还支持用于针对不同原因的验证失败返回更加准确的额外错误信息。比如,[[yii\validators\NumberValidator|number]] +验证器就支持 [[yii\validators\NumberValidator::tooBig|tooBig]] 和 [[yii\validators\NumberValidator::tooSmall|tooSmall]] +两种错误消息用于分别返回输入值是太大还是太小。你也可以像配置验证器的 +其他属性一样配置它们俩各自的错误信息。 + ### 验证事件 -当调用 [[yii\base\Model::validate()]] 方法的过程里,它同时会调用两个特殊的方法,把它们重写掉可以实现自定义验证过程的目的: +当调用 [[yii\base\Model::validate()]] 方法的过程里,它同时会调用两个特殊的方法, +把它们重写掉可以实现自定义验证过程的目的: + +* [[yii\base\Model::beforeValidate()]]:在默认的实现中会触发 [[yii\base\Model::EVENT_BEFORE_VALIDATE]] 事件。 + 你可以重写该方法或者响应此事件,来在验证开始之前,先进行一些预处理的工作。 + (比如,标准化数据输入)该方法应该返回一个布尔值,用于标明验证是否通过。 +* [[yii\base\Model::afterValidate()]]:在默认的实现中会触发 [[yii\base\Model::EVENT_AFTER_VALIDATE]] 事件。 + 你可以重写该方法或者响应此事件,来在验证结束之后, + 再进行一些收尾的工作。 -* [[yii\base\Model::beforeValidate()]]:在默认的实现中会触发 [[yii\base\Model::EVENT_BEFORE_VALIDATE]] 事件。你可以重写该方法或者响应此事件,来在验证开始之前,先进行一些预处理的工作。(比如,标准化数据输入)该方法应该返回一个布尔值,用于标明验证是否通过。 -* [[yii\base\Model::afterValidate()]]:在默认的实现中会触发 [[yii\base\Model::EVENT_AFTER_VALIDATE]] 事件。你可以重写该方法或者响应此事件,来在验证结束之后,再进行一些收尾的工作。 ### 条件式验证 -若要只在某些条件满足时,才验证相关特性,比如:是否验证某特性取决于另一特性的值,你可以通过 -[[yii\validators\Validator::when|when]] 属性来定义相关条件。举例而言, +若要只在某些条件满足时,才验证相关特性,比如:是否验证某特性取决于另一特性的值, +你可以通过[[yii\validators\Validator::when|when]] +属性来定义相关条件。举例而言, ```php ['state', 'required', 'when' => function($model) { @@ -131,8 +180,9 @@ public function rules() function ($model, $attribute) ``` -若你需要支持客户端的条件验证,你应该配置 -[[yii\validators\Validator::whenClient|whenClient]] 属性,它会读入一条包含有 JavaScript 函数的字符串。这个函数将被用于确定该客户端验证规则是否被启用。比如, +若你需要支持客户端的条件验证,你应该配置[[yii\validators\Validator::whenClient|whenClient]] 属性, +它会读入一条包含有 JavaScript 函数的字符串。 +这个函数将被用于确定该客户端验证规则是否被启用。比如, ```php ['state', 'required', 'when' => function ($model) { @@ -145,9 +195,11 @@ function ($model, $attribute) ### 数据预处理 -用户输入经常需要进行数据过滤,或者叫预处理。比如你可能会需要先去掉 `username` 输入的收尾空格。你可以通过使用验证规则来实现此目的。 +用户输入经常需要进行数据过滤,或者叫预处理。比如你可能会需要先去掉 `username` 输入的收尾空格。 +你可以通过使用验证规则来实现此目的。 -下面的例子展示了如何去掉输入信息的首尾空格,并将空输入返回为 null。具体方法为通过调用 [trim](tutorial-core-validators.md#trim) 和 [default](tutorial-core-validators.md#default) 核心验证器: +下面的例子展示了如何去掉输入信息的首尾空格,并将空输入返回为 null。具体方法为通过调用 +[trim](tutorial-core-validators.md#trim) 和 [default](tutorial-core-validators.md#default) 核心验证器: ```php return [ @@ -156,9 +208,11 @@ return [ ]; ``` -也还可以用更加通用的 [filter(滤镜)](tutorial-core-validators.md#filter) 核心验证器来执行更加复杂的数据过滤。 +也还可以用更加通用的 [filter(滤镜)](tutorial-core-validators.md#filter) +核心验证器来执行更加复杂的数据过滤。 -如你所见,这些验证规则并不真的对输入数据进行任何验证。而是,对输入数据进行一些处理,然后把它们存回当前被验证的模型特性。 +如你所见,这些验证规则并不真的对输入数据进行任何验证。而是,对输入数据进行一些处理, +然后把它们存回当前被验证的模型特性。 ### 处理空输入 @@ -176,8 +230,9 @@ return [ ]; ``` -默认情况下,当输入项为空字符串,空数组,或 null 时,会被视为“空值”。你也可以通过配置 -[[yii\validators\Validator::isEmpty]] 属性来自定义空值的判定规则。比如, +默认情况下,当输入项为空字符串,空数组,或 null 时,会被视为“空值”。 +你也可以通过配置[[yii\validators\Validator::isEmpty]] +属性来自定义空值的判定规则。比如, ```php ['agree', 'required', 'isEmpty' => function ($value) { @@ -186,8 +241,9 @@ return [ ``` > Note: 对于绝大多数验证器而言,若其 [[yii\base\Validator::skipOnEmpty]] 属性为默认值 -true,则它们不会对空值进行任何处理。也就是当他们的关联特性接收到空值时,相关验证会被直接略过。在 -[核心验证器](tutorial-core-validators.md) 之中,只有 `captcha`(验证码),`default`(默认值),`filter`(滤镜),`required`(必填),以及 `trim`(去首尾空格),这几个验证器会处理空输入。 + true,则它们不会对空值进行任何处理。也就是当他们的关联特性接收到空值时,相关验证会被直接略过。在 + [核心验证器](tutorial-core-validators.md) 之中,只有 `captcha`(验证码),`default`(默认值), + `filter`(滤镜),`required`(必填),以及 `trim`(去首尾空格),这几个验证器会处理空输入。 ## 临时验证 @@ -208,7 +264,8 @@ if ($validator->validate($email, $error)) { } ``` -> Note: 不是所有的验证器都支持这种形式的验证。比如 [unique(唯一性)](tutorial-core-validators.md#unique)核心验证器就就是一个例子,它的设计初衷就是只作用于模型类内部的。 +> Note: 不是所有的验证器都支持这种形式的验证。比如 [unique(唯一性)](tutorial-core-validators.md#unique)核心验证器就就是一个例子, + 它的设计初衷就是只作用于模型类内部的。 若你需要针对一系列值执行多项验证,你可以使用 [[yii\base\DynamicModel]] 。它支持即时添加特性和验证规则的定义。它的使用规则是这样的: @@ -229,8 +286,9 @@ public function actionSearch($name, $email) } ``` -[[yii\base\DynamicModel::validateData()]] 方法会创建一个 `DynamicModel` 的实例对象,并通过给定数据定义模型特性(以 `name` 和 `email` 为例),之后用给定规则调用 -[[yii\base\Model::validate()]] 方法。 +[[yii\base\DynamicModel::validateData()]] 方法会创建一个 `DynamicModel` 的实例对象, +并通过给定数据定义模型特性(以 `name` 和 `email` 为例), +之后用给定规则调用[[yii\base\Model::validate()]] 方法。 除此之外呢,你也可以用如下的更加“传统”的语法来执行临时数据验证: @@ -252,18 +310,21 @@ public function actionSearch($name, $email) 验证之后你可以通过调用 [[yii\base\DynamicModel::hasErrors()|hasErrors()]] 方法来检查验证通过与否,并通过 [[yii\base\DynamicModel::errors|errors]] -属性获得验证的错误信息,过程与普通模型类一致。你也可以访问模型对象内定义的动态特性,就像: +属性获得验证的错误信息,过程与普通模型类一致。 +你也可以访问模型对象内定义的动态特性,就像: `$model->name` 和 `$model->email`。 ## 创建验证器(Validators) -除了使用 Yii 的发布版里所包含的[核心验证器](tutorial-core-validators.md)之外,你也可以创建你自己的验证器。自定义的验证器可以是**行内验证器**,也可以是**独立验证器**。 +除了使用 Yii 的发布版里所包含的[核心验证器](tutorial-core-validators.md)之外,你也可以创建你自己的验证器。 +自定义的验证器可以是**行内验证器**,也可以是**独立验证器**。 ### 行内验证器(Inline Validators) -行内验证器是一种以模型方法或匿名函数的形式定义的验证器。这些方法/函数的结构如下: +行内验证器是一种以模型方法或匿名函数的形式定义的验证器。 +这些方法/函数的结构如下: ```php /** @@ -273,7 +334,8 @@ public function actionSearch($name, $email) function ($attribute, $params) ``` -若某特性的验证失败了,该方法/函数应该调用 [[yii\base\Model::addError()]] 保存错误信息到模型内。这样这些错误就能在之后的操作中,被读取并展现给终端用户。 +若某特性的验证失败了,该方法/函数应该调用 [[yii\base\Model::addError()]] 保存错误信息到模型内。 +这样这些错误就能在之后的操作中,被读取并展现给终端用户。 下面是一些例子: @@ -288,17 +350,13 @@ class MyForm extends Model public function rules() { return [ - // 以模型方法 validateCountry() 形式定义的行内验证器 + // an inline validator defined as the model method validateCountry() ['country', 'validateCountry'], - // 以匿名函数形式定义的行内验证器 + // an inline validator defined as an anonymous function ['token', function ($attribute, $params) { if (!ctype_alnum($this->$attribute)) { -<<<<<<< HEAD - $this->addError($attribute, '令牌本身必须包含字母或数字。'); -======= - $this->addError($attribute, 'token 本身必须包含字母或数字。'); ->>>>>>> yiichina/master + $this->addError($attribute, 'The token must contain letters or digits.'); } }], ]; @@ -306,32 +364,34 @@ class MyForm extends Model public function validateCountry($attribute, $params) { -<<<<<<< HEAD - if (!in_array($this->$attribute, ['兲朝', '墙外'])) { - $this->addError($attribute, '国家必须为 "兲朝" 或 "墙外" 中的一个。'); -======= if (!in_array($this->$attribute, ['USA', 'Web'])) { $this->addError($attribute, 'The country must be either "USA" or "Web".'); ->>>>>>> yiichina/master } } } ``` -> Note: 缺省状态下,行内验证器不会在关联特性的输入值为空或该特性已经在其他验证中失败的情况下起效。若你想要确保该验证器始终启用的话,你可以在定义规则时,酌情将 [[yii\validators\Validator::skipOnEmpty|skipOnEmpty]] 以及 [[yii\validators\Validator::skipOnError|skipOnError]] - 属性设为 false,比如, +> Note: 缺省状态下,行内验证器不会在关联特性的输入值为空或该特性已经在其他验证中失败的情况下起效。 + 若你想要确保该验证器始终启用的话,你可以在定义规则时,酌情将 + [[yii\validators\Validator::skipOnEmpty|skipOnEmpty]] 以及 + [[yii\validators\Validator::skipOnError|skipOnError]]属性设为 false,比如, +> > ```php -[ - ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false], -] -``` +> [ +> ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false], +> ] +> ``` ### 独立验证器(Standalone Validators) 独立验证器是继承自 [[yii\validators\Validator]] 或其子类的类。你可以通过重写 [[yii\validators\Validator::validateAttribute()]] 来实现它的验证规则。若特性验证失败,可以调用 -[[yii\base\Model::addError()]] 以保存错误信息到模型内,操作与 [inline validators](#inline-validators) 所需操作完全一样。比如, +[[yii\base\Model::addError()]] 以保存错误信息到模型内, +操作与 [inline validators](#inline-validators) 所需操作完全一样。比如, + + +For example the inline validator above could be moved into new [[components/validators/CountryValidator]] class. ```php namespace app\components; @@ -342,35 +402,64 @@ class CountryValidator extends Validator { public function validateAttribute($model, $attribute) { -<<<<<<< HEAD - if (!in_array($model->$attribute, ['兲朝', '墙外'])) { - $this->addError($attribute, '国家必须为 "兲朝" 或 "墙外" 中的一个。'); -======= if (!in_array($model->$attribute, ['USA', 'Web'])) { - $this->addError($attribute, 'The country must be either "USA" or "Web".'); ->>>>>>> yiichina/master + $this->addError($model, $attribute, 'The country must be either "USA" or "Web".'); } } } ``` -若你想要验证器支持不使用 model 的数据验证,你还应该重写 -[[yii\validators\Validator::validate()]] 方法。你也可以通过重写 -[[yii\validators\Validator::validateValue()]] 方法替代 -`validateAttribute()` 和 `validate()`,因为默认状态下,后两者的实现使用过调用 -`validateValue()`实现的。 +若你想要验证器支持不使用 model 的数据验证,你还应该重写[[yii\validators\Validator::validate()]] 方法。 +你也可以通过重写[[yii\validators\Validator::validateValue()]] 方法替代 +`validateAttribute()` 和 `validate()`,因为默认状态下, +后两者的实现使用过调用`validateValue()`实现的。 + +Below is an example of how you could use the above validator class within your model. + +```php +namespace app\models; + +use Yii; +use yii\base\Model; +use app\components\validators\CountryValidator; + +class EntryForm extends Model +{ + public $name; + public $email; + public $country; + + public function rules() + { + return [ + [['name', 'email'], 'required'], + ['country', CountryValidator::className()], + ['email', 'email'], + ]; + } +} +``` ## 客户端验证器(Client-Side Validation) -当终端用户通过 HTML 表单提供相关输入信息时,我们可能会需要用到基于 JavaScript 的客户端验证。因为,它可以让用户更快速的得到错误信息,也因此可以提供更好的用户体验。你可以使用或自己实现除服务器端验证之外,**还能额外**客户端验证功能的验证器。 +当终端用户通过 HTML 表单提供相关输入信息时,我们可能会需要用到基于 JavaScript 的客户端验证。 +因为,它可以让用户更快速的得到错误信息,也因此可以提供更好的用户体验。 +你可以使用或自己实现除服务器端验证之外,**还能额外**客户端验证功能的验证器。 -> Info: 尽管客户端验证为加分项,但它不是必须项。它存在的主要意义在于给用户提供更好的客户体验。正如“永远不要相信来自终端用户的输入信息”,也同样永远不要相信客户端验证。基于这个理由,你应该始终如前文所描述的那样,通过调用 [[yii\base\Model::validate()]] 方法执行服务器端验证。 +> Info: 尽管客户端验证为加分项,但它不是必须项。它存在的主要意义在于给用户提供更好的客户体验。 + 正如“永远不要相信来自终端用户的输入信息”,也同样永远不要相信客户端验证。基于这个理由, + 你应该始终如前文所描述的那样,通过调用 [[yii\base\Model::validate()]] + 方法执行服务器端验证。 ### 使用客户端验证 -许多[核心验证器](tutorial-core-validators.md)都支持开箱即用的客户端验证。你只需要用 [[yii\widgets\ActiveForm]] 的方式构建 HTML 表单即可。比如,下面的 `LoginForm`(登录表单)声明了两个规则:其一为 [required](tutorial-core-validators.md#required) 核心验证器,它同时支持客户端与服务器端的验证;另一个则采用 `validatePassword` 行内验证器,它只支持服务器端。 +许多[核心验证器](tutorial-core-validators.md)都支持开箱即用的客户端验证。你只需要用 [[yii\widgets\ActiveForm]] +的方式构建 HTML 表单即可。比如,下面的 `LoginForm`(登录表单)声明了两个规则:其一为 [required](tutorial-core-validators.md#required) +核心验证器,它同时支持客户端与服务器端的验证;另一个则采用 +`validatePassword` 行内验证器,它只支持服务器端。 + ```php namespace app\models; @@ -405,7 +494,9 @@ class LoginForm extends Model } ``` -使用如下代码构建的 HTML 表单包含两个输入框 `username` 以及 `password`。如果你在没有输入任何东西之前提交表单,就会在没有任何与服务器端的通讯的情况下,立刻收到一个要求你填写空白项的错误信息。 +使用如下代码构建的 HTML 表单包含两个输入框 `username` 以及 `password`。 +如果你在没有输入任何东西之前提交表单,就会在没有任何与服务器端的通讯的情况下, +立刻收到一个要求你填写空白项的错误信息。 ```php @@ -415,21 +506,31 @@ class LoginForm extends Model ``` -幕后的运作过程是这样的:[[yii\widgets\ActiveForm]] 会读取声明在模型类中的验证规则,并生成那些支持支持客户端验证的验证器所需的 JavaScript 代码。当用户修改输入框的值,或者提交表单时,就会触发相应的客户端验证 JS 代码。 +幕后的运作过程是这样的:[[yii\widgets\ActiveForm]] 会读取声明在模型类中的验证规则, +并生成那些支持支持客户端验证的验证器所需的 JavaScript 代码。当用户修改输入框的值, +或者提交表单时,就会触发相应的客户端验证 JS 代码。 -若你需要完全关闭客户端验证,你只需配置 [[yii\widgets\ActiveForm::enableClientValidation]] 属性为 false。你同样可以关闭各个输入框各自的客户端验证,只要把它们的 [[yii\widgets\ActiveField::enableClientValidation]] 属性设为 false。 +若你需要完全关闭客户端验证,你只需配置 +[[yii\widgets\ActiveForm::enableClientValidation]] +属性为 false。你同样可以关闭各个输入框各自的客户端验证, +只要把它们的 [[yii\widgets\ActiveField::enableClientValidation]] +属性设为 false。 ### 自己实现客户端验证 要穿件一个支持客户端验证的验证器,你需要实现 -[[yii\validators\Validator::clientValidateAttribute()]] 方法,用于返回一段用于运行客户端验证的 JavaScript 代码。在这段 JavaScript 代码中,你可以使用以下预定义的变量: +[[yii\validators\Validator::clientValidateAttribute()]] 方法, +用于返回一段用于运行客户端验证的 JavaScript 代码。 +在这段 JavaScript 代码中,你可以使用以下预定义的变量: - `attribute`:正在被验证的模型特性的名称。 - `value`:进行验证的值。 - `messages`:一个用于暂存模型特性的报错信息的数组。 +- `deferred`: an array which deferred objects can be pushed into (explained in the next subsection). -在下面的例子里,我们会创建一个 `StatusValidator`,它会通过比对现有的状态数据,验证输入值是否为一个有效的状态。该验证器同时支持客户端以及服务器端验证。 +在下面的例子里,我们会创建一个 `StatusValidator`,它会通过比对现有的状态数据, +验证输入值是否为一个有效的状态。该验证器同时支持客户端以及服务器端验证。 ```php namespace app\components; @@ -466,9 +567,148 @@ JS; } ``` -> Tip: 上述代码主要是演示了如何支持客户端验证。在具体实践中,你可以使用 [in](tutorial-core-validators.md#in) 核心验证器来达到同样的目的。比如这样的验证规则: +> Tip: 上述代码主要是演示了如何支持客户端验证。在具体实践中, +> 你可以使用 [in](tutorial-core-validators.md#in) 核心验证器来达到同样的目的。 +> 比如这样的验证规则: +> > ```php -[ - ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()], -] +> [ +> ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()], +> ] +> ``` + +> Tip: If you need to work with client validation manually i.e. dynamically add fields or do some custom UI logic, refer +> to [Working with ActiveForm via JavaScript](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-activeform-js.md) +> in Yii 2.0 Cookbook. + +### Deferred Validation + +If you need to perform asynchronous client-side validation, you can create [Deferred objects](http://api.jquery.com/category/deferred-object/). +For example, to perform a custom AJAX validation, you can use the following code: + +```php +public function clientValidateAttribute($model, $attribute, $view) +{ + return << 150) { + messages.push('Image too wide!!'); + } + def.resolve(); + } + var reader = new FileReader(); + reader.onloadend = function() { + img.src = reader.result; + } + reader.readAsDataURL(file); + + deferred.push(def); +JS; +} +``` + +> Note: The `resolve()` method must be called after the attribute has been validated. Otherwise the main form + validation will not complete. + +For simplicity, the `deferred` array is equipped with a shortcut method `add()` which automatically creates a Deferred +object and adds it to the `deferred` array. Using this method, you can simplify the above example as follows, + +```php +public function clientValidateAttribute($model, $attribute, $view) +{ + return << 150) { + messages.push('Image too wide!!'); + } + def.resolve(); + } + var reader = new FileReader(); + reader.onloadend = function() { + img.src = reader.result; + } + reader.readAsDataURL(file); + }); +JS; +} +``` + + +### AJAX Validation + +Some validations can only be done on the server side, because only the server has the necessary information. +For example, to validate if a username is unique or not, it is necessary to check the user table on the server side. +You can use AJAX-based validation in this case. It will trigger an AJAX request in the background to validate the +input while keeping the same user experience as the regular client-side validation. + +To enable AJAX validation for a single input field, configure the [[yii\widgets\ActiveField::enableAjaxValidation|enableAjaxValidation]] +property of that field to be true and specify a unique form `id`: + +```php +use yii\widgets\ActiveForm; + +$form = ActiveForm::begin([ + 'id' => 'registration-form', +]); + +echo $form->field($model, 'username', ['enableAjaxValidation' => true]); + +// ... + +ActiveForm::end(); +``` + +To enable AJAX validation for the whole form, configure [[yii\widgets\ActiveForm::enableAjaxValidation|enableAjaxValidation]] +to be true at the form level: + +```php +$form = ActiveForm::begin([ + 'id' => 'contact-form', + 'enableAjaxValidation' => true, +]); +``` + +> Note: When the `enableAjaxValidation` property is configured at both the input field level and the form level, + the former will take precedence. + +You also need to prepare the server so that it can handle the AJAX validation requests. +This can be achieved by a code snippet like the following in the controller actions: + +```php +if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) { + Yii::$app->response->format = Response::FORMAT_JSON; + return ActiveForm::validate($model); +} +``` + +The above code will check whether the current request is an AJAX. If yes, it will respond to +this request by running the validation and returning the errors in JSON format. + +> Info: You can also use [Deferred Validation](#deferred-validation) to perform AJAX validation. + However, the AJAX validation feature described here is more systematic and requires less coding effort. + +When both `enableClientValidation` and `enableAjaxValidation` are set to true, AJAX validation request will be triggered +only after the successful client validation. diff --git a/docs/guide-zh-CN/intro-upgrade-from-v1.md b/docs/guide-zh-CN/intro-upgrade-from-v1.md index 3b6e1f3..6f496c1 100644 --- a/docs/guide-zh-CN/intro-upgrade-from-v1.md +++ b/docs/guide-zh-CN/intro-upgrade-from-v1.md @@ -1,23 +1,33 @@ 从 Yii 1.1 升级 =============== -2.0 版框架是完全重写的,在 1.1 和 2.0 两个版本之间存在相当多差异。因此从 1.1 版升级并不像小版本间的跨越那么简单,通过本指南你将会了解两个版本间主要的不同之处。 +2.0 版框架是完全重写的,在 1.1 和 2.0 两个版本之间存在相当多差异。 +因此从 1.1 版升级并不像小版本间的跨越那么简单,通过本指南你将会 +了解两个版本间主要的不同之处。 如果你之前没有用过 Yii 1.1,可以跳过本章,直接从"[入门篇](start-installation.md)"开始读起。 -请注意,Yii 2.0 引入了很多本章并没有涉及到的新功能。强烈建议你通读整部权威指南来了解所有新特性。这样有可能会发现一些以前你要自己开发的功能,而现在已经被包含在核心代码中了。 +请注意,Yii 2.0 引入了很多本章并没有涉及到的新功能。强烈建议你通读整部权威指南 +来了解所有新特性。这样有可能会发现一些以前你要自己开发的功能,而现在已经被包含 +在核心代码中了。 安装 ------------ -Yii 2.0 完全拥抱 [Composer](https://getcomposer.org/),它是事实上的 PHP 依赖管理工具。核心框架以及扩展的安装都通过 Composer 来处理。想要了解更多如何安装 Yii 2.0 请参阅本指南的 [安装 Yii](start-installation.md) 章节。如果你想创建新扩展,或者把你已有的 Yii 1.1 的扩展改写成兼容 2.0 的版本,你可以参考 [创建扩展](structure-extensions.md#creating-extensions) 章节。 +Yii 2.0 完全拥抱 [Composer](https://getcomposer.org/),它是事实上的 PHP 依赖管理工具。 +核心框架以及扩展的安装都通过 Composer 来处理。想要了解更多如何安装 Yii 2.0 请参阅本 +指南的 [安装 Yii](start-installation.md) 章节。如果你想创建新扩展,或者把你已有的 +Yii 1.1 的扩展改写成兼容 2.0 的版本,你可以参考 +[创建扩展](structure-extensions.md#creating-extensions) 章节。 PHP 需求 ---------------- -Yii 2.0 需要 PHP 5.4 或更高版本,该版本相对于 Yii 1.1 所需求的 PHP 5.2 而言有巨大的改进。因此在语言层面上有很多的值得注意的不同之处。下面是 PHP 层的主要变化汇总: +Yii 2.0 需要 PHP 5.4 或更高版本,该版本相对于 Yii 1.1 所需求的 PHP 5.2 而言有巨大的改进。 +因此在语言层面上有很多的值得注意的不同之处。 +下面是 PHP 层的主要变化汇总: - [命名空间](http://php.net/manual/zh/language.namespaces.php) - [匿名函数](http://php.net/manual/zh/functions.anonymous.php) @@ -27,29 +37,41 @@ Yii 2.0 需要 PHP 5.4 或更高版本,该版本相对于 Yii 1.1 所需求的 - [延迟静态绑定](http://php.net/manual/zh/language.oop5.late-static-bindings.php) - [日期和时间](http://php.net/manual/zh/book.datetime.php) - [Traits](http://php.net/manual/zh/language.oop5.traits.php) -- [intl](http://php.net/manual/zh/book.intl.php) Yii 2.0 使用 PHP 扩展 `intl` 来支持国际化的相关功能。 +- [intl](http://php.net/manual/zh/book.intl.php) Yii 2.0 使用 PHP 扩展 `intl` + 来支持国际化的相关功能。 命名空间 --------- -Yii 2.0 里最明显的改动就数命名空间的使用了。几乎每一个核心类都引入了命名空间,比如 `yii\web\Request`。1.1 版类名前缀 “C” 已经不再使用。当前的命名方案与目录结构相吻合。例如,`yii\web\Request` 就表明对应的类文件是 Yii 框架文件夹下的 `web/Request.php` 文件。 +Yii 2.0 里最明显的改动就数命名空间的使用了。几乎每一个核心类都引入了命名空间, +比如 `yii\web\Request`。1.1 版类名前缀 “C” 已经不再使用。当前的命名方案与目录结构相吻合。 +例如,`yii\web\Request` 就表明对应的类文件是 +Yii 框架文件夹下的 `web/Request.php` 文件。 -有了 Yii 的类自动加载器,你可以直接使用全部核心类而不需要显式包含具体文件。 +(有了 Yii 的类自动加载器,你可以直接使用全部核心类而不需要显式包含 +具体文件。) 组件(Component)与对象(Object) -------------------- -Yii 2.0 把 1.1 中的 `CComponent` 类拆分成了两个类:[[yii\base\Object]] 和 [[yii\base\Component]]。[[yii\base\Object|Object]] 类是一个轻量级的基类,你可以通过 getters 和 setters 来定义[对象的属性](concept-properties.md)。[[yii\base\Component|Component]] 类继承自 [[yii\base\Object|Object]],同时进一步支持 [事件](concept-events.md) 和 [行为](concept-behaviors.md)。 +Yii 2.0 把 1.1 中的 `CComponent` 类拆分成了两个类:[[yii\base\Object]] 和 [[yii\base\Component]]。 +[[yii\base\Object|Object]] 类是一个轻量级的基类,你可以通过 getters 和 setters 来定义 +[对象的属性](concept-properties.md)。[[yii\base\Component|Component]] 类继承自 [[yii\base\Object|Object]], +同时进一步支持 [事件](concept-events.md) 和 [行为](concept-behaviors.md)。 -如果你不需要用到事件或行为,应该考虑使用 [[yii\base\Object|Object]] 类作为基类。这种类通常用来表示基本的数据结构。 +如果你不需要用到事件或行为,应该考虑使用 +[[yii\base\Object|Object]] 类作为基类。 +这种类通常用来表示基本的数据结构。 对象的配置 -------------------- -[[yii\base\Object|Object]] 类引入了一种统一对象配置的方法。所有 [[yii\base\Object|Object]] 的子类都应该用以下方法声明它的构造方法(如果需要的话),以正确配置它自身: +[[yii\base\Object|Object]] 类引入了一种统一对象配置的方法。 +所有 [[yii\base\Object|Object]] 的子类都应该用以下方法声明 +它的构造方法(如果需要的话),以正确配置它自身: ```php class MyClass extends \yii\base\Object @@ -70,9 +92,13 @@ class MyClass extends \yii\base\Object } ``` -在上面的例子里,构造方法的最后一个参数必须传入一个配置数组,包含一系列用于在方法结尾初始化相关属性的键值对。你可以重写 [[yii\base\Object::init()|init()]] 方法来执行一些需要在配置生效后进行的初始化工作。 +在上面的例子里,构造方法的最后一个参数必须传入一个配置数组, +包含一系列用于在方法结尾初始化相关属性的键值对。你可以重写 +[[yii\base\Object::init()|init()]] 方法来执行一些需要在配置 +生效后进行的初始化工作。 -你可以通过遵循以下约定俗成的编码习惯,来使用配置数组创建并配置新的对象: +你可以通过遵循以下约定俗成的编码习惯, +来使用配置数组创建并配置新的对象: ```php $object = Yii::createObject([ @@ -88,7 +114,8 @@ $object = Yii::createObject([ 事件(Event) ------ -在 Yii 1 中,通常通过定义 `on` 开头的方法(例如 `onBeforeSave`)来创建事件。而在 Yii 2 中,你可以使用任意的事件名了。同时通过调用 [[yii\base\Component::trigger()|trigger()]] 方法来触发相关事件: +在 Yii 1 中,通常通过定义 `on` 开头的方法(例如 `onBeforeSave`)来创建事件。而在 Yii 2 中,你可以使用任意的事件名了。 +同时通过调用 [[yii\base\Component::trigger()|trigger()]] 方法来触发相关事件: ```php $event = new \yii\base\Event; @@ -109,9 +136,18 @@ $component->on($eventName, $handler); 路径别名(Path Alias) ------------ -Yii 2.0 将路径别名的应用扩大至文件/目录路径和 URL。Yii 2.0 中路径别名必须以 `@` 符号开头,以区别于普通文件目录路径或 URL。例如 `@yii` 就是指向 Yii 安装目录的别名。绝大多数 Yii 核心代码都支持别名。例如 [[yii\caching\FileCache::cachePath]] 就同时支持路径别名或普通的目录地址。 +Yii 2.0 将路径别名的应用扩大至文件/目录路径和 URL。 +Yii 2.0 中路径别名必须以 `@` 符号开头, +以区别于普通文件目录路径或 URL。例如 `@yii` 就是指向 Yii 安装目录的别名。 +绝大多数 Yii 核心代码都支持别名。例如 [[yii\caching\FileCache::cachePath]] +就同时支持路径别名或普通的目录地址。 -路径别名也和类的命名空间密切相关。建议给每一个根命名空间定义一个路径别名,从而无须额外配置,便可启动 Yii 的类自动加载机制。例如,因为有 `@yii` 指向 Yii 安装目录,那类似 `yii\web\Request` 的类就能被 Yii 自动加载。同理,若你用了一个第三方的类库,如 Zend Framework,你只需定义一个名为 `@Zend` 的路径别名指向该框架的安装目录。之后 Yii 就可以自动加载任意 Zend Framework 中的类了。 +路径别名也和类的命名空间密切相关。建议给每一个根命名空间定义一个路径别名, +从而无须额外配置,便可启动 Yii 的类自动加载机制。 +例如,因为有 `@yii` 指向 Yii 安装目录,那类似 `yii\web\Request` +的类就能被 Yii 自动加载。同理,若你用了一个第三方的类库, +如 Zend Framework,你只需定义一个名为 `@Zend` 的路径别名指向该 +框架的安装目录。之后 Yii 就可以自动加载任意 Zend Framework 中的类了。 更多路径别名信息请参阅[路径别名](concept-aliases.md)章节。 @@ -119,23 +155,33 @@ Yii 2.0 将路径别名的应用扩大至文件/目录路径和 URL。Yii 2.0 视图(View) ----- -Yii 2 中视图最明显的改动是视图内的特殊变量 `$this` 不再指向当前控制器或小部件,而是指向**视图**对象,它是 2.0 中引入的全新概念。**视图**对象为 [[yii\web\View]] 的实例,他代表了 MVC 模式中的视图部分。如果你想要在视图中访问一个控制器或小部件,可以使用 `$this->context`。 +Yii 2 中视图最明显的改动是视图内的特殊变量 `$this` 不再指向当前控制器或小部件, +而是指向**视图**对象,它是 2.0 中引入的全新概念。**视图**对象为 +[[yii\web\View]] 的实例,他代表了 MVC 模式中的视图部分。 +如果你想要在视图中访问一个控制器或小部件,可以使用 `$this->context`。 -要在其他视图里渲染一个局部视图,使用 `$this->render()`,而不是 `$this->renderPartial()`。`render()` 现在只返回渲染结果,而不是直接显示它,所以现在你必须显式地把它 **echo** 出来。像这样: +要在其他视图里渲染一个局部视图,使用 `$this->render()`,而不是 `$this->renderPartial()`。 +`render()` 现在只返回渲染结果,而不是直接显示它,所以现在你必须显式地把它 **echo** 出来。像这样: ```php echo $this->render('_item', ['item' => $item]); ``` -除了使用 PHP 作为主要的模板语言,Yii 2.0 也装备了两种流行模板引擎的官方支持:Smarty 和 Twig。过去的 Prado 模板引擎不再被支持。要使用这些模板引擎,你需要配置 `view` 应用组件,给它设置 [[yii\base\View::$renderers|View::$renderers]] 属性。具体请参阅[模板引擎](tutorial-template-engines.md)章节。 +除了使用 PHP 作为主要的模板语言,Yii 2.0 也装备了两种流行模板引擎的官方支持: +Smarty 和 Twig。过去的 Prado 模板引擎不再被支持。要使用这些模板引擎, +你需要配置 `view` 应用组件,给它设置 +[[yii\base\View::$renderers|View::$renderers]] 属性。 +具体请参阅[模板引擎](tutorial-template-engines.md)章节。 模型(Model) ------ -Yii 2.0 使用 [[yii\base\Model]] 作为模型基类,类似于 1.1 的 `CModel` 。`CFormModel` 被完全弃用了,现在要创建表单模型类,可以通过继承 [[yii\base\Model]] 类来实现。 +Yii 2.0 使用 [[yii\base\Model]] 作为模型基类,类似于 1.1 的 `CModel` 。`CFormModel` 被完全弃用了, +现在要创建表单模型类,可以通过继承 [[yii\base\Model]] 类来实现。 -Yii 2.0 引进了名为 [[yii\base\Model::scenarios()|scenarios()]] 的新方法来声明支持的场景,并指明在哪个场景下某属性必须经过验证,可否被视为安全值等等。如: +Yii 2.0 引进了名为 [[yii\base\Model::scenarios()|scenarios()]] 的新方法来声明支持的场景, +并指明在哪个场景下某属性必须经过验证,可否被视为安全值等等。如: ```php public function scenarios() @@ -147,20 +193,27 @@ public function scenarios() } ``` -上面的代码声明了两个场景:`backend` 和 `frontend` 。对于 `backend` 场景,`email` 和 `role` 属性值都是安全的,且能进行批量赋值。对于 `frontend` 场景,`email` 能批量赋值而 `role` 不能。 `email` 和 `role` 都必须通过规则验证。 +上面的代码声明了两个场景:`backend` 和 `frontend` 。对于 `backend` 场景, +`email` 和 `role` 属性值都是安全的,且能进行批量赋值。对于 `frontend` 场景, +`email` 能批量赋值而 `role` 不能。 `email` 和 `role` 都必须通过规则验证。 [[yii\base\Model::rules()|rules()]] 方法仍用于声明验证规则。注意,由于引入了 [[yii\base\Model::scenarios()|scenarios()]],现在已经没有 `unsafe` 验证器了。 -大多数情况下,如果 [[yii\base\Model::rules()|rules()]] 方法内已经完整地指定场景了,那就不必覆写 [[yii\base\Model::scenarios()|scenarios()]],也不必声明 `unsafe` 属性值。 +大多数情况下,如果 [[yii\base\Model::rules()|rules()]] 方法内已经完整地指定场景了, +那就不必覆写 [[yii\base\Model::scenarios()|scenarios()]], +也不必声明 `unsafe` 属性值。 要了解更多有关模型的细节,请参考[模型](structure-models.md)章节。 + 控制器(Controller) ----------- -Yii 2.0 使用 [[yii\web\Controller]] 作为控制器的基类,它类似于 1.1 的 `CController`。使用 [[yii\base\Action]] 作为操作类的基类。 +Yii 2.0 使用 [[yii\web\Controller]] 作为控制器的基类,它类似于 1.1 的 `CController`。 +使用 [[yii\base\Action]] 作为操作类的基类。 -这些变化最明显的影响是,当你在写控制器操作的代码时,应该返回(return)要渲染的内容而不是输出(echo)它: +这些变化最明显的影响是,当你在写控制器操作的代码时, +应该返回(return)要渲染的内容而不是输出(echo)它: ```php public function actionView($id) @@ -182,7 +235,9 @@ public function actionView($id) Yii 2.0 使用 [[yii\base\Widget]] 作为小部件基类,类似于 1.1 的 `CWidget`。 -为了让框架获得更好的 IDE 支持,Yii 2.0 引进了一个调用小部件的新语法。包含 [[yii\base\Widget::begin()|begin()]],[[yii\base\Widget::end()|end()]] 和 [[yii\base\Widget::widget()|widget()]] 三个静态方法,用法如下: +为了让框架获得更好的 IDE 支持,Yii 2.0 引进了一个调用小部件的新语法。 +包含 [[yii\base\Widget::begin()|begin()]],[[yii\base\Widget::end()|end()]] +和 [[yii\base\Widget::widget()|widget()]] 三个静态方法,用法如下: ```php use yii\widgets\Menu; @@ -206,9 +261,14 @@ ActiveForm::end(); 主题(Theme) ------ -2.0 主题的运作方式跟以往完全不同了。它们现在基于**路径映射机制**,该机制会把一个源视图文件的路径映射到一个主题视图文件路径。举例来说,如果路径映射为 `['/web/views' => '/web/themes/basic']`,那么 `/web/views/site/index.php` 视图经过主题修饰的版本就会是 `/web/themes/basic/site/index.php`。也因此让主题现在可以应用在任何视图文件之上,甚至是渲染控制器上下文环境之外的视图文件或小部件。 +2.0 主题的运作方式跟以往完全不同了。它们现在基于**路径映射机制**, +该机制会把一个源视图文件的路径映射到一个主题视图文件路径。 +举例来说,如果路径映射为 `['/web/views' => '/web/themes/basic']`, +那么 `/web/views/site/index.php` 视图经过主题修饰的版本就会是 `/web/themes/basic/site/index.php`。 +也因此让主题现在可以应用在任何视图文件之上,甚至是渲染控制器上下文环境之外的视图文件或小部件。 -同样,`CThemeManager` 组件已经被移除了。取而代之的 `theme` 成为了 `view` 应用组件的一个可配置属性。 +同样,`CThemeManager` 组件已经被移除了。取而代之的 `theme` 成为了 `view` +应用组件的一个可配置属性。 更多细节请参考[主题](output-theming.md)章节。 @@ -216,9 +276,13 @@ ActiveForm::end(); 控制台应用(Console Application) -------------------- -控制台应用现在如普通的 Web 应用程序一样,由控制器组成,控制台的控制器继承自 [[yii\console\Controller]],类似于 1.1 的 `CConsoleCommand`。 +控制台应用现在如普通的 Web 应用程序一样,由控制器组成,控制台的控制器继承自 [[yii\console\Controller]], +类似于 1.1 的 `CConsoleCommand`。 -运行控制台命令使用 `yii `,其中 `` 代表控制器的路由(如 `sitemap/index`)。额外的匿名参数传递到对应的控制器操作方法,而有名的参数根据 [[yii\console\Controller::options()]] 的声明来解析。 +运行控制台命令使用 `yii `,其中 `` +代表控制器的路由(如 `sitemap/index`)。 +额外的匿名参数传递到对应的控制器操作方法,而有名的参数根据 +[[yii\console\Controller::options()]] 的声明来解析。 Yii 2.0 支持基于代码注释自动生成相的关命令行帮助(help)信息。 @@ -230,7 +294,9 @@ Yii 2.0 支持基于代码注释自动生成相的关命令行帮助(help) Yii 2.0 移除了原来内置的日期格式器和数字格式器,为了支持 [PECL intl PHP module](http://pecl.php.net/package/intl)(PHP 的国际化扩展)的使用。 -消息翻译现在由 `i18n` 应用组件执行。该组件管理一系列消息源,允许使用基于消息类别的不同消息源。 +消息翻译现在由 `i18n` 应用组件执行。 +该组件管理一系列消息源, +允许使用基于消息类别的不同消息源。 更多细节请参阅[国际化(Internationalization)](tutorial-i18n.md)章节。 @@ -238,7 +304,9 @@ Yii 2.0 移除了原来内置的日期格式器和数字格式器,为了支持 操作过滤器(Action Filters) -------------- -操作的过滤现在通过行为(behavior)来实现。要定义一个新的,自定义的过滤器,请继承 [[yii\base\ActionFilter]] 类。要使用一个过滤器,需要把过滤器类作为一个 `behavior` 绑定到控制器上。例如,要使用 [[yii\filters\AccessControl]] 过滤器,你需要在控制器内添加如下代码: +操作的过滤现在通过行为(behavior)来实现。要定义一个新的,自定义的过滤器, +请继承 [[yii\base\ActionFilter]] 类。要使用一个过滤器,需要把过滤器类作为一个 `behavior` 绑定到控制器上。 +例如,要使用 [[yii\filters\AccessControl]] 过滤器,你需要在控制器内添加如下代码: ```php public function behaviors() @@ -262,7 +330,11 @@ public function behaviors() Yii 2.0 引入了一个新的概念,称为**资源包**(Asset Bundle),以代替 1.1 的脚本包概念。 -一个资源包是一个目录下的资源文件集合(如 JavaScript 文件、CSS 文件、图片文件等)。每一个资源包被表示为一个类,该类继承自 [[yii\web\AssetBundle]]。用 [[yii\web\AssetBundle::register()]] 方法注册一个资源包后,就使它的资源可被 Web 访问了,注册了资源包的页面会自动包含和引用资源包内指定的 JS 和 CSS 文件。 +一个资源包是一个目录下的资源文件集合(如 JavaScript 文件、CSS 文件、图片文件等)。 +每一个资源包被表示为一个类,该类继承自 [[yii\web\AssetBundle]]。 +用 [[yii\web\AssetBundle::register()]] 方法注册一个资源包后, +就使它的资源可被 Web 访问了, +注册了资源包的页面会自动包含和引用资源包内指定的 JS 和 CSS 文件。 更多细节请参阅 [前端资源管理(Asset)](structure-assets.md) 章节。 @@ -283,7 +355,10 @@ Yii 2.0 很多常用的静态助手类,包括: 表单 ----- -Yii 2.0 引进了**表单栏(field)**的概念,用来创建一个基于 [[yii\widgets\ActiveForm]] 的表单。一个表单栏是一个由标签、输入框、错误消息(可能还有提示文字)组成的容器,被表示为一个 [[yii\widgets\ActiveField|ActiveField]] 对象。使用表单栏建立表单的过程比以前更整洁利落: +Yii 2.0 引进了**表单栏(field)**的概念,用来创建一个基于 [[yii\widgets\ActiveForm]] 的表单。 +一个表单栏是一个由标签、输入框、错误消息(可能还有提示文字)组成的容器, +被表示为一个 [[yii\widgets\ActiveField|ActiveField]] 对象。 +使用表单栏建立表单的过程比以前更整洁利落: ```php @@ -301,7 +376,10 @@ Yii 2.0 引进了**表单栏(field)**的概念,用来创建一个基于 [[ 查询生成器(Query Builder) ------------- -Yii 1.1 中,查询语句的生成分散在多个类中,包括 `CDbCommand`,`CDbCriteria` 以及 `CDbCommandBuilder`。Yii 2.0 以 [[yii\db\Query|Query]] 对象的形式表示一个数据库查询,这个对象使用 [[yii\db\QueryBuilder|QueryBuilder]] 在幕后生成 SQL 语句。例如: +Yii 1.1 中,查询语句的生成分散在多个类中,包括 `CDbCommand`,`CDbCriteria` 以及 `CDbCommandBuilder`。 +Yii 2.0 以 [[yii\db\Query|Query]] 对象的形式表示一个数据库查询, +这个对象使用 [[yii\db\QueryBuilder|QueryBuilder]] 在幕后生成 SQL 语句。 +例如: ```php $query = new \yii\db\Query(); @@ -322,9 +400,11 @@ $rows = $command->queryAll(); 活动记录(Active Record) ------------- -Yii 2.0 的[活动记录](db-active-record.md)改动了很多。两个最显而易见的改动分别涉及查询语句的生成(query building)和关联查询的处理(relational query handling)。 +Yii 2.0 的[活动记录](db-active-record.md)改动了很多。两个最显而易见的改动分别涉及查询语句的生成 +(query building)和关联查询的处理(relational query handling)。 -1.1 中的 `CDbCriteria` 类在 Yii 2 中被 [[yii\db\ActiveQuery]] 所替代。这个类是继承自 [[yii\db\Query]],因此也继承了所有查询生成方法。开始拼装一个查询可以调用 [[yii\db\ActiveRecord::find()]] 方法进行: +1.1 中的 `CDbCriteria` 类在 Yii 2 中被 [[yii\db\ActiveQuery]] 所替代。这个类是继承自 [[yii\db\Query]], +因此也继承了所有查询生成方法。开始拼装一个查询可以调用 [[yii\db\ActiveRecord::find()]] 方法进行: ```php // 检索所有“活动的”客户和订单,并以 ID 排序: @@ -334,7 +414,9 @@ $customers = Customer::find() ->all(); ``` -要声明一个关联关系,只需简单地定义一个 getter 方法来返回一个 [[yii\db\ActiveQuery|ActiveQuery]] 对象。getter 方法定义的属性名代表关联表名称。如,以下代码声明了一个名为 `orders` 的关系(1.1 中必须在 `relations()` 方法内声明关系): +要声明一个关联关系,只需简单地定义一个 getter 方法来返回一个 [[yii\db\ActiveQuery|ActiveQuery]] 对象。 +getter 方法定义的属性名代表关联表名称。如,以下代码声明了一个名为 `orders` 的关系 +(1.1 中必须在 `relations()` 方法内声明关系): ```php class Customer extends \yii\db\ActiveRecord @@ -345,22 +427,29 @@ class Customer extends \yii\db\ActiveRecord } } ``` -现在你就可以通过调用 `$customer->orders` 来访问关联表中某用户的订单了。你还可以用以下代码进行一场指定条件的实时关联查询: + +现在你就可以通过调用 `$customer->orders` 来访问关联表中某用户的订单了。 +你还可以用以下代码进行一场指定条件的实时关联查询: + ```php $orders = $customer->getOrders()->andWhere('status=1')->all(); ``` +当贪婪加载一段关联关系时,Yii 2.0 和 1.1 的运作机理并不相同。 +具体来说,在 1.1 中使用一条 JOIN 语句同时查询主表和关联表记录。 +在 Yii 2.0 中会使用两个没有 JOIN 的 SQL 语句:第一条语句取回主表记录, +第二条通过主表记录经主键筛选后查询关联表记录。 -当贪婪加载一段关联关系时,Yii 2.0 和 1.1 的运作机理并不相同。具体来说,在 1.1 中使用一条 JOIN 语句同时查询主表和关联表记录。在 Yii 2.0 中会使用两个没有 JOIN 的 SQL 语句:第一条语句取回主表记录,第二条通过主表记录经主键筛选后查询关联表记录。 - -当生成返回大量记录的查询时,可以链式书写 [[yii\db\ActiveQuery::asArray()|asArray()]] 方法,这样会以数组的形式返回查询结果,而不必返回 -[[yii\db\ActiveRecord|ActiveRecord]] 对象,这能显著降低因大量记录读取所消耗的 CPU 时间和内存。如: +当生成返回大量记录的查询时,可以链式书写 [[yii\db\ActiveQuery::asArray()|asArray()]] 方法, +这样会以数组的形式返回查询结果,而不必返回[[yii\db\ActiveRecord|ActiveRecord]] 对象, +这能显著降低因大量记录读取所消耗的 CPU 时间和内存。如: ```php $customers = Customer::find()->asArray()->all(); ``` -另一个改变是你不能再通过公共变量定义属性(Attribute)的默认值了。如果你需要这么做的话,可以在你的记录类的 `init` 方法中设置它们。 +另一个改变是你不能再通过公共变量定义属性(Attribute)的默认值了。 +如果你需要这么做的话,可以在你的记录类的 `init` 方法中设置它们。 ```php public function init() @@ -370,15 +459,19 @@ public function init() } ``` -曾几何时,在 1.1 中重写一个活动记录类的构造方法会导致一些问题。它们不会在 2.0 中出现了。需要注意的是,如果你需要在构造方法中添加一些参数,恐怕必须重写 [[yii\db\ActiveRecord::instantiate()]] 方法。 +曾几何时,在 1.1 中重写一个活动记录类的构造方法会导致一些问题。 +它们不会在 2.0 中出现了。需要注意的是,如果你需要在构造方法中添加一些参数,恐怕必须重写 [[yii\db\ActiveRecord::instantiate()]] 方法。 -活动记录方面还有很多其他的变化与改进,请参考[活动记录](db-active-record.md)章节以了解更多细节。 +活动记录方面还有很多其他的变化与改进, +请参考[活动记录](db-active-record.md)章节以了解更多细节。 活动记录行为(Active Record Behaviors) ------------------------------------ -在 2.0 中遗弃了活动记录行为基类 `CActiveRecordBehavior`。如果你想创建活动记录行为,需要直接继承 `yii\base\Behavior`。如果行为类中需要表示一些事件,需要像这样覆写 `events()` 方法: +在 2.0 中遗弃了活动记录行为基类 `CActiveRecordBehavior`。 +如果你想创建活动记录行为,需要直接继承 `yii\base\Behavior`。 +如果行为类中需要表示一些事件,需要像这样覆写 `events()` 方法: ```php namespace app\components; @@ -408,7 +501,9 @@ class MyBehavior extends Behavior 用户及身份验证接口(IdentityInterface) ------------------------------------- -1.1 中的 `CWebUser` 类现在被 [[yii\web\User]] 所取代,随之 `CUserIdentity` 类也不在了。与之相对的,为达到相同目的,你可以实现 [[yii\web\IdentityInterface]] 接口,它使用起来更直观。在高级应用模版里提供了一个这样的一个例子。 +1.1 中的 `CWebUser` 类现在被 [[yii\web\User]] 所取代,随之 `CUserIdentity` 类也不在了。与之相对的, +为达到相同目的,你可以实现 [[yii\web\IdentityInterface]] 接口,它使用起来更直观。 +在高级应用模版里提供了一个这样的一个例子。 要了解更多细节请参考[认证(Authentication)](security-authentication.md),[授权(Authorization)](security-authorization.md)以及[高级应用模版](tutorial-advanced-app.md) 这三个章节。 @@ -416,7 +511,10 @@ class MyBehavior extends Behavior URL 管理 -------- -Yii 2.0 的 URL 管理跟 1.1 中很像。一个主要的改进是现在的 URL 管理支持**可选参数**了。比如,如果你在 2.0 中定义了一个下面这样的规则,那么它可以同时匹配 `post/popular` 和 `post/1/popular` 两种 URL。而在 1.1 中为达成相同效果,必须要使用两条规则。 +Yii 2.0 的 URL 管理跟 1.1 中很像。一个主要的改进是现在的 URL 管理支持**可选参数**了。 +比如,如果你在 2.0 中定义了一个下面这样的规则,那么它可以同时匹配 `post/popular` +和 `post/1/popular` 两种 URL。而在 1.1 中为达成相同效果, +必须要使用两条规则。 ```php [ @@ -428,7 +526,15 @@ Yii 2.0 的 URL 管理跟 1.1 中很像。一个主要的改进是现在的 URL 请参考[URL 解析和生成](runtime-url-handling.md) 章节,以了解更多细节。. +An important change in the naming convention for routes is that camel case names of controllers +and actions are now converted to lower case where each word is separated by a hypen, e.g. the controller +id for the `CamelCaseController` will be `camel-case`. +See the section about [controller IDs](structure-controllers.md#controller-ids) and [action IDs](structure-controllers.md#action-ids) for more details. + + 同时使用 Yii 1.1 和 2.x ---------------------- -如果你有一些遗留的 Yii 1.1 代码,需要跟 Yii 2.0 一起使用,可以参考 [1.1 和 2.0 共用](tutorial-yii-integration.md)章节。 +如果你有一些遗留的 Yii 1.1 代码,需要跟 Yii 2.0 一起使用, +可以参考 [1.1 和 2.0 共用](tutorial-yii-integration.md)章节。 + diff --git a/docs/guide-zh-CN/intro-yii.md b/docs/guide-zh-CN/intro-yii.md index c5ad3cb..75a5d4a 100644 --- a/docs/guide-zh-CN/intro-yii.md +++ b/docs/guide-zh-CN/intro-yii.md @@ -1,40 +1,60 @@ Yii 是什么 =========== -Yii 是一个高性能,基于组件的 PHP 框架,用于快速开发现代 Web 应用程序。名字 Yii (读作 `易`)在中文里有“极致简单与不断演变”两重含义,也可看作 **Yes It Is**! 的缩写。 +Yii 是一个高性能,基于组件的 PHP 框架,用于快速开发现代 Web 应用程序。 +名字 Yii (读作 `易`)在中文里有“极致简单与不断演变”两重含义, +也可看作 **Yes It Is**! 的缩写。 Yii 最适合做什么? --------------------- -Yii 是一个通用的 Web 编程框架,即可以用于开发各种用 PHP 构建的 Web 应用。因为基于组件的框架结构和设计精巧的缓存支持,它特别适合开发大型应用,如门户网站、社区、内容管理系统(CMS)、电子商务项目和 RESTful Web 服务等。 +Yii 是一个通用的 Web 编程框架,即可以用于开发各种用 PHP 构建的 Web 应用。 +因为基于组件的框架结构和设计精巧的缓存支持,它特别适合开发大型应用, +如门户网站、社区、内容管理系统(CMS)、电子商务项目 +和 RESTful Web 服务等。 + Yii 和其他框架相比呢? ------------------------------------------- 如果你有其它框架使用经验,那么你会很开心看到 Yii 所做的努力: -- 和其他 PHP 框架类似,Yii 实现了 MVC(Model-View-Controller)设计模式并基于该模式组织代码。 -- Yii 的代码简洁优雅,这是它的编程哲学。它永远不会为了刻板地遵照某种设计模式而对代码进行过度的设计。 -- Yii 是一个全栈框架,提供了大量久经考验,开箱即用的特性:对关系型和 NoSQL 数据库都提供了查询生成器和 -ActiveRecord;RESTful API 的开发支持;多层缓存支持,等等。 +- 和其他 PHP 框架类似,Yii 实现了 MVC(Model-View-Controller) + 设计模式并基于该模式组织代码。 +- Yii 的代码简洁优雅,这是它的编程哲学。它永远不会为了刻板地 + 遵照某种设计模式而对代码进行过度的设计。 +- Yii 是一个全栈框架,提供了大量久经考验,开箱即用的特性: + 对关系型和 NoSQL 数据库都提供了查询生成器和 + ActiveRecord;RESTful API 的开发支持;多层缓存支持,等等。 - Yii 非常易于扩展。你可以自定义或替换几乎任何一处核心代码。你还会受益于 Yii -坚实可靠的扩展架构,使用、再开发或再发布扩展。 + 坚实可靠的扩展架构,使用、再开发或再发布扩展。 - 高性能始终是 Yii 的首要目标之一。 -Yii 不是一场独角戏,它由一个[强大的开发者团队](http://www.yiiframework.com/about/)提供支持,也有一个庞大的专家社区,持续不断地对 Yii 的开发作出贡献。Yii 开发者团队始终对 Web 开发趋势和其他框架及项目中的最佳实践和特性保持密切关注,那些有意义的最佳实践及特性会被不定期的整合进核心框架中,并提供简单优雅的接口。 +Yii 不是一场独角戏,它由一个[强大的开发者团队](http://www.yiiframework.com/about/) +提供支持,也有一个庞大的专家社区,持续不断地对 Yii 的开发作出贡献。Yii 开发者团队 +始终对 Web 开发趋势和其他框架及项目中的最佳实践和特性保持密切关注, +那些有意义的最佳实践及特性会被不定期的整合进核心框架中, +并提供简单优雅的接口。 +[about_yii]: http://www.yiiframework.com/about/ Yii 版本 ------------ -Yii 当前有两个主要版本:1.1 和 2.0。 1.1 版是上代的老版本,现在处于维护状态。2.0 版是一个完全重写的版本,采用了最新的技术和协议,包括依赖包管理器 Composer、PHP 代码规范 PSR、命名空间、Traits(特质)等等。 2.0 版代表新一代框架,是未来几年中我们的主要开发版本。本指南主要基于 2.0 版编写。 +Yii 当前有两个主要版本:1.1 和 2.0。 1.1 版是上代的老版本,现在处于维护状态。 +2.0 版是一个完全重写的版本,采用了最新的技术和协议,包括依赖包管理器 +Composer、PHP 代码规范 PSR、命名空间、Traits(特质)等等。 2.0 版代表新一代框架, +是未来几年中我们的主要开发版本。本指南主要基于 2.0 版编写。 系统要求和先决条件 ------------------------------ -Yii 2.0 需要 PHP 5.4.0 或以上版本支持。你可以通过运行任何 Yii 发行包中附带的系统要求检查器查看每个具体特性所需的 PHP 配置。 +Yii 2.0 需要 PHP 5.4.0 或以上版本支持。你可以通过运行任何 Yii 发行包 +中附带的系统要求检查器查看每个具体特性所需的 PHP 配置。 -使用 Yii 需要对面向对象编程(OOP)有基本了解,因为 Yii 是一个纯面向对象的框架。Yii 2.0 还使用了 PHP 的最新特性,例如[命名空间](http://www.php.net/manual/en/language.namespaces.php)和[Trait(特质)](http://www.php.net/manual/en/language.oop5.traits.php)。理解这些概念将有助于你更快地掌握 Yii 2.0。 +使用 Yii 需要对面向对象编程(OOP)有基本了解,因为 Yii 是一个纯面向对象的框架。 +Yii 2.0 还使用了 PHP 的最新特性,例如[命名空间](http://www.php.net/manual/en/language.namespaces.php) +和[Trait(特质)](http://www.php.net/manual/en/language.oop5.traits.php) 。理解这些概念将有助于你更快地掌握 Yii 2.0。