diff --git a/docs/guide-zh-CN/db-dao.md b/docs/guide-zh-CN/db-dao.md index d5277a0..b657a43 100644 --- a/docs/guide-zh-CN/db-dao.md +++ b/docs/guide-zh-CN/db-dao.md @@ -1,9 +1,16 @@ 数据库访问 (DAO) ======== -Yii 包含了一个建立在 PHP PDO 之上的数据访问层 (DAO). DAO为不同的数据库提供了一套统一的API. 其中```ActiveRecord``` 提供了数据库与模型(MVC 中的 M,Model) 的交互,```QueryBuilder``` 用于创建动态的查询语句. DAO提供了简单高效的SQL查询,可以用在与数据库交互的各个地方. +Yii 包含了一个建立在 PHP PDO 之上的数据访问层 (DAO)。DAO为不同的数据库提供了一套统一的API。 +其中```ActiveRecord``` 提供了数据库与模型(MVC 中的 M,Model) 的交互,```QueryBuilder``` 用于创建动态的查询语句。 +DAO提供了简单高效的SQL查询,可以用在与数据库交互的各个地方. + +When using Yii DAO, you mainly need to deal with plain SQLs and PHP arrays. As a result, it is the most efficient +way to access databases. However, because SQL syntax may vary for different databases, using Yii DAO also means +you have to take extra effort to create a database-agnostic application. + +Yii DAO supports the following databases out of box: -Yii 默认支持以下数据库 (DBMS): - [MySQL](http://www.mysql.com/) - [MariaDB](https://mariadb.com/) - [SQLite](http://sqlite.org/) @@ -12,9 +19,22 @@ Yii 默认支持以下数据库 (DBMS): - [Oracle](http://www.oracle.com/us/products/database/overview/index.html) - [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): 版本>=2005. -##配置 -开始使用数据库首先需要配置数据库连接组件,通过添加 db 组件到应用配置实现("基础的" Web 应用是 config/web.php),DSN( Data Source Name )是数据源名称,用于指定数据库信息.如下所示: +## Creating DB Connections + +To access a database, you first need to connect to it by creating an instance of [[yii\db\Connection]]: + +```php +$db = new yii\db\Connection([ + 'dsn' => 'mysql:host=localhost;dbname=example', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', +]); +``` + +Because a DB connection often needs to be accessed in different places, a common practice is to configure it +in terms of an [application component](structure-application-components.md) like the following: ```php return [ @@ -23,16 +43,9 @@ return [ // ... 'db' => [ 'class' => 'yii\db\Connection', - 'dsn' => 'mysql:host=localhost;dbname=mydatabase', // MySQL, MariaDB - //'dsn' => 'sqlite:/path/to/database/file', // SQLite - //'dsn' => 'pgsql:host=localhost;port=5432;dbname=mydatabase', // PostgreSQL - //'dsn' => 'cubrid:dbname=demodb;host=localhost;port=33000', // CUBRID - //'dsn' => 'sqlsrv:Server=localhost;Database=mydatabase', // MS SQL Server, sqlsrv driver - //'dsn' => 'dblib:host=localhost;dbname=mydatabase', // MS SQL Server, dblib driver - //'dsn' => 'mssql:host=localhost;dbname=mydatabase', // MS SQL Server, mssql driver - //'dsn' => 'oci:dbname=//localhost:1521/mydatabase', // Oracle - 'username' => 'root', //数据库用户名 - 'password' => '', //数据库密码 + 'dsn' => 'mysql:host=localhost;dbname=example', + 'username' => 'root', + 'password' => '', 'charset' => 'utf8', ], ], @@ -40,21 +53,27 @@ return [ ]; ``` -请参考PHP manual获取更多有关 DSN 格式信息。 -配置连接组件后可以使用以下语法访问: +You can then access the DB connection via the expression `Yii::$app->db`. -<<<<<<< HEAD -```$connection = \Yii::$app->db;``` +> Tip: You can configure multiple DB application components if your application needs to access multiple databases. -请参考```[[yii\db\Connection]]```获取可配置的属性列表。 -======= -`$connection = \Yii::$app->db;` +When configuring a DB connection, you should always specify its Data Source Name (DSN) via the [[yii\db\Connection::dsn|dsn]] +property. The format of DSN varies for different databases. Please refer to the [PHP manual](http://www.php.net/manual/en/function.PDO-construct.php) +for more details. Below are some examples: + +* MySQL, MariaDB: `mysql:host=localhost;dbname=mydatabase` +* SQLite: `sqlite:/path/to/database/file` +* PostgreSQL: `pgsql:host=localhost;port=5432;dbname=mydatabase` +* CUBRID: `cubrid:dbname=demodb;host=localhost;port=33000` +* MS SQL Server (via sqlsrv driver): `sqlsrv:Server=localhost;Database=mydatabase` +* MS SQL Server (via dblib driver): `dblib:host=localhost;dbname=mydatabase` +* MS SQL Server (via mssql driver): `mssql:host=localhost;dbname=mydatabase` +* Oracle: `oci:dbname=//localhost:1521/mydatabase` -请参考`[[yii\db\Connection]]`获取可配置的属性列表。 ->>>>>>> yiichina/master -如果你想通过ODBC连接数据库,则需要配置[[yii\db\Connection::driverName]] 属性,例如: +Note that if you are connecting with a database via ODBC, you should configure the [[yii\db\Connection::driverName]] +property so that Yii can know the actual database type. For example, -``` +```php 'db' => [ 'class' => 'yii\db\Connection', 'driverName' => 'mysql', @@ -64,150 +83,202 @@ return [ ], ``` -注意:如果需要同时使用多个数据库可以定义多个连接组件: +Besides the [[yii\db\Connection::dsn|dsn]] property, you often need to configure [[yii\db\Connection::username|username]] +and [[yii\db\Connection::password|password]]. Please refer to [[yii\db\Connection]] for the full list of configurable properties. + +> Info: When you create a DB connection instance, the actual connection to the database is not established until + you execute the first SQL or you call the [[yii\db\Connection::open()|open()]] method explicitly. + +> Tip: Sometimes you may want to execute some queries right after the database connection is established to initialize +> some environment variables (e.g., to set the timezone or character set). You can do so by registering an event handler +> for the [[yii\db\Connection::EVENT_AFTER_OPEN|afterOpen]] event +> of the database connection. You may register the handler directly in the application configuration like so: +> +> ```php +> 'db' => [ +> // ... +> 'on afterOpen' => function($event) { +> // $event->sender refers to the DB connection +> $event->sender->createCommand("SET time_zone = 'UTC'")->execute(); +> } +> ], +> ``` + + +## Executing SQL Queries + +Once you have a database connection instance, you can execute a SQL query by taking the following steps: + +1. Create a [[yii\db\Command]] with a plain SQL query; +2. Bind parameters (optional); +3. Call one of the SQL execution methods in [[yii\db\Command]]. +The following example shows various ways of fetching data from a database: + ```php -return [ - // ... - 'components' => [ - // ... - 'db' => [ - 'class' => 'yii\db\Connection', - 'dsn' => 'mysql:host=localhost;dbname=mydatabase', - 'username' => 'root', - 'password' => '', - 'charset' => 'utf8', - ], - 'secondDb' => [ - 'class' => 'yii\db\Connection', - 'dsn' => 'sqlite:/path/to/database/file', - ], - ], - // ... -]; -``` -在代码中通过以下方式使用: +// return a set of rows. each row is an associative array of column names and values. +// an empty array is returned if the query returned no results +$posts = Yii::$app->db->createCommand('SELECT * FROM post') + ->queryAll(); -``` -$primaryConnection = \Yii::$app->db; -$secondaryConnection = \Yii::$app->secondDb; -``` +// return a single row (the first row) +// false is returned if the query has no result +$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=1') + ->queryOne(); -如果不想定义数据库连接为全局[应用](structure-application-components.md)组件,可以在代码中直接初始化使用: +// return a single column (the first column) +// an empty array is returned if the query returned no results +$titles = Yii::$app->db->createCommand('SELECT title FROM post') + ->queryColumn(); -``` -$connection = new \yii\db\Connection([ - 'dsn' => $dsn, - 'username' => $username, - 'password' => $password, -]); -$connection->open(); +// return a scalar value +// false is returned if the query has no result +$count = Yii::$app->db->createCommand('SELECT COUNT(*) FROM post') + ->queryScalar(); ``` ->小提示:如果在创建了连接后需要执行额外的 SQL 查询,可以添加以下代码到应用配置文件: +> Note: To preserve precision, the data fetched from databases are all represented as strings, even if the corresponding + database column types are numerical. -``` -return [ - // ... - 'components' => [ - // ... - 'db' => [ - 'class' => 'yii\db\Connection', - // ... - 'on afterOpen' => function($event) { - $event->sender->createCommand("SET time_zone = 'UTC'")->execute(); - } - ], - ], - // ... -]; + +### Binding Parameters + +When creating a DB command from a SQL with parameters, you should almost always use the approach of binding parameters +to prevent SQL injection attacks. For example, + +```php +$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status') + ->bindValue(':id', $_GET['id']) + ->bindValue(':status', 1) + ->queryOne(); ``` -##SQL 基础查询 +In the SQL statement, you can embed one or multiple parameter placeholders (e.g. `:id` in the above example). A parameter +placeholder should be a string starting with a colon. You may then call one of the following parameter binding methods +to bind the parameter values: -一旦有了连接实例就可以通过[[yii\db\Command]]执行 SQL 查询。 +* [[yii\db\Command::bindValue()|bindValue()]]: bind a single parameter value +* [[yii\db\Command::bindValues()|bindValues()]]: bind multiple parameter values in one call +* [[yii\db\Command::bindParam()|bindParam()]]: similar to [[yii\db\Command::bindValue()|bindValue()]] but also + support binding parameter references. -###SELECT 查询 -查询返回多行: +The following example shows alternative ways of binding parameters: -``` -$command = $connection->createCommand('SELECT * FROM post'); -$posts = $command->queryAll(); -``` -返回单行: -<<<<<<< HEAD -======= +```php +$params = [':id' => $_GET['id'], ':status' => 1]; ->>>>>>> yiichina/master -``` -$command = $connection->createCommand('SELECT * FROM post WHERE id=1'); -$post = $command->queryOne(); +$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status') + ->bindValues($params) + ->queryOne(); + +$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status', $params) + ->queryOne(); ``` -查询多行单值: -<<<<<<< HEAD -======= +Parameter binding is implemented via [prepared statements](http://php.net/manual/en/mysqli.quickstart.prepared-statements.php). +Besides preventing SQL injection attacks, it may also improve performance by preparing a SQL statement once and +executing it multiple times with different parameters. For example, ->>>>>>> yiichina/master -``` -$command = $connection->createCommand('SELECT title FROM post'); -$titles = $command->queryColumn(); -``` -查询标量值/计算值: +```php +$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id'); -``` -$command = $connection->createCommand('SELECT COUNT(*) FROM post'); -$postCount = $command->queryScalar(); +$post1 = $command->bindValue(':id', 1)->queryOne(); +$post2 = $command->bindValue(':id', 2)->queryOne(); +// ... ``` -###UPDATE, INSERT, DELETE 更新、插入和删除等 +Because [[yii\db\Command::bindParam()|bindParam()]] supports binding parameters by references, the above code +can also be written like the following: + +```php +$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id') + ->bindParam(':id', $id); -如果执行 SQL 不返回任何数据可使用命令中的 execute 方法: +$id = 1; +$post1 = $command->queryOne(); +$id = 2; +$post2 = $command->queryOne(); +// ... ``` -$command = $connection->createCommand('UPDATE post SET status=1 WHERE id=1'); -$command->execute(); + +Notice that you bind the placeholder to the `$id` variable before the execution, and then change the value of that variable +before each subsequent execution (this is often done with loops). Executing queries in this manner can be vastly +more efficient than running a new query for every different parameter value. + + +### Executing Non-SELECT Queries + +The `queryXyz()` methods introduced in the previous sections all deal with SELECT queries which fetch data from databases. +For queries that do not bring back data, you should call the [[yii\db\Command::execute()]] method instead. For example, + +```php +Yii::$app->db->createCommand('UPDATE post SET status=1 WHERE id=1') + ->execute(); ``` -你可以使用`insert`,`update`,`delete` 方法,这些方法会根据参数生成合适的SQL并执行. + +The [[yii\db\Command::execute()]] method returns the number of rows affected by the SQL execution. + +For INSERT, UPDATE and DELETE queries, instead of writing plain SQLs, you may call [[yii\db\Command::insert()|insert()]], +[[yii\db\Command::update()|update()]], [[yii\db\Command::delete()|delete()]], respectively, to build the corresponding +SQLs. These methods will properly quote table and column names and bind parameter values. For example, ```php -// INSERT -$connection->createCommand()->insert('user', [ +// INSERT (table name, column values) +Yii::$app->db->createCommand()->insert('user', [ 'name' => 'Sam', 'age' => 30, ])->execute(); -// INSERT 一次插入多行 -$connection->createCommand()->batchInsert('user', ['name', 'age'], [ +// UPDATE (table name, column values, condition) +Yii::$app->db->createCommand()->update('user', ['status' => 1], 'age > 30')->execute(); + +// DELETE (table name, condition) +Yii::$app->db->createCommand()->delete('user', 'status = 0')->execute(); +``` + +You may also call [[yii\db\Command::batchInsert()|batchInsert()]] to insert multiple rows in one shot, which is much +more efficient than inserting one row at a time: + +```php +// table name, column names, column values +Yii::$app->db->createCommand()->batchInsert('user', ['name', 'age'], [ ['Tom', 30], ['Jane', 20], ['Linda', 25], ])->execute(); +``` -// UPDATE -$connection->createCommand()->update('user', ['status' => 1], 'age > 30')->execute(); +Note that the aforementioned methods only create the query and you always have to call [[yii\db\Command::execute()|execute()]] +to actually run them. -// DELETE -$connection->createCommand()->delete('user', 'status = 0')->execute(); -``` -###引用的表名和列名 +## Quoting Table and Column Names -大多数时间都使用以下语法来安全地引用表名和列名: +When writing database-agnostic code, properly quoting table and column names is often a headache because +different databases have different name quoting rules. To overcome this problem, you may use the following +quoting syntax introduced by Yii: -```php -$sql = "SELECT COUNT([[$column]]) FROM {{table}}"; -$rowCount = $connection->createCommand($sql)->queryScalar(); -``` -以上代码`[[$column]]` 会转变为引用恰当的列名,而`{{table}}` 就转变为引用恰当的表名。 -表名有个特殊的变量 {{%Y}} ,如果设置了表前缀使用该变体可以自动在表名前添加前缀: +* `[[column name]]`: enclose a column name to be quoted in double square brackets; +* `{{table name}}`: enclose a table name to be quoted in double curly brackets. + +Yii DAO will automatically convert such constructs into the corresponding quoted column or table names using the +DBMS specific syntax. +For example, ```php -$sql = "SELECT COUNT([[$column]]) FROM {{%$table}}"; -$rowCount = $connection->createCommand($sql)->queryScalar(); +// executes this SQL for MySQL: SELECT COUNT(`id`) FROM `employee` +$count = Yii::$app->db->createCommand("SELECT COUNT([[id]]) FROM {{employee}}") + ->queryScalar(); ``` -如果在配置文件如下设置了表前缀,以上代码将在 tbl_table 这个表查询结果: + +### Using Table Prefix + +If most of your DB tables names share a common prefix, you may use the table prefix feature provided +by Yii DAO. + +First, specify the table prefix via the [[yii\db\Connection::tablePrefix]] property in the application config: ```php return [ @@ -222,115 +293,164 @@ return [ ]; ``` -手工引用表名和列名的另一个选择是使用[[yii\db\Connection::quoteTableName()]] 和 [[yii\db\Connection::quoteColumnName()]]: +Then in your code, whenever you need to refer to a table whose name contains such a prefix, use the syntax +`{{%table_name}}`. The percentage character will be automatically replaced with the table prefix that you have specified +when configuring the DB connection. For example, ```php -$column = $connection->quoteColumnName($column); -$table = $connection->quoteTableName($table); -$sql = "SELECT COUNT($column) FROM $table"; -$rowCount = $connection->createCommand($sql)->queryScalar(); +// executes this SQL for MySQL: SELECT COUNT(`id`) FROM `tbl_employee` +$count = Yii::$app->db->createCommand("SELECT COUNT([[id]]) FROM {{%employee}}") + ->queryScalar(); ``` -###预处理语句 -为安全传递查询参数可以使用预处理语句,首先应当使用`:placeholder`占位,再将变量绑定到对应占位符: +## Performing Transactions -```php -$command = $connection->createCommand('SELECT * FROM post WHERE id=:id'); -$command->bindValue(':id', $_GET['id']); -$post = $command->query(); -``` - -另一种用法是准备一次预处理语句而执行多次查询: +When running multiple related queries in a sequence, you may need to wrap them in a transaction to ensure the integrity +and consistency of your database. If any of the queries fails, the database will be rolled back to the state as if +none of these queries were executed. + +The following code shows a typical way of using transactions: ```php -$command = $connection->createCommand('DELETE FROM post WHERE id=:id'); -$command->bindParam(':id', $id); - -$id = 1; -$command->execute(); - -$id = 2; -$command->execute(); +Yii::$app->db->transaction(function($db) { + $db->createCommand($sql1)->execute(); + $db->createCommand($sql2)->execute(); + // ... executing other SQL statements ... +}); ``` ->提示,在执行前绑定变量,然后在每个执行中改变变量的值(一般用在循环中)比较高效. -##事务 - -当你需要顺序执行多个相关的的`query`时,你可以把他们封装到一个事务中去保护数据一致性.Yii提供了一个简单的接口来实现事务操作. -如下执行 SQL 事务查询语句: +The above code is equivalent to the following, which gives you more control about the error handling code: ```php -$transaction = $connection->beginTransaction(); +$db = Yii::$app->db; +$transaction = $db->beginTransaction(); + try { - $connection->createCommand($sql1)->execute(); - $connection->createCommand($sql2)->execute(); - // ... 执行其他 SQL 语句 ... + $db->createCommand($sql1)->execute(); + $db->createCommand($sql2)->execute(); + // ... executing other SQL statements ... + $transaction->commit(); -} catch(Exception $e) { + +} catch(\Exception $e) { + $transaction->rollBack(); + + throw $e; } ``` -我们通过[[yii\db\Connection::beginTransaction()|beginTransaction()]]开始一个事务,通过`try catch` 捕获异常.当执行成功,通过[[yii\db\Transaction::commit()|commit()]]提交事务并结束,当发生异常失败通过[[yii\db\Transaction::rollBack()|rollBack()]]进行事务回滚. -如需要也可以嵌套多个事务: +By calling the [[yii\db\Connection::beginTransaction()|beginTransaction()]] method, a new transaction is started. +The transaction is represented as a [[yii\db\Transaction]] object stored in the `$transaction` variable. Then, +the queries being executed are enclosed in a `try...catch...` block. If all queries are executed successfully, +the [[yii\db\Transaction::commit()|commit()]] method is called to commit the transaction. Otherwise, if an exception +will be triggered and caught, the [[yii\db\Transaction::rollBack()|rollBack()]] method is called to roll back +the changes made by the queries prior to that failed query in the transaction. `throw $e` will then re-throw the +exception as if we had not caught it, so the normal error handling process will take care of it. + + +### Specifying Isolation Levels + +Yii also supports setting [isolation levels] for your transactions. By default, when starting a new transaction, +it will use the default isolation level set by your database system. You can override the default isolation level as follows, ```php -// 外部事务 -$transaction1 = $connection->beginTransaction(); -try { - $connection->createCommand($sql1)->execute(); +$isolationLevel = \yii\db\Transaction::REPEATABLE_READ; - // 内部事务 - $transaction2 = $connection->beginTransaction(); - try { - $connection->createCommand($sql2)->execute(); - $transaction2->commit(); - } catch (Exception $e) { - $transaction2->rollBack(); - } +Yii::$app->db->transaction(function ($db) { + .... +}, $isolationLevel); + +// or alternatively - $transaction1->commit(); -} catch (Exception $e) { - $transaction1->rollBack(); -} +$transaction = Yii::$app->db->beginTransaction($isolationLevel); ``` ->注意你使用的数据库必须支持`Savepoints`才能正确地执行,以上代码在所有关系数据中都可以执行,但是只有支持`Savepoints`才能保证安全性。 -Yii 也支持为事务设置隔离级别`isolation levels`,当执行事务时会使用数据库默认的隔离级别,你也可以为事物指定隔离级别. -Yii 提供了以下常量作为常用的隔离级别 +Yii provides four constants for the most common isolation levels: + +- [[\yii\db\Transaction::READ_UNCOMMITTED]] - the weakest level, Dirty reads, non-repeatable reads and phantoms may occur. +- [[\yii\db\Transaction::READ_COMMITTED]] - avoid dirty reads. +- [[\yii\db\Transaction::REPEATABLE_READ]] - avoid dirty reads and non-repeatable reads. +- [[\yii\db\Transaction::SERIALIZABLE]] - the strongest level, avoids all of the above named problems. + +Besides using the above constants to specify isolation levels, you may also use strings with a valid syntax supported +by the DBMS that you are using. For example, in PostgreSQL, you may use `SERIALIZABLE READ ONLY DEFERRABLE`. -- [[\yii\db\Transaction::READ_UNCOMMITTED]] - 允许读取改变了的还未提交的数据,可能导致脏读、不可重复读和幻读 -- [[\yii\db\Transaction::READ_COMMITTED]] - 允许并发事务提交之后读取,可以避免脏读,可能导致重复读和幻读。 -- [[\yii\db\Transaction::REPEATABLE_READ]] - 对相同字段的多次读取结果一致,可导致幻读。 -- [[\yii\db\Transaction::SERIALIZABLE]] - 完全服从ACID的原则,确保不发生脏读、不可重复读和幻读。 +Note that some DBMS allow setting the isolation level only for the whole connection. Any subsequent transactions +will get the same isolation level even if you do not specify any. When using this feature +you may need to set the isolation level for all transactions explicitly to avoid conflicting settings. +At the time of this writing, only MSSQL and SQLite are affected by this limitation. -你可以使用以上常量或者使用一个string字符串命令,在对应数据库中执行该命令用以设置隔离级别,比如对于`postgres`有效的命令为`SERIALIZABLE READ ONLY DEFERRABLE`. +> Note: SQLite only supports two isolation levels, so you can only use `READ UNCOMMITTED` and `SERIALIZABLE`. +Usage of other levels will result in an exception being thrown. -> Note: 某些数据库只能针对连接来设置事务隔离级别,所以你必须要为连接明确制定隔离级别.目前受影响的数据库:`MSSQL SQLite` +> Note: PostgreSQL does not allow setting the isolation level before the transaction starts so you can not +specify the isolation level directly when starting the transaction. +You have to call [[yii\db\Transaction::setIsolationLevel()]] in this case after the transaction has started. -> Note: SQLite 只支持两种事务隔离级别,所以你只能设置`READ UNCOMMITTED` 和 `SERIALIZABLE`.使用其他隔离级别会抛出异常. +[isolation levels]: http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels -> Note: PostgreSQL 不允许在事务开始前设置隔离级别,所以你不能在事务开始时指定隔离级别.你可以在事务开始之后调用[[yii\db\Transaction::setIsolationLevel()]] 来设置. -关于隔离级别[isolation levels]: http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels +### Nesting Transactions -##数据库复制和读写分离 +If your DBMS supports Savepoint, you may nest multiple transactions like the following: -很多数据库支持数据库复制 [database replication](http://en.wikipedia.org/wiki/Replication_(computing)#Database_replication)来提高可用性和响应速度. 在数据库复制中,数据总是从*主服务器* 到 *从服务器*. 所有的插入和更新等写操作在主服务器执行,而读操作在从服务器执行. +```php +Yii::$app->db->transaction(function ($db) { + // outer transaction + + $db->transaction(function ($db) { + // inner transaction + }); +}); +``` -通过配置[[yii\db\Connection]]可以实现数据库复制和读写分离. +Or alternatively, + +```php +$db = Yii::$app->db; +$outerTransaction = $db->beginTransaction(); +try { + $db->createCommand($sql1)->execute(); + + $innerTransaction = $db->beginTransaction(); + try { + $db->createCommand($sql2)->execute(); + $innerTransaction->commit(); + } catch (\Exception $e) { + $innerTransaction->rollBack(); + throw $e; + } + + $outerTransaction->commit(); +} catch (\Exception $e) { + $outerTransaction->rollBack(); + throw $e; +} +``` + + +## Replication and Read-Write Splitting + +Many DBMS support [database replication](http://en.wikipedia.org/wiki/Replication_(computing)#Database_replication) +to get better database availability and faster server response time. With database replication, data are replicated +from the so-called *master servers* to *slave servers*. All writes and updates must take place on the master servers, +while reads may also take place on the slave servers. + +To take advantage of database replication and achieve read-write splitting, you can configure a [[yii\db\Connection]] +component like the following: ```php [ 'class' => 'yii\db\Connection', - // 配置主服务器 + // configuration for the master 'dsn' => 'dsn for master server', 'username' => 'master', 'password' => '', - // 配置从服务器 + // common configuration for slaves 'slaveConfig' => [ 'username' => 'slave', 'password' => '', @@ -340,7 +460,7 @@ Yii 提供了以下常量作为常用的隔离级别 ], ], - // 配置从服务器组 + // list of slave configurations 'slaves' => [ ['dsn' => 'dsn for slave server 1'], ['dsn' => 'dsn for slave server 2'], @@ -349,31 +469,46 @@ Yii 提供了以下常量作为常用的隔离级别 ], ] ``` -以上的配置实现了一主多从的结构,从服务器用以执行读查询,主服务器执行写入查询,读写分离的功能由后台代码自动完成.调用者无须关心.例如: + +The above configuration specifies a setup with a single master and multiple slaves. One of the slaves will +be connected and used to perform read queries, while the master will be used to perform write queries. +Such read-write splitting is accomplished automatically with this configuration. For example, ```php -// 使用以上配置创建数据库连接对象 -$db = Yii::createObject($config); +// create a Connection instance using the above configuration +Yii::$app->db = Yii::createObject($config); -// 通过从服务器执行查询操作 -$rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll(); +// query against one of the slaves +$rows = Yii::$app->db->createCommand('SELECT * FROM user LIMIT 10')->queryAll(); -// 通过主服务器执行更新操作 -$db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute(); +// query against the master +Yii::$app->db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute(); ``` -> Note: 通过[[yii\db\Command::execute()]] 执行的查询被认为是写操作,所有使用[[yii\db\Command]]来执行的其他查询方法被认为是读操作.你可以通过`$db->slave`得到当前正在使用能够的从服务器. -`Connection`组件支持从服务器的负载均衡和故障转移,当第一次执行读查询时,会随即选择一个从服务器进行连接,如果连接失败则又选择另一个,如果所有从服务器都不可用,则会连接主服务器。你可以配置[[yii\db\Connection::serverStatusCache|server status cache]]来记住那些不能连接的从服务器,使Yii 在一段时间[[yii\db\Connection::serverRetryInterval]].内不会重复尝试连接那些根本不可用的从服务器. +> Info: Queries performed by calling [[yii\db\Command::execute()]] are considered as write queries, while + all other queries done through one of the "query" methods of [[yii\db\Command]] are read queries. + You can get the currently active slave connection via `Yii::$app->db->slave`. + +The `Connection` component supports load balancing and failover between slaves. +When performing a read query for the first time, the `Connection` component will randomly pick a slave and +try connecting to it. If the slave is found "dead", it will try another one. If none of the slaves is available, +it will connect to the master. By configuring a [[yii\db\Connection::serverStatusCache|server status cache]], +a "dead" server can be remembered so that it will not be tried again during a +[[yii\db\Connection::serverRetryInterval|certain period of time]]. -> Note: 在上述配置中,每个从服务器连接超时时间被指定为10s. 如果在10s内不能连接,则被认为该服务器已经挂掉.你也可以自定义超时参数. +> Info: In the above configuration, a connection timeout of 10 seconds is specified for every slave. + This means if a slave cannot be reached in 10 seconds, it is considered as "dead". You can adjust this parameter + based on your actual environment. + + +You can also configure multiple masters with multiple slaves. For example, -你也可以配置多主多从的结构,例如: ```php [ 'class' => 'yii\db\Connection', - // 配置主服务器 + // common configuration for masters 'masterConfig' => [ 'username' => 'master', 'password' => '', @@ -383,13 +518,13 @@ $db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute(); ], ], - // 配置主服务器组 + // list of master configurations 'masters' => [ ['dsn' => 'dsn for master server 1'], ['dsn' => 'dsn for master server 2'], ], - // 配置从服务器 + // common configuration for slaves 'slaveConfig' => [ 'username' => 'slave', 'password' => '', @@ -399,7 +534,7 @@ $db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute(); ], ], - // 配置从服务器组 + // list of slave configurations 'slaves' => [ ['dsn' => 'dsn for slave server 1'], ['dsn' => 'dsn for slave server 2'], @@ -408,18 +543,26 @@ $db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute(); ], ] ``` -上述配置制定了2个主服务器和4个从服务器.`Connection`组件也支持主服务器的负载均衡和故障转移,与从服务器不同的是,如果所有主服务器都不可用,则会抛出异常. -> Note: 当你使用[[yii\db\Connection::masters|masters]]来配置一个或多个主服务器时,`Connection`中关于数据库连接的其他属性(例如:`dsn`, `username`, `password`)都会被忽略. +The above configuration specifies two masters and four slaves. The `Connection` component also supports +load balancing and failover between masters just as it does between slaves. A difference is that when none +of the masters are available an exception will be thrown. + +> Note: When you use the [[yii\db\Connection::masters|masters]] property to configure one or multiple + masters, all other properties for specifying a database connection (e.g. `dsn`, `username`, `password`) + with the `Connection` object itself will be ignored. -事务默认使用主服务器的连接,并且在事务执行中的所有操作都会使用主服务器的连接,例如: + +By default, transactions use the master connection. And within a transaction, all DB operations will use +the master connection. For example, ```php -// 在主服务器连接上开始事务 +$db = Yii::$app->db; +// the transaction is started on the master connection $transaction = $db->beginTransaction(); try { - // 所有的查询都在主服务器上执行 + // both queries are performed against the master $rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll(); $db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute(); @@ -430,56 +573,67 @@ try { } ``` -如果你想在从服务器上执行事务操作则必须要明确地指定,比如: +If you want to start a transaction with the slave connection, you should explicitly do so, like the following: ```php -$transaction = $db->slave->beginTransaction(); +$transaction = Yii::$app->db->slave->beginTransaction(); ``` -有时你想强制使用主服务器来执行读查询,你可以调用`seMaster()`方法. +Sometimes, you may want to force using the master connection to perform a read query. This can be achieved +with the `useMaster()` method: ```php -$rows = $db->useMaster(function ($db) { +$rows = Yii::$app->db->useMaster(function ($db) { return $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll(); }); ``` -你也可以设置`$db->enableSlaves` 为`false`来使所有查询都在主服务器上执行. - -##操作数据库模式 - -###获得模式信息 - -你可以通过 [[yii\db\Schema]]实例来获取Schema信息: - -```php -$schema = $connection->getSchema(); -``` -该实例包括一系列方法来检索数据库多方面的信息: +You may also directly set `Yii::$app->db->enableSlaves` to be false to direct all queries to the master connection. -```php -$tables = $schema->getTableNames(); -``` -更多信息请参考[[yii\db\Schema]] -###修改模式 +## Working with Database Schema -除了基础的 SQL 查询,[[yii\db\Command]]还包括一系列方法来修改数据库模式: +Yii DAO provides a whole set of methods to let you manipulate the database schema, such as creating new tables, +dropping a column from a table, etc. These methods are listed as follows: -- 创建/重命名/删除/清空表 -- 增加/重命名/删除/修改字段 -- 增加/删除主键 -- 增加/删除外键 -- 创建/删除索引 +* [[yii\db\Command::createTable()|createTable()]]: creating a table +* [[yii\db\Command::renameTable()|renameTable()]]: renaming a table +* [[yii\db\Command::dropTable()|dropTable()]]: removing a table +* [[yii\db\Command::truncateTable()|truncateTable()]]: removing all rows in a table +* [[yii\db\Command::addColumn()|addColumn()]]: adding a column +* [[yii\db\Command::renameColumn()|renameColumn()]]: renaming a column +* [[yii\db\Command::dropColumn()|dropColumn()]]: removing a column +* [[yii\db\Command::alterColumn()|alterColumn()]]: altering a column +* [[yii\db\Command::addPrimaryKey()|addPrimaryKey()]]: adding a primary key +* [[yii\db\Command::dropPrimaryKey()|dropPrimaryKey()]]: removing a primary key +* [[yii\db\Command::addForeignKey()|addForeignKey()]]: adding a foreign key +* [[yii\db\Command::dropForeignKey()|dropForeignKey()]]: removing a foreign key +* [[yii\db\Command::createIndex()|createIndex()]]: creating an index +* [[yii\db\Command::dropIndex()|dropIndex()]]: removing an index -使用示例: +These methods can be used like the following: ```php -// 创建表 -$connection->createCommand()->createTable('post', [ +// CREATE TABLE +Yii::$app->db->createCommand()->createTable('post', [ 'id' => 'pk', 'title' => 'string', 'text' => 'text', ]); ``` -完整参考请查看[[yii\db\Command]]. + +The above array describes the name and types of the columns to be created. For the column types, Yii provides +a set of abstract data types, that allow you to define a database agnostic schema. These are converted to +DBMS specific type definitions dependent on the database, the table is created in. +Please refer to the API documentation of the [[yii\db\Command::createTable()|createTable()]]-method for more information. + +Besides changing the database schema, you can also retrieve the definition information about a table through +the [[yii\db\Connection::getTableSchema()|getTableSchema()]] method of a DB connection. For example, + +```php +$table = Yii::$app->db->getTableSchema('post'); +``` + +The method returns a [[yii\db\TableSchema]] object which contains the information about the table's columns, +primary keys, foreign keys, etc. All these information are mainly utilized by [query builder](db-query-builder.md) +and [active record](db-active-record.md) to help you write database-agnostic code. diff --git a/docs/guide-zh-CN/db-migrations.md b/docs/guide-zh-CN/db-migrations.md index fc69d98..865411e 100644 --- a/docs/guide-zh-CN/db-migrations.md +++ b/docs/guide-zh-CN/db-migrations.md @@ -1,9 +1,12 @@ 数据库迁移 =========== -在开发和维护一个数据库驱动的应用程序时,数据库的结构会随代码的改变而改变。例如,在开发应用程序的过程中,会增加一张新表且必须得加进来; +在开发和维护一个数据库驱动的应用程序时, +数据库的结构会随代码的改变而改变。 +例如,在开发应用程序的过程中,会增加一张新表且必须得加进来; 在应用程序被部署到生产环境后,需要建立一个索引来提高查询的性能等等。 -因为一个数据库结构发生改变的时候源代码也经常会需要做出改变,Yii 提供了一个 *数据库迁移* 功能,该功能可以记录数据库的变化, +因为一个数据库结构发生改变的时候源代码也经常会需要做出改变, +Yii 提供了一个 *数据库迁移* 功能,该功能可以记录数据库的变化, 以便使数据库和源代码一起受版本控制。 如下的步骤向我们展示了数据库迁移工具是如何为开发团队所使用的: @@ -11,7 +14,8 @@ 1. Tim 创建了一个新的迁移对象(例如,创建一张新的表单,改变字段的定义等)。 2. Tim 将这个新的迁移对象提交到代码管理系统(例如,Git,Mercurial)。 3. Doug 从代码管理系统当中更新版本并获取到这个新的迁移对象。 -4. Doug 把这个迁移对象提交到本地的开发数据库当中,这样一来,Doug 同步了 Tim 所做的修改。 +4. Doug 把这个迁移对象提交到本地的开发数据库当中, + 这样一来,Doug 同步了 Tim 所做的修改。 如下的步骤向我们展示了如何发布一个附带数据库迁移的新版本到生产环境当中: @@ -27,9 +31,12 @@ Yii 提供了一整套的迁移命令行工具,通过这些工具你可以: * 重新提交迁移; * 现实迁移历史和状态。 -所有的这些工具都可以通过 `yii migrate` 命令来进行操作。 在这一章节,我们将详细的介绍如何使用这些工具来完成各种各样的任务。你也可以通过 `yii help migrate` 命令来获取每一种工具的具体使用方法。 +所有的这些工具都可以通过 `yii migrate` 命令来进行操作。 +在这一章节,我们将详细的介绍如何使用这些工具来完成各种各样的任务。 +你也可以通过 `yii help migrate` 命令来获取每一种工具的具体使用方法。 -> 注意:迁移不仅仅只作用于数据库表,它同样会调整现有的数据来适应新的表单、创建 RBAC 分层、又或者是清除缓存。 +> 注意:迁移不仅仅只作用于数据库表, + 它同样会调整现有的数据来适应新的表单、创建 RBAC 分层、又或者是清除缓存。 ## 创建迁移 @@ -40,43 +47,63 @@ Yii 提供了一整套的迁移命令行工具,通过这些工具你可以: yii migrate/create ``` -必填参数 `name` 的作用是对新的迁移做一个简要的描述。例如,如果这个迁移是用来创建一个叫做 *news* 的表单的,那么你可以使用 `create_news_table` 这个名称并运行如下命令: +必填参数 `name` 的作用是对新的迁移做一个简要的描述。 +例如,如果这个迁移是用来创建一个叫做 *news* 的表单的, +那么你可以使用 `create_news_table` 这个名称并运行如下命令: ``` yii migrate/create create_news_table ``` -> 注意:因为 `name` 参数会被用来生成迁移的类名的一部分,所以该参数应当只包含字母、数字和下划线。 +> 注意:因为 `name` 参数会被用来生成迁移的类名的一部分, + 所以该参数应当只包含字母、数字和下划线。 -如上命令将会在 `@app/migrations` 目录下创建一个新的名为 `m150101_185401_create_news_table.php` 的 PHP 类文件。该文件包含如下的代码,它们用来声明一个迁移类 `m150101_185401_create_news_table`,并附有代码框架: +如上命令将会在 `@app/migrations` 目录下创建一个新的名为 `m150101_185401_create_news_table.php` 的 PHP 类文件。 +该文件包含如下的代码,它们用来声明一个迁移类 `m150101_185401_create_news_table`, +并附有代码框架: ```php _` 的格式自动生成,其中 +每个数据库迁移都会被定义为一个继承自 [[yii\db\Migration]] 的 PHP 类。 +类的名称按照 `m_` 的格式自动生成,其中 * `` 指执行创建迁移命令的 UTC 时间。 * `` 和你执行命令时所带的 `name` 参数值相同。 -在迁移类当中,你应当在 `up()` 方法中编写改变数据库结构的代码。你可能还需要在 `down()` 方法中编写代码来恢复由 `up()` 方法所做的改变。 -当你通过 migration 升级数据库时, `up()` 方法将会被调用,反之, `down()` 将会被调用。如下代码展示了如何通过迁移类来创建一张 `news` 表: +在迁移类当中,你应当在 `up()` 方法中编写改变数据库结构的代码。 +你可能还需要在 `down()` 方法中编写代码来恢复由 `up()` 方法所做的改变。 +当你通过 migration 升级数据库时, `up()` 方法将会被调用,反之, `down()` 将会被调用。 +如下代码展示了如何通过迁移类来创建一张 `news` 表: ```php @@ -102,27 +129,38 @@ class m150101_185401_create_news_table extends \yii\db\Migration } ``` -> 注意:并不是所有迁移都是可恢复的。例如,如果 `up()` 方法删除了表中的一行数据,这将无法通过 `down()` 方法来恢复这条数据。有时候,你也许只是懒得去执行 `down()` 方法了,因为它在恢复数据库迁移方面并不是那么的通用。在这种情况下,你应当在 `down()` 方法中返回 `false` 来表明这个 migration 是无法恢复的。 - -migration 的基类 [[yii\db\Migration]] 通过 [[yii\db\Migration::db|db]] 属性来连接了数据库。你可以通过 [配合数据库工作](db-dao.md#working-with-database-schema-) 章节中所描述的那些方法来操作数据库表。 +> 注意:并不是所有迁移都是可恢复的。例如,如果 `up()` 方法删除了表中的一行数据, + 这将无法通过 `down()` 方法来恢复这条数据。有时候,你也许只是懒得去执行 `down()` 方法了, + 因为它在恢复数据库迁移方面并不是那么的通用。在这种情况下, + 你应当在 `down()` 方法中返回 `false` 来表明这个 migration 是无法恢复的。 +migration 的基类 [[yii\db\Migration]] 通过 [[yii\db\Migration::db|db]] 属性来连接了数据库。 +你可以通过 [配合数据库工作](db-dao.md#working-with-database-schema-) +章节中所描述的那些方法来操作数据库表。 -当你通过 migration 创建一张表或者字段的时候,你应该使用 *抽象类型* 而不是 *实体类型*,这样一来你的迁移对象就可以从特定的 DBMS 当中抽离出来。 -[[yii\db\Schema]] 类定义了一整套可用的抽象类型常量。这些常量的格式为 `TYPE_`。例如,`TYPE_PK` 指代自增主键类型;`TYPE_STRING` 指代字符串类型。 -当迁移对象被提交到某个特定的数据库的时候,这些抽象类型将会被转换成相对应的实体类型。以 MySQL 为例,`TYPE_PK` 将会变成 `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`, 而 `TYPE_STRING` 则变成 `varchar(255)`。 +当你通过 migration 创建一张表或者字段的时候,你应该使用 *抽象类型* 而不是 *实体类型*, +这样一来你的迁移对象就可以从特定的 DBMS 当中抽离出来。 +[[yii\db\Schema]] 类定义了一整套可用的抽象类型常量。这些常量的格式为 `TYPE_`。 +例如,`TYPE_PK` 指代自增主键类型;`TYPE_STRING` 指代字符串类型。 +当迁移对象被提交到某个特定的数据库的时候,这些抽象类型将会被转换成相对应的实体类型。 +以 MySQL 为例,`TYPE_PK` 将会变成 `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`, +而 `TYPE_STRING` 则变成 `varchar(255)`。 -在使用抽象类型的时候,你可以添加额外的约束条件。在上面的例子当中, `NOT NULL` 被添加到 `Schema::TYPE_STRING` 当中来指定该字段不能为空。 +在使用抽象类型的时候,你可以添加额外的约束条件。在上面的例子当中, +`NOT NULL` 被添加到 `Schema::TYPE_STRING` 当中来指定该字段不能为空。 -> 提示:抽象类型和实体类型之间的映射关系是由每个具体的 `QueryBuilder` 类当中的 [[yii\db\QueryBuilder::$typeMap|$typeMap]] 属性所指定的。 +> 提示:抽象类型和实体类型之间的映射关系是由每个具体的 `QueryBuilder` + 类当中的 [[yii\db\QueryBuilder::$typeMap|$typeMap]] 属性所指定的。 -从 2.0.5 的版本开始,schema 构造器提供了更加方便的方法来定义字段,因此上面的 migration 可以被改写成: +Since version 2.0.6, you can make use of the newly introduced schema builder which provides more convenient way of defining column schema. +So the migration above could be written like the following: ```php +dropTable('news'); } +} +``` + +A list of all available methods for defining the column types is available in the API documentation of [[yii\db\SchemaBuilderTrait]]. + + +## Generating Migrations + +Since version 2.0.7 migration console provides a convenient way to create migrations. + +If the migration name is of a special form, for example `create_xxx` or `drop_xxx` then the generated migration +file will contain extra code, in this case for creating/dropping tables. +In the following all variants of this feature are described. + +### Create Table + +```php +yii migrate/create create_post +``` + +generates + +```php +/** + * Handles the creation for table `post`. + */ +class m150811_220037_create_post extends Migration +{ + /** + * @inheritdoc + */ + public function up() + { + $this->createTable('post', [ + 'id' => $this->primaryKey() + ]); + } + + /** + * @inheritdoc + */ + public function down() + { + $this->dropTable('post'); + } +} +``` + +To create table fields right away, specify them via `--fields` option. + +```php +yii migrate/create create_post --fields="title:string,body:text" +``` + +generates + +```php +/** + * Handles the creation for table `post`. + */ +class m150811_220037_create_post extends Migration +{ + /** + * @inheritdoc + */ + public function up() + { + $this->createTable('post', [ + 'id' => $this->primaryKey(), + 'title' => $this->string(), + 'body' => $this->text(), + ]); + } + /** + * @inheritdoc + */ + public function down() + { + $this->dropTable('post'); + } } + +``` + +You can specify more field parameters. + +```php +yii migrate/create create_post --fields="title:string(12):notNull:unique,body:text" ``` +generates + +```php +/** + * Handles the creation for table `post`. + */ +class m150811_220037_create_post extends Migration +{ + /** + * @inheritdoc + */ + public function up() + { + $this->createTable('post', [ + 'id' => $this->primaryKey(), + 'title' => $this->string(12)->notNull()->unique(), + 'body' => $this->text() + ]); + } + + /** + * @inheritdoc + */ + public function down() + { + $this->dropTable('post'); + } +} +``` + +> Note: primary key is added automatically and is named `id` by default. If you want to use another name you may +> specify it explicitly like `--fields="name:primaryKey"`. + +#### Foreign keys + +Since 2.0.8 the generator supports foreign keys using the `foreignKey` keyword. + +```php +yii migrate/create create_post --fields="author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string,body:text" +``` + +generates + +```php +/** + * Handles the creation for table `post`. + * Has foreign keys to the tables: + * + * - `user` + * - `category` + */ +class m160328_040430_create_post extends Migration +{ + /** + * @inheritdoc + */ + public function up() + { + $this->createTable('post', [ + 'id' => $this->primaryKey(), + 'author_id' => $this->integer()->notNull(), + 'category_id' => $this->integer()->defaultValue(1), + 'title' => $this->string(), + 'body' => $this->text(), + ]); + + // creates index for column `author_id` + $this->createIndex( + 'idx-post-author_id', + 'post', + 'author_id' + ); + + // add foreign key for table `user` + $this->addForeignKey( + 'fk-post-author_id', + 'post', + 'author_id', + 'user', + 'id', + 'CASCADE' + ); + + // creates index for column `category_id` + $this->createIndex( + 'idx-post-category_id', + 'post', + 'category_id' + ); + + // add foreign key for table `category` + $this->addForeignKey( + 'fk-post-category_id', + 'post', + 'category_id', + 'category', + 'id', + 'CASCADE' + ); + } + + /** + * @inheritdoc + */ + public function down() + { + // drops foreign key for table `user` + $this->dropForeignKey( + 'fk-post-author_id', + 'post' + ); + + // drops index for column `author_id` + $this->dropIndex( + 'idx-post-author_id', + 'post' + ); + + // drops foreign key for table `category` + $this->dropForeignKey( + 'fk-post-category_id', + 'post' + ); + + // drops index for column `category_id` + $this->dropIndex( + 'idx-post-category_id', + 'post' + ); + + $this->dropTable('post'); + } +} +``` + +The position of the `foreignKey` keyword in the column description doesn't +change the generated code. That means: + +- `author_id:integer:notNull:foreignKey(user)` +- `author_id:integer:foreignKey(user):notNull` +- `author_id:foreignKey(user):integer:notNull` + +All generate the same code. + +The `foreignKey` keyword can take a parameter between parenthesis which will be +the name of the related table for the generated foreign key. If no parameter +is passed then the table name will be deduced from the column name. + +In the example above `author_id:integer:notNull:foreignKey(user)` will generate a +column named `author_id` with a foreign key to the `user` table while +`category_id:integer:defaultValue(1):foreignKey` will generate a column +`category_id` with a foreign key to the `category` table. + +### Drop Table + +```php +yii migrate/create drop_post --fields="title:string(12):notNull:unique,body:text" +``` + +generates + +```php +class m150811_220037_drop_post extends Migration +{ + public function up() + { + $this->dropTable('post'); + } + + public function down() + { + $this->createTable('post', [ + 'id' => $this->primaryKey(), + 'title' => $this->string(12)->notNull()->unique(), + 'body' => $this->text() + ]); + } +} +``` + +### Add Column + +If the migration name is of the form `add_xxx_to_yyy` then the file content would contain `addColumn` and `dropColumn` +statements necessary. + +To add column: + +```php +yii migrate/create add_position_to_post --fields="position:integer" +``` + +generates + +```php +class m150811_220037_add_position_to_post extends Migration +{ + public function up() + { + $this->addColumn('post', 'position', $this->integer()); + } + + public function down() + { + $this->dropColumn('post', 'position'); + } +} +``` + +### Drop Column + +If the migration name is of the form `drop_xxx_from_yyy` then the file content would contain `addColumn` and `dropColumn` +statements necessary. + +```php +yii migrate/create drop_position_from_post --fields="position:integer" +``` + +generates + +```php +class m150811_220037_drop_position_from_post extends Migration +{ + public function up() + { + $this->dropColumn('post', 'position'); + } + + public function down() + { + $this->addColumn('post', 'position', $this->integer()); + } +} +``` + +### Add Junction Table + +If the migration name is in if the form of `create_junction_xxx_and_yyy` then code necessary to create junction table +will be generated. + +```php +yii migrate/create create_junction_post_and_tag --fields="created_at:dateTime" +``` + +generates + +```php +/** + * Handles the creation for table `post_tag`. + * Has foreign keys to the tables: + * + * - `post` + * - `tag` + */ +class m160328_041642_create_junction_post_and_tag extends Migration +{ + /** + * @inheritdoc + */ + public function up() + { + $this->createTable('post_tag', [ + 'post_id' => $this->integer(), + 'tag_id' => $this->integer(), + 'created_at' => $this->dateTime(), + 'PRIMARY KEY(post_id, tag_id)', + ]); + + // creates index for column `post_id` + $this->createIndex( + 'idx-post_tag-post_id', + 'post_tag', + 'post_id' + ); + + // add foreign key for table `post` + $this->addForeignKey( + 'fk-post_tag-post_id', + 'post_tag', + 'post_id', + 'post', + 'id', + 'CASCADE' + ); + + // creates index for column `tag_id` + $this->createIndex( + 'idx-post_tag-tag_id', + 'post_tag', + 'tag_id' + ); + + // add foreign key for table `tag` + $this->addForeignKey( + 'fk-post_tag-tag_id', + 'post_tag', + 'tag_id', + 'tag', + 'id', + 'CASCADE' + ); + } + + /** + * @inheritdoc + */ + public function down() + { + // drops foreign key for table `post` + $this->dropForeignKey( + 'fk-post_tag-post_id', + 'post_tag' + ); + + // drops index for column `post_id` + $this->dropIndex( + 'idx-post_tag-post_id', + 'post_tag' + ); + + // drops foreign key for table `tag` + $this->dropForeignKey( + 'fk-post_tag-tag_id', + 'post_tag' + ); + + // drops index for column `tag_id` + $this->dropIndex( + 'idx-post_tag-tag_id', + 'post_tag' + ); + + $this->dropTable('post_tag'); + } +} +``` ### 事务迁移 -当需要实现复杂的数据库迁移的时候,确定每一个迁移的执行是否成功或失败就变得相当重要了,因为这将影响到数据库的完整性和一致性。为了达到这个目标,我们建议你把每个迁移里面的数据库操作都封装到一个 [transaction](db-dao.md#performing-transactions-) 里面。 +当需要实现复杂的数据库迁移的时候,确定每一个迁移的执行是否成功或失败就变得相当重要了, +因为这将影响到数据库的完整性和一致性。为了达到这个目标,我们建议你把每个迁移里面的 +数据库操作都封装到一个 [transaction](db-dao.md#performing-transactions-) 里面。 -实现事务迁移的一个更为简便的方法是把迁移的代码都放到 `safeUp()` 和 `safeDown()` 方法里面。它们与 `up()` 和 `down()` 的不同点就在于它们是被隐式的封装到事务当中的。如此一来,只要这些方法里面的任何一个操作失败了,那么所有之前的操作都会被自动的回滚。 +实现事务迁移的一个更为简便的方法是把迁移的代码都放到 `safeUp()` 和 `safeDown()` 方法里面。 +它们与 `up()` 和 `down()` 的不同点就在于它们是被隐式的封装到事务当中的。 +如此一来,只要这些方法里面的任何一个操作失败了,那么所有之前的操作都会被自动的回滚。 在如下的例子当中,除了创建 `news` 表以外,我们还插入了一行初始化数据到表里面。 @@ -179,17 +643,25 @@ class m150101_185401_create_news_table extends Migration } ``` -需要注意的是,当你在 `safeUp()` 当中执行多个数据库操作的时候,你应该在 `safeDown()` 方法当中反转它们的执行顺序。在上面的例子当中,我们在 `safeUp()` 方法当中首先创建了一张表,然后插入了一条数据;而在 `safeDown()` 方法当中,我们首先删除那一行数据,然后才删除那张表。 +需要注意的是,当你在 `safeUp()` 当中执行多个数据库操作的时候,你应该在 `safeDown()` 方法当中反转它们的执行顺序。 +在上面的例子当中,我们在 `safeUp()` 方法当中首先创建了一张表,然后插入了一条数据;而在 `safeDown()` 方法当中, +我们首先删除那一行数据,然后才删除那张表。 -> 注意:并不是所有的数据库都支持事务。有些数据库查询也是不能被放倒事务里面的。在 [implicit commit](http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html) 章节当中有相关的例子可以参考。如果遇到这种情况的话,那么你应该使用 `up()` 和 `down()` 方法进行替代。 +> 注意:并不是所有的数据库都支持事务。有些数据库查询也是不能被放倒事务里面的。 + 在 [implicit commit](http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html) 章节当中有相关的例子可以参考。 + 如果遇到这种情况的话,那么你应该使用 `up()` 和 `down()` 方法进行替代。 ### 访问数据库的方法 -迁移的基类 [[yii\db\Migration]] 提供了一整套访问和操作数据库的方法。你可能会发现这些方法的命名和 [[yii\db\Command]] 类提供的 [DAO 方法](db-dao.md) 很类似。 -例如,[[yii\db\Migration::createTable()]] 方法可以创建一张新的表,这和 [[yii\db\Command::createTable()]] 的功能是一模一样的。 +迁移的基类 [[yii\db\Migration]] 提供了一整套访问和操作数据库的方法。 +你可能会发现这些方法的命名和 [[yii\db\Command]] 类提供的 [DAO 方法](db-dao.md) 很类似。 +例如,[[yii\db\Migration::createTable()]] 方法可以创建一张新的表, +这和 [[yii\db\Command::createTable()]] 的功能是一模一样的。 -使用 [[yii\db\Migration]] 所提供的方法的好处在于你不需要再显式的创建 [[yii\db\Command]] 实例,而且在执行每个方法的时候都会显示一些有用的信息来告诉我们数据库操作是不是都已经完成,还有它们完成这些操作花了多长时间等等。 +使用 [[yii\db\Migration]] 所提供的方法的好处在于你不需要再显式的创建 [[yii\db\Command]] 实例, +而且在执行每个方法的时候都会显示一些有用的信息来告诉我们数据库操作是不是都已经完成, +还有它们完成这些操作花了多长时间等等。 如下是所有这些数据库访问方法的列表: @@ -212,8 +684,21 @@ class m150101_185401_create_news_table extends Migration * [[yii\db\Migration::dropForeignKey()|dropForeignKey()]]: 删除一个外键 * [[yii\db\Migration::createIndex()|createIndex()]]: 创建一个索引 * [[yii\db\Migration::dropIndex()|dropIndex()]]: 删除一个索引 +* [[yii\db\Migration::addCommentOnColumn()|addCommentOnColumn()]]: adding comment to column +* [[yii\db\Migration::dropCommentFromColumn()|dropCommentFromColumn()]]: dropping comment from column +* [[yii\db\Migration::addCommentOnTable()|addCommentOnTable()]]: adding comment to table +* [[yii\db\Migration::dropCommentFromTable()|dropCommentFromTable()]]: dropping comment from table -> 提示:[[yii\db\Migration]] 并没有提供数据库的查询方法。这是因为通常你是不需要去数据库把数据一行一行查出来再显示出来的。另外一个原因是你完全可以使用强大的 [Query Builder 查询构建器](db-query-builder.md) 来构建和查询。 +> 提示:[[yii\db\Migration]] 并没有提供数据库的查询方法。 + 这是因为通常你是不需要去数据库把数据一行一行查出来再显示出来的。 + 另外一个原因是你完全可以使用强大的 [Query Builder 查询构建器](db-query-builder.md) 来构建和查询。 + +> Note: When manipulating data using a migration you may find that using your [Active Record](db-active-record.md) classes +> for this might be useful because some of the logic is already implemented there. Keep in mind however, that in contrast +> to code written in the migrations, who's nature is to stay constant forever, application logic is subject to change. +> So when using Active Record in migration code, changes to the logic in the Active Record layer may accidentally break +> existing migrations. For this reason migration code should be kept independent from other application logic such +> as Active Record classes. ## 提交迁移 @@ -224,20 +709,32 @@ class m150101_185401_create_news_table extends Migration yii migrate ``` -这条命令会列出迄今为止所有未提交的迁移。如果你确定你需要提交这些迁移,它将会按照类名当中的时间戳的顺序,一个接着一个的运行每个新的迁移类里面的 `up()` 或者是 `safeUp()` 方法。如果其中任意一个迁移提交失败了,那么这条命令将会退出并停止剩下的那些还未执行的迁移。 +这条命令会列出迄今为止所有未提交的迁移。如果你确定你需要提交这些迁移, +它将会按照类名当中的时间戳的顺序,一个接着一个的运行每个新的迁移类里面的 `up()` 或者是 `safeUp()` 方法。 +如果其中任意一个迁移提交失败了, +那么这条命令将会退出并停止剩下的那些还未执行的迁移。 + +> Tip: In case you don't have command line at your server you may try [web shell](https://github.com/samdark/yii2-webshell) +> extension. -对于每一个成功提交的迁移,这条命令都会在一个叫做 `migration` 的数据库表中插入一条包含应用程序成功提交迁移的记录,该记录将帮助迁移工具判断哪些迁移已经提交, -哪些还没有提交。 +对于每一个成功提交的迁移,这条命令都会在一个叫做 `migration` +的数据库表中插入一条包含应用程序成功提交迁移的记录, +该记录将帮助迁移工具判断哪些迁移已经提交,哪些还没有提交。 -> 提示:迁移工具将会自动在数据库当中创建 `migration` 表,该数据库是在该命令的 [[yii\console\controllers\MigrateController::db|db]] 选项当中指定的。默认情况下,是由 `db` [application component](structure-application-components.md) 指定的。 +> 提示:迁移工具将会自动在数据库当中创建 `migration` 表, + 该数据库是在该命令的 [[yii\console\controllers\MigrateController::db|db]] 选项当中指定的。 + 默认情况下,是由 `db` [application component](structure-application-components.md) 指定的。 -有时,你可能只需要提交一个或者少数的几个迁移,你可以使用该命令指定需要执行的条数,而不是执行所有的可用迁移。例如,如下命令将会尝试提交前三个可用的迁移: +有时,你可能只需要提交一个或者少数的几个迁移, +你可以使用该命令指定需要执行的条数,而不是执行所有的可用迁移。 +例如,如下命令将会尝试提交前三个可用的迁移: ``` yii migrate 3 ``` -你也可以指定一个特定的迁移,按照如下格式使用 `migrate/to` 命令来指定数据库应该提交哪一个迁移: +你也可以指定一个特定的迁移,按照如下格式使用 `migrate/to` 命令 +来指定数据库应该提交哪一个迁移: ``` yii migrate/to 150101_185401 # using timestamp to specify the migration 使用时间戳来指定迁移 @@ -246,7 +743,8 @@ yii migrate/to m150101_185401_create_news_table # using full name 使用全名 yii migrate/to 1392853618 # using UNIX timestamp 使用 UNIX 时间戳 ``` -如果在指定要提交的迁移前面还有未提交的迁移,那么在执行这个被指定的迁移之前,这些还未提交的迁移会先被提交。 +如果在指定要提交的迁移前面还有未提交的迁移,那么在执行这个被指定的迁移之前, +这些还未提交的迁移会先被提交。 如果被指定提交的迁移在之前已经被提交过,那么在其之后的那些迁移将会被还原。 @@ -260,12 +758,14 @@ yii migrate/down # revert the most recently applied migration 还原最近 yii migrate/down 3 # revert the most 3 recently applied migrations 还原最近三次提交的迁移 ``` -> 注意:并不是所有的迁移都能被还原。尝试还原这类迁移将可能导致报错甚至是终止所有的还原进程。 +> 注意:并不是所有的迁移都能被还原。 + 尝试还原这类迁移将可能导致报错甚至是终止所有的还原进程。 ## 重做迁移 -重做迁移的意思是先还原指定的迁移,然后再次提交。如下所示: +重做迁移的意思是先还原指定的迁移,然后再次提交。 +如下所示: ``` yii migrate/redo # redo the last applied migration 重做最近一次提交的迁移 @@ -292,7 +792,10 @@ yii migrate/new all # 显示所有还未提交的迁移 ## 修改迁移历史 -有时候你也许需要简单的标记一下你的数据库已经升级到一个特定的迁移,而不是实际提交或者是还原迁移。这个经常会发生在你手动的改变数据库的一个特定状态,而又不想相应的迁移被重复提交。那么你可以使用如下命令来达到目的: +有时候你也许需要简单的标记一下你的数据库已经升级到一个特定的迁移, +而不是实际提交或者是还原迁移。 +这个经常会发生在你手动的改变数据库的一个特定状态,而又不想相应的迁移被重复提交。 +那么你可以使用如下命令来达到目的: ``` yii migrate/mark 150101_185401 # 使用时间戳来指定迁移 @@ -301,7 +804,8 @@ yii migrate/mark m150101_185401_create_news_table # 使用全名 yii migrate/mark 1392853618 # 使用 UNIX 时间戳 ``` -该命令将会添加或者删除 `migration` 表当中的某几行数据来表明数据库已经提交到了指定的某个迁移上。执行这条命令期间不会有任何的迁移会被提交或还原。 +该命令将会添加或者删除 `migration` 表当中的某几行数据来表明数据库已经提交到了指定的某个迁移上。 +执行这条命令期间不会有任何的迁移会被提交或还原。 ## 自定义迁移 @@ -313,19 +817,44 @@ yii migrate/mark 1392853618 # 使用 UNIX 时间戳 迁移命令附带了几个命令行选项,可以用来自定义它的行为: -* `interactive`: boolean (默认值为 true),指定是否以交互模式来运行迁移。当被设置为 true 时,在命令执行某些操作前,会提示用户。如果你希望在后台执行该命令,那么你应该把它设置成 false。 +* `interactive`: boolean (默认值为 true),指定是否以交互模式来运行迁移。 + 当被设置为 true 时,在命令执行某些操作前,会提示用户。如果你希望在后台执行该命令, + 那么你应该把它设置成 false。 -* `migrationPath`: string (默认值为 `@app/migrations`),指定存放所有迁移类文件的目录。该选项可以是一个目录的路径,也可以是 [路径别名](concept-aliases.md)。需要注意的是指定的目录必选存在,否则将会触发一个错误。 +* `migrationPath`: string (默认值为 `@app/migrations`),指定存放所有迁移类文件的目录。该选项可以是一个目录的路径, + 也可以是 [路径别名](concept-aliases.md)。需要注意的是指定的目录必选存在, + 否则将会触发一个错误。 -* `migrationTable`: string (默认值为 `migration`),指定用于存储迁移历史信息的数据库表名称。如果这张表不存在,那么迁移命令将自动创建这张表。当然你也可以使用这样的字段结构: `version varchar(255) primary key, apply_time integer` 来手动创建这张表。 +* `migrationTable`: string (默认值为 `migration`),指定用于存储迁移历史信息的数据库表名称。 + 如果这张表不存在,那么迁移命令将自动创建这张表。当然你也可以使用这样的字段结构: + `version varchar(255) primary key, apply_time integer` 来手动创建这张表。 -* `db`: string (默认值为 `db`),指定数据库 [application component](structure-application-components.md) 的 ID。它指的是将会被该命令迁移的数据库。 +* `db`: string (默认值为 `db`),指定数据库 [application component](structure-application-components.md) 的 ID。 + 它指的是将会被该命令迁移的数据库。 -* `templateFile`: string (defaults to `@yii/views/migration.php`),指定生产迁移框架代码类文件的模版文件路径。该选项即可以使用文件路径来指定,也可以使用路径 [别名](concept-aliases.md) 来指定。该模版文件是一个可以使用预定义变量 `$className` 来获取迁移类名称的 PHP 脚本。 +* `templateFile`: string (defaults to `@yii/views/migration.php`), + 指定生产迁移框架代码类文件的模版文件路径。 + 该选项即可以使用文件路径来指定,也可以使用路径 [别名](concept-aliases.md) 来指定。 + 该模版文件是一个可以使用预定义变量 `$className` 来获取迁移类名称的 PHP 脚本。 + +* `generatorTemplateFiles`: array (defaults to `[ + 'create_table' => '@yii/views/createTableMigration.php', + 'drop_table' => '@yii/views/dropTableMigration.php', + 'add_column' => '@yii/views/addColumnMigration.php', + 'drop_column' => '@yii/views/dropColumnMigration.php', + 'create_junction' => '@yii/views/createJunctionMigration.php' + ]`), specifies template files for generating migration code. See "[Generating Migrations](#generating-migrations)" + for more details. + +* `fields`: array of column definition strings used for creating migration code. Defaults to `[]`. The format of each + definition is `COLUMN_NAME:COLUMN_TYPE:COLUMN_DECORATOR`. For example, `--fields=name:string(12):notNull` produces + a string column of size 12 which is not null. 如下例子向我们展示了如何使用这些选项: -例如,如果我们需要迁移一个 `forum` 模块,而该迁移文件放在该模块下的 `migrations` 目录当中,那么我们可以使用如下命令: +例如,如果我们需要迁移一个 `forum` 模块, +而该迁移文件放在该模块下的 `migrations` 目录当中, +那么我们可以使用如下命令: ``` # 在 forum 模块中以非交互模式进行迁移 @@ -335,7 +864,8 @@ yii migrate --migrationPath=@app/modules/forum/migrations --interactive=0 ### 全局配置命令 -在运行迁移命令的时候每次都要重复的输入一些同样的参数会很烦人,这时候,你可以选择在应用程序配置当中进行全局配置,一劳永逸: +在运行迁移命令的时候每次都要重复的输入一些同样的参数会很烦人,这时候, +你可以选择在应用程序配置当中进行全局配置,一劳永逸: ```php return [ @@ -348,12 +878,15 @@ return [ ]; ``` -如上所示配置,在每次运行迁移命令的时候,`backend_migration` 表将会被用来记录迁移历史。你再也不需要通过 `migrationTable` 命令行参数来指定这张历史纪录表了。 +如上所示配置,在每次运行迁移命令的时候, +`backend_migration` 表将会被用来记录迁移历史。 +你再也不需要通过 `migrationTable` 命令行参数来指定这张历史纪录表了。 ## 迁移多个数据库 -默认情况下,迁移将会提交到由 `db` [application component](structure-application-components.md) 所定义的同一个数据库当中。如果你需要提交到不同的数据库,你可以像下面那样指定 `db` 命令行选项, +默认情况下,迁移将会提交到由 `db` [application component](structure-application-components.md) 所定义的同一个数据库当中。 +如果你需要提交到不同的数据库,你可以像下面那样指定 `db` 命令行选项, ``` yii migrate --db=db2 @@ -361,11 +894,13 @@ yii migrate --db=db2 上面的命令将会把迁移提交到 `db2` 数据库当中。 -偶尔有限时候你需要提交 *一些* 迁移到一个数据库,而另外一些则提交到另一个数据库。为了达到这个目的,你应该在实现一个迁移类的时候指定需要用到的数据库组件的 ID , +偶尔有限时候你需要提交 *一些* 迁移到一个数据库,而另外一些则提交到另一个数据库。 +为了达到这个目的,你应该在实现一个迁移类的时候指定需要用到的数据库组件的 ID , 如下所示: ```php -use yii\db\Schema; + 提示:除了在 [[yii\db\Migration::db|db]] 参数当中进行设置以外,你还可以通过在迁移类中创建新的数据库连接来操作不同的数据库。然后通过这些连接再使用 [DAO 方法](db-dao.md) 来操作不同的数据库。 +> 提示:除了在 [[yii\db\Migration::db|db]] 参数当中进行设置以外, + 你还可以通过在迁移类中创建新的数据库连接来操作不同的数据库。 + 然后通过这些连接再使用 [DAO 方法](db-dao.md) 来操作不同的数据库。 -另外一个可以让你迁移多个数据库的策略是把迁移存放到不同的目录下,然后你可以通过如下命令分别对不同的数据库进行迁移: +另外一个可以让你迁移多个数据库的策略是把迁移存放到不同的目录下, +然后你可以通过如下命令分别对不同的数据库进行迁移: ``` yii migrate --migrationPath=@app/migrations/db1 --db=db1 @@ -392,4 +932,5 @@ yii migrate --migrationPath=@app/migrations/db2 --db=db2 ... ``` -第一条命令将会把 `@app/migrations/db1` 目录下的迁移提交到 `db1` 数据库当中,第二条命令则会把 `@app/migrations/db2` 下的迁移提交到 `db2` 数据库当中,以此类推。 +第一条命令将会把 `@app/migrations/db1` 目录下的迁移提交到 `db1` 数据库当中, +第二条命令则会把 `@app/migrations/db2` 下的迁移提交到 `db2` 数据库当中,以此类推。 diff --git a/docs/guide-zh-CN/db-query-builder.md b/docs/guide-zh-CN/db-query-builder.md index 96b98bf..8f11a85 100644 --- a/docs/guide-zh-CN/db-query-builder.md +++ b/docs/guide-zh-CN/db-query-builder.md @@ -89,6 +89,9 @@ $query->select(['user_id' => 'user.id', 'email']); $query->select(["CONCAT(first_name, ' ', last_name) AS full_name", 'email']); ``` +As with all places where raw SQL is involved, you may use the [DBMS agnostic quoting syntax](db-dao.md#quoting-table-and-column-names) +for table and column names when writing DB expressions in select. + 从 2.0.1 的版本开始你就可以使用子查询了。在定义每一个子查询的时候, 你应该使用 [[yii\db\Query]] 对象。例如: @@ -163,13 +166,17 @@ $query->from(['u' => $subQuery]); #### 字符串格式 -在定义非常简单的查询条件的时候,字符串格式是最合适的。它看起来和原生 SQL 语句差不多。例如: +在定义非常简单的查询条件的时候,字符串格式是最合适的。 +它看起来和原生 SQL 语句差不多。例如: ```php $query->where('status=1'); -// 或者使用参数绑定来绑定动态参数值 +// or use parameter binding to bind dynamic parameter values $query->where('status=:status', [':status' => $status]); + +// raw SQL using MySQL YEAR() function on a date field +$query->where('YEAR(somedate) = 2015'); ``` 千万不要像如下的例子一样直接在条件语句当中嵌入变量,特别是当这些变量来源于终端用户输入的时候, @@ -188,6 +195,8 @@ $query->where('status=:status') ->addParams([':status' => $status]); ``` +As with all places where raw SQL is involved, you may use the [DBMS agnostic quoting syntax](db-dao.md#quoting-table-and-column-names) +for table and column names when writing conditions in string format. #### 哈希格式 @@ -215,6 +224,9 @@ $userQuery = (new Query())->select('id')->from('user'); $query->where(['id' => $userQuery]); ``` +Using the Hash Format, Yii internally uses parameter binding so in contrast to the [string format](#string-format), here +you do not have to add parameters manually. + #### 操作符格式 @@ -224,8 +236,8 @@ $query->where(['id' => $userQuery]); [操作符, 操作数1, 操作数2, ...] ``` -其中每个操作数可以是字符串格式、哈希格式或者嵌套的操作符格式,而操作符可以是如下列表中的一个: - +其中每个操作数可以是字符串格式、哈希格式或者嵌套的操作符格式, +而操作符可以是如下列表中的一个: - `and`: 操作数会被 `AND` 关键字串联起来。例如,`['and', 'id=1', 'id=2']` 将会生成 `id=1 AND id=2`。如果操作数是一个数组,它也会按上述规则转换成 @@ -239,8 +251,8 @@ $query->where(['id' => $userQuery]); 的取值范围。例如,`['between', 'id', 1, 10]` 将会生成 `id BETWEEN 1 AND 10`。 -- `not between`: 用法和 `BETWEEN` 操作符类似,这里就不再赘述。 - +- `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN` + in the generated condition. - `in`: 第一个操作数应为字段名称或者 DB 表达式。第二个操作符既可以是一个数组, 也可以是一个 `Query` 对象。它会转换成`IN` 条件语句。如果第二个操作数是一个 @@ -253,7 +265,8 @@ $query->where(['id' => $userQuery]); - `not in`: 用法和 `in` 操作符类似,这里就不再赘述。 -- `like`: 第一个操作数应为一个字段名称或 DB 表达式,第二个操作数可以使字符串或数组, +- `like`: 第一个操作数应为一个字段名称或 DB 表达式, + 第二个操作数可以使字符串或数组, 代表第一个操作数需要模糊查询的值。比如,`['like', 'name', 'tester']` 会生成 `name LIKE '%tester%'`。 如果范围值是一个数组,那么将会生成用 `AND` 串联起来的 多个 `like` 语句。例如,`['like', 'name', ['test', 'sample']]` 将会生成 @@ -265,9 +278,8 @@ $query->where(['id' => $userQuery]); 当使用转义映射(又或者没有提供第三个操作数的时候),第二个操作数的值的前后 将会被加上百分号。 - - > 注意:当使用 PostgreSQL 的时候你还可以使用 [`ilike`](http://www.postgresql.org/docs/8.3/static/functions-matching.html#FUNCTIONS-LIKE), - > 该方法对大小写不敏感。 +> 注意:当使用 PostgreSQL 的时候你还可以使用 [`ilike`](http://www.postgresql.org/docs/8.3/static/functions-matching.html#FUNCTIONS-LIKE), +> 该方法对大小写不敏感。 - `or like`: 用法和 `like` 操作符类似,区别在于当第二个操作数为数组时, 会使用 `OR` 来串联多个 `LIKE` 条件语句。 @@ -286,6 +298,9 @@ $query->where(['id' => $userQuery]); - `>`, `<=`, 或者其他包含两个操作数的合法 DB 操作符: 第一个操作数必须为字段的名称, 而第二个操作数则应为一个值。例如,`['>', 'age', 10]` 将会生成 `age>10`。 +Using the Operator Format, Yii internally uses parameter binding so in contrast to the [string format](#string-format), here +you do not have to add parameters manually. + #### 附加条件 @@ -336,6 +351,20 @@ $query->filterWhere([ 你可以使用 [[yii\db\Query::andFilterWhere()|andFilterWhere()]] 和 [[yii\db\Query::orFilterWhere()|orFilterWhere()]] 方法 来追加额外的过滤条件。 +Additionally, there is [[yii\db\Query::andFilterCompare()]] that can intelligently determine operator based on what's +in the value: + +```php +$query->andFilterCompare('name', 'John Doe'); +$query->andFilterCompare('rating', '>9'); +$query->andFilterCompare('value', '<=100'); +``` + +You can also specify operator explicitly: + +```php +$query->andFilterCompare('name', 'Doe', 'like'); +``` ### [[yii\db\Query::orderBy()|orderBy()]] @@ -448,8 +477,11 @@ $query->join('LEFT JOIN', 'post', 'post.user_id = user.id'); - `$type`: 连接类型,例如:`'INNER JOIN'`, `'LEFT JOIN'`。 - `$table`: 将要连接的表名称。 -- `$on`: 可选参数,连接条件,即 `ON` 子句。请查阅 [where()](#where) - 获取更多有关于条件定义的细节。 +- `$on`: optional, the join condition, i.e., the `ON` fragment. Please refer to [where()](#where) for details + about specifying a condition. Note, that the array syntax does **not** work for specifying a column based + condition, e.g. `['user.id' => 'comment.userId']` will result in a condition where the user id must be equal + to the string `'comment.userId'`. You should use the string syntax instead and specify the condition as + `'user.id = comment.userId'`. - `$params`: 可选参数,与连接条件绑定的参数。 你可以分别调用如下的快捷方法来指定 `INNER JOIN`, `LEFT JOIN` 和 `RIGHT JOIN`。 @@ -602,13 +634,19 @@ $query = (new \yii\db\Query()) 该匿名函数将带有一个包含了当前行的数据的 `$row` 参数,并且返回用作当前行索引的 标量值(译者注:就是简单的数值或者字符串,而不是其他复杂结构,例如数组)。 +> Note: In contrast to query methods like [[yii\db\Query::groupBy()|groupBy()]] or [[yii\db\Query::orderBy()|orderBy()]] +> which are converted to SQL and are part of the query, this method works after the data has been fetched from the database. +> That means that only those column names can be used that have been part of SELECT in your query. +> Also if you selected a column with table prefix, e.g. `customer.id`, the result set will only contain `id` so you have to call +> `->indexBy('id')` without table prefix. + ### 批处理查询 当需要处理大数据的时候,像 [[yii\db\Query::all()]] 这样的方法就不太合适了, 因为它们会把所有数据都读取到内存上。为了保持较低的内存需求, Yii 提供了一个 -所谓的批处理查询的支持。批处理查询会利用数据游标将数据以批为单位取出来。 - +所谓的批处理查询的支持。批处理查询会利用数据游标 +将数据以批为单位取出来。 批处理查询的用法如下: @@ -638,7 +676,8 @@ foreach ($query->each() as $user) { 相对于 [[yii\db\Query::all()]] 方法,批处理查询每次只读取 100 行的数据到内存。 如果你在处理完这些数据后及时丢弃这些数据,那么批处理查询可以很好的帮助降低内存的占用率。 -如果你通过 [[yii\db\Query::indexBy()]] 方法为查询结果指定了索引字段,那么批处理查询将仍然保持相对应的索引方案,例如, +如果你通过 [[yii\db\Query::indexBy()]] 方法为查询结果指定了索引字段, +那么批处理查询将仍然保持相对应的索引方案,例如, ```php @@ -653,4 +692,3 @@ foreach ($query->batch() as $users) { foreach ($query->each() as $username => $user) { } ``` - diff --git a/docs/guide-zh-CN/output-client-scripts.md b/docs/guide-zh-CN/output-client-scripts.md index 3cb0302..f3c883f 100644 --- a/docs/guide-zh-CN/output-client-scripts.md +++ b/docs/guide-zh-CN/output-client-scripts.md @@ -33,12 +33,12 @@ $this->registerJs("var options = ".json_encode($options).";", View::POS_END, 'my $this->registerJsFile('http://example.com/js/main.js', ['depends' => [\yii\web\JqueryAsset::className()]]); ``` -[[yii\web\View::registerJsFile()|registerJsFile()]] 中参数的使用与 [[yii\web\View::registerCssFile()|registerCssFile()]] 中的参数使用类似。 -在上面的例子中,我们注册了 `main.js` 文件,并且依赖于 `JqueryAsset` 类。这意味着 `main.js` 文件将被添加在 `jquery.js` 的后面。 +[[yii\web\View::registerJsFile()|registerJsFile()]] 中参数的使用与 +[[yii\web\View::registerCssFile()|registerCssFile()]] 中的参数使用类似。 +在上面的例子中,我们注册了 `main.js` 文件,并且依赖于 `JqueryAsset` 类。 +这意味着 `main.js` 文件将被添加在 `jquery.js` 的后面。 如果没有这个依赖规范的话,`main.js`和 `jquery.js` 两者之间的顺序将不会被定义。 - - 和 [[yii\web\View::registerCssFile()|registerCssFile()]] 一样,我们强烈建议您使用 [asset bundles](structure-assets.md) 来注册外部JS文件,而非使用 [[yii\web\View::registerJsFile()|registerJsFile()]] 来注册。 @@ -85,12 +85,12 @@ $this->registerCssFile("http://example.com/css/themes/black-and-white.css", [ 上面的代码将在页面的头部添加一个link引入CSS文件。 * 第一个参数指明被注册的CSS文件。 -* 第二个参数指明 `` 标签的HTML属性,选项 `depends` 是专门处理指明CSS文件依赖于哪个资源包。在这种情况下,依赖资源包就是 - [[yii\bootstrap\BootstrapAsset|BootstrapAsset]]。这意味着CSS文件将被添加在 [[yii\bootstrap\BootstrapAsset|BootstrapAsset]] 之后。 -* 最后一个参数指明一个ID来标识这个CSS文件。假如这个参数未传,CSS文件的URL将被作为ID来替代。 - - - +* 第二个参数指明 `` 标签的HTML属性,选项 `depends` 是专门处理 + 指明CSS文件依赖于哪个资源包。在这种情况下,依赖资源包就是 + [[yii\bootstrap\BootstrapAsset|BootstrapAsset]]。这意味着CSS文件将 + 被添加在 [[yii\bootstrap\BootstrapAsset|BootstrapAsset]] 之后。 +* 最后一个参数指明一个ID来标识这个CSS文件。假如这个参数未传, + CSS文件的URL将被作为ID来替代。 我们强烈建议使用 [asset bundles](structure-assets.md) 来注册外部CSS文件, diff --git a/docs/guide-zh-CN/output-data-providers.md b/docs/guide-zh-CN/output-data-providers.md index ef5cf38..b412e0d 100644 --- a/docs/guide-zh-CN/output-data-providers.md +++ b/docs/guide-zh-CN/output-data-providers.md @@ -6,8 +6,8 @@ 因为分页和排序数据的任务是很常见的,所以Yii提供了一组封装好的*data provider*类。 数据提供者是一个实现了 [[yii\data\DataProviderInterface]] 接口的类。 -它主要用于获取分页和数据排序。它经常用在 [data widgets](output-data-widgets.md) 小物件里,方便终端用户进行分页与数据排序。 - +它主要用于获取分页和数据排序。它经常用在 [data widgets](output-data-widgets.md) +小物件里,方便终端用户进行分页与数据排序。 下面的数据提供者类都包含在Yii的发布版本里面: @@ -36,11 +36,11 @@ $count = $provider->getCount(); $totalCount = $provider->getTotalCount(); ``` -你可以通过配置 [[yii\data\BaseDataProvider::pagination|pagination]] 和 [[yii\data\BaseDataProvider::sort|sort]] -的属性来设定数据提供者的分页和排序行为。属性分别对应于 [[yii\data\Pagination]] 和 [[yii\data\Sort]]。 +你可以通过配置 [[yii\data\BaseDataProvider::pagination|pagination]] 和 +[[yii\data\BaseDataProvider::sort|sort]]的属性来设定数据提供者的分页和排序行为。 +属性分别对应于 [[yii\data\Pagination]] 和 [[yii\data\Sort]]。 你也可以对它们配置false来禁用分页和排序特性。 - [Data widgets](output-data-widgets.md),诸如 [[yii\grid\GridView]],有一个属性名叫 `dataProvider` ,这个属性能够提供一个 数据提供者的示例并且可以显示所提供的数据,例如, @@ -50,8 +50,8 @@ echo yii\grid\GridView::widget([ ]); ``` -这些数据提供者的主要区别在于数据源的指定方式上。在下面的部分,我们将详细介绍这些数据提供者的使用方法。 - +这些数据提供者的主要区别在于数据源的指定方式上。在下面的部分, +我们将详细介绍这些数据提供者的使用方法。 ## 活动数据提供者 @@ -91,9 +91,9 @@ use yii\db\Query; $query = (new Query())->from('post')->where(['status' => 1]); ``` -> 注意:假如查询已经指定了 `orderBy` 从句,则终端用户给定的新的排序说明(通过 `sort` 来配置的)将被添加到已经存在的从句中。 - 任何已经存在的 `limit` 和 `offset` 从句都将被终端用户所请求的分页(通过 `pagination` 所配置的)所重写。 - +> 注意:假如查询已经指定了 `orderBy` 从句,则终端用户给定的新的排序说明(通过 `sort` 来配置的) + 将被添加到已经存在的从句中。任何已经存在的 `limit` 和 `offset` 从句都将被终端用户所请求的分页 + (通过 `pagination` 所配置的)所重写。 默认情况下,[[yii\data\ActiveDataProvider]]使用 `db` 应用组件来作为数据库连接。你可以通过配置 [[yii\data\ActiveDataProvider::db]] 的属性来使用不同数据库连接。 @@ -101,10 +101,10 @@ $query = (new Query())->from('post')->where(['status' => 1]); ## SQL数据提供者 -[[yii\data\SqlDataProvider]] 应用的时候需要结合需要获取数据的SQL语句。基于 [[yii\data\SqlDataProvider::sort|sort]] 和 -[[yii\data\SqlDataProvider::pagination|pagination]] 规格,数据提供者会根据所请求的数据页面(期望的顺序)来调整 `ORDER BY` 和 `LIMIT` -的SQL从句。 - +[[yii\data\SqlDataProvider]] 应用的时候需要结合需要获取数据的SQL语句。 +基于 [[yii\data\SqlDataProvider::sort|sort]] 和 +[[yii\data\SqlDataProvider::pagination|pagination]] 规格, +数据提供者会根据所请求的数据页面(期望的顺序)来调整 `ORDER BY` 和 `LIMIT` 的SQL从句。 为了使用 [[yii\data\SqlDataProvider]],你应该指定 [[yii\data\SqlDataProvider::sql|sql]] 属性以及 [[yii\data\SqlDataProvider::totalCount|totalCount]] 属性,例如, @@ -136,21 +136,21 @@ $provider = new SqlDataProvider([ $models = $provider->getModels(); ``` -> 说明:[[yii\data\SqlDataProvider::totalCount|totalCount]] 的属性只有你需要分页数据的时候才会用到。 - 这是因为通过 [[yii\data\SqlDataProvider::sql|sql]] 指定的SQL语句将被数据提供者所修改并且只返回当前页面数据。 - 数据提供者为了正确计算可用页面的数量仍旧需要知道数据项的总数。 - +> 说明:[[yii\data\SqlDataProvider::totalCount|totalCount]] 的属性只有你需要 + 分页数据的时候才会用到。这是因为通过 [[yii\data\SqlDataProvider::sql|sql]] + 指定的SQL语句将被数据提供者所修改并且只返回当 + 前页面数据。数据提供者为了正确计算可用页面的数量仍旧需要知道数据项的总数。 ## 数组数据提供者 -[[yii\data\ArrayDataProvider]] 非常适用于大的数组。数据提供者允许你返回一个经过一个或者多个字段排序的数组数据页面。 -为了使用 [[yii\data\ArrayDataProvider]],你应该指定 [[yii\data\ArrayDataProvider::allModels|allModels]] 属性 -作为一个大的数组。 -这个大数组的元素既可以是一些关联数组(例如:[DAO](db-dao.md)查询出来的结果)也可以是一些对象(例如:[Active Record](db-active-record.md)实例) +[[yii\data\ArrayDataProvider]] 非常适用于大的数组。数据提供者允许你返回一个 +经过一个或者多个字段排序的数组数据页面。为了使用 [[yii\data\ArrayDataProvider]], +你应该指定 [[yii\data\ArrayDataProvider::allModels|allModels]] 属性作为一个大的数组。 +这个大数组的元素既可以是一些关联数组(例如:[DAO](db-dao.md)查询出来的结果) +也可以是一些对象(例如:[Active Record](db-active-record.md)实例) 例如, - ```php use yii\data\ArrayDataProvider; @@ -203,10 +203,10 @@ $ids = $provider->getKeys(); ``` 在上面的例子中,因为你提供给 [[yii\data\ActiveDataProvider]] 一个 [[yii\db\ActiveQuery]] 对象, -它是足够智能地返回一些主键值作为键。你也可以明确指出键值应该怎样被计算出来,计算的方式是通过使用一个字段名或者一个可调用的计算键值来配置。 +它是足够智能地返回一些主键值作为键。你也可以明确指出键值应该怎样被计算出来, +计算的方式是通过使用一个字段名或者一个可调用的计算键值来配置。 例如, - ```php // 使用 "slug" 字段作为键值 $provider = new ActiveDataProvider([ @@ -230,12 +230,12 @@ $provider = new ActiveDataProvider([ 一个简单的方式是从 [[yii\data\BaseDataProvider]] 去扩展,这种方式允许你关注数据提供者的核心逻辑。 这时,你主要需要实现下面的一些方法: -- [[yii\data\BaseDataProvider::prepareModels()|prepareModels()]]:准备好在当前页面可用的数据模型,并且作为一个数组返回它们。 - -- [[yii\data\BaseDataProvider::prepareKeys()|prepareKeys()]]:接受一个当前可用的数据模型的数组,并且返回一些与它们相关联的键。 - -- [[yii\data\BaseDataProvider::prepareTotalCount()|prepareTotalCount]]: 在数据提供者中返回一个标识出数据模型总数的值。 - +- [[yii\data\BaseDataProvider::prepareModels()|prepareModels()]]:准备好在当前页面可用的数据模型, + 并且作为一个数组返回它们。 +- [[yii\data\BaseDataProvider::prepareKeys()|prepareKeys()]]:接受一个当前可用的数据模型的数组, + 并且返回一些与它们相关联的键。 +- [[yii\data\BaseDataProvider::prepareTotalCount()|prepareTotalCount]]: 在数据提供者中返回一个 + 标识出数据模型总数的值。 下面是一个数据提供者的例子,这个数据提供者可以高效地读取CSV数据: diff --git a/docs/guide-zh-CN/output-data-widgets.md b/docs/guide-zh-CN/output-data-widgets.md index 8dd30bf..5189301 100644 --- a/docs/guide-zh-CN/output-data-widgets.md +++ b/docs/guide-zh-CN/output-data-widgets.md @@ -3,8 +3,8 @@ Yii提供了一套数据小部件 [widgets](structure-widgets.md) ,这些小部件可以用于显示数据。 [DetailView](#detail-view) 小部件能够用于显示一条记录数据, -[ListView](#list-view) 和 [GridView](#grid-view) 小部件能够用于显示一个拥有分页、排序和过滤功能的一个列表或者表格。 - +[ListView](#list-view) 和 [GridView](#grid-view) 小部件能够用于显示一个拥有分页、 +排序和过滤功能的一个列表或者表格。 DetailView @@ -40,8 +40,8 @@ ListView [[yii\widgets\ListView|ListView]] 小部件用于显示数据提供者 [data provider](output-data-providers.md) 提供的数据。 每个数据模型用指定的视图文件 [[yii\widgets\ListView::$itemView|view file]] 来渲染。 -因为它提供开箱即用式的(译者注:封装好的)分页、排序以及过滤这样一些特性,所以它可以很方便地为最终用户显示信息并同时创建数据管理界面。 - +因为它提供开箱即用式的(译者注:封装好的)分页、排序以及过滤这样一些特性, +所以它可以很方便地为最终用户显示信息并同时创建数据管理界面。 一个典型的用法如下例所示: @@ -82,8 +82,8 @@ use yii\helpers\HtmlPurifier; - `$index`:整型,是由数据提供者返回的数组中以0起始的数据项的索引。 - `$widget`:类型是ListView,是小部件的实例。 -假如你需要传递附加数据到每一个视图中,你可以像下面这样用 [[yii\widgets\ListView::$viewParams|$viewParams]] 属性传递键值对: - +假如你需要传递附加数据到每一个视图中,你可以像下面这样用 [[yii\widgets\ListView::$viewParams|$viewParams]] +属性传递键值对: ```php echo ListView::widget([ @@ -104,11 +104,11 @@ GridView -------- 数据网格或者说 GridView 小部件是Yii中最强大的部件之一。如果你需要快速建立系统的管理后台, -GridView 非常有用。它从数据提供者 [data provider](output-data-providers.md) 中取得数据并使用 [[yii\grid\GridView::columns|columns]] 属性的一组列配置,在一个表格中渲染每一行数据。 - - -表中的每一行代表一个数据项的数据,并且一列通常表示该项的属性(某些列可以对应于属性或静态文本的复杂表达式)。 +GridView 非常有用。它从数据提供者 [data provider](output-data-providers.md) 中取得数据并使用 +[[yii\grid\GridView::columns|columns]] 属性的一组列配置,在一个表格中渲染每一行数据。 +表中的每一行代表一个数据项的数据,并且一列通常表示该项的属性 +(某些列可以对应于属性或静态文本的复杂表达式)。 使用GridView的最少代码如下: @@ -158,8 +158,8 @@ echo GridView::widget([ ]); ``` -请注意,假如配置中没有指定 [[yii\grid\GridView::columns|columns]] 属性,那么Yii会试图显示数据提供者的模型中所有可能的列。 - +请注意,假如配置中没有指定 [[yii\grid\GridView::columns|columns]] 属性, +那么Yii会试图显示数据提供者的模型中所有可能的列。 ### 列类 @@ -223,10 +223,10 @@ echo GridView::widget([ ]); ``` -在上面的代码中,`text` 对应于 [[\yii\i18n\Formatter::asText()]]。列的值作为第一个参数传递。在第二列的定义中,`date` 对应于 [[\yii\i18n\Formatter::asDate()]]。 +在上面的代码中,`text` 对应于 [[\yii\i18n\Formatter::asText()]]。列的值作为第一个参数传递。 +在第二列的定义中,`date` 对应于 [[\yii\i18n\Formatter::asDate()]]。 同样地,列值也是通过第一个参数传递的,而 'php:Y-m-d' 用作第二个参数的值。 - 可用的格式化方法列表,请参照 [section about Data Formatting](output-formatting.md)。 数据列配置,还有一个”快捷格式化串”的方法,详情见API文档 [[yii\grid\GridView::columns|columns]]。 @@ -265,13 +265,30 @@ echo GridView::widget([ } ``` - 在上面的代码中,`$url` 是列为按钮创建的URL,`$model`是当前要渲染的模型对象,并且 `$key` 是在数据提供者数组中模型的键。 - + 在上面的代码中,`$url` 是列为按钮创建的URL,`$model`是当前要渲染的模型对象, + 并且 `$key` 是在数据提供者数组中模型的键。 - [[yii\grid\ActionColumn::urlCreator|urlCreator]] 是使用指定的模型信息来创建一个按钮URL的回调函数。 -该回调的原型和 [[yii\grid\ActionColumn::createUrl()]] 是一样的。 -假如这个属性没有设置,按钮的URL将使用 [[yii\grid\ActionColumn::createUrl()]] 来创建。 + 该回调的原型和 [[yii\grid\ActionColumn::createUrl()]] 是一样的。 + 假如这个属性没有设置,按钮的URL将使用 [[yii\grid\ActionColumn::createUrl()]] 来创建。 +- [[yii\grid\ActionColumn::visibleButtons|visibleButtons]] is an array of visibility conditions for each button. + The array keys are the button names (without curly brackets), and the values are the boolean true/false or the + anonymous function. When the button name is not specified in this array it will be shown by default. + The callbacks must use the following signature: + + ```php + function ($model, $key, $index) { + return $model->status === 'editable'; + } + ``` + + Or you can pass a boolean value: + ```php + [ + 'update' => \Yii::$app->user->can('update') + ] + ``` #### 复选框列 @@ -291,8 +308,8 @@ echo GridView::widget([ ], ``` -用户可点击复选框来选择网格中的一些行。被选择的行可通过调用下面的JavaScript代码来获得: - +用户可点击复选框来选择网格中的一些行。被选择的行可通过调用下面的 +JavaScript代码来获得: ```javascript var keys = $('#grid').yiiGridView('getSelectedRows'); @@ -322,11 +339,12 @@ echo GridView::widget([ ### 数据过滤 -为了过滤数据的 GridView 需要一个模型 [model](structure-models.md) 来 从过滤表单接收数据,以及调整数据提供者的查询对象,以满足搜索条件。 +为了过滤数据的 GridView 需要一个模型 [model](structure-models.md) 来 +从过滤表单接收数据,以及调整数据提供者的查询对象,以满足搜索条件。 使用活动记录 [active records](db-active-record.md) 时,通常的做法是 创建一个能够提供所需功能的搜索模型类(可以使用 [Gii](start-gii.md) 来生成)。 -这个类为搜索定义了验证规则并且提供了一个将会返回数据提供者对象的 `search()` 方法。 - +这个类为搜索定义了验证规则并且提供了一个将会返回数据提供者对象 +的 `search()` 方法。 为了给 `Post` 模型增加搜索能力,我们可以像下面的例子一样创建 `PostSearch` 模型: @@ -377,9 +395,11 @@ class PostSearch extends Post return $dataProvider; } } - ``` +> Tip: See [Query Builder](db-query-builder.md) and especially [Filter Conditions](db-query-builder.md#filter-conditions) +> to learn how to build filtering query. + 你可以在控制器中使用如下方法为网格视图获取数据提供者: ```php @@ -404,18 +424,95 @@ echo GridView::widget([ ]); ``` +### Separate filter form -### 处理关系型模型 +Most of the time using GridView header filters is enough, but in case you need a separate filter form, +you can easily add it as well. You can create partial view `_search.php` with the following contents: -当我们在一个网格视图中显示活动数据的时候,你可能会遇到这种情况,就是显示关联表的列的值,例如:发帖者的名字,而不是显示他的 `id`。 -当 `Post` 模型有一个关联的属性名(译者注: `Post` 模型中用 `hasOne` 定义 `getAuthor()` 函数) -叫 `author` 并且作者模型(译者注:本例的作者模型是 `users` )有一个属性叫 `name`,那么你可以通过在 [[yii\grid\GridView::$columns]] -中定义属性名为 `author.name` 来处理。这时的网格视图能显示作者名了,但是默认是不支持按作者名排序和过滤的。 -你需要调整上个章节介绍的 `PostSearch` 模型,以添加此功能。 +```php + + +
+ ['index'], + 'method' => 'get', + ]); ?> + field($model, 'title') ?> -为了使关联列能够排序,你需要连接关系表,以及添加排序规则到数据提供者的排序组件中: + field($model, 'creation_date') ?> +
+ 'btn btn-primary']) ?> + 'btn btn-default']) ?> +
+ + +
+``` + +and include it in `index.php` view like so: + +```php +render('_search', ['model' => $searchModel]) ?> +``` + +> Note: if you use Gii to generate CRUD code, the separate filter form (`_search.php`) is generated by default, +but is commented in `index.php` view. Uncomment it and it's ready to use! + +Separate filter form is useful when you need to filter by fields, that are not displayed in GridView +or for special filtering conditions, like date range. For filtering by date range we can add non DB attributes +`createdFrom` and `createdTo` to the search model: + +```php +class PostSearch extends Post +{ + /** + * @var string + */ + public $createdFrom; + + /** + * @var string + */ + public $createdTo; +} +``` + +Extend query conditions in the `search()` method like so: + +```php +$query->andFilterWhere(['>=', 'creation_date', $this->createdFrom]) + ->andFilterWhere(['<=', 'creation_date', $this->createdTo]); +``` + +And add the representative fields to the filter form: + +```php +field($model, 'creationFrom') ?> + +field($model, 'creationTo') ?> +``` + +### 处理关系型模型 + +当我们在一个网格视图中显示活动数据的时候,你可能会遇到这种情况,就是显示关联表的列的值, +例如:发帖者的名字,而不是显示他的 `id`。当 `Post` 模型有一个关联的属性名(译者注: `Post` 模型中用 `hasOne` 定义 `getAuthor()` 函数) +叫 `author` 并且作者模型(译者注:本例的作者模型是 `users` )有一个属性叫 `name`, +那么你可以通过在 [[yii\grid\GridView::$columns]] 中定义属性名为 `author.name` 来处理。 +这时的网格视图能显示作者名了,但是默认是不支持按作者名排序和过滤的。 +你需要调整上个章节介绍的 `PostSearch` 模型,以添加此功能。 + +为了使关联列能够排序,你需要连接关系表, +以及添加排序规则到数据提供者的排序组件中: ```php $query = Post::find(); @@ -426,6 +523,7 @@ $dataProvider = new ActiveDataProvider([ // 连接与 `users` 表相关联的 `author` 表 // 并将 `users` 表的别名设为 `author` $query->joinWith(['author' => function($query) { $query->from(['author' => 'users']); }]); +// since version 2.0.7, the above line can be simplified to $query->joinWith('author AS author'); // 使得关联字段可以排序 $dataProvider->sort->attributes['author.name'] = [ 'asc' => ['author.name' => SORT_ASC], @@ -461,14 +559,15 @@ $query->andFilterWhere(['LIKE', 'author.name', $this->getAttribute('author.name' > 信息:在上面的代码中,我们使用相同的字符串作为关联名称和表别名; > 然而,当你的表别名和关联名称不相同的时候,你得注意在哪使用你的别名,在哪使用你的关联名称。 -> 一个简单的规则是在每个构建数据库查询的地方使用别名,而在所有其他和定义相关的诸如:`attributes()` 和 `rules()` 等地方使用关联名称。 +> 一个简单的规则是在每个构建数据库查询的地方使用别名,而在所有其他和定义相关的诸如: +>`attributes()` 和 `rules()` 等地方使用关联名称。 +> +>例如,你使用 `au` 作为作者关系表的别名,那么联查语句就要写成像下面这样: > -> -> 例如,你使用 `au` 作为作者关系表的别名,那么联查语句就要写成像下面这样: -> > ```php -> $query->joinWith(['author' => function($query) { $query->from(['au' => 'users']); }]); -> ``` +>$query->joinWith(['author' => function($query) { $query->from(['au' => 'users']); }]); +>``` +> > 当别名已经在关联函数中定义了时,也可以只调用 `$query->joinWith(['author']);`。 > > 在过滤条件中,别名必须使用,但属性名称保持不变: @@ -486,8 +585,8 @@ $query->andFilterWhere(['LIKE', 'author.name', $this->getAttribute('author.name' > ]; > ``` > -> 同样,当指定使用 [[yii\data\Sort::defaultOrder|defaultOrder]] 来排序的时候,你需要使用关联名称替代别名: -> +> 同样,当指定使用 [[yii\data\Sort::defaultOrder|defaultOrder]] 来排序的时候, +>你需要使用关联名称替代别名: > > ```php > $dataProvider->sort->defaultOrder = ['author.name' => SORT_ASC]; @@ -568,12 +667,12 @@ class UserView extends ActiveRecord ### 单个页面多个网格视图部件 你可以在一个单独页面中使用多个网格视图,但是一些额外的配置是必须的,为的就是它们相互之间不干扰。 -当使用多个网格视图实例的时候,你必须要为生成的排序和分页对象配置不同的参数名,以便于每个网格视图有它们各自独立的排序和分页。 -你可以通过设置 [[yii\data\Sort::sortParam|sortParam]] 和 [[yii\data\Pagination::pageParam|pageParam]],对应于数据提供者的 -[[yii\data\BaseDataProvider::$sort|sort]] 和 [[yii\data\BaseDataProvider::$pagination|pagination]] 实例。 - - - +当使用多个网格视图实例的时候,你必须要为生成的排序和分页对象配置不同的参数名, +以便于每个网格视图有它们各自独立的排序和分页。 +你可以通过设置 [[yii\data\Sort::sortParam|sortParam]] 和 +[[yii\data\Pagination::pageParam|pageParam]],对应于数据提供者的 +[[yii\data\BaseDataProvider::$sort|sort]] 和 +[[yii\data\BaseDataProvider::$pagination|pagination]] 实例。 假如我们想要同时显示 `Post` 和 `User` 模型,这两个模型已经在 `$userProvider` 和 `$postProvider` 这两个数据提供者中准备好, 具体做法如下: @@ -600,6 +699,44 @@ echo GridView::widget([ ### Using GridView 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 to update only the +[[yii\grid\GridView|GridView]] content when using filters. + +```php +use yii\widgets\Pjax; +use yii\grid\GridView; + +Pjax::begin([ + // PJax options +]); + Gridview::widget([ + // GridView options + ]); +Pjax::end(); +``` + +Pjax also works for the links inside the [[yii\widgets\Pjax|Pjax]] widget and +for the links specified by [[yii\widgets\Pjax::$linkSelector|Pjax::$linkSelector]]. +But this might be a problem for the links of an [[yii\grid\ActionColumn|ActionColumn]]. +To prevent this, add the HTML attribute `data-pjax="0"` to the links when you edit +the [[yii\grid\ActionColumn::$buttons|ActionColumn::$buttons]] property. + +#### GridView/ListView with Pjax in Gii + +Since 2.0.5, the CRUD generator of [Gii](start-gii.md) has an option called +`$enablePjax` that can be used via either web interface or command line. + +```php +yii gii/crud --controllerClass="backend\\controllers\PostController" \ + --modelClass="common\\models\\Post" \ + --enablePjax=1 +``` + +Which generates a [[yii\widgets\Pjax|Pjax]] widget wrapping the +[[yii\grid\GridView|GridView]] or [[yii\widgets\ListView|ListView]] widgets. + +Further reading +--------------- -待定 +- [Rendering Data in Yii 2 with GridView and ListView](http://www.sitepoint.com/rendering-data-in-yii-2-with-gridview-and-listview/) by Arno Slatius. diff --git a/docs/guide-zh-CN/output-formatting.md b/docs/guide-zh-CN/output-formatting.md index b0d907e..c318386 100644 --- a/docs/guide-zh-CN/output-formatting.md +++ b/docs/guide-zh-CN/output-formatting.md @@ -1,184 +1,229 @@ 数据格式器 ============== -Yii提供一个格式化类来格式化输出,以使输出数据对终端用户更友好易读, -[[yii\i18n\Formatter]] 是一个助手类,作为 [应用组件](structure-application-components.md) 使用,默认名为`formatter`。 - -它提供一些方法用来格式化数据,如日期/时间、数字或其他常用的本地化格式, -两种方式使用格式器: - -1. 直接使用格式化方法(所有的格式器方法以 `as`做前缀): - - ```php - echo Yii::$app->formatter->asDate('2014-01-01', 'long'); // 输出: January 1, 2014 - echo Yii::$app->formatter->asPercent(0.125, 2); // 输出: 12.50% - echo Yii::$app->formatter->asEmail('cebe@example.com'); // 输出: cebe@example.com - echo Yii::$app->formatter->asBoolean(true); // 输出: Yes - // 也可处理null值的输出显示: - echo Yii::$app->formatter->asDate(null); // 输出: (Not set) - ``` - -2. 使用 [[yii\i18n\Formatter::format()|format()]] 方法和格式化名, - 该方法也被一些小部件如[[yii\grid\GridView]] 和 [[yii\widgets\DetailView]]使用,在小部件配置中可以指定列的数据格式。 - - ```php - echo Yii::$app->formatter->format('2014-01-01', 'date'); // 输出: January 1, 2014 - // 可使用数组来指定格式化方法的参数: - // `2` 是asPercent()方法的参数$decimals的值 - echo Yii::$app->formatter->format(0.125, ['percent', 2]); // 输出: 12.50% - ``` - -当[PHP intl extension](http://php.net/manual/en/book.intl.php)安装时,格式器的输出会本地化, -为此可配置格式器的 [[yii\i18n\Formatter::locale|locale]] 属性,如果没有配置, -应用配置 [[yii\base\Application::language|language]] 作为当前区域,更多详情参考 [国际化](tutorial-i18n.md)一节。 -然后格式器根据当前区域为日期和数字选择正确的格式,包括月份和星期也会转换到当前语言, -日期格式也会被 [[yii\i18n\Formatter::timeZone|timeZone]] 参数影响, -该参数如果没有明确配置会使用应用的 [[yii\base\Application::timeZone|from the application]] 参数。 - -日期格式根据不同区域输出不同的结果,如下例所示: -For example the date format call will output different results for different locales: +To display data in a more readable format for users, you may format them using the `formatter` [application component](structure-application-components.md). +By default the formatter is implemented by [[yii\i18n\Formatter]] which provides a set of methods to format data as +date/time, numbers, currencies, and other commonly used formats. You can use the formatter like the following, ```php -Yii::$app->formatter->locale = 'en-US'; -echo Yii::$app->formatter->asDate('2014-01-01'); // 输出: January 1, 2014 -Yii::$app->formatter->locale = 'de-DE'; -echo Yii::$app->formatter->asDate('2014-01-01'); // 输出: 1. Januar 2014 -Yii::$app->formatter->locale = 'ru-RU'; -echo Yii::$app->formatter->asDate('2014-01-01'); // 输出: 1 января 2014 г. +$formatter = \Yii::$app->formatter; + +// output: January 1, 2014 +echo $formatter->asDate('2014-01-01', 'long'); + +// output: 12.50% +echo $formatter->asPercent(0.125, 2); + +// output: cebe@example.com +echo $formatter->asEmail('cebe@example.com'); + +// output: Yes +echo $formatter->asBoolean(true); +// it also handles display of null values: + +// output: (Not set) +echo $formatter->asDate(null); ``` -> 注意不管[PHP intl extension](http://php.net/manual/en/book.intl.php)有没有安装,PHP编译的ICU库不同,格式化结果可能不同, -> 所以为确保不同环境下得到相同的输出,推荐在每个环境下安装PHP intl扩展以及相同的ICU库, -> 可参考: [为国际化设置PHP环境](tutorial-i18n.md#setup-environment). +As you can see, all these methods are named as `asXyz()`, where `Xyz` stands for a supported format. Alternatively, +you may format data using the generic method [[yii\i18n\Formatter::format()|format()]], which allows you to control +the desired format programmatically and is commonly used by widgets like [[yii\grid\GridView]] and [[yii\widgets\DetailView]]. +For example, +```php +// output: January 1, 2014 +echo Yii::$app->formatter->format('2014-01-01', 'date'); -配置格式器 -------------------------- +// you can also use an array to specify parameters for the format method: +// `2` is the value for the $decimals parameter of the asPercent()-method. +// output: 12.50% +echo Yii::$app->formatter->format(0.125, ['percent', 2]); +``` -可配置[[yii\i18n\Formatter|formatter class]]的属性来调整格式器方法的默认格式, -可以在[应用主体配置](concept-configurations.md#application-configurations) 中配置 `formatter` 组件应用到整个项目, -配置样例如下所示, -更多关于可用属性的详情请参考 [[yii\i18n\Formatter|API documentation of the Formatter class]] 和接下来一小节。 +> Note: The formatter component is designed to format values to be displayed for the end user. If you want +> to convert user input into machine readable format, or just format a date in a machine readable format, +> the formatter is not the right tool for that. +> To convert user input for date and number values you may use [[yii\validators\DateValidator]] and [[yii\validators\NumberValidator]] +> respectively. For simple conversion between machine readable date and time formats, +> the PHP [date()](http://php.net/manual/en/function.date.php)-function is enough. + +## Configuring Formatter + +You may customize the formatting rules by configuring the `formatter` component in the [application configuration](concept-configurations.md#application-configurations). +For example, ```php -'components' => [ - 'formatter' => [ - 'dateFormat' => 'dd.MM.yyyy', - 'decimalSeparator' => ',', - 'thousandSeparator' => ' ', - 'currencyCode' => 'EUR', - ], -], +return [ + 'components' => [ + 'formatter' => [ + 'dateFormat' => 'dd.MM.yyyy', + 'decimalSeparator' => ',', + 'thousandSeparator' => ' ', + 'currencyCode' => 'EUR', + ], + ], +]; ``` -格式化日期和时间 -------------------------------- +Please refer to [[yii\i18n\Formatter]] for the properties that may be configured. -格式器类为格式化日期和时间提供了多个方法: -The formatter class provides different methods for formatting date and time values. These are: -- [[yii\i18n\Formatter::asDate()|date]] - 值被格式化成日期,如 `January, 01 2014`. -- [[yii\i18n\Formatter::asTime()|time]] - 值被格式化成时间,如 `14:23`. -- [[yii\i18n\Formatter::asDatetime()|datetime]] - 值被格式化成日期和时间,如 `January, 01 2014 14:23`. -- [[yii\i18n\Formatter::asTimestamp()|timestamp]] - 值被格式化成 [unix 时间戳](http://en.wikipedia.org/wiki/Unix_time) 如 `1412609982`. -- [[yii\i18n\Formatter::asRelativeTime()|relativeTime]] - 值被格式化成和当前时间比较的时间间隔并用人们易读的格式,如`1 hour ago`. +## Formatting Date and Time Values -可配置格式器的属性[[yii\i18n\Formatter::$dateFormat|$dateFormat]], [[yii\i18n\Formatter::$timeFormat|$timeFormat]] -和[[yii\i18n\Formatter::$datetimeFormat|$datetimeFormat]]来全局指定[[yii\i18n\Formatter::asDate()|date]], -[[yii\i18n\Formatter::asTime()|time]] 和 [[yii\i18n\Formatter::asDatetime()|datetime]] 方法的日期和时间格式。 +The formatter supports the following output formats that are related with date and time: -格式器默认会使用一个快捷格式,它根据当前启用的区域来解析, -这样日期和时间会格式化成用户国家和语言通用的格式, -有四种不同的快捷格式: +- [[yii\i18n\Formatter::asDate()|date]]: the value is formatted as a date, e.g. `January 01, 2014`. +- [[yii\i18n\Formatter::asTime()|time]]: the value is formatted as a time, e.g. `14:23`. +- [[yii\i18n\Formatter::asDatetime()|datetime]]: the value is formatted as date and time, e.g. `January 01, 2014 14:23`. +- [[yii\i18n\Formatter::asTimestamp()|timestamp]]: the value is formatted as a [unix timestamp](http://en.wikipedia.org/wiki/Unix_time), e.g. `1412609982`. +- [[yii\i18n\Formatter::asRelativeTime()|relativeTime]]: the value is formatted as the time interval between a date + and now in human readable form e.g. `1 hour ago`. +- [[yii\i18n\Formatter::asDuration()|duration]]: the value is formatted as a duration in human readable format. e.g. `1 day, 2 minutes`. -- `en_GB`区域的 `short` 会打印日期为 `06/10/2014`,时间为 `15:58` -- `medium` 会分别打印 `6 Oct 2014` 和 `15:58:42`, -- `long` 会分别打印 `6 October 2014` 和 `15:58:42 GMT`, -- `full` 会分别打印 `Monday, 6 October 2014` 和 `15:58:42 GMT`. +The default date and time formats used for the [[yii\i18n\Formatter::asDate()|date]], [[yii\i18n\Formatter::asTime()|time]], +and [[yii\i18n\Formatter::asDatetime()|datetime]] methods can be customized globally by configuring +[[yii\i18n\Formatter::dateFormat|dateFormat]], [[yii\i18n\Formatter::timeFormat|timeFormat]], and +[[yii\i18n\Formatter::datetimeFormat|datetimeFormat]]. -另外你可使用[ICU 项目](http://site.icu-project.org/) 定义的语法来自定义格式, -ICU项目在该URL:下的手册有介绍, -或者可使用PHP [date()](http://php.net/manual/de/function.date.php) 方法的语法字符串并加上前缀`php:`. +You can specify date and time formats using the [ICU syntax](http://userguide.icu-project.org/formatparse/datetime). +You can also use the [PHP date() syntax](http://php.net/manual/en/function.date.php) with a prefix `php:` to differentiate +it from ICU syntax. For example, ```php -// ICU 格式化 +// ICU format echo Yii::$app->formatter->asDate('now', 'yyyy-MM-dd'); // 2014-10-06 -// PHP date()-格式化 + +// PHP date()-format echo Yii::$app->formatter->asDate('now', 'php:Y-m-d'); // 2014-10-06 ``` -### 时区 +When working with applications that need to support multiple languages, you often need to specify different date +and time formats for different locales. To simplify this task, you may use format shortcuts (e.g. `long`, `short`), instead. +The formatter will turn a format shortcut into an appropriate format according to the currently active [[yii\i18n\Formatter::locale|locale]]. +The following format shortcuts are supported (the examples assume `en_GB` is the active locale): + +- `short`: will output `06/10/2014` for date and `15:58` for time; +- `medium`: will output `6 Oct 2014` and `15:58:42`; +- `long`: will output `6 October 2014` and `15:58:42 GMT`; +- `full`: will output `Monday, 6 October 2014` and `15:58:42 GMT`. -当格式化日期和时间时,Yii会将它们转换为对应的 [[yii\i18n\Formatter::timeZone|configured time zone]] 时区, -输入的值在没有指定时区时候会被当作UTC时间,因此,推荐存储所有的日期和时间为UTC而不是UNIX时间戳,UNIX通常也是UTC。 -如果输入值所在的时区不同于UTC,时区应明确指定,如下所示: +Since version 2.0.7 it is also possible to format dates in different calendar systems. +Please refer to the API documentation of the formatters [[yii\i18n\Formatter::$calendar|$calendar]]-property on how to set a different calendar. + + +### Time Zones + +When formatting date and time values, Yii will convert them to the target [[yii\i18n\Formatter::timeZone|time zone]]. +The value being formatted is assumed to be in UTC, unless a time zone is explicitly given or you have configured +[[yii\i18n\Formatter::defaultTimeZone]]. + +In the following examples, we assume the target [[yii\i18n\Formatter::timeZone|time zone]] is set as `Europe/Berlin`. ```php -// 假定 Yii::$app->timeZone = 'Europe/Berlin'; +// formatting a UNIX timestamp as a time echo Yii::$app->formatter->asTime(1412599260); // 14:41:00 + +// formatting a datetime string (in UTC) as a time echo Yii::$app->formatter->asTime('2014-10-06 12:41:00'); // 14:41:00 + +// formatting a datetime string (in CEST) as a time echo Yii::$app->formatter->asTime('2014-10-06 14:41:00 CEST'); // 14:41:00 ``` -<<<<<<< .merge_file_a06508 -> 注意:时区从属于全世界各国政府定的规则,可能会频繁的变更,因此你的系统的时区数据库可能不是最新的信息, -======= -> Note: 时区从属于全世界各国政府定的规则,可能会频繁的变更,因此你的系统的时区数据库可能不是最新的信息, ->>>>>>> .merge_file_a06028 -> 可参考 [ICU manual](http://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data) -> 关于更新时区数据库的详情, -> 也可参考:[为国际化设置PHP环境](tutorial-i18n.md#setup-environment). +> Note: As time zones are subject to rules made by the governments around the world and may change frequently, it is +> likely that you do not have the latest information in the time zone database installed on your system. +> You may refer to the [ICU manual](http://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data) +> for details on updating the time zone database. Please also read +> [Setting up your PHP environment for internationalization](tutorial-i18n.md#setup-environment). -格式化数字 ------------------- +## Formatting Numbers -格式器类提供如下方法格式化数值: -For formatting numeric values the formatter class provides the following methods: +The formatter supports the following output formats that are related with numbers: -- [[yii\i18n\Formatter::asInteger()|integer]] - 值被格式化成整型,如 `42`. -- [[yii\i18n\Formatter::asDecimal()|decimal]] - 值被格式化成十进制数字并带有小数位和千分位,如 `42.123`. -- [[yii\i18n\Formatter::asPercent()|percent]] - 值被格式化成百分率,如 `42%`. -- [[yii\i18n\Formatter::asScientific()|scientific]] - 值被格式化成科学计数型,如`4.2E4`. -- [[yii\i18n\Formatter::asCurrency()|currency]] - 值被格式化成货币格式,如 `£420.00`. -- [[yii\i18n\Formatter::asSize()|size]] - 字节值被格式化成易读的值,如 `410 kibibytes`. +- [[yii\i18n\Formatter::asInteger()|integer]]: the value is formatted as an integer e.g. `42`. +- [[yii\i18n\Formatter::asDecimal()|decimal]]: the value is formatted as a decimal number considering decimal and thousand + separators e.g. `2,542.123` or `2.542,123`. +- [[yii\i18n\Formatter::asPercent()|percent]]: the value is formatted as a percent number e.g. `42%`. +- [[yii\i18n\Formatter::asScientific()|scientific]]: the value is formatted as a number in scientific format e.g. `4.2E4`. +- [[yii\i18n\Formatter::asCurrency()|currency]]: the value is formatted as a currency value e.g. `£420.00`. + Note that for this function to work properly, the locale needs to include a country part e.g. `en_GB` or `en_US` because language only + would be ambiguous in this case. +- [[yii\i18n\Formatter::asSize()|size]]: the value that is a number of bytes is formatted as a human readable size e.g. `410 kibibytes`. +- [[yii\i18n\Formatter::asShortSize()|shortSize]]: is the short version of [[yii\i18n\Formatter::asSize()|size]], e.g. `410 KiB`. -可配置[[yii\i18n\Formatter::decimalSeparator|decimalSeparator]] 和 [[yii\i18n\Formatter::thousandSeparator|thousandSeparator]] -属性来调整数字格式化的格式,默认和当前区域相同。 +The format for number formatting can be adjusted using the [[yii\i18n\Formatter::decimalSeparator|decimalSeparator]] and +[[yii\i18n\Formatter::thousandSeparator|thousandSeparator]], both of which take default values according to the +active [[yii\i18n\Formatter::locale|locale]]. -更多高级配置, [[yii\i18n\Formatter::numberFormatterOptions]] 和 [[yii\i18n\Formatter::numberFormatterTextOptions]] -可用于配置内部使用 [Numberformatter class](http://php.net/manual/en/class.numberformatter.php) - -为调整数字的小数部分的最大值和最小值,可配置如下属性: +For more advanced configuration, [[yii\i18n\Formatter::numberFormatterOptions]] and [[yii\i18n\Formatter::numberFormatterTextOptions]] +can be used to configure the [NumberFormatter class](http://php.net/manual/en/class.numberformatter.php) used internally +to implement the formatter. For example, to adjust the maximum and minimum value of fraction digits, you can configure +the [[yii\i18n\Formatter::numberFormatterOptions]] property like the following: ```php -[ +'numberFormatterOptions' => [ NumberFormatter::MIN_FRACTION_DIGITS => 0, NumberFormatter::MAX_FRACTION_DIGITS => 2, ] ``` -其他格式器 ----------------- - -除了日期、时间和数字格式化外,Yii提供其他用途提供一些实用的格式器: -Additional to date, time and number formatting, Yii provides a set of other useful formatters for different purposes: - -- [[yii\i18n\Formatter::asRaw()|raw]] - 输出值和原始值一样,除了`null`值会用[[nullDisplay]]格式化,这是一个伪格式器; -- [[yii\i18n\Formatter::asText()|text]] - 值会经过HTML编码; - 这是[GridView DataColumn](output-data-widgets.md#data-column)默认使用的格式; -- [[yii\i18n\Formatter::asNtext()|ntext]] - 值会格式化成HTML编码的纯文本,新行会转换成换行符; -- [[yii\i18n\Formatter::asParagraphs()|paragraphs]] - 值会转换成HTML编码的文本段落,用`

`标签包裹; -- [[yii\i18n\Formatter::asHtml()|html]] - 值会被[[HtmlPurifier]]过滤来避免XSS跨域攻击,可传递附加选项如`['html', ['Attr.AllowedFrameTargets' => ['_blank']]]; -- [[yii\i18n\Formatter::asEmail()|email]] - 值会格式化成 `mailto`-链接; -- [[yii\i18n\Formatter::asImage()|image]] - 值会格式化成图片标签; -- [[yii\i18n\Formatter::asUrl()|url]] - 值会格式化成超链接; -- [[yii\i18n\Formatter::asBoolean()|boolean]] - 值会格式化成布尔型值,默认情况下 `true` 对应 `Yes`,`false` 对应 `No`, - 可根据应用语言配置进行翻译,可以配置[[yii\i18n\Formatter::booleanFormat]]-属性来调整; - -`null`-值 -------------- - -对于PHP的`null`值,格式器类会打印一个占位符而不是空字符串,空字符串默认会显示对应当前语言`(not set)`, -可配置[[yii\i18n\Formatter::nullDisplay|nullDisplay]]-属性配置一个自定义占位符, -如果对处理`null`值没有特殊要求,可设置[[yii\i18n\Formatter::nullDisplay|nullDisplay]] 为 `null`. + +## Other Formats + +Besides date/time and number formats, Yii also supports other commonly used formats, including + +- [[yii\i18n\Formatter::asRaw()|raw]]: the value is outputted as is, this is a pseudo-formatter that has no effect except that + `null` values will be formatted using [[nullDisplay]]. +- [[yii\i18n\Formatter::asText()|text]]: the value is HTML-encoded. + This is the default format used by the [GridView DataColumn](output-data-widgets.md#data-column). +- [[yii\i18n\Formatter::asNtext()|ntext]]: the value is formatted as an HTML-encoded plain text with newlines converted + into line breaks. +- [[yii\i18n\Formatter::asParagraphs()|paragraphs]]: the value is formatted as HTML-encoded text paragraphs wrapped + into `

` tags. +- [[yii\i18n\Formatter::asHtml()|html]]: the value is purified using [[HtmlPurifier]] to avoid XSS attacks. You can + pass additional options such as `['html', ['Attr.AllowedFrameTargets' => ['_blank']]]`. +- [[yii\i18n\Formatter::asEmail()|email]]: the value is formatted as a `mailto`-link. +- [[yii\i18n\Formatter::asImage()|image]]: the value is formatted as an image tag. +- [[yii\i18n\Formatter::asUrl()|url]]: the value is formatted as a hyperlink. +- [[yii\i18n\Formatter::asBoolean()|boolean]]: the value is formatted as a boolean. By default `true` is rendered + as `Yes` and `false` as `No`, translated to the current application language. You can adjust this by configuring + the [[yii\i18n\Formatter::booleanFormat]] property. + + +## Null Values + +Null values are specially formatted. Instead of displaying an empty string, the formatter will convert it into a +preset string which defaults to `(not set)` translated into the current application language. You can configure the +[[yii\i18n\Formatter::nullDisplay|nullDisplay]] property to customize this string. + + +## Localizing Data Format + +As aforementioned, the formatter may use the currently active [[yii\i18n\Formatter::locale|locale]] to determine how +to format a value that is suitable in the target country/region. For example, the same date value may be formatted +differently for different locales: + +```php +Yii::$app->formatter->locale = 'en-US'; +echo Yii::$app->formatter->asDate('2014-01-01'); // output: January 1, 2014 + +Yii::$app->formatter->locale = 'de-DE'; +echo Yii::$app->formatter->asDate('2014-01-01'); // output: 1. Januar 2014 + +Yii::$app->formatter->locale = 'ru-RU'; +echo Yii::$app->formatter->asDate('2014-01-01'); // output: 1 января 2014 г. +``` + +By default, the currently active [[yii\i18n\Formatter::locale|locale]] is determined by the value of +[[yii\base\Application::language]]. You may override it by setting the [[yii\i18n\Formatter::locale]] property explicitly. + +> Note: The Yii formatter relies on the [PHP intl extension](http://php.net/manual/en/book.intl.php) to support +> localized data formatting. Because different versions of the ICU library compiled with PHP may cause different +> formatting results, it is recommended that you use the same ICU version for all your environments. For more details, +> please refer to [Setting up your PHP environment for internationalization](tutorial-i18n.md#setup-environment). +> +> If the intl extension is not installed, the data will not be localized. +> +> Note that for date values that are before year 1901 or after 2038, they will not be localized on 32-bit systems, even +> if the intl extension is installed. This is because in this case ICU is using 32-bit UNIX timestamps to date values. diff --git a/docs/guide-zh-CN/output-pagination.md b/docs/guide-zh-CN/output-pagination.md index bd90ffd..0bf0aff 100644 --- a/docs/guide-zh-CN/output-pagination.md +++ b/docs/guide-zh-CN/output-pagination.md @@ -1,45 +1,72 @@ 分页 ========== -当一次要在一个页面上显示很多数据时,通过需要将其分为 -几部分,每个部分都包含一些数据列表并且一次只显示一部分。这些部分在网页上被称为 -分页。 +当一次要在一个页面上显示很多数据时,通过需要将其分为几部分,每个部分都包含一些数据 +列表并且一次只显示一部分。这些部分在网页上被称为 *分页*。 -如果你使用 [数据提供者](output-data-providers.md) 和 [数据小部件](output-data-widgets.md) 中之一, -分页已经自动为你整理。否则,你需要创建 [[\yii\data\Pagination]] -对象为其填充数据,例如 [[\yii\data\Pagination::$totalCount|总记录数]], -[[\yii\data\Pagination::$pageSize|每页数量]] 和 [[\yii\data\Pagination::$page|当前页码]],在 -查询中使用它并且填充到 [[\yii\widgets\LinkPager|链接分页]]。 +Yii uses a [[yii\data\Pagination]] object to represent the information about a pagination scheme. In particular, +* [[yii\data\Pagination::$totalCount|total count]] specifies the total number of data items. Note that this + is usually much more than the number of data items needed to display on a single page. +* [[yii\data\Pagination::$pageSize|page size]] specifies how many data items each page contains. The default + value is 20. +* [[yii\data\Pagination::$page|current page]] gives the current page number (zero-based). The default value + value is 0, meaning the first page. -首先在控制器的动作中,我们创建分页对象并且为其填充数据: +With a fully specified [[yii\data\Pagination]] object, you can retrieve and display data partially. For example, +if you are fetching data from a database, you can specify the `OFFSET` and `LIMIT` clause of the DB query with +the corresponding values provided by the pagination. Below is an example, ```php -function actionIndex() -{ - $query = Article::find()->where(['status' => 1]); - $countQuery = clone $query; - $pages = new Pagination(['totalCount' => $countQuery->count()]); - $models = $query->offset($pages->offset) - ->limit($pages->limit) - ->all(); - - return $this->render('index', [ - 'models' => $models, - 'pages' => $pages, - ]); -} +use yii\data\Pagination; + +// build a DB query to get all articles with status = 1 +$query = Article::find()->where(['status' => 1]); + +// get the total number of articles (but do not fetch the article data yet) +$count = $query->count(); + +// create a pagination object with the total count +$pagination = new Pagination(['totalCount' => $count]); + +// limit the query using the pagination and retrieve the articles +$articles = $query->offset($pagination->offset) + ->limit($pagination->limit) + ->all(); ``` -其次在视图中我们输出的模板为当前页并通过分页对象链接到该页: +Which page of articles will be returned in the above example? It depends on whether a query parameter named `page` +is given. By default, the pagination will attempt to set the [[yii\data\Pagination::$page|current page]] to be +the value of the `page` parameter. If the parameter is not provided, then it will default to 0. + +To facilitate building the UI element that supports pagination, Yii provides the [[yii\widgets\LinkPager]] widget +that displays a list of page buttons upon which users can click to indicate which page of data should be displayed. +The widget takes a pagination object so that it knows what is the current page and how many page buttons should +be displayed. For example, ```php -foreach ($models as $model) { - // 在这里显示 $model -} +use yii\widgets\LinkPager; -// 显示分页 echo LinkPager::widget([ - 'pagination' => $pages, + 'pagination' => $pagination, ]); ``` + +If you want to build UI element manually, you may use [[yii\data\Pagination::createUrl()]] to create URLs that +would lead to different pages. The method requires a page parameter and will create a properly formatted URL +containing the page parameter. For example, + +```php +// specifies the route that the URL to be created should use +// If you do not specify this, the currently requested route will be used +$pagination->route = 'article/index'; + +// displays: /index.php?r=article%2Findex&page=100 +echo $pagination->createUrl(100); + +// displays: /index.php?r=article%2Findex&page=101 +echo $pagination->createUrl(101); +``` + +> Tip: You can customize the name of the `page` query parameter by configuring the + [[yii\data\Pagination::pageParam|pageParam]] property when creating the pagination object. diff --git a/docs/guide-zh-CN/output-sorting.md b/docs/guide-zh-CN/output-sorting.md index 6c14640..d7d7b6a 100644 --- a/docs/guide-zh-CN/output-sorting.md +++ b/docs/guide-zh-CN/output-sorting.md @@ -1,56 +1,89 @@ 排序 ======= -有时显示数据会根据一个或多个属性进行排序。如果你正在使用 -[数据提供者](output-data-providers.md) 和 [数据小部件](output-data-widgets.md) 中之一,排序 -可以为你自动处理。否则,你应该创建一个 [[yii\data\Sort]] 实例,配置好后 -将其应用到查询中。也可以传递给视图,可以在视图中通过某些属性创建链接来排序。 +When displaying multiple rows of data, it is often needed that the data be sorted according to some columns +specified by end users. Yii uses a [[yii\data\Sort]] object to represent the information about a sorting schema. +In particular, -如下是一个典型的使用范例, +* [[yii\data\Sort::$attributes|attributes]] specifies the *attributes* by which the data can be sorted. + An attribute can be as simple as a [model attribute](structure-models.md#attributes). It can also be a composite + one by combining multiple model attributes or DB columns. More details will be given in the following. +* [[yii\data\Sort::$attributeOrders|attributeOrders]] gives the currently requested ordering directions for + each attribute. +* [[yii\data\Sort::$orders|orders]] gives the ordering directions in terms of the low-level columns. + +To use [[yii\data\Sort]], first declare which attributes can be sorted. Then retrieve the currently requested +ordering information from [[yii\data\Sort::$attributeOrders|attributeOrders]] or [[yii\data\Sort::$orders|orders]] +and use them to customize the data query. For example, ```php -function actionIndex() -{ - $sort = new Sort([ - 'attributes' => [ - 'age', - 'name' => [ - 'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC], - 'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC], - 'default' => SORT_DESC, - 'label' => 'Name', - ], +use yii\data\Sort; + +$sort = new Sort([ + 'attributes' => [ + 'age', + 'name' => [ + 'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC], + 'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC], + 'default' => SORT_DESC, + 'label' => 'Name', ], - ]); - - $models = Article::find() - ->where(['status' => 1]) - ->orderBy($sort->orders) - ->all(); - - return $this->render('index', [ - 'models' => $models, - 'sort' => $sort, - ]); -} + ], +]); + +$articles = Article::find() + ->where(['status' => 1]) + ->orderBy($sort->orders) + ->all(); ``` -在视图中: +In the above example, two attributes are declared for the [[yii\data\Sort|Sort]] object: `age` and `name`. + +The `age` attribute is a *simple* attribute corresponding to the `age` attribute of the `Article` Active Record class. +It is equivalent to the following declaration: ```php -// 显示指向排序动作的链接 +'age' => [ + 'asc' => ['age' => SORT_ASC], + 'desc' => ['age' => SORT_DESC], + 'default' => SORT_ASC, + 'label' => Inflector::camel2words('age'), +] +``` + +The `name` attribute is a *composite* attribute defined by `first_name` and `last_name` of `Article`. It is declared +using the following array structure: + +- The `asc` and `desc` elements specify how to sort by the attribute in ascending and descending directions, respectively. + Their values represent the actual columns and the directions by which the data should be sorted by. You can specify + one or multiple columns to indicate simple ordering or composite ordering. +- The `default` element specifies the direction by which the attribute should be sorted when initially requested. + It defaults to ascending order, meaning if it is not sorted before and you request to sort by this attribute, + the data will be sorted by this attribute in ascending order. +- The `label` element specifies what label should be used when calling [[yii\data\Sort::link()]] to create a sort link. + If not set, [[yii\helpers\Inflector::camel2words()]] will be called to generate a label from the attribute name. + Note that it will not be HTML-encoded. + +> Info: You can directly feed the value of [[yii\data\Sort::$orders|orders]] to the database query to build + its `ORDER BY` clause. Do not use [[yii\data\Sort::$attributeOrders|attributeOrders]] because some of the + attributes may be composite and cannot be recognized by the database query. + +You can call [[yii\data\Sort::link()]] to generate a hyperlink upon which end users can click to request sorting +the data by the specified attribute. You may also call [[yii\data\Sort::createUrl()]] to create a sortable URL. +For example, + +```php +// specifies the route that the URL to be created should use +// If you do not specify this, the currently requested route will be used +$sort->route = 'article/index'; + +// display links leading to sort by name and age, respectively echo $sort->link('name') . ' | ' . $sort->link('age'); -foreach ($models as $model) { - // 在这里显示 $model -} +// displays: /index.php?r=article%2Findex&sort=age +echo $sort->createUrl('age'); ``` -以上,我们声明了支持了两个属性的排序:`name` 和 `age`。 -我们通过排序信息来查询以便于查询结果通过 Sort 对象 -排序后更加准确有序。在视图中,我们通过相应的属性 -展示了链接到页的两个超链接和数据排序。 - -[[yii\data\Sort|Sort]] 类将获得自动传递的请求参数 -并相应地调整排序选项。 -你可以通过配置 [[yii\data\Sort::$params|$params]] 属性来调整参数。 +[[yii\data\Sort]] checks the `sort` query parameter to determine which attributes are being requested for sorting. +You may specify a default ordering via [[yii\data\Sort::defaultOrder]] when the query parameter is not present. +You may also customize the name of the query parameter by configuring the [[yii\data\Sort::sortParam|sortParam]] property. diff --git a/docs/guide-zh-CN/output-theming.md b/docs/guide-zh-CN/output-theming.md index ec38f68..ed3de40 100644 --- a/docs/guide-zh-CN/output-theming.md +++ b/docs/guide-zh-CN/output-theming.md @@ -10,12 +10,12 @@ - [[yii\base\Theme::basePath]]:指定包含主题资源(CSS, JS, images, 等等)的基准目录。 - [[yii\base\Theme::baseUrl]]:指定主题资源的基准URL。 -- [[yii\base\Theme::pathMap]]:指定视图文件的替换规则。更多细节将在下面介绍。 - - -例如,如果你在 `SiteController` 里面调用 `$this->render('about')`,那你将渲染视图文件 `@app/views/site/about.php`。 -然而,如果你在下面的应用配置中开启了主题功能,那么`@app/themes/basic/site/about.php` 文件将会被渲染。 +- [[yii\base\Theme::pathMap]]:指定视图文件的替换规则。 + 更多细节将在下面介绍。 +例如,如果你在 `SiteController` 里面调用 `$this->render('about')`,那你将渲染 +视图文件 `@app/views/site/about.php` 。然而,如果你在下面的应用配置中开启了主 +题功能,那么 `@app/themes/basic/site/about.php` 文件将会被渲染。 ```php return [ @@ -34,7 +34,7 @@ return [ ``` > 信息:主题支持路径别名。当我们在做视图替换的时候, -路径别名将被转换成实际的文件路径或者URL。 + 路径别名将被转换成实际的文件路径或者URL。 你可以通过 [[yii\base\View::theme]] 属性访问 [[yii\base\Theme]] 对象。例如,在一个视图文件里,你可以写下面的代码, 因为 `$this` 指向视图对象: diff --git a/docs/guide-zh-CN/rest-authentication.md b/docs/guide-zh-CN/rest-authentication.md index c0d1af4..1ed1053 100644 --- a/docs/guide-zh-CN/rest-authentication.md +++ b/docs/guide-zh-CN/rest-authentication.md @@ -1,19 +1,24 @@ 认证 ============== -和Web应用不同,RESTful APIs 通常是无状态的,也就意味着不应使用sessions 或 cookies, +和Web应用不同,RESTful APIs 通常是无状态的, +也就意味着不应使用sessions 或 cookies, 因此每个请求应附带某种授权凭证,因为用户授权状态可能没通过sessions 或 cookies维护, -常用的做法是每个请求都发送一个秘密的access token来认证用户,由于access token可以唯一识别和认证用户, +常用的做法是每个请求都发送一个秘密的access token来认证用户, +由于access token可以唯一识别和认证用户, **API 请求应通过HTTPS来防止man-in-the-middle (MitM) 中间人攻击**. 下面有几种方式来发送access token: * [HTTP 基本认证](http://en.wikipedia.org/wiki/Basic_access_authentication): access token - 当作用户名发送,应用在access token可安全存在API使用端的场景,例如,API使用端是运行在一台服务器上的程序。 + 当作用户名发送,应用在access token可安全存在API使用端的场景, + 例如,API使用端是运行在一台服务器上的程序。 * 请求参数: access token 当作API URL请求参数发送,例如 - `https://example.com/users?access-token=xxxxxxxx`,由于大多数服务器都会保存请求参数到日志, + `https://example.com/users?access-token=xxxxxxxx` , + 由于大多数服务器都会保存请求参数到日志, 这种方式应主要用于`JSONP` 请求,因为它不能使用HTTP头来发送access token -* [OAuth 2](http://oauth.net/2/): 使用者从认证服务器上获取基于OAuth2协议的access token,然后通过 +* [OAuth 2](http://oauth.net/2/): 使用者从认证服务器上获取基于 + OAuth2协议的access token,然后通过 [HTTP Bearer Tokens](http://tools.ietf.org/html/rfc6750) 发送到API 服务器。 Yii 支持上述的认证方式,你也可很方便的创建新的认证方式。 @@ -23,26 +28,25 @@ Yii 支持上述的认证方式,你也可很方便的创建新的认证方式 1. 配置`user` 应用组件: - 设置 [[yii\web\User::enableSession|enableSession]] 属性为 `false`. - 设置 [[yii\web\User::loginUrl|loginUrl]] 属性为`null` 显示一个HTTP 403 错误而不是跳转到登录界面. -2. 在你的REST 控制器类中配置`authenticator` 行为来指定使用哪种认证方式 +2. 在你的REST 控制器类中配置`authenticator` + 行为来指定使用哪种认证方式 3. 在你的[[yii\web\User::identityClass|user identity class]] 类中实现 [[yii\web\IdentityInterface::findIdentityByAccessToken()]] 方法. -步骤1不是必要的,但是推荐配置,因为RESTful APIs应为无状态的,当[[yii\web\User::enableSession|enableSession]]为false, +步骤1不是必要的,但是推荐配置,因为RESTful APIs应为无状态的, +当[[yii\web\User::enableSession|enableSession]]为false, 请求中的用户认证状态就不能通过session来保持,每个请求的认证通过步骤2和3来实现。 -<<<<<<< .merge_file_a06500 -> 提示: 如果你将RESTful APIs作为应用开发,可以设置应用配置中 `user` 组件的[[yii\web\User::enableSession|enableSession]], -======= -> Tip: 如果你将RESTful APIs作为应用开发,可以设置应用配置中 `user` 组件的[[yii\web\User::enableSession|enableSession]], ->>>>>>> .merge_file_a05220 - 如果将RESTful APIs作为模块开发,可以在模块的 `init()` 方法中增加如下代码,如下所示: +> 提示: 如果你将RESTful APIs作为应用开发,可以设置应用配置中 +> `user` 组件的[[yii\web\User::enableSession|enableSession]], +> 如果将RESTful APIs作为模块开发,可以在模块的 `init()` 方法中增加如下代码,如下所示: -```php -public function init() -{ - parent::init(); - \Yii::$app->user->enableSession = false; -} -``` +> ```php +> public function init() +> { +> parent::init(); +> \Yii::$app->user->enableSession = false; +> } +> ``` 例如,为使用HTTP Basic Auth,可配置`authenticator` 行为,如下所示: @@ -89,7 +93,6 @@ public function behaviors() 例如,一个简单的场景,当每个用户只有一个access token, 可存储access token 到user表的`access_token`列中, 方法可在`User`类中简单实现,如下所示: - ```php use yii\db\ActiveRecord; use yii\web\IdentityInterface; @@ -103,22 +106,22 @@ class User extends ActiveRecord implements IdentityInterface } ``` -在上述认证启用后,对于每个API请求,请求控制器都会在它的`beforeAction()`步骤中对用户进行认证。 +在上述认证启用后,对于每个API请求,请求控制器都会在它的`beforeAction()` +步骤中对用户进行认证。 如果认证成功,控制器再执行其他检查(如频率限制,操作权限),然后再执行操作, 授权用户信息可使用`Yii::$app->user->identity`获取. -如果认证失败,会发送一个HTTP状态码为401的响应,并带有其他相关信息头(如HTTP 基本认证会有`WWW-Authenticate` 头信息). +如果认证失败,会发送一个HTTP状态码为401的响应,并带有其他相关信息头 +(如HTTP 基本认证会有`WWW-Authenticate` 头信息). -<<<<<<< .merge_file_a06500 ## 授权 -======= -## 授权 ->>>>>>> .merge_file_a05220 -在用户认证成功后,你可能想要检查他是否有权限执行对应的操作来获取资源,这个过程称为 *authorization* , +在用户认证成功后,你可能想要检查他是否有权限执行对应的操作来获取资源, +这个过程称为 *authorization* , 详情请参考 [Authorization section](security-authorization.md). -如果你的控制器从[[yii\rest\ActiveController]]类继承,可覆盖 [[yii\rest\Controller::checkAccess()|checkAccess()]] 方法 +如果你的控制器从[[yii\rest\ActiveController]]类继承, +可覆盖 [[yii\rest\Controller::checkAccess()|checkAccess()]] 方法 来执行授权检查,该方法会被[[yii\rest\ActiveController]]内置的操作调用。 diff --git a/docs/guide-zh-CN/rest-controllers.md b/docs/guide-zh-CN/rest-controllers.md index 6d298e7..429a6ea 100644 --- a/docs/guide-zh-CN/rest-controllers.md +++ b/docs/guide-zh-CN/rest-controllers.md @@ -1,14 +1,18 @@ 控制器 =========== -在创建资源类和指定资源格输出式化后,下一步就是创建控制器操作将资源通过RESTful APIs展现给终端用户。 +在创建资源类和指定资源格输出式化后,下一步就是创建控制器操作将资源 +通过RESTful APIs展现给终端用户。 -Yii 提供两个控制器基类来简化创建RESTful 操作的工作:[[yii\rest\Controller]] 和 [[yii\rest\ActiveController]], -两个类的差别是后者提供一系列将资源处理成[Active Record](db-active-record.md)的操作。 -因此如果使用[Active Record](db-active-record.md)内置的操作会比较方便,可考虑将控制器类 -继承[[yii\rest\ActiveController]],它会让你用最少的代码完成强大的RESTful APIs. +Yii 提供两个控制器基类来简化创建RESTful 操作的工作: +[[yii\rest\Controller]] 和 [[yii\rest\ActiveController]], +两个类的差别是后者提供一系列将资源处理成[Active Record](db-active-record.md) +的操作。因此如果使用[Active Record](db-active-record.md) +内置的操作会比较方便,可考虑将控制器类继承[[yii\rest\ActiveController]], +它会让你用最少的代码完成强大的RESTful APIs. -[[yii\rest\Controller]] 和 [[yii\rest\ActiveController]] 提供以下功能,一些功能在后续章节详细描述: +[[yii\rest\Controller]] 和 [[yii\rest\ActiveController]] 提供以下功能, +一些功能在后续章节详细描述: * HTTP 方法验证; * [内容协商和数据格式化](rest-response-formatting.md); @@ -21,18 +25,17 @@ Yii 提供两个控制器基类来简化创建RESTful 操作的工作:[[yii\rest * 对操作和资源进行用户认证. -<<<<<<< .merge_file_a00416 -## 创建控制器类 -======= ## 创建控制器类 ->>>>>>> .merge_file_a02328 -当创建一个新的控制器类,控制器类的命名最好使用资源名称的单数格式,例如,提供用户信息的控制器 +当创建一个新的控制器类,控制器类的命名最好使用资源 +名称的单数格式,例如,提供用户信息的控制器 可命名为`UserController`. -创建新的操作和Web应用中创建操作类似,唯一的差别是Web应用中调用`render()`方法渲染一个视图作为返回值, -对于RESTful操作直接返回数据,[[yii\rest\Controller::serializer|serializer]] 和 -[[yii\web\Response|response object]] 会处理原始数据到请求格式的转换,例如 +创建新的操作和Web应用中创建操作类似,唯一的差别是Web应用中 +调用`render()`方法渲染一个视图作为返回值,对于RESTful操作 +直接返回数据,[[yii\rest\Controller::serializer|serializer]] 和 +[[yii\web\Response|response object]] 会处理原始数据到请求格式的转换, +例如 ```php public function actionView($id) @@ -42,24 +45,18 @@ public function actionView($id) ``` -<<<<<<< .merge_file_a00416 -## 过滤器 -======= ## 过滤器 ->>>>>>> .merge_file_a02328 [[yii\rest\Controller]]提供的大多数RESTful API功能通过[过滤器](structure-filters.md)实现. 特别是以下过滤器会按顺序执行: -* [[yii\filters\ContentNegotiator|contentNegotiator]]: 支持内容协商,在 [响应格式化](rest-response-formatting.md) 一节描述; -* [[yii\filters\VerbFilter|verbFilter]]: 支持HTTP 方法验证; - the [Authentication](rest-authentication.md) section; -<<<<<<< .merge_file_a00416 -* [[yii\filters\AuthMethod|authenticator]]: 支持用户认证,在[认证](rest-authentication.md)一节描述; -======= -* [[yii\filters\auth\AuthMethod|authenticator]]: 支持用户认证,在[认证](rest-authentication.md)一节描述; ->>>>>>> .merge_file_a02328 -* [[yii\filters\RateLimiter|rateLimiter]]: 支持频率限制,在[频率限制](rest-rate-limiting.md) 一节描述. +* [[yii\filters\ContentNegotiator|contentNegotiator]]: 支持内容协商, + 在 [响应格式化](rest-response-formatting.md) 一节描述; +* [[yii\filters\VerbFilter|verbFilter]]: 支持HTTP 方法验证;the [Authentication](rest-authentication.md) section; +* [[yii\filters\auth\AuthMethod|authenticator]]: 支持用户认证, + 在[认证](rest-authentication.md)一节描述; +* [[yii\filters\RateLimiter|rateLimiter]]: 支持频率限制, + 在[频率限制](rest-rate-limiting.md) 一节描述. 这些过滤器都在[[yii\rest\Controller::behaviors()|behaviors()]]方法中声明, 可覆盖该方法来配置单独的过滤器,禁用某个或增加你自定义的过滤器。 @@ -79,23 +76,14 @@ public function behaviors() ``` -<<<<<<< .merge_file_a00416 -## 继承 `ActiveController` - -如果你的控制器继承[[yii\rest\ActiveController]],应设置[[yii\rest\ActiveController::modelClass||modelClass]] 属性 -为通过该控制器返回给用户的资源类名,该类必须继承[[yii\db\ActiveRecord]]. - - -### 自定义操作 -======= ## 继承 `ActiveController` -如果你的控制器继承[[yii\rest\ActiveController]],应设置[[yii\rest\ActiveController::modelClass|modelClass]] 属性 +如果你的控制器继承[[yii\rest\ActiveController]],应设置 +[[yii\rest\ActiveController::modelClass|modelClass]] 属性 为通过该控制器返回给用户的资源类名,该类必须继承[[yii\db\ActiveRecord]]. ### 自定义操作 ->>>>>>> .merge_file_a02328 [[yii\rest\ActiveController]] 默认提供一下操作: @@ -132,24 +120,19 @@ public function prepareDataProvider() 请参考独立操作类的参考文档学习哪些配置项有用。 -<<<<<<< .merge_file_a00416 -### 执行访问检查 -======= ### 执行访问检查 ->>>>>>> .merge_file_a02328 通过RESTful APIs显示数据时,经常需要检查当前用户是否有权限访问和操作所请求的资源, -在[[yii\rest\ActiveController]]中,可覆盖[[yii\rest\ActiveController::checkAccess()|checkAccess()]]方法来完成权限检查。 +在[[yii\rest\ActiveController]]中,可覆盖[[yii\rest\ActiveController::checkAccess()|checkAccess()]] +方法来完成权限检查。 ```php /** - * Checks the privilege of the current user. 检查当前用户的权限 + * Checks the privilege of the current user. * * This method should be overridden to check whether the current user has the privilege * to run the specified action against the specified data model. * If the user does not have access, a [[ForbiddenHttpException]] should be thrown. - * 本方法应被覆盖来检查当前用户是否有权限执行指定的操作访问指定的数据模型 - * 如果用户没有权限,应抛出一个[[ForbiddenHttpException]]异常 * * @param string $action the ID of the action to be executed * @param \yii\base\Model $model the model to be accessed. If null, it means no specific model is being accessed. @@ -158,16 +141,12 @@ public function prepareDataProvider() */ public function checkAccess($action, $model = null, $params = []) { - // 检查用户能否访问 $action 和 $model - // 访问被拒绝应抛出ForbiddenHttpException + // check if the user can access $action and $model + // throw ForbiddenHttpException if access should be denied } ``` `checkAccess()` 方法默认会被[[yii\rest\ActiveController]]默认操作所调用,如果创建新的操作并想执行权限检查, 应在新的操作中明确调用该方法。 -<<<<<<< .merge_file_a00416 > 提示: 可使用[Role-Based Access Control (RBAC) 基于角色权限控制组件](security-authorization.md)实现`checkAccess()`。 -======= -> Tip: 可使用[Role-Based Access Control (RBAC) 基于角色权限控制组件](security-authorization.md)实现`checkAccess()`。 ->>>>>>> .merge_file_a02328 diff --git a/docs/guide-zh-CN/rest-error-handling.md b/docs/guide-zh-CN/rest-error-handling.md index d2cb708..fb30642 100644 --- a/docs/guide-zh-CN/rest-error-handling.md +++ b/docs/guide-zh-CN/rest-error-handling.md @@ -1,7 +1,8 @@ 错误处理 ============== -处理一个 RESTful API 请求时, 如果有一个用户请求错误或服务器发生意外时, 你可以简单地抛出一个异常来通知用户出错了。 +处理一个 RESTful API 请求时, 如果有一个用户请求错误或服务器发生意外时, +你可以简单地抛出一个异常来通知用户出错了。 如果你能找出错误的原因 (例如,所请求的资源不存在),你应该 考虑抛出一个适当的HTTP状态代码的异常 (例如, [[yii\web\NotFoundHttpException]] 意味着一个404 HTTP状态代码)。 Yii 将通过HTTP状态码和文本 @@ -16,7 +17,6 @@ Transfer-Encoding: chunked Content-Type: application/json; charset=UTF-8 { - "type": "yii\\web\\NotFoundHttpException", "name": "Not Found Exception", "message": "The requested resource was not found.", "code": 0, @@ -41,8 +41,6 @@ Content-Type: application/json; charset=UTF-8 * `422`: 数据验证失败 (例如,响应一个 `POST` 请求)。 请检查响应体内详细的错误消息。 * `429`: 请求过多。 由于限速请求被拒绝。 * `500`: 内部服务器错误。 这可能是由于内部程序错误引起的。 -<<<<<<< .merge_file_a05756 -======= ## 自定义错误响应 @@ -94,4 +92,3 @@ return [ 当 `suppress_response_code` 作为 `GET` 参数传递时,上面的代码 将重新按照自己定义的格式响应(无论失败还是成功)。 ->>>>>>> .merge_file_a06412 diff --git a/docs/guide-zh-CN/rest-quick-start.md b/docs/guide-zh-CN/rest-quick-start.md index 133b7d4..220a6c0 100644 --- a/docs/guide-zh-CN/rest-quick-start.md +++ b/docs/guide-zh-CN/rest-quick-start.md @@ -37,7 +37,8 @@ class UserController extends ActiveController } ``` -控制器类扩展自 [[yii\rest\ActiveController]]。通过指定 [[yii\rest\ActiveController::modelClass|modelClass]] +控制器类扩展自 [[yii\rest\ActiveController]]。通过指定 +[[yii\rest\ActiveController::modelClass|modelClass]] 作为 `app\models\User`, 控制器就能知道使用哪个模型去获取和处理数据。 @@ -60,8 +61,6 @@ class UserController extends ActiveController 用户的数据就能通过美化的 URL 和有意义的 http 动词进行访问和操作。 -<<<<<<< .merge_file_a05804 -======= ## 启用 JSON 输入 为了使 API 接收 JSON 格式的输入数据,配置 `request` 应用程序组件的 [[yii\web\Request::$parsers|parsers]] @@ -79,7 +78,6 @@ class UserController extends ActiveController `application/x-www-form-urlencoded` 和 `multipart/form-data` 输入格式。 ->>>>>>> .merge_file_a05620 ## 尝试 随着以上所做的最小的努力,你已经完成了创建用于访问用户数据 @@ -95,28 +93,16 @@ class UserController extends ActiveController * `OPTIONS /users`: 显示关于末端 `/users` 支持的动词 * `OPTIONS /users/123`: 显示有关末端 `/users/123` 支持的动词 -<<<<<<< .merge_file_a05804 -> 补充:Yii 将在末端使用的控制器的名称自动变为复数。(译注:个人感觉这里应该变为注意) - -你可以访问你的API用`curl`命令如下, -======= -> Info: Yii 将在末端使用的控制器的名称自动变为复数。(译注:个人感觉这里应该变为注意) +> 提示: Yii 将在末端使用的控制器的名称自动变为复数。(译注:个人感觉这里应该变为注意) > 你可以用 [[yii\rest\UrlRule::$pluralize]]-属性来配置此项。 你可以访问你的API用 `curl` 命令如下, ->>>>>>> .merge_file_a05620 ``` $ curl -i -H "Accept:application/json" "http://localhost/users" HTTP/1.1 200 OK -<<<<<<< .merge_file_a05804 -Date: Sun, 02 Mar 2014 05:31:43 GMT -Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y -X-Powered-By: PHP/5.4.20 -======= ... ->>>>>>> .merge_file_a05620 X-Pagination-Total-Count: 1000 X-Pagination-Page-Count: 50 X-Pagination-Current-Page: 1 @@ -140,24 +126,14 @@ Content-Type: application/json; charset=UTF-8 ] ``` -<<<<<<< .merge_file_a05804 -试着改变可接受的内容类型为`application/xml`,你会看到结果以 XML 格式返回: -======= 试着改变可接受的内容类型为`application/xml`, 你会看到结果以 XML 格式返回: ->>>>>>> .merge_file_a05620 ``` $ curl -i -H "Accept:application/xml" "http://localhost/users" HTTP/1.1 200 OK -<<<<<<< .merge_file_a05804 -Date: Sun, 02 Mar 2014 05:31:43 GMT -Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y -X-Powered-By: PHP/5.4.20 -======= ... ->>>>>>> .merge_file_a05620 X-Pagination-Total-Count: 1000 X-Pagination-Page-Count: 50 X-Pagination-Current-Page: 1 @@ -182,9 +158,6 @@ Content-Type: application/xml ``` -<<<<<<< .merge_file_a05804 -> 技巧:你还可以通过 Web 浏览器中输入 URL `http://localhost/users` 来访问你的 API。 -======= 以下命令将创建一个新的用户通过发送JSON格式的用户数据的 POST 请求: ``` @@ -200,7 +173,6 @@ Content-Type: application/json; charset=UTF-8 ``` > Tip: 你还可以通过 Web 浏览器中输入 URL `http://localhost/users` 来访问你的 API。 ->>>>>>> .merge_file_a05620 尽管如此,你可能需要一些浏览器插件来发送特定的 headers 请求。 如你所见,在 headers 响应,有关于总数,页数的信息,等等。 @@ -211,11 +183,7 @@ Content-Type: application/json; charset=UTF-8 例如:URL `http://localhost/users?fields=id,email` 将只返回 `id` 和 `email` 字段。 -<<<<<<< .merge_file_a05804 -> 补充:你可能已经注意到了 `http://localhost/users` 的结果包括一些敏感字段, -======= > Info: 你可能已经注意到了 `http://localhost/users` 的结果包括一些敏感字段, ->>>>>>> .merge_file_a05620 > 例如 `password_hash`, `auth_key` 你肯定不希望这些出现在你的 API 结果中。 > 你应该在 [响应格式](rest-response-formatting.md) 部分中过滤掉这些字段。 @@ -231,10 +199,5 @@ Content-Type: application/json; charset=UTF-8 你可以使用 [[yii\rest\UrlRule]] 简化路由到你的 API 末端。 -<<<<<<< .merge_file_a05804 -为了方便维护你的WEB前端和后端,建议你开发接口作为一个单独的应用程序,虽然这不是必须的。 -======= 为了方便维护你的WEB前端和后端,建议你开发接口作为一个单独的应用程序, 虽然这不是必须的。 ->>>>>>> .merge_file_a05620 - diff --git a/docs/guide-zh-CN/rest-rate-limiting.md b/docs/guide-zh-CN/rest-rate-limiting.md index ef81929..563030c 100644 --- a/docs/guide-zh-CN/rest-rate-limiting.md +++ b/docs/guide-zh-CN/rest-rate-limiting.md @@ -1,20 +1,13 @@ 速率限制 ============= -<<<<<<< .merge_file_a04232 -为防止滥用,你应该考虑增加速率限制到您的API。 -例如,您可以限制每个用户的API的使用是在10分钟内最多100次的API调用。 -如果一个用户同一个时间段内太多的请求被接收, 将返回响应状态代码 429 (这意味着过多的请求)。 -======= 为防止滥用,你应该考虑增加速率限制到您的 API。 例如,您可以限制每个用户的 API 的使用是在 10 分钟内最多 100 次的 API 调用。 如果一个用户在规定的时间内太多的请求被接收,将返回响应状态代码 429 (这意味着过多的请求)。 ->>>>>>> .merge_file_a03968 要启用速率限制, [[yii\web\User::identityClass|user identity class]] 应该实现 [[yii\filters\RateLimitInterface]]. 这个接口需要实现以下三个方法: -<<<<<<< .merge_file_a04232 * `getRateLimit()`: 返回允许的请求的最大数目及时间,例如,`[100, 600]` 表示在600秒内最多100次的API调用。 * `loadAllowance()`: 返回剩余的允许的请求和相应的UNIX时间戳数 当最后一次速率限制检查时。 @@ -25,22 +18,33 @@ 的这两列值的读和保存。为了提高性能,你也可以 考虑使用缓存或NoSQL存储这些信息。 -一旦 identity 实现所需的接口, Yii 会自动使用 [[yii\filters\RateLimiter]] -为 [[yii\rest\Controller]] 配置一个行为过滤器来执行速率限制检查。 如果速度超出限制 -该速率限制器将抛出一个 [[yii\web\TooManyRequestsHttpException]]。 你可以在你的 REST -控制器类里配置速率限制, -======= -* `getRateLimit()`: 返回允许的请求的最大数目及时间,例如,`[100, 600]` 表示在 600 秒内最多 100 次的 API 调用。 -* `loadAllowance()`: 返回剩余的允许的请求和最后一次速率限制检查时相应的 UNIX 时间戳数。 -* `saveAllowance()`: 保存剩余的允许请求数和当前的 UNIX 时间戳。 +Implementation in the `User` model could look like the following: -你可以在 user 表中使用两列来记录容差和时间戳信息。 -`loadAllowance()` 和 `saveAllowance()` 可以通过实现对符合当前身份验证的用户的这两列值的读和保存。为了提高性能,你也可以 -考虑使用缓存或 NoSQL 存储这些信息。 +```php +public function getRateLimit($request, $action) +{ + return [$this->rateLimit, 1]; // $rateLimit requests per second +} + +public function loadAllowance($request, $action) +{ + return [$this->allowance, $this->allowance_updated_at]; +} + +public function saveAllowance($request, $action, $allowance, $timestamp) +{ + $this->allowance = $allowance; + $this->allowance_updated_at = $timestamp; + $this->save(); +} +``` + +一旦 identity 实现所需的接口, Yii 会自动使用 [[yii\filters\RateLimiter]]为 [[yii\rest\Controller]] +配置一个行为过滤器来执行速率限制检查。 如果速度超出限制该速率限制器将抛出一个 [[yii\web\TooManyRequestsHttpException]]。 +你可以在你的 REST 控制器类里配置速率限制, -一旦 identity 实现所需的接口, Yii 会自动使用 [[yii\filters\RateLimiter]] -为 [[yii\rest\Controller]] 配置一个行为过滤器来执行速率限制检查。如果速度超出限制,该速率限制器将抛出一个 [[yii\web\TooManyRequestsHttpException]]。你可以参考以下代码在你的 REST 控制器类里配置速率限制: ->>>>>>> .merge_file_a03968 +你可以在 user 表中使用两列来记录容差和时间戳信息。`loadAllowance()` 和 `saveAllowance()` 可以通过实现对符合当前身份 +验证的用户的这两列值的读和保存。为了提高性能,你也可以考虑使用缓存或 NoSQL 存储这些信息。 ```php public function behaviors() @@ -51,20 +55,12 @@ public function behaviors() } ``` -<<<<<<< .merge_file_a04232 -当速率限制被激活,默认情况下每个响应将包含以下HTTP头发送 -目前的速率限制信息: -======= -当速率限制被激活,默认情况下每个响应将包含以下 HTTP 头发送目前的速率限制信息: ->>>>>>> .merge_file_a03968 +当速率限制被激活,默认情况下每个响应将包含以下 HTTP +头发送目前的速率限制信息: * `X-Rate-Limit-Limit`: 同一个时间段所允许的请求的最大数目; * `X-Rate-Limit-Remaining`: 在当前时间段内剩余的请求的数量; * `X-Rate-Limit-Reset`: 为了得到最大请求数所等待的秒数。 -<<<<<<< .merge_file_a04232 -你可以禁用这些头信息通过配置 [[yii\filters\RateLimiter::enableRateLimitHeaders]] 为false, -======= 你可以禁用这些头信息通过配置 [[yii\filters\RateLimiter::enableRateLimitHeaders]] 为 false, ->>>>>>> .merge_file_a03968 就像在上面的代码示例所示。 diff --git a/docs/guide-zh-CN/rest-resources.md b/docs/guide-zh-CN/rest-resources.md index d887a7d..c4d0b20 100644 --- a/docs/guide-zh-CN/rest-resources.md +++ b/docs/guide-zh-CN/rest-resources.md @@ -4,32 +4,35 @@ RESTful 的 API 都是关于访问和操作 *资源*,可将资源看成MVC模式中的 [模型](structure-models.md) -在如何代表一个资源没有固定的限定,在Yii中通常使用 [[yii\base\Model]] 或它的子类(如 [[yii\db\ActiveRecord]]) +在如何代表一个资源没有固定的限定,在Yii中通常使用 [[yii\base\Model]] +或它的子类(如 [[yii\db\ActiveRecord]]) 代表资源,是为以下原因: -* [[yii\base\Model]] 实现了 [[yii\base\Arrayable]] 接口,它允许你通过RESTful API自定义你想要公开的资源数据。 -* [[yii\base\Model]] 支持 [输入验证](input-validation.md), 在你的RESTful API需要支持数据输入时非常有用。 -* [[yii\db\ActiveRecord]] 提供了强大的数据库访问和操作方面的支持,如资源数据需要存到数据库它提供了完美的支持。 +* [[yii\base\Model]] 实现了 [[yii\base\Arrayable]] 接口,它允许你通过RESTful API + 自定义你想要公开的资源数据。 +* [[yii\base\Model]] 支持 [输入验证](input-validation.md), 在你的RESTful API + 需要支持数据输入时非常有用。 +* [[yii\db\ActiveRecord]] 提供了强大的数据库访问和操作方面的支持, + 如资源数据需要存到数据库它提供了完美的支持。 -本节主要描述资源类如何从 [[yii\base\Model]] (或它的子类) 继承并指定哪些数据可通过RESTful API返回,如果资源类没有 +本节主要描述资源类如何从 [[yii\base\Model]] (或它的子类) +继承并指定哪些数据可通过RESTful API返回,如果资源类没有 继承 [[yii\base\Model]] 会将它所有的公开成员变量返回。 -<<<<<<< .merge_file_a01056 -## 字段 -======= ## 字段 ->>>>>>> .merge_file_a06160 当RESTful API响应中包含一个资源时,该资源需要序列化成一个字符串。 Yii将这个过程分成两步,首先,资源会被[[yii\rest\Serializer]]转换成数组, -然后,该数组会通过[[yii\web\ResponseFormatterInterface|response formatters]]根据请求格式(如JSON, XML)被序列化成字符串。 +然后,该数组会通过[[yii\web\ResponseFormatterInterface|response formatters]] +根据请求格式(如JSON, XML)被序列化成字符串。 当开发一个资源类时应重点关注第一步。 -通过覆盖 [[yii\base\Model::fields()|fields()]] 和/或 [[yii\base\Model::extraFields()|extraFields()]] 方法, +通过覆盖 [[yii\base\Model::fields()|fields()]] 和/或 +[[yii\base\Model::extraFields()|extraFields()]] 方法, 可指定资源中称为 *字段* 的数据放入展现数组中,两个方法的差别为前者指定默认包含到展现数组的字段集合, -后者指定由于终端用户的请求包含 `expand` 参数哪些额外的字段应被包含到展现数组,例如, - +后者指定由于终端用户的请求包含 `expand` 参数哪些额外的字段应被包含到展现数组, +例如, ``` // 返回fields()方法中申明的所有字段 @@ -46,21 +49,20 @@ http://localhost/users?fields=id,email&expand=profile ``` -<<<<<<< .merge_file_a01056 -### 覆盖 `fields()` 方法 -======= ### 覆盖 `fields()` 方法 ->>>>>>> .merge_file_a06160 [[yii\base\Model::fields()]] 默认返回模型的所有属性作为字段, [[yii\db\ActiveRecord::fields()]] 只返回和数据表关联的属性作为字段。 -可覆盖 `fields()` 方法来增加、删除、重命名、重定义字段,`fields()` 的返回值应为数组,数组的键为字段名 -数组的值为对应的字段定义,可为属性名或返回对应的字段值的匿名函数,特殊情况下,如果字段名和属性名相同, +可覆盖 `fields()` 方法来增加、删除、重命名、重定义字段,`fields()` 的返回值应为数组, +数组的键为字段名数组的值为对应的字段定义, +可为属性名或返回对应的字段值的匿名函数, +特殊情况下,如果字段名和属性名相同, 可省略数组的键,例如 ```php -// 明确列出每个字段,适用于你希望数据表或模型属性修改时不导致你的字段修改(保持后端API兼容性) +// 明确列出每个字段,适用于你希望数据表或模型属性 +//修改时不导致你的字段修改(保持后端API兼容性) public function fields() { return [ @@ -75,7 +77,8 @@ public function fields() ]; } -// 过滤掉一些字段,适用于你希望继承父类实现同时你想屏蔽掉一些敏感字段 +// 过滤掉一些字段,适用于你希望继承父类 +//实现同时你想屏蔽掉一些敏感字段 public function fields() { $fields = parent::fields(); @@ -87,23 +90,19 @@ public function fields() } ``` -<<<<<<< .merge_file_a01056 -> 警告: 模型的所有属性默认会被包含到API结果中,应检查数据确保没包含敏感数据,如果有敏感数据, -> 应覆盖`fields()`过滤掉,在上述例子中,我们选择过滤掉 `auth_key`, `password_hash` 和 `password_reset_token`. - - -### 覆盖 `extraFields()` 方法 -======= -> Warning: 模型的所有属性默认会被包含到API结果中,应检查数据确保没包含敏感数据,如果有敏感数据, -> 应覆盖`fields()`过滤掉,在上述例子中,我们选择过滤掉 `auth_key`, `password_hash` 和 `password_reset_token`. +> 警告: 模型的所有属性默认会被包含到API结果中, +> 应检查数据确保没包含敏感数据,如果有敏感数据, +> 应覆盖`fields()`过滤掉,在上述例子中,我们选择过滤掉 `auth_key`, +> `password_hash` 和 `password_reset_token`. ### 覆盖 `extraFields()` 方法 ->>>>>>> .merge_file_a06160 -[[yii\base\Model::extraFields()]] 默认返回空值,[[yii\db\ActiveRecord::extraFields()]] 返回和数据表关联的属性。 +[[yii\base\Model::extraFields()]] 默认返回空值,[[yii\db\ActiveRecord::extraFields()]] +返回和数据表关联的属性。 -`extraFields()` 返回的数据格式和 `fields()` 相同,一般`extraFields()` 主要用于指定哪些值为对象的字段, +`extraFields()` 返回的数据格式和 `fields()` 相同,一般`extraFields()` +主要用于指定哪些值为对象的字段, 例如,给定以下字段申明 ```php @@ -135,17 +134,16 @@ public function extraFields() ``` -<<<<<<< .merge_file_a01056 -## 链接 -======= ## 链接 ->>>>>>> .merge_file_a06160 -[HATEOAS](http://en.wikipedia.org/wiki/HATEOAS), 是Hypermedia as the Engine of Application State的缩写, -提升RESTful API 应返回允许终端用户访问的资源操作的信息,HATEOAS 的目的是在API中返回包含相关链接信息的资源数据。 +[HATEOAS](http://en.wikipedia.org/wiki/HATEOAS), 是 +Hypermedia as the Engine of Application State的缩写, +提升RESTful API 应返回允许终端用户访问的资源操作的信息, +HATEOAS 的目的是在API中返回包含相关链接信息的资源数据。 -资源类通过实现[[yii\web\Linkable]] 接口来支持HATEOAS,该接口包含方法 [[yii\web\Linkable::getLinks()|getLinks()]] 来返回 -[[yii\web\Link|links]] 列表,典型情况下应返回包含代表本资源对象URL的 `self` 链接,例如 +资源类通过实现[[yii\web\Linkable]] 接口来支持HATEOAS, +该接口包含方法 [[yii\web\Linkable::getLinks()|getLinks()]] 来返回 +[[yii\web\Link|links]] 列表,典型情况下应返回包含代表本资源对象URL的 `self` 链接,例如, ```php use yii\db\ActiveRecord; @@ -164,7 +162,8 @@ class User extends ActiveRecord implements Linkable } ``` -当响应中返回一个`User` 对象,它会包含一个 `_links` 单元表示和用户相关的链接,例如 +当响应中返回一个`User` 对象,它会包含一个 `_links` +单元表示和用户相关的链接,例如 ``` { @@ -180,16 +179,14 @@ class User extends ActiveRecord implements Linkable ``` -<<<<<<< .merge_file_a01056 -## 集合 -======= ## 集合 ->>>>>>> .merge_file_a06160 -资源对象可以组成 *集合*,每个集合包含一组相同类型的资源对象。 +资源对象可以组成 *集合*,每个集合包含一组相同 +类型的资源对象。 集合可被展现成数组,更多情况下展现成 [data providers](output-data-providers.md). -因为data providers支持资源的排序和分页,这个特性在 RESTful API 返回集合时也用到,例如This is because data providers support sorting and pagination +因为data providers支持资源的排序和分页,这个特性在 RESTful API 返回集合时也用到, +例如This is because data providers support sorting and pagination 如下操作返回post资源的data provider: ```php @@ -210,7 +207,8 @@ class PostController extends Controller } ``` -当在RESTful API响应中发送data provider 时, [[yii\rest\Serializer]] 会取出资源的当前页并组装成资源对象数组, +当在RESTful API响应中发送data provider 时, [[yii\rest\Serializer]] +会取出资源的当前页并组装成资源对象数组, [[yii\rest\Serializer]] 也通过如下HTTP头包含页码信息: * `X-Pagination-Total-Count`: 资源所有数量; diff --git a/docs/guide-zh-CN/rest-response-formatting.md b/docs/guide-zh-CN/rest-response-formatting.md index 7b2bd62..b4a9ff7 100644 --- a/docs/guide-zh-CN/rest-response-formatting.md +++ b/docs/guide-zh-CN/rest-response-formatting.md @@ -9,13 +9,9 @@ 2. 资源对象转换为数组, 如在 [Resources](rest-resources.md) 部分中所描述的。 通过 [[yii\rest\Serializer]] 来完成。 3. 通过内容协商步骤将数组转换成字符串。 -<<<<<<< .merge_file_a03016 - [yii\web\ResponseFormatterInterface|response formatters]] 通过 - [yii\web\Response::formatters|response]] 应用程序组件来注册完成。 -======= [[yii\web\ResponseFormatterInterface|response formatters]] 通过 - [[yii\web\Response::formatters|response]] 应用程序组件来注册完成。 ->>>>>>> .merge_file_a05144 + [[yii\web\Response::formatters|response]] + [应用程序组件]((structure-application-components.md))来注册完成。 ## 内容协商 diff --git a/docs/guide-zh-CN/rest-routing.md b/docs/guide-zh-CN/rest-routing.md index d635c95..2e09c14 100644 --- a/docs/guide-zh-CN/rest-routing.md +++ b/docs/guide-zh-CN/rest-routing.md @@ -70,9 +70,23 @@ 'extraPatterns' => [ 'GET search' => 'search', ], +] ``` -您可能已经注意到控制器ID`user`以复数形式出现在`users`末端。 -这是因为 [[yii\rest\UrlRule]] 能够为他们使用的末端全自动复数化控制器ID。 -您可以通过设置 [[yii\rest\UrlRule::pluralize]] 为false 来禁用此行为,如果您想 -使用一些特殊的名字您可以通过配置 [[yii\rest\UrlRule::controller]] 属性。 +您可能已经注意到控制器ID`user`以复数形式出现在`users`末端。这是因为 [[yii\rest\UrlRule]] +能够为他们使用的末端全自动复数化控制器ID。您可以通过设置 [[yii\rest\UrlRule::pluralize]] +为false 来禁用此行为,如果您想使用一些特殊的名字您可以通过配置 [[yii\rest\UrlRule::controller]] 属性。 + +> Info: The pluralization of controller IDs is done by [[yii\helpers\Inflector::pluralize()]]. The method respects + special pluralization rules. For example, the word `box` will be pluralized as `boxes` instead of `boxs`. + +In case when the automatic pluralization does not meet your requirement, you may also configure the +[[yii\rest\UrlRule::controller]] property to explicitly specify how to map a name used in endpoint URLs to +a controller ID. For example, the following code maps the name `u` to the controller ID `user`. + +```php +[ + 'class' => 'yii\rest\UrlRule', + 'controller' => ['u' => 'user'], +] +``` diff --git a/docs/guide-zh-CN/rest-versioning.md b/docs/guide-zh-CN/rest-versioning.md index 9e2f4a4..9e7dc2e 100644 --- a/docs/guide-zh-CN/rest-versioning.md +++ b/docs/guide-zh-CN/rest-versioning.md @@ -1,14 +1,18 @@ 版本 ========== -你的API应该是版本化的。不像你完全控制在客户端和服务器端Web应用程序代码, 对于API,您通常没有对API的客户端代码的控制权。 -因此,应该尽可能的保持向后兼容性(BC),如果一些不能向后兼容的变化必须引入 -APIs,你应该增加版本号。你可以参考[Semantic Versioning](http://semver.org/) -有关设计的API的版本号的详细信息。 +A good API is *versioned*: changes and new features are implemented in new versions of the API instead of continually altering just one version. Unlike Web applications, with which you have full control of both the client-side and server-side +code, APIs are meant to be used by clients beyond your control. For this reason, backward +compatibility (BC) of the APIs should be maintained whenever possible. If a change that may break BC is necessary, you should introduce it in new version of the API, and bump up the version number. Existing clients can continue to use the old, working version of the API; and new or upgraded clients can get the new functionality in the new API version. -关于如何实现API版本,一个常见的做法是在API的URL中嵌入版本号。 -例如,`http://example.com/v1/users`代表`/users`版本1的API. 另一种API版本化的方法最近用的非常多的是把版本号放入HTTP请求头,通常是通过`Accept`头, -如下: +> Tip: Refer to [Semantic Versioning](http://semver.org/) +for more information on designing API version numbers. + +关于如何实现API版本,一个常见的做法是在API的URL中嵌入版本号。例如,`http://example.com/v1/users`代表`/users`版本1的API. +另一种API版本化的方法最近用的非常多的是把版本号放入HTTP请求头,通常是通过`Accept`头,如下: + +Another method of API versioning, +which has gained momentum recently, is to put the version number in the HTTP request headers. This is typically done through the `Accept` header: ``` // 通过参数 @@ -49,6 +53,7 @@ api/ models/ User.php Post.php + Module.php v2/ controllers/ UserController.php @@ -56,6 +61,7 @@ api/ models/ User.php Post.php + Module.php ``` 你的应用程序配置应该这样: diff --git a/docs/guide-zh-CN/runtime-bootstrapping.md b/docs/guide-zh-CN/runtime-bootstrapping.md index 8c2e318..109ce9d 100644 --- a/docs/guide-zh-CN/runtime-bootstrapping.md +++ b/docs/guide-zh-CN/runtime-bootstrapping.md @@ -1,32 +1,46 @@ 启动引导(Bootstrapping) ============= -启动引导是指:在应用开始解析并处理新接受请求之前,一个预先准备环境的过程。启动引导会在两个地方具体进行:[入口脚本(Entry Script)](structure-entry-scripts.md) +启动引导是指:在应用开始解析并处理新接受请求之前,一个预先准备环境的过程。 +启动引导会在两个地方具体进行:[入口脚本(Entry Script)](structure-entry-scripts.md) 和 [应用主体(application)](structure-applications.md)。 -在[入口脚本](structure-entry-scripts.md)里,需注册各个类库的类文件自动加载器(Class Autoloader,简称自动加载器)。这主要包括通过其 `autoload.php` 文件加载的 -Composer 自动加载器,以及通过 `Yii` 类加载的 Yii 自动加载器。之后,入口脚本会加载应用的 -[配置(configuration)](concept-configurations.md) -并创建一个 [应用主体](structure-applications.md) 的实例。 +在[入口脚本](structure-entry-scripts.md)里,需注册各个类库的类文件自动加载器(Class Autoloader,简称自动加载器)。 +这主要包括通过其 `autoload.php` 文件加载的Composer 自动加载器,以及通过 `Yii` 类加载的 Yii 自动加载器。之后, +入口脚本会加载应用的[配置(configuration)](concept-configurations.md)并创建一个 +[应用主体](structure-applications.md) 的实例。 在应用主体的构造函数中,会执行以下引导工作: -1. 调用 [[yii\base\Application::preInit()|preInit()]](预初始化)方法,配置一些高优先级的应用属性,比如 [[yii\base\Application::basePath|basePath]] 属性。 +1. 调用 [[yii\base\Application::preInit()|preInit()]](预初始化)方法,配置一些高优先级的应用属性, + 比如 [[yii\base\Application::basePath|basePath]] 属性。 2. 注册[[yii\base\Application::errorHandler|错误处理器(ErrorHandler)]]。 3. 通过给定的应用配置初始化应用的各属性。 4. 通过调用 [[yii\base\Application::init()|init()]](初始化)方法,它会顺次调用 [[yii\base\Application::bootstrap()|bootstrap()]] 从而运行引导组件。 - 加载扩展清单文件(extension manifest file) `vendor/yiisoft/extensions.php`。 - - 创建并运行各个扩展声明的 [引导组件(bootstrap components)](structure-extensions.md#bootstrapping-classes)。 - - 创建并运行各个 [应用组件](structure-application-components.md) 以及在应用的 [Bootstrap 属性](structure-applications.md#bootstrap)中声明的各个 + - 创建并运行各个扩展声明的 + [引导组件(bootstrap components)](structure-extensions.md#bootstrapping-classes)。 + - 创建并运行各个 [应用组件](structure-application-components.md) + 以及在应用的 [Bootstrap 属性](structure-applications.md#bootstrap)中声明的各个 [模块(modules)组件](structure-modules.md)(如果有)。 -因为引导工作必须在处理**每一次**请求之前都进行一遍,因此让该过程尽可能轻量化就异常重要,请尽可能地优化这一步骤。 +因为引导工作必须在处理**每一次**请求之前都进行一遍, +因此让该过程尽可能轻量化就异常重要,请尽可能地优化这一步骤。 -请尽量不要注册太多引导组件。只有他需要在 HTTP 请求处理的全部生命周期中都作用时才需要使用它。举一个用到它的范例:一个模块需要注册额外的 URL 解析规则,就应该把它列在应用的 -[bootstrap 属性](structure-applications.md#bootstrap)之中,这样该 URL 解析规则才能在解析请求之前生效。(译注:换言之,为了性能需要,除了 URL -解析等少量操作之外,绝大多数组件都应该按需加载,而不是都放在引导过程中。) +请尽量不要注册太多引导组件。只有他需要在 HTTP 请求处理的全部生命周期中都作用时才需要使用它。 +举一个用到它的范例:一个模块需要注册额外的 URL 解析规则,就应该把它列在应用的 +[bootstrap 属性](structure-applications.md#bootstrap)之中, +这样该 URL 解析规则才能在解析请求之前生效。 +(译注:换言之,为了性能需要,除了 URL 解析等少量操作之外,绝大多数组件都应该按需加载,而不是都放在引导过程中。) -在生产环境中,可以开启字节码缓存,比如 APC,来进一步最小化加载和解析 PHP 文件所需的时间。 +在生产环境中,可以开启字节码缓存,比如 APC, +来进一步最小化加载和解析 PHP 文件所需的时间。 -一些大型应用都包含有非常复杂的应用[配置](concept-configurations.md),它们会被分割到许多更小的配置文件中。此时,可以考虑将整个配置数组缓存起来,并在入口脚本创建应用实例之前直接从缓存中加载。 +[PHP OPcache]: http://php.net/manual/en/intro.opcache.php +[APC]: http://php.net/manual/en/book.apc.php + +一些大型应用都包含有非常复杂的应用[配置](concept-configurations.md), +它们会被分割到许多更小的配置文件中。 +此时,可以考虑将整个配置数组缓存起来, +并在入口脚本创建应用实例之前直接从缓存中加载。 diff --git a/docs/guide-zh-CN/runtime-handling-errors.md b/docs/guide-zh-CN/runtime-handling-errors.md index 6cefbd8..77834ac 100644 --- a/docs/guide-zh-CN/runtime-handling-errors.md +++ b/docs/guide-zh-CN/runtime-handling-errors.md @@ -5,7 +5,8 @@ Yii 内置了一个[[yii\web\ErrorHandler|error handler]]错误处理器,它 Yii错误处理器做以下工作来提升错误处理效果: * 所有非致命PHP错误(如,警告,提示)会转换成可获取异常; -* 异常和致命的PHP错误会被显示,在调试模式会显示详细的函数调用栈和源代码行数。 +* 异常和致命的PHP错误会被显示, + 在调试模式会显示详细的函数调用栈和源代码行数。 * 支持使用专用的 [控制器操作](structure-controllers.md#actions) 来显示错误; * 支持不同的错误响应格式; @@ -30,7 +31,8 @@ return [ 使用如上代码,异常页面最多显示20条源代码。 -如前所述,错误处理器将所有非致命PHP错误转换成可获取异常,也就是说可以使用如下代码处理PHP错误: +如前所述,错误处理器将所有非致命PHP错误转换成可获取异常, +也就是说可以使用如下代码处理PHP错误: ```php use Yii; @@ -45,8 +47,10 @@ try { // execution continues... ``` -如果你想显示一个错误页面告诉用户请求是无效的或无法处理的,可简单地抛出一个 [[yii\web\HttpException|HTTP exception]]异常, -如 [[yii\web\NotFoundHttpException]]。错误处理器会正确地设置响应的HTTP状态码并使用合适的错误视图页面来显示错误信息。 +如果你想显示一个错误页面告诉用户请求是无效的或无法处理的, +可简单地抛出一个 [[yii\web\HttpException|HTTP exception]]异常,如 [[yii\web\NotFoundHttpException]]。 +错误处理器会正确地设置响应的HTTP状态码并使用合适的错误视图页面来显示错误信息。 + ```php use yii\web\NotFoundHttpException; @@ -57,15 +61,13 @@ throw new NotFoundHttpException(); ## 自定义错误显示 -[[yii\web\ErrorHandler|error handler]]错误处理器根据常量`YII_DEBUG`的值来调整错误显示, -当`YII_DEBUG` 为 true (表示在调试模式),错误处理器会显示异常以及详细的函数调用栈和源代码行数来帮助调试, +[[yii\web\ErrorHandler|error handler]]错误处理器根据常量 +`YII_DEBUG`的值来调整错误显示,当`YII_DEBUG` 为 true (表示在调试模式), +错误处理器会显示异常以及详细的函数调用栈和源代码行数来帮助调试, 当`YII_DEBUG` 为 false,只有错误信息会被显示以防止应用的敏感信息泄漏。 -<<<<<<< .merge_file_a02672 -> 补充: 如果异常是继承 [[yii\base\UserException]],不管`YII_DEBUG`为何值,函数调用栈信息都不会显示, -======= -> Info: 如果异常是继承 [[yii\base\UserException]],不管`YII_DEBUG`为何值,函数调用栈信息都不会显示, ->>>>>>> .merge_file_a06884 +> Info: 如果异常是继承 [[yii\base\UserException]], +不管`YII_DEBUG`为何值,函数调用栈信息都不会显示, 这是因为这种错误会被认为是用户产生的错误,开发人员不需要去修正。 [[yii\web\ErrorHandler|error handler]] 错误处理器默认使用两个[视图](structure-views.md)显示错误: @@ -81,7 +83,8 @@ throw new NotFoundHttpException(); ### 使用错误操作 使用指定的错误[操作](structure-controllers.md) 来自定义错误显示更方便, -为此,首先配置`errorHandler`组件的 [[yii\web\ErrorHandler::errorAction|errorAction]] 属性,类似如下: +为此,首先配置`errorHandler`组件的 [[yii\web\ErrorHandler::errorAction|errorAction]] 属性, +类似如下: ```php return [ @@ -93,7 +96,8 @@ return [ ]; ``` -[[yii\web\ErrorHandler::errorAction|errorAction]] 属性使用[路由](structure-controllers.md#routes)到一个操作, +[[yii\web\ErrorHandler::errorAction|errorAction]] 属性使用 +[路由](structure-controllers.md#routes)到一个操作, 上述配置表示不用显示函数调用栈信息的错误会通过执行`site/error`操作来显示。 可以创建`site/error` 操作如下所示: @@ -117,7 +121,8 @@ class SiteController extends Controller } ``` -上述代码定义`error` 操作使用[[yii\web\ErrorAction]] 类,该类渲染名为`error`视图来显示错误。 +上述代码定义`error` 操作使用[[yii\web\ErrorAction]] 类, +该类渲染名为`error`视图来显示错误。 除了使用[[yii\web\ErrorAction]], 可定义`error` 操作使用类似如下的操作方法: @@ -136,21 +141,26 @@ public function actionError() * `name`: 错误名称 * `message`: 错误信息 -* `exception`: 更多详细信息的异常对象,如HTTP 状态码,错误码,错误调用栈等。 +* `exception`: 更多详细信息的异常对象,如HTTP 状态码,错误码, + 错误调用栈等。 -<<<<<<< .merge_file_a02672 -> 补充: 如果你使用 [基础应用模板](start-installation.md) 或 [高级应用模板](tutorial-advanced-app.md), -======= > Info: 如果你使用 [基础应用模板](start-installation.md) 或 [高级应用模板](tutorial-advanced-app.md), ->>>>>>> .merge_file_a06884 错误操作和错误视图已经定义好了。 +> Note: If you need to redirect in an error handler, do it the following way: +> ```php +> Yii::$app->getResponse()->redirect($url)->send(); +> return; +> ``` + ### 自定义错误格式 错误处理器根据[响应](runtime-responses.md)设置的格式来显示错误, -如果[[yii\web\Response::format|response format]] 响应格式为`html`, 会使用错误或异常视图来显示错误信息,如上一小节所述。 -对于其他的响应格式,错误处理器会错误信息作为数组赋值给[[yii\web\Response::data]]属性,然后转换到对应的格式, +如果[[yii\web\Response::format|response format]] 响应格式为`html`, +会使用错误或异常视图来显示错误信息,如上一小节所述。 +对于其他的响应格式,错误处理器会错误信息作为数组赋值给[[yii\web\Response::data]]属性, +然后转换到对应的格式, 例如,如果响应格式为`json`,可以看到如下响应信息: ``` @@ -168,7 +178,8 @@ Content-Type: application/json; charset=UTF-8 } ``` -可在应用配置中响应`response`组件的`beforeSend`事件来自定义错误响应格式。 +可在应用配置中响应`response`组件的`beforeSend` +事件来自定义错误响应格式。 ```php return [ diff --git a/docs/guide-zh-CN/runtime-logging.md b/docs/guide-zh-CN/runtime-logging.md index 8083a3c..f6e7e9e 100644 --- a/docs/guide-zh-CN/runtime-logging.md +++ b/docs/guide-zh-CN/runtime-logging.md @@ -1,10 +1,10 @@ 日志 ======= -Yii提供了一个强大的日志框架,这个框架具有高度的可定制性和可扩展性。使用这个框架,你可以轻松地记录各种类型的消息,过滤它们, +Yii提供了一个强大的日志框架,这个框架具有高度的可定制性和可扩展性。使用这个框架, +你可以轻松地记录各种类型的消息,过滤它们, 并且将它们收集到不同的目标,诸如文件,数据库,邮件。 - 使用Yii日志框架涉及下面的几个步骤: * 在你代码里的各个地方记录 [log messages](#log-messages); @@ -32,36 +32,36 @@ Yii提供了一个强大的日志框架,这个框架具有高度的可定制 Yii::trace('start calculating average revenue'); ``` -> 信息:日志消息可以是字符串,也可以是复杂的数据,诸如数组或者对象。[log targets](#log-targets) 的义务是正确处理日志消息。 -默认情况下,假如一条日志消息不是一个字符串,它将被导出为一个字符串,通过调用 [[yii\helpers\VarDumper::export()]]。 - +> 注意:日志消息可以是字符串,也可以是复杂的数据,诸如数组或者对象。 +[log targets](#log-targets) 的义务是正确处理日志消息。默认情况下, +假如一条日志消息不是一个字符串,它将被导出为一个字符串,通过调用 [[yii\helpers\VarDumper::export()]]。 -为了更好地组织和过滤日志消息,我们建议您为每个日志消息指定一个适当的类别。 -您可以为类别选择一个分层命名方案,这将使得 [log targets](#log-targets) 在基于它们的分类来过滤消息变得更加容易。 -一个简单而高效的命名方案是使用PHP魔术常量 `__METHOD__` 作为分类名称。这种方式也在Yii框架的核心代码中得到应用, +为了更好地组织和过滤日志消息,我们建议您为每个日志消息指定一个适当的类别。您可以为类别选择一个分层命名方案, +这将使得 [log targets](#log-targets) 在基于它们的分类来过滤消息变得更加容易。 +一个简单而高效的命名方案是使用PHP魔术常量 `__METHOD__` 作为分类名称。 +这种方式也在Yii框架的核心代码中得到应用, 例如, - ```php Yii::trace('start calculating average revenue', __METHOD__); ``` -`__METHOD__` 常量计算值作为该常量出现的地方的方法名(完全限定的类名前缀)。例如,假如上面那行代码在这个方法内被调用,则它将等于字符串 +`__METHOD__` 常量计算值作为该常量出现的地方的方法名(完全限定的类名前缀)。 +例如,假如上面那行代码在这个方法内被调用,则它将等于字符串 `'app\controllers\RevenueController::calculate'`。 - -> 信息:上面所描述的日志方法实际上是 [[yii\log\Logger|logger object]] 对象(一个通过表达式 `Yii::getLogger()` 可访问的单例) -的方法 [[yii\log\Logger::log()|log()]] 的一个快捷方式。当足够的消息被记录或者当应用结束时,日志对象将会调用一个 [[yii\log\Dispatcher|message dispatcher]] +> 注意:上面所描述的日志方法实际上是 [[yii\log\Logger|logger object]] 对象(一个通过表达式 `Yii::getLogger()` 可访问的单例) +的方法 [[yii\log\Logger::log()|log()]] 的一个快捷方式。当足够的消息被记录或者当应用结束时, +日志对象将会调用一个 [[yii\log\Dispatcher|message dispatcher]] 调度对象将已经记录的日志消息发送到已注册的 [log targets](#log-targets) 目标中。 - ## 日志目标 -一个日志目标是一个 [[yii\log\Target]] 类或者它的子类的实例。它将通过他们的严重层级和类别来过滤日志消息,然后将它们导出到一些媒介中。 -例如,一个 [[yii\log\DbTarget|database target]] 目标导出已经过滤的日志消息到一个数据的表里面,而一个 [[yii\log\EmailTarget|email target]] -目标将日志消息导出到指定的邮箱地址里。 - +一个日志目标是一个 [[yii\log\Target]] 类或者它的子类的实例。 +它将通过他们的严重层级和类别来过滤日志消息,然后将它们导出到一些媒介中。 +例如,一个 [[yii\log\DbTarget|database target]] 目标导出已经过滤的日志消息到一个数据的表里面, +而一个 [[yii\log\EmailTarget|email target]]目标将日志消息导出到指定的邮箱地址里。 在一个应用里,通过配置在应用配置里的 `log` [application component](structure-application-components.md) ,你可以注册多个日志目标。 就像下面这样: @@ -133,17 +133,17 @@ Yii配备了以下的内建日志目标。请参考关于这些类的API文档 [[yii\log\Target::categories|categories]] 属性是一个包含消息分类名称或者模式的数组。 一个目标将只处理那些在这个数组中能够找到对应的分类或者其中一个相匹配的模式的消息。 -一个分类模式是一个以星号 `*` 结尾的分类名前缀。假如一个分类名与分类模式具有相同的前缀,那么该分类名将和分类模式相匹配。 -例如,`yii\db\Command::execute` 和 `yii\db\Command::query` 都是作为分类名称运用在 [[yii\db\Command]] 类来记录日志消息的。 +一个分类模式是一个以星号 `*` 结尾的分类名前缀。假如一个分类名与分类模式具有相同的前缀, +那么该分类名将和分类模式相匹配。例如, +`yii\db\Command::execute` 和 `yii\db\Command::query` 都是作为分类名称运用在 [[yii\db\Command]] 类来记录日志消息的。 它们都是匹配模式 `yii\db\*`。 - -假如你没有指定 [[yii\log\Target::categories|categories]] 属性,这意味着目标将会处理 *任何* 分类的消息。 - +假如你没有指定 [[yii\log\Target::categories|categories]] 属性, +这意味着目标将会处理 *任何* 分类的消息。 除了通过 [[yii\log\Target::categories|categories]] 属性设置白名单分类,你也可以通过 [[yii\log\Target::except|except]] -属性来设置某些分类作为黑名单。假如一条消息的分类在这个属性中被发现或者是匹配其中一个,那么它将不会在目标中被处理。 - +属性来设置某些分类作为黑名单。假如一条消息的分类在这个属性中被发现或者是匹配其中一个, +那么它将不会在目标中被处理。 在下面的目标配置中指明了目标应该只处理错误和警告消息,当分类的名称匹配 `yii\db\*` 或者是 `yii\web\HttpException:*` 的时候, 但是除了 `yii\web\HttpException:404`。 @@ -162,17 +162,17 @@ Yii配备了以下的内建日志目标。请参考关于这些类的API文档 ] ``` -> 信息:当一个HTTP异常通过 [error handler](runtime-handling-errors.md) 被捕获的时候,一个错误消息将以 `yii\web\HttpException:ErrorCode` +> 注意:当一个HTTP异常通过 [error handler](runtime-handling-errors.md) 被捕获的时候,一个错误消息将以 `yii\web\HttpException:ErrorCode` 这样的格式的分类名被记录下来。例如,[[yii\web\NotFoundHttpException]] 将会引发一个分类是 `yii\web\HttpException:404` 的 错误消息。 ### 消息格式化 -日志目标以某种格式导出过滤过的日志消息。例如,假如你安装一个 [[yii\log\FileTarget]] 类的日志目标, +日志目标以某种格式导出过滤过的日志消息。例如, +假如你安装一个 [[yii\log\FileTarget]] 类的日志目标, 你应该能找出一个日志消息类似下面的 `runtime/log/app.log` 文件: - ``` 2014-10-04 18:10:15 [::1][][-][trace][yii\base\Module::getModule] Loading module: debug ``` @@ -200,10 +200,10 @@ Timestamp [IP address][User ID][Session ID][Severity Level][Category] Message Te 除了消息前缀以外,日志目标也可以追加一些上下文信息到每组日志消息中。 默认情况下,这些全局的PHP变量的值被包含在:`$_GET`, `$_POST`, `$_FILES`, `$_COOKIE`,`$_SESSION` 和 `$_SERVER` 中。 -你可以通过配置 [[yii\log\Target::logVars]] 属性适应这个行为,这个属性是你想要通过日志目标包含的全局变量名称。 +你可以通过配置 [[yii\log\Target::logVars]] 属性适应这个行为, +这个属性是你想要通过日志目标包含的全局变量名称。 举个例子,下面的日志目标配置指明了只有 `$_SERVER` 变量的值将被追加到日志消息中。 - ```php [ 'class' => 'yii\log\FileTarget', @@ -211,11 +211,11 @@ Timestamp [IP address][User ID][Session ID][Severity Level][Category] Message Te ] ``` -你可以将 `logVars` 配置成一个空数组来完全禁止上下文信息包含。或者假如你想要实现你自己提供上下文信息的方式, +你可以将 `logVars` 配置成一个空数组来完全禁止上下文信息包含。 +或者假如你想要实现你自己提供上下文信息的方式, 你可以重写 [[yii\log\Target::getContextMessage()]] 方法。 - ### 消息跟踪级别 在开发的时候,通常希望看到每个日志消息来自哪里。这个是能够被实现的,通过配置 `log` 组件的 [[yii\log\Dispatcher::traceLevel|traceLevel]] 属性, @@ -234,22 +234,22 @@ return [ ``` 上面的应用配置设置了 [[yii\log\Dispatcher::traceLevel|traceLevel]] 的层级,假如 `YII_DEBUG` 开启则是3,否则是0。 -这意味着,假如 `YII_DEBUG` 开启,每个日志消息在日志消息被记录的时候,将被追加最多3个调用堆栈层级;假如 `YII_DEBUG` 关闭, +这意味着,假如 `YII_DEBUG` 开启,每个日志消息在日志消息被记录的时候, +将被追加最多3个调用堆栈层级;假如 `YII_DEBUG` 关闭, 那么将没有调用堆栈信息被包含。 - -> 信息:获得调用堆栈信息并不是不重要。因此,你应该只在开发或者调试一个应用的时候使用这个特性。 - +> 注意:获得调用堆栈信息并不是不重要。因此, +你应该只在开发或者调试一个应用的时候使用这个特性。 ### 消息刷新和导出 -如上所述,通过 [[yii\log\Logger|logger object]] 对象,日志消息被保存在一个数组里。为了这个数组的内存消耗, -当数组积累了一定数量的日志消息,日志对象每次都将刷新被记录的消息到 [log targets](#log-targets) 中。 +如上所述,通过 [[yii\log\Logger|logger object]] 对象,日志消息被保存在一个数组里。 +为了这个数组的内存消耗,当数组积累了一定数量的日志消息, +日志对象每次都将刷新被记录的消息到 [log targets](#log-targets) 中。 你可以通过配置 `log` 组件的 [[yii\log\Dispatcher::flushInterval|flushInterval]] 属性来自定义数量: - ```php return [ 'bootstrap' => ['log'], @@ -262,7 +262,7 @@ return [ ]; ``` -> 信息:当应用结束的时候,消息刷新也会发生,这样才能确保日志目标能够接收完整的日志消息。 +> 注意:当应用结束的时候,消息刷新也会发生,这样才能确保日志目标能够接收完整的日志消息。 当 [[yii\log\Logger|logger object]] 对象刷新日志消息到 [log targets](#log-targets) 的时候,它们并 不能立即获取导出的消息。相反,消息导出仅仅在一个日志目标累积了一定数量的过滤消息的时候才会发生。你可以通过配置 @@ -277,11 +277,11 @@ return [ ``` 因为刷新和导出层级的设置,默认情况下,当你调用 `Yii::trace()` 或者任何其他的记录方法,你将不能在日志目标中立即看到日志消息。 -这对于一些长期运行的控制台应用来说可能是一个问题。为了让每个日志消息在日志目标中能够立即出现,你应该设置 [[yii\log\Dispatcher::flushInterval|flushInterval]] +这对于一些长期运行的控制台应用来说可能是一个问题。为了让每个日志消息在日志目标中能够立即出现, +你应该设置 [[yii\log\Dispatcher::flushInterval|flushInterval]] 和 [[yii\log\Target::exportInterval|exportInterval]] 都为1, 就像下面这样: - ```php return [ 'bootstrap' => ['log'], @@ -311,8 +311,8 @@ return [ Yii::$app->log->targets['file']->enabled = false; ``` -上面的代码要求您将目标命名为 `file`,像下面展示的那样,在 `targets` 数组中使用使用字符串键: - +上面的代码要求您将目标命名为 `file`,像下面展示的那样, +在 `targets` 数组中使用使用字符串键: ```php return [ @@ -335,20 +335,20 @@ return [ ### 创建新的目标 -创建一个新的日志目标类非常地简单。你主要需要实现 [[yii\log\Target::export()]] 方法来发送 [[yii\log\Target::messages]] 数组的 -内容到一个指定的媒体中。你可以调用 [[yii\log\Target::formatMessage()]] 方法去格式化每个消息。更多细节,你可以参考任何一个包含在Yii -发布版中的日志目标类。 - +创建一个新的日志目标类非常地简单。你主要需要实现 [[yii\log\Target::export()]] +方法来发送 [[yii\log\Target::messages]] 数组的 +内容到一个指定的媒体中。你可以调用 [[yii\log\Target::formatMessage()]] 方法去格式化每个消息。 +更多细节,你可以参考任何一个包含在Yii发布版中的日志目标类。 ## 性能分析 -性能分析是一个特殊的消息记录类型,它通常用在测量某段代码块的时间,并且找出性能瓶颈是什么。举个例子,[[yii\db\Command]] 类 +性能分析是一个特殊的消息记录类型,它通常用在测量某段代码块的时间, +并且找出性能瓶颈是什么。举个例子,[[yii\db\Command]] 类 使用性能分析找出每个数据库查询的时间。 - -为了使用性能分析,首先确定需要进行分析的代码块。然后像下面这样围住每个代码块: - +为了使用性能分析,首先确定需要进行分析的代码块。 +然后像下面这样围住每个代码块: ```php \Yii::beginProfile('myBenchmark'); @@ -379,6 +379,6 @@ return [ 假如你漏掉 `\Yii::endProfile('block1')` 或者切换了 `\Yii::endProfile('block1')` 和 `\Yii::endProfile('block2')` 的 顺序,那么性能分析将不会工作。 -对于每个被分析的代码块,一个带有严重程度 `profile` 的日志消息被记录。你可以配置一个 [log target](#log-targets) 去收集这些 +对于每个被分析的代码块,一个带有严重程度 `profile` 的日志消息被记录。 +你可以配置一个 [log target](#log-targets) 去收集这些 消息,并且导出他们。[Yii debugger](tool-debugger.md) 有一个内建的性能分析面板能够展示分析结果。 - diff --git a/docs/guide-zh-CN/runtime-overview.md b/docs/guide-zh-CN/runtime-overview.md index a1c61c2..157abd2 100644 --- a/docs/guide-zh-CN/runtime-overview.md +++ b/docs/guide-zh-CN/runtime-overview.md @@ -6,7 +6,8 @@ 1. 用户提交指向 [入口脚本](structure-entry-scripts.md) `web/index.php` 的请求。 2. 入口脚本会加载 [配置数组](concept-configurations.md) 并创建一个 [应用](structure-applications.md) 实例用于处理该请求。 -3. 应用会通过 [request(请求)](runtime-requests.md) 应用组件解析被请求的 [路由](runtime-routing.md)。 +3. 应用会通过 [request(请求)](runtime-requests.md) + 应用组件解析被请求的 [路由](runtime-routing.md)。 4. 应用创建一个 [controller(控制器)](structure-controllers.md) 实例具体处理请求。 5. 控制器会创建一个 [action(动作)](structure-controllers.md) 实例并为该动作执行相关的 Filters(访问过滤器)。 6. 如果任何一个过滤器验证失败,该动作会被取消。 diff --git a/docs/guide-zh-CN/runtime-requests.md b/docs/guide-zh-CN/runtime-requests.md index bc863b5..d1f9371 100644 --- a/docs/guide-zh-CN/runtime-requests.md +++ b/docs/guide-zh-CN/runtime-requests.md @@ -38,10 +38,10 @@ $name = $request->post('name', ''); 直接访问 `$_GET` 和 `$_POST`。 这使你更容易编写测试用例,因为你可以伪造数据来创建一个模拟请求组件。 -当实现 [RESTful APIs](rest-quick-start.md) 接口的时候,你经常需要获取通过PUT, PATCH或者其他的 [request methods](#request-methods) +当实现 [RESTful APIs](rest-quick-start.md) 接口的时候,你经常需要获取通过PUT, +PATCH或者其他的 [request methods](#request-methods) 请求方法提交上来的参数。你可以通过调用 [[yii\web\Request::getBodyParam()]] 方法来获取这些参数。例如, - ```php $request = Yii::$app->request; @@ -118,16 +118,16 @@ if ($headers->has('User-Agent')) { /* 这是一个 User-Agent 头 */ } * [[yii\web\Request::acceptableLanguages|acceptableLanguages]]:返回用户可接受的语言。 返回的语言是按照他们的偏好层次来排序的。第一个参数代表最优先的语言。 -假如你的应用支持多语言,并且你想在终端用户最喜欢的语言中显示页面,那么你可以使用语言协商方法 [[yii\web\Request::getPreferredLanguage()]]。 -这个方法通过 [[yii\web\Request::acceptableLanguages|acceptableLanguages]] 在你的应用中所支持的语言列表里进行比较筛选,返回最适合的语言。 - +假如你的应用支持多语言,并且你想在终端用户最喜欢的语言中显示页面, +那么你可以使用语言协商方法 [[yii\web\Request::getPreferredLanguage()]]。 +这个方法通过 [[yii\web\Request::acceptableLanguages|acceptableLanguages]] +在你的应用中所支持的语言列表里进行比较筛选,返回最适合的语言。 - -> 提示:你也可以使用 [[yii\filters\ContentNegotiator|ContentNegotiator]] 过滤器进行动态确定哪些内容类型和语言应该在响应中使用。 +> 提示:你也可以使用 [[yii\filters\ContentNegotiator|ContentNegotiator]] + 过滤器进行动态确定哪些内容类型和语言应该在响应中使用。 这个过滤器实现了上面介绍的内容协商的属性和方法。 - ## 客户端信息 你可以通过 [[yii\web\Request::userHost|userHost]] 和 [[yii\web\Request::userIP|userIP]] 分别获取host name和客户机的IP地址, diff --git a/docs/guide-zh-CN/runtime-responses.md b/docs/guide-zh-CN/runtime-responses.md index 06d65dd..c85d353 100644 --- a/docs/guide-zh-CN/runtime-responses.md +++ b/docs/guide-zh-CN/runtime-responses.md @@ -2,13 +2,11 @@ ========= 当应用完成处理一个[请求](runtime-requests.md)后, 会生成一个[[yii\web\Response|response]]响应对象并发送给终端用户 -响应对象包含的信息有HTTP状态码,HTTP头和主体内容等, 网页应用开发的最终目的本质上就是根据不同的请求构建这些响应对象。 +响应对象包含的信息有HTTP状态码,HTTP头和主体内容等, +网页应用开发的最终目的本质上就是根据不同的请求构建这些响应对象。 -<<<<<<< HEAD -在大多是情况下主要处理继承自 [[yii\web\Response]] 的 `response` [应用组件](structure-application-components.md), -======= -在大多数情况下主要处理继承自 [[yii\web\Response]] 的 `response` [应用组件](structure-application-components.md), ->>>>>>> yiichina/master +在大多数情况下主要处理继承自 [[yii\web\Response]] 的 +`response` [应用组件](structure-application-components.md), 尽管如此,Yii也允许你创建你自己的响应对象并发送给终端用户,这方面后续会阐述。 在本节,将会描述如何构建响应和发送给终端用户。 @@ -25,7 +23,8 @@ Yii::$app->response->statusCode = 200; ``` -尽管如此,大多数情况下不需要明确设置状态码,因为 [[yii\web\Response::statusCode]] 状态码默认为200, +尽管如此,大多数情况下不需要明确设置状态码, +因为 [[yii\web\Response::statusCode]] 状态码默认为200, 如果需要指定请求失败,可抛出对应的HTTP异常,如下所示: ```php @@ -33,7 +32,8 @@ throw new \yii\web\NotFoundHttpException; ``` 当[错误处理器](runtime-handling-errors.md) 捕获到一个异常,会从异常中提取状态码并赋值到响应, -对于上述的 [[yii\web\NotFoundHttpException]] 对应HTTP 404状态码,以下为Yii预定义的HTTP异常: +对于上述的 [[yii\web\NotFoundHttpException]] 对应HTTP 404状态码, +以下为Yii预定义的HTTP异常: * [[yii\web\BadRequestHttpException]]: status code 400. * [[yii\web\ConflictHttpException]]: status code 409. @@ -47,7 +47,8 @@ throw new \yii\web\NotFoundHttpException; * [[yii\web\UnauthorizedHttpException]]: status code 401. * [[yii\web\UnsupportedMediaTypeHttpException]]: status code 415. -如果想抛出的异常不在如上列表中,可创建一个[[yii\web\HttpException]]异常,带上状态码抛出,如下: +如果想抛出的异常不在如上列表中,可创建一个[[yii\web\HttpException]]异常, +带上状态码抛出,如下: ```php throw new \yii\web\HttpException(402); @@ -56,7 +57,8 @@ throw new \yii\web\HttpException(402); ## HTTP 头部 -可在 `response` 组件中操控[[yii\web\Response::headers|header collection]]来发送HTTP头部信息,例如: +可在 `response` 组件中操控[[yii\web\Response::headers|header collection]]来发送HTTP头部信息, +例如: ```php $headers = Yii::$app->response->headers; @@ -71,14 +73,16 @@ $headers->set('Pragma', 'no-cache'); $values = $headers->remove('Pragma'); ``` -> Info: 头名称是大小写敏感的,在[[yii\web\Response::send()]]方法调用前新注册的头信息并不会发送给用户。 +> Info: 头名称是大小写敏感的,在[[yii\web\Response::send()]] + 方法调用前新注册的头信息并不会发送给用户。 ## 响应主体 大多是响应应有一个主体存放你想要显示给终端用户的内容。 -如果已有格式化好的主体字符串,可赋值到响应的[[yii\web\Response::content]]属性,例如: +如果已有格式化好的主体字符串,可赋值到响应的[[yii\web\Response::content]]属性, +例如: ```php Yii::$app->response->content = 'hello world!'; @@ -101,8 +105,10 @@ Yii支持以下可直接使用的格式,每个实现了[[yii\web\ResponseForma * [[yii\web\Response::FORMAT_XML|XML]]: 通过 [[yii\web\XmlResponseFormatter]]来实现. * [[yii\web\Response::FORMAT_JSON|JSON]]: 通过 [[yii\web\JsonResponseFormatter]]来实现. * [[yii\web\Response::FORMAT_JSONP|JSONP]]: 通过 [[yii\web\JsonResponseFormatter]]来实现. +* [[yii\web\Response::FORMAT_RAW|RAW]]: use this format if you want to send the response directly without applying any formatting. -上述响应主体可明确地被设置,但是在大多数情况下是通过 [操作](structure-controllers.md) 方法的返回值隐式地设置,常用场景如下所示: +上述响应主体可明确地被设置,但是在大多数情况下是通过 [操作](structure-controllers.md) 方法的返回值隐式地设置, +常用场景如下所示: ```php public function actionIndex() @@ -111,9 +117,11 @@ public function actionIndex() } ``` -上述的 `index` 操作返回 `index` 视图渲染结果,返回值会被 `response` 组件格式化后发送给终端用户。 +上述的 `index` 操作返回 `index` 视图渲染结果, +返回值会被 `response` 组件格式化后发送给终端用户。 -因为响应格式默认为[[yii\web\Response::FORMAT_HTML|HTML]], 只需要在操作方法中返回一个字符串, +因为响应格式默认为[[yii\web\Response::FORMAT_HTML|HTML]], +只需要在操作方法中返回一个字符串, 如果想使用其他响应格式,应在返回数据前先设置格式,例如: ```php @@ -127,7 +135,8 @@ public function actionInfo() } ``` -如上所述,触雷使用默认的 `response` 应用组件,也可创建自己的响应对象并发送给终端用户,可在操作方法中返回该响应对象,如下所示: +如上所述,触雷使用默认的 `response` 应用组件,也可创建自己的响应对象并发送给终端用户, +可在操作方法中返回该响应对象,如下所示: ```php public function actionInfo() @@ -144,15 +153,18 @@ public function actionInfo() ``` > Note: 如果创建你自己的响应对象,将不能在应用配置中设置 `response` 组件,尽管如此, - 可使用 [依赖注入](concept-di-container.md) 应用通用配置到你新的响应对象。 + 可使用 [依赖注入](concept-di-container.md) + 应用通用配置到你新的响应对象。 ## 浏览器跳转 -浏览器跳转依赖于发送一个`Location` HTTP 头,因为该功能通常被使用,Yii提供对它提供了特别的支持。 +浏览器跳转依赖于发送一个`Location` HTTP 头, +因为该功能通常被使用,Yii提供对它提供了特别的支持。 可调用[[yii\web\Response::redirect()]] 方法将用户浏览器跳转到一个URL地址,该方法设置合适的 -带指定URL的 `Location` 头并返回它自己为响应对象,在操作的方法中,可调用缩写版[[yii\web\Controller::redirect()]],例如: +带指定URL的 `Location` 头并返回它自己为响应对象, +在操作的方法中,可调用缩写版[[yii\web\Controller::redirect()]],例如: ```php public function actionOld() @@ -161,7 +173,8 @@ public function actionOld() } ``` -在如上代码中,操作的方法返回`redirect()` 方法的结果,如前所述,操作的方法返回的响应对象会被当总响应发送给终端用户。 +在如上代码中,操作的方法返回`redirect()` 方法的结果,如前所述, +操作的方法返回的响应对象会被当总响应发送给终端用户。 除了操作方法外,可直接调用[[yii\web\Response::redirect()]] 再调用 [[yii\web\Response::send()]] 方法来确保没有其他内容追加到响应中。 @@ -171,26 +184,31 @@ public function actionOld() ``` > Info: [[yii\web\Response::redirect()]] 方法默认会设置响应状态码为302,该状态码会告诉浏览器请求的资源 - *临时* 放在另一个URI地址上,可传递一个301状态码告知浏览器请求的资源已经 *永久* 重定向到新的URId地址。 + *临时* 放在另一个URI地址上, + 可传递一个301状态码告知浏览器请求的资源已经 *永久* 重定向到新的URId地址。 -如果当前请求为AJAX 请求,发送一个 `Location` 头不会自动使浏览器跳转,为解决这个问题, +如果当前请求为AJAX 请求, +发送一个 `Location` 头不会自动使浏览器跳转,为解决这个问题, [[yii\web\Response::redirect()]] 方法设置一个值为要跳转的URL的`X-Redirect` 头, 在客户端可编写JavaScript 代码读取该头部值然后让浏览器跳转对应的URL。 > Info: Yii 配备了一个`yii.js` JavaScript 文件提供常用JavaScript功能,包括基于`X-Redirect`头的浏览器跳转, - 因此,如果你使用该JavaScript 文件(通过[[yii\web\YiiAsset]] 资源包注册),就不需要编写AJAX跳转的代码。 + 因此,如果你使用该JavaScript 文件(通过[[yii\web\YiiAsset]] 资源包注册), + 就不需要编写AJAX跳转的代码。 ## 发送文件 -和浏览器跳转类似,文件发送是另一个依赖指定HTTP头的功能,Yii提供方法集合来支持各种文件发送需求,它们对HTTP头都有内置的支持。 +和浏览器跳转类似,文件发送是另一个依赖指定HTTP头的功能, +Yii提供方法集合来支持各种文件发送需求,它们对HTTP头都有内置的支持。 * [[yii\web\Response::sendFile()]]: 发送一个已存在的文件到客户端 * [[yii\web\Response::sendContentAsFile()]]: 发送一个文本字符串作为文件到客户端 * [[yii\web\Response::sendStreamAsFile()]]: 发送一个已存在的文件流作为文件到客户端 这些方法都将响应对象作为返回值,如果要发送的文件非常大,应考虑使用 -[[yii\web\Response::sendStreamAsFile()]] 因为它更节约内存,以下示例显示在控制器操作中如何发送文件: +[[yii\web\Response::sendStreamAsFile()]] 因为它更节约内存, +以下示例显示在控制器操作中如何发送文件: ```php public function actionDownload() @@ -199,14 +217,17 @@ public function actionDownload() } ``` -如果不是在操作方法中调用文件发送方法,在后面还应调用 [[yii\web\Response::send()]] 没有其他内容追加到响应中。 +如果不是在操作方法中调用文件发送方法, +在后面还应调用 [[yii\web\Response::send()]] 没有其他内容追加到响应中。 ```php \Yii::$app->response->sendFile('path/to/file.txt')->send(); ``` -一些浏览器提供特殊的名为*X-Sendfile*的文件发送功能,原理为将请求跳转到服务器上的文件, -Web应用可在服务器发送文件前结束,为使用该功能,可调用[[yii\web\Response::xSendFile()]], +一些浏览器提供特殊的名为*X-Sendfile*的文件发送功能, +原理为将请求跳转到服务器上的文件, +Web应用可在服务器发送文件前结束,为使用该功能, +可调用[[yii\web\Response::xSendFile()]], 如下简要列出一些常用Web服务器如何启用`X-Sendfile` 功能: - Apache: [X-Sendfile](http://tn123.org/mod_xsendfile) @@ -218,7 +239,8 @@ Web应用可在服务器发送文件前结束,为使用该功能,可调用[[ ## 发送响应 -在[[yii\web\Response::send()]] 方法调用前响应中的内容不会发送给用户,该方法默认在[[yii\base\Application::run()]] +在[[yii\web\Response::send()]] 方法调用前响应中的内容不会发送给用户, +该方法默认在[[yii\base\Application::run()]] 结尾自动调用,尽管如此,可以明确调用该方法强制立即发送响应。 [[yii\web\Response::send()]] 方法使用以下步骤来发送响应: @@ -234,4 +256,5 @@ Web应用可在服务器发送文件前结束,为使用该功能,可调用[[ 一旦[[yii\web\Response::send()]] 方法被执行后,其他地方调用该方法会被忽略, 这意味着一旦响应发出后,就不能再追加其他内容。 -如你所见[[yii\web\Response::send()]] 触发了几个实用的事件,通过响应这些事件可调整或包装响应。 +如你所见[[yii\web\Response::send()]] 触发了几个实用的事件, +通过响应这些事件可调整或包装响应。 diff --git a/docs/guide-zh-CN/runtime-routing.md b/docs/guide-zh-CN/runtime-routing.md index d8302a8..cd615f3 100644 --- a/docs/guide-zh-CN/runtime-routing.md +++ b/docs/guide-zh-CN/runtime-routing.md @@ -2,26 +2,100 @@ ======= 当[入口脚本](structure-entry-scripts.md)在调用 [[yii\web\Application::run()|run()]] -方法时,它进行的第一个操作就是解析输入的请求,然后实例化对应的[控制器操作](structure-controllers.md)处理这个请求。该过程就被称为**引导路由(routing)**。(译注:中文里既是动词也是名词) +方法时,它进行的第一个操作就是解析输入的请求,然后实例化对应的[控制器操作](structure-controllers.md)处理这个请求。 +该过程就被称为**引导路由(routing)**。(译注:中文里既是动词也是名词) +The reverse process of routing is called *URL creation*, which creates a URL from a given route +and the associated query parameters. When the created URL is later requested, the routing process can resolve it +back into the original route and query parameters. + +The central piece responsible for routing and URL creation is the [[yii\web\UrlManager|URL manager]], +which is registered as the `urlManager` [application component](structure-application-components.md). The [[yii\web\UrlManager|URL manager]] +provides the [[yii\web\UrlManager::parseRequest()|parseRequest()]] method to parse an incoming request into +a route and the associated query parameters and the [[yii\web\UrlManager::createUrl()|createUrl()]] method to +create a URL from a given route and its associated query parameters. + +By configuring the `urlManager` component in the application configuration, you can let your application +recognize arbitrary URL formats without modifying your existing application code. For example, you can +use the following code to create a URL for the `post/view` action: + +```php +use yii\helpers\Url; + +// Url::to() calls UrlManager::createUrl() to create a URL +$url = Url::to(['post/view', 'id' => 100]); +``` + +Depending on the `urlManager` configuration, the created URL may look like one of the following (or other format). +And if the created URL is requested later, it will still be parsed back into the original route and query parameter value. + +``` +/index.php?r=post%2Fview&id=100 +/index.php/post/100 +/posts/100 +``` + + +## URL Formats -## 解析路由 +The [[yii\web\UrlManager|URL manager]] supports two URL formats: the default URL format and the pretty URL format. -路由引导的第一步,是把传入请求解析为一个路由。如我们在 [控制器(Controllers)](structure-controllers.md#routes) -章节中所描述的那样,路由是一个用于定位控制器操作的地址。这个过程通过 `request` 应用组件的 [[yii\web\Request::resolve()|resolve()]] -方法实现,该方法会调用 [URL 管理器](runtime-url-handling.md) 进行实质上的请求解析工作。 +The default URL format uses a query parameter named `r` to represent the route and normal query parameters +to represent the query parameters associated with the route. For example, the URL `/index.php?r=post/view&id=100` represents +the route `post/view` and the `id` query parameter 100. The default URL format does not require any configuration of +the [[yii\web\UrlManager|URL manager]] and works in any Web server setup. + +The pretty URL format uses the extra path following the entry script name to represent the route and the associated +query parameters. For example, the extra path in the URL `/index.php/post/100` is `/post/100` which may represent +the route `post/view` and the `id` query parameter 100 with a proper [[yii\web\UrlManager::rules|URL rule]]. To use +the pretty URL format, you will need to design a set of [[yii\web\UrlManager::rules|URL rules]] according to the actual +requirement about how the URLs should look like. -默认情况下,传入请求会包含一个名为 `r` 的 `GET` 参数,它的值即被视为路由。但是如果启用 -[[yii\web\UrlManager::enablePrettyUrl|美化 URL 功能]],那么在确定请求的路由时,就会进行更多处理。具体的细节请参考 -[URL 的解析与生成](runtime-url-handling.md) 章节。 +You may switch between the two URL formats by toggling the [[yii\web\UrlManager::enablePrettyUrl|enablePrettyUrl]] +property of the [[yii\web\UrlManager|URL manager]] without changing any other application code. + + +## Routing + +Routing involves two steps. In the first step, the incoming request is parsed into a route and the associated +query parameters. In the second step, a [controller action](structure-controllers.md#actions) corresponding to the parsed route +is created to handle the request. + +When using the default URL format, parsing a request into a route is as simple as getting the value of a `GET` +query parameter named `r`. + +When using the pretty URL format, the [[yii\web\UrlManager|URL manager]] will examine the registered +[[yii\web\UrlManager::rules|URL rules]] to find matching one that can resolve the request into a route. +If such a rule cannot be found, a [[yii\web\NotFoundHttpException]] exception will be thrown. -假使某路由最终实在无法被确定,那么 `request` 组件会抛出 [[yii\web\NotFoundHttpException]] 异常(译注:大名鼎鼎的 404)。 +Once the request is parsed into a route, it is time to create the controller action identified by the route. +The route is broken down into multiple parts by the slashes in it. For example, `site/index` will be +broken into `site` and `index`. Each part is an ID which may refer to a module, a controller or an action. +Starting from the first part in the route, the application takes the following steps to create modules (if any), +controller and action: + +1. Set the application as the current module. +2. Check if the [[yii\base\Module::controllerMap|controller map]] of the current module contains the current ID. + If so, a controller object will be created according to the controller configuration found in the map, + and Step 5 will be taken to handle the rest part of the route. +3. Check if the ID refers to a module listed in the [[yii\base\Module::modules|modules]] property of + the current module. If so, a module is created according to the configuration found in the module list, + and Step 2 will be taken to handle the next part of the route under the context of the newly created module. +4. Treat the ID as a [controller ID](structure-controllers.md#controller-ids) and create a controller object. Do the next step with the rest part of + the route. +5. The controller looks for the current ID in its [[yii\base\Controller::actions()|action map]]. If found, + it creates an action according to the configuration found in the map. Otherwise, the controller will + attempt to create an inline action which is defined by an action method corresponding to the current [action ID](structure-controllers.md#action-ids). + +Among the above steps, if any error occurs, a [[yii\web\NotFoundHttpException]] will be thrown, indicating +the failure of the routing process. ### 缺省路由 如果传入请求并没有提供一个具体的路由,(一般这种情况多为于对首页的请求)此时就会启用由 -[[yii\web\Application::defaultRoute]] 属性所指定的缺省路由。该属性的默认值为 `site/index`,它指向 `site` 控制器的 `index` +[[yii\web\Application::defaultRoute]] 属性所指定的缺省路由。 +该属性的默认值为 `site/index`,它指向 `site` 控制器的 `index` 操作。你可以像这样在应用配置中调整该属性的值: ```php @@ -45,25 +119,529 @@ return [ ]; ``` -`catchAll` 属性需要传入一个数组做参数,该数组的第一个元素为路由,剩下的元素会(以名值对的形式)指定绑定于该操作的各个参数。 +With the above configuration, the `site/offline` action will be used to handle all incoming requests. + +The `catchAll` property should take an array whose first element specifies a route, and +the rest of the elements (name-value pairs) specify the parameters to be [bound to the action](structure-controllers.md#action-parameters). + +> Info: Debug panel on development environment will not work when this property is enabled + + +## Creating URLs + +Yii provides a helper method [[yii\helpers\Url::to()]] to create various kinds of URLs from given routes and +their associated query parameters. For example, + +```php +use yii\helpers\Url; + +// creates a URL to a route: /index.php?r=post%2Findex +echo Url::to(['post/index']); + +// creates a URL to a route with parameters: /index.php?r=post%2Fview&id=100 +echo Url::to(['post/view', 'id' => 100]); + +// creates an anchored URL: /index.php?r=post%2Fview&id=100#content +echo Url::to(['post/view', 'id' => 100, '#' => 'content']); + +// creates an absolute URL: http://www.example.com/index.php?r=post%2Findex +echo Url::to(['post/index'], true); + +// creates an absolute URL using the https scheme: https://www.example.com/index.php?r=post%2Findex +echo Url::to(['post/index'], 'https'); +``` + +Note that in the above example, we assume the default URL format is being used. If the pretty URL format is enabled, +the created URLs will be different, according to the [[yii\web\UrlManager::rules|URL rules]] in use. + +The route passed to the [[yii\helpers\Url::to()]] method is context sensitive. It can be either a *relative* route +or an *absolute* route which will be normalized according to the following rules: + +- If the route is an empty string, the currently requested [[yii\web\Controller::route|route]] will be used; +- If the route contains no slashes at all, it is considered to be an action ID of the current controller + and will be prepended with the [[\yii\web\Controller::uniqueId|uniqueId]] value of the current controller; +- If the route has no leading slash, it is considered to be a route relative to the current module and + will be prepended with the [[\yii\base\Module::uniqueId|uniqueId]] value of the current module. + +Starting from version 2.0.2, you may specify a route in terms of an [alias](concept-aliases.md). If this is the case, +the alias will first be converted into the actual route which will then be turned into an absolute route according +to the above rules. + +For example, assume the current module is `admin` and the current controller is `post`, + +```php +use yii\helpers\Url; + +// currently requested route: /index.php?r=admin%2Fpost%2Findex +echo Url::to(['']); + +// a relative route with action ID only: /index.php?r=admin%2Fpost%2Findex +echo Url::to(['index']); + +// a relative route: /index.php?r=admin%2Fpost%2Findex +echo Url::to(['post/index']); + +// an absolute route: /index.php?r=post%2Findex +echo Url::to(['/post/index']); + +// /index.php?r=post%2Findex assume the alias "@posts" is defined as "/post/index" +echo Url::to(['@posts']); +``` + +The [[yii\helpers\Url::to()]] method is implemented by calling the [[yii\web\UrlManager::createUrl()|createUrl()]] +and [[yii\web\UrlManager::createAbsoluteUrl()|createAbsoluteUrl()]] methods of the [[yii\web\UrlManager|URL manager]]. +In the next few subsections, we will explain how to configure the [[yii\web\UrlManager|URL manager]] to customize +the format of the created URLs. + +The [[yii\helpers\Url::to()]] method also supports creating URLs that are NOT related with particular routes. +Instead of passing an array as its first parameter, you should pass a string in this case. For example, + +```php +use yii\helpers\Url; + +// currently requested URL: /index.php?r=admin%2Fpost%2Findex +echo Url::to(); + +// an aliased URL: http://example.com +Yii::setAlias('@example', 'http://example.com/'); +echo Url::to('@example'); + +// an absolute URL: http://example.com/images/logo.gif +echo Url::to('/images/logo.gif', true); +``` + +Besides the `to()` method, the [[yii\helpers\Url]] helper class also provides several other convenient URL creation +methods. For example, + +```php +use yii\helpers\Url; + +// home page URL: /index.php?r=site%2Findex +echo Url::home(); + +// the base URL, useful if the application is deployed in a sub-folder of the Web root +echo Url::base(); + +// the canonical URL of the currently requested URL +// see https://en.wikipedia.org/wiki/Canonical_link_element +echo Url::canonical(); + +// remember the currently requested URL and retrieve it back in later requests +Url::remember(); +echo Url::previous(); +``` + + +## Using Pretty URLs + +To use pretty URLs, configure the `urlManager` component in the application configuration like the following: + +```php +[ + 'components' => [ + 'urlManager' => [ + 'enablePrettyUrl' => true, + 'showScriptName' => false, + 'enableStrictParsing' => false, + 'rules' => [ + // ... + ], + ], + ], +] +``` + +The [[yii\web\UrlManager::enablePrettyUrl|enablePrettyUrl]] property is mandatory as it toggles the pretty URL format. +The rest of the properties are optional. However, their configuration shown above is most commonly used. + +* [[yii\web\UrlManager::showScriptName|showScriptName]]: this property determines whether the entry script + should be included in the created URLs. For example, instead of creating a URL `/index.php/post/100`, + by setting this property to be false, a URL `/post/100` will be generated. +* [[yii\web\UrlManager::enableStrictParsing|enableStrictParsing]]: this property determines whether to enable + strict request parsing. If strict parsing is enabled, the incoming requested URL must match at least one of + the [[yii\web\UrlManager::rules|rules]] in order to be treated as a valid request, or a [[yii\web\NotFoundHttpException]] + will be thrown. If strict parsing is disabled, when none of the [[yii\web\UrlManager::rules|rules]] matches + the requested URL, the path info part of the URL will be treated as the requested route. +* [[yii\web\UrlManager::rules|rules]]: this property contains a list of rules specifying how to parse and create + URLs. It is the main property that you should work with in order to create URLs whose format satisfies your + particular application requirement. + +> Note: In order to hide the entry script name in the created URLs, besides setting + [[yii\web\UrlManager::showScriptName|showScriptName]] to be false, you may also need to configure your Web server + so that it can correctly identify which PHP script should be executed when a requested URL does not explicitly + specify one. If you are using Apache Web server, you may refer to the recommended configuration as described in the + [Installation](start-installation.md#recommended-apache-configuration) section. + + +### URL Rules + +A URL rule is an instance of [[yii\web\UrlRule]] or its child class. Each URL rule consists of a pattern used +for matching the path info part of URLs, a route, and a few query parameters. A URL rule can be used to parse a request +if its pattern matches the requested URL. A URL rule can be used to create a URL if its route and query parameter +names match those that are given. + +When the pretty URL format is enabled, the [[yii\web\UrlManager|URL manager]] uses the URL rules declared in its +[[yii\web\UrlManager::rules|rules]] property to parse incoming requests and create URLs. In particular, +to parse an incoming request, the [[yii\web\UrlManager|URL manager]] examines the rules in the order they are +declared and looks for the *first* rule that matches the requested URL. The matching rule is then used to +parse the URL into a route and its associated parameters. Similarly, to create a URL, the [[yii\web\UrlManager|URL manager]] +looks for the first rule that matches the given route and parameters and uses that to create a URL. + +You can configure [[yii\web\UrlManager::rules]] as an array with keys being the patterns and values the corresponding +routes. Each pattern-route pair constructs a URL rule. For example, the following [[yii\web\UrlManager::rules|rules]] +configuration declares two URL rules. The first rule matches a URL `posts` and maps it into the route `post/index`. +The second rule matches a URL matching the regular expression `post/(\d+)` and maps it into the route `post/view` and +a parameter named `id`. + +```php +[ + 'posts' => 'post/index', + 'post/' => 'post/view', +] +``` + +> Info: The pattern in a rule is used to match the path info part of a URL. For example, the path info of + `/index.php/post/100?source=ad` is `post/100` (the leading and ending slashes are ignored) which matches + the pattern `post/(\d+)`. + +Besides declaring URL rules as pattern-route pairs, you may also declare them as configuration arrays. Each configuration +array is used to configure a single URL rule object. This is often needed when you want to configure other +properties of a URL rule. For example, + +```php +[ + // ...other url rules... + + [ + 'pattern' => 'posts', + 'route' => 'post/index', + 'suffix' => '.json', + ], +] +``` + +By default if you do not specify the `class` option for a rule configuration, it will take the default +class [[yii\web\UrlRule]]. + -当设置了 `catchAll` 属性时,它会替换掉所有从输入的请求中解析出来的路由。如果是上文的这种设置,用于处理所有传入请求的操作都会是相同的 `site/offline`。 +### Named Parameters +A URL rule can be associated with a few named query parameters which are specified in the pattern in the format +of ``, where `ParamName` specifies the parameter name and `RegExp` is an optional regular +expression used to match parameter values. If `RegExp` is not specified, it means the parameter value should be +a string without any slash. + +> Note: You can only specify regular expressions for parameters. The rest part of a pattern is considered as plain text. + +When a rule is used to parse a URL, it will fill the associated parameters with values matching the corresponding +parts of the URL, and these parameters will be made available in `$_GET` later by the `request` application component. +When the rule is used to create a URL, it will take the values of the provided parameters and insert them at the +places where the parameters are declared. + +Let's use some examples to illustrate how named parameters work. Assume we have declared the following three URL rules: + +```php +[ + 'posts//' => 'post/index', + 'posts' => 'post/index', + 'post/' => 'post/view', +] +``` + +When the rules are used to parse URLs: + +- `/index.php/posts` is parsed into the route `post/index` using the second rule; +- `/index.php/posts/2014/php` is parsed into the route `post/index`, the `year` parameter whose value is 2014 + and the `category` parameter whose value is `php` using the first rule; +- `/index.php/post/100` is parsed into the route `post/view` and the `id` parameter whose value is 100 using + the third rule; +- `/index.php/posts/php` will cause a [[yii\web\NotFoundHttpException]] when [[yii\web\UrlManager::enableStrictParsing]] + is `true`, because it matches none of the patterns. If [[yii\web\UrlManager::enableStrictParsing]] is `false` (the + default value), the path info part `posts/php` will be returned as the route. + +And when the rules are used to create URLs: + +- `Url::to(['post/index'])` creates `/index.php/posts` using the second rule; +- `Url::to(['post/index', 'year' => 2014, 'category' => 'php'])` creates `/index.php/posts/2014/php` using the first rule; +- `Url::to(['post/view', 'id' => 100])` creates `/index.php/post/100` using the third rule; +- `Url::to(['post/view', 'id' => 100, 'source' => 'ad'])` creates `/index.php/post/100?source=ad` using the third rule. + Because the `source` parameter is not specified in the rule, it is appended as a query parameter in the created URL. +- `Url::to(['post/index', 'category' => 'php'])` creates `/index.php/post/index?category=php` using none of the rules. + Note that since none of the rules applies, the URL is created by simply appending the route as the path info + and all parameters as the query string part. + + +### Parameterizing Routes + +You can embed parameter names in the route of a URL rule. This allows a URL rule to be used for matching multiple +routes. For example, the following rules embed `controller` and `action` parameters in the routes. + +```php +[ + '//' => '/', + '/' => '/view', + 's' => '/index', +] +``` + +To parse a URL `/index.php/comment/100/create`, the first rule will apply, which sets the `controller` parameter to +be `comment` and `action` parameter to be `create`. The route `/` is thus resolved as `comment/create`. + +Similarly, to create a URL for the route `comment/index`, the third rule will apply, which creates a URL `/index.php/comments`. + +> Info: By parameterizing routes, it is possible to greatly reduce the number of URL rules, which can significantly + improve the performance of [[yii\web\UrlManager|URL manager]]. + +By default, all parameters declared in a rule are required. If a requested URL does not contain a particular parameter, +or if a URL is being created without a particular parameter, the rule will not apply. To make some of the parameters +optional, you can configure the [[yii\web\UrlRule::defaults|defaults]] property of a rule. Parameters listed in this +property are optional and will take the specified values when they are not provided. + +In the following rule declaration, the `page` and `tag` parameters are both optional and will take the value of 1 and +empty string, respectively, when they are not provided. + +```php +[ + // ...other rules... + [ + 'pattern' => 'posts//', + 'route' => 'post/index', + 'defaults' => ['page' => 1, 'tag' => ''], + ], +] +``` + +The above rule can be used to parse or create any of the following URLs: + +* `/index.php/posts`: `page` is 1, `tag` is ''. +* `/index.php/posts/2`: `page` is 2, `tag` is ''. +* `/index.php/posts/2/news`: `page` is 2, `tag` is `'news'`. +* `/index.php/posts/news`: `page` is 1, `tag` is `'news'`. + +Without using optional parameters, you would have to create 4 rules to achieve the same result. + + +### Rules with Server Names + +It is possible to include Web server names in the patterns of URL rules. This is mainly useful when your application +should behave differently for different Web server names. For example, the following rules will parse the URL +`http://admin.example.com/login` into the route `admin/user/login` and `http://www.example.com/login` into `site/login`. + +```php +[ + 'http://admin.example.com/login' => 'admin/user/login', + 'http://www.example.com/login' => 'site/login', +] +``` + +You can also embed parameters in the server names to extract dynamic information from them. For example, the following rule +will parse the URL `http://en.example.com/posts` into the route `post/index` and the parameter `language=en`. + +```php +[ + 'http://.example.com/posts' => 'post/index', +] +``` + +> Note: Rules with server names should NOT include the subfolder of the entry script in their patterns. For example, if the application is under `http://www.example.com/sandbox/blog`, then you should use the pattern + `http://www.example.com/posts` instead of `http://www.example.com/sandbox/blog/posts`. This will allow your application + to be deployed under any directory without the need to change your application code. + + +### URL Suffixes + +You may want to add suffixes to the URLs for various purposes. For example, you may add `.html` to the URLs so that they +look like URLs for static HTML pages; you may also add `.json` to the URLs to indicate the expected content type +of the response. You can achieve this goal by configuring the [[yii\web\UrlManager::suffix]] property like +the following in the application configuration: + +```php +[ + 'components' => [ + 'urlManager' => [ + 'enablePrettyUrl' => true, + 'showScriptName' => false, + 'enableStrictParsing' => true, + 'suffix' => '.html', + 'rules' => [ + // ... + ], + ], + ], +] +``` + +The above configuration will allow the [[yii\web\UrlManager|URL manager]] to recognize requested URLs and also create +URLs with `.html` as their suffix. + +> Tip: You may set `/` as the URL suffix so that the URLs all end with a slash. + +> Note: When you configure a URL suffix, if a requested URL does not have the suffix, it will be considered as + an unrecognized URL. This is a recommended practice for SEO (search engine optimization). + +Sometimes you may want to use different suffixes for different URLs. This can be achieved by configuring the +[[yii\web\UrlRule::suffix|suffix]] property of individual URL rules. When a URL rule has this property set, it will +override the suffix setting at the [[yii\web\UrlManager|URL manager]] level. For example, the following configuration +contains a customized URL rule which uses `.json` as its suffix instead of the global one `.html`. + +```php +[ + 'components' => [ + 'urlManager' => [ + 'enablePrettyUrl' => true, + 'showScriptName' => false, + 'enableStrictParsing' => true, + 'suffix' => '.html', + 'rules' => [ + // ... + [ + 'pattern' => 'posts', + 'route' => 'post/index', + 'suffix' => '.json', + ], + ], + ], + ], +] +``` + + +### HTTP Methods + +When implementing RESTful APIs, it is commonly needed that the same URL be parsed into different routes according to +the HTTP methods being used. This can be easily achieved by prefixing the supported HTTP methods to the patterns of +the rules. If a rule supports multiple HTTP methods, separate the method names with commas. For example, the following +rules have the same pattern `post/` with different HTTP method support. A request for `PUT post/100` will +be parsed into `post/create`, while a request for `GET post/100` will be parsed into `post/view`. + +```php +[ + 'PUT,POST post/' => 'post/create', + 'DELETE post/' => 'post/delete', + 'post/' => 'post/view', +] +``` + +> Note: If a URL rule contains HTTP method(s) in its pattern, the rule will only be used for parsing purpose. + It will be skipped when the [[yii\web\UrlManager|URL manager]] is called to create URLs. + +> Tip: To simplify the routing of RESTful APIs, Yii provides a special URL rule class [[yii\rest\UrlRule]] + which is very efficient and supports some fancy features such as automatic pluralization of controller IDs. + For more details, please refer to the [Routing](rest-routing.md) section about developing RESTful APIs. + + +### Customizing Rules + +In the previous examples, URL rules are mainly declared in terms of pattern-route pairs. This is a commonly used +shortcut format. In certain scenarios, you may want to customize a URL rule by configuring its other properties, such +as [[yii\web\UrlRule::suffix]]. This can be done by using a full configuration array to specify a rule. The following +example is extracted from the [URL Suffixes](#url-suffixes) subsection, + +```php +[ + // ...other url rules... + + [ + 'pattern' => 'posts', + 'route' => 'post/index', + 'suffix' => '.json', + ], +] +``` + +> Info: By default if you do not specify the `class` option for a rule configuration, it will take the default + class [[yii\web\UrlRule]]. + + +### Adding Rules Dynamically + +URL rules can be dynamically added to the [[yii\web\UrlManager|URL manager]]. This is often needed by redistributable +[modules](structure-modules.md) which want to manage their own URL rules. In order for the dynamically added rules +to take effect during the routing process, you should add them during the [bootstrapping](runtime-bootstrapping.md) +stage. For modules, this means they should implement [[yii\base\BootstrapInterface]] and add the rules in the +[[yii\base\BootstrapInterface::bootstrap()|bootstrap()]] method like the following: + +```php +public function bootstrap($app) +{ + $app->getUrlManager()->addRules([ + // rule declarations here + ], false); +} +``` + +Note that you should also list these modules in [[yii\web\Application::bootstrap]] so that they can participate the +[bootstrapping](runtime-bootstrapping.md) process. + + +### Creating Rule Classes + +Despite the fact that the default [[yii\web\UrlRule]] class is flexible enough for the majority of projects, there +are situations when you have to create your own rule classes. For example, in a car dealer Web site, you may want +to support the URL format like `/Manufacturer/Model`, where both `Manufacturer` and `Model` must match some data +stored in a database table. The default rule class will not work here because it relies on statically declared patterns. + +We can create the following URL rule class to solve this problem. + +```php +namespace app\components; + +use yii\web\UrlRuleInterface; +use yii\base\Object; + +class CarUrlRule extends Object implements UrlRuleInterface +{ + + public function createUrl($manager, $route, $params) + { + if ($route === 'car/index') { + if (isset($params['manufacturer'], $params['model'])) { + return $params['manufacturer'] . '/' . $params['model']; + } elseif (isset($params['manufacturer'])) { + return $params['manufacturer']; + } + } + return false; // this rule does not apply + } + + public function parseRequest($manager, $request) + { + $pathInfo = $request->getPathInfo(); + if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches)) { + // check $matches[1] and $matches[3] to see + // if they match a manufacturer and a model in the database + // If so, set $params['manufacturer'] and/or $params['model'] + // and return ['car/index', $params] + } + return false; // this rule does not apply + } +} +``` + +And use the new rule class in the [[yii\web\UrlManager::rules]] configuration: + +```php +[ + // ...other rules... + + [ + 'class' => 'app\components\CarUrlRule', + // ...configure other properties... + ], +] +``` -## 创建操作 -一旦请求路由被确定了,紧接着的步骤就是创建一个“操作(action)”对象,用以响应该路由。 +## Performance Consideration -路由可以用里面的斜杠分割成多个组成片段,举个栗子,`site/index` 可以分解为 `site` 和 `index` -两部分。每个片段都是指向某一模块(Module)、控制器(Controller)或操作(action)的 ID。 +When developing a complex Web application, it is important to optimize URL rules so that it takes less time to parse +requests and create URLs. -从路由的首个片段开始,应用会经过以下流程依次创建模块(如果有),控制器,以及操作: +By using parameterized routes, you may reduce the number of URL rules, which can significantly improve performance. -1. 设置应用主体为当前模块。 -2. 检查当前模块的 [[yii\base\Module::controllerMap|controller map(控制器映射表)]] 是否包含当前 ID。如果是,会根据该表中的配置创建一个控制器对象,然后跳到步骤五执行该路由的后续片段。 -3. 检查该 ID 是否指向当前模块中 [[yii\base\Module::modules|modules]] 属性里的模块列表中的一个模块。如果是,会根据该模块表中的配置创建一个模块对象,然后会以新创建的模块为环境,跳回步骤二解析下一段路由。 -4. 将该 ID 视为控制器 ID,并创建控制器对象。用下个步骤解析路由里剩下的片段。 -5. 控制器会在他的 [[yii\base\Controller::actions()|action map(操作映射表)]]里搜索当前 ID。如果找得到,它会根据该映射表中的配置创建一个操作对象;反之,控制器则会尝试创建一个与该 ID - 相对应,由某个 action 方法所定义的行内操作(inline action)。 +When parsing or creating URLs, [[yii\web\UrlManager|URL manager]] examines URL rules in the order they are declared. +Therefore, you may consider adjusting the order of the URL rules so that more specific and/or more commonly used rules are placed before less used ones. -在上面的步骤里,如果有任何错误发生,都会抛出 [[yii\web\NotFoundHttpException]],指出路由引导的过程失败了。 \ No newline at end of file +If some URL rules share the same prefix in their patterns or routes, you may consider using [[yii\web\GroupUrlRule]] +so that they can be more efficiently examined by [[yii\web\UrlManager|URL manager]] as a group. This is often the case +when your application is composed by modules, each having its own set of URL rules with module ID as their common prefixes. diff --git a/docs/guide-zh-CN/runtime-sessions-cookies.md b/docs/guide-zh-CN/runtime-sessions-cookies.md index 9db42f6..f34d4dc 100644 --- a/docs/guide-zh-CN/runtime-sessions-cookies.md +++ b/docs/guide-zh-CN/runtime-sessions-cookies.md @@ -9,7 +9,8 @@ Sessions 和 Cookies ## Sessions 和 [请求](runtime-requests.md) 和 [响应](runtime-responses.md)类似, -默认可通过为[[yii\web\Session]] 实例的`session` [应用组件](structure-application-components.md) 来访问sessions。 +默认可通过为[[yii\web\Session]] 实例的`session` +[应用组件](structure-application-components.md) 来访问sessions。 ### 开启和关闭 Sessions @@ -38,7 +39,6 @@ $session->destroy(); ### 访问Session数据 -To access the data stored in session, you can do the following: 可使用如下方式访问session中的数据: ```php @@ -69,14 +69,12 @@ foreach ($session as $name => $value) ... foreach ($_SESSION as $name => $value) ... ``` -<<<<<<< .merge_file_a02856 -> 补充: 当使用`session`组件访问session数据时候,如果session没有开启会自动开启, -======= -> Info: 当使用`session`组件访问session数据时候,如果session没有开启会自动开启, ->>>>>>> .merge_file_a01508 +> 注意: 当使用`session`组件访问session数据时候, +如果session没有开启会自动开启, 这和通过`$_SESSION`不同,`$_SESSION`要求先执行`session_start()`。 -当session数据为数组时,`session`组件会限制你直接修改数据中的单元项,例如: +当session数据为数组时,`session`组件会限制你直接修改数据中的单元项, +例如: ```php $session = Yii::$app->session; @@ -121,30 +119,31 @@ $session['captcha.number'] = 5; $session['captcha.lifetime'] = 3600; ``` -为更好的性能和可读性,推荐最后一种方案,也就是不用存储session变量为数组, +为更好的性能和可读性,推荐最后一种方案, +也就是不用存储session变量为数组, 而是将每个数组项变成有相同键前缀的session变量。 ### 自定义Session存储 -[[yii\web\Session]] 类默认存储session数据为文件到服务器上,Yii提供以下session类实现不同的session存储方式: +[[yii\web\Session]] 类默认存储session数据为文件到服务器上, +Yii提供以下session类实现不同的session存储方式: * [[yii\web\DbSession]]: 存储session数据在数据表中 * [[yii\web\CacheSession]]: 存储session数据到缓存中,缓存和配置中的[缓存组件](caching-data.md#cache-components)相关 * [[yii\redis\Session]]: 存储session数据到以[redis](http://redis.io/) 作为存储媒介中 * [[yii\mongodb\Session]]: 存储session数据到[MongoDB](http://www.mongodb.org/). -所有这些session类支持相同的API方法集,因此,切换到不同的session存储介质不需要修改项目使用session的代码。 +所有这些session类支持相同的API方法集,因此, +切换到不同的session存储介质不需要修改项目使用session的代码。 -<<<<<<< .merge_file_a02856 -> 注意: 如果通过`$_SESSION`访问使用自定义存储介质的session,需要确保session已经用[[yii\web\Session::open()]] 开启, -======= -> Note: 如果通过`$_SESSION`访问使用自定义存储介质的session,需要确保session已经用[[yii\web\Session::open()]] 开启, ->>>>>>> .merge_file_a01508 +> 注意: 如果通过`$_SESSION`访问使用自定义存储介质的session, + 需要确保session已经用[[yii\web\Session::open()]] 开启, 这是因为在该方法中注册自定义session存储处理器。 学习如何配置和使用这些组件类请参考它们的API文档,如下为一个示例 -显示如何在应用配置中配置[[yii\web\DbSession]]将数据表作为session存储介质。 +显示如何在应用配置中配置[[yii\web\DbSession]] +将数据表作为session存储介质。 ```php return [ @@ -175,18 +174,17 @@ CREATE TABLE session - PostgreSQL: BYTEA - MSSQL: BLOB -<<<<<<< .merge_file_a02856 > 注意: 根据php.ini 设置的 `session.hash_function`,你需要调整`id`列的长度, -======= -> Note: 根据 php.ini 设置的 `session.hash_function`,你需要调整`id`列的长度, ->>>>>>> .merge_file_a01508 - 例如,如果 `session.hash_function=sha256` ,应使用长度为64而不是40的char类型。 + 例如,如果 `session.hash_function=sha256` , + 应使用长度为64而不是40的char类型。 ### Flash 数据 -Flash数据是一种特别的session数据,它一旦在某个请求中设置后,只会在下次请求中有效,然后该数据就会自动被删除。 -常用于实现只需显示给终端用户一次的信息,如用户提交一个表单后显示确认信息。 +Flash数据是一种特别的session数据,它一旦在某个请求中设置后, +只会在下次请求中有效,然后该数据就会自动被删除。 +常用于实现只需显示给终端用户一次的信息, +如用户提交一个表单后显示确认信息。 可通过`session`应用组件设置或访问`session`,例如: @@ -226,22 +224,31 @@ $session->addFlash('alerts', 'You are promoted.'); $alerts = $session->getFlash('alerts'); ``` -<<<<<<< .merge_file_a02856 > 注意: 不要在相同名称的flash数据中使用[[yii\web\Session::setFlash()]] 的同时也使用[[yii\web\Session::addFlash()]], -======= -> Note: 不要在相同名称的flash数据中使用[[yii\web\Session::setFlash()]] 的同时也使用[[yii\web\Session::addFlash()]], ->>>>>>> .merge_file_a01508 因为后一个防范会自动将flash信息转换为数组以使新的flash数据可追加进来,因此, - 当你调用[[yii\web\Session::getFlash()]]时,会发现有时获取到一个数组,有时获取到一个字符串, + 当你调用[[yii\web\Session::getFlash()]]时, + 会发现有时获取到一个数组,有时获取到一个字符串, 取决于你调用这两个方法的顺序。 +> Tip: For displaying Flash messages you can use [[yii\bootstrap\Alert|bootstrap Alert]] widget in the following way: +> +> ```php +> echo Alert::widget([ +> 'options' => ['class' => 'alert-info'], +> 'body' => Yii::$app->session->getFlash('postDeleted'), +> ]); +> ``` + ## Cookies Yii使用 [[yii\web\Cookie]]对象来代表每个cookie,[[yii\web\Request]] 和 [[yii\web\Response]] -通过名为'cookies'的属性维护一个cookie集合,前者的cookie 集合代表请求提交的cookies, +通过名为'cookies'的属性维护一个cookie集合, +前者的cookie 集合代表请求提交的cookies, 后者的cookie集合表示发送给用户的cookies。 +The part of the application dealing with request and response directly is controller. Therefore, cookies should be +read and sent in controller. ### 读取 Cookies @@ -272,7 +279,6 @@ if (isset($cookies['language'])) ... ### 发送 Cookies -You can send cookies to end users using the following code: 可使用如下代码发送cookie到终端用户: ```php @@ -296,34 +302,27 @@ unset($cookies['language']); [[yii\web\Cookie::domain|domain]], [[yii\web\Cookie::expire|expire]] 可配置这些属性到cookie中并添加到响应的cookie集合中。 -<<<<<<< .merge_file_a02856 -> 注意: 为安全起见[[yii\web\Cookie::httpOnly]] 被设置为true,这可减少客户端脚本访问受保护cookie(如果浏览器支持)的风险, -======= -> Note: 为安全起见[[yii\web\Cookie::httpOnly]] 被设置为true,这可减少客户端脚本访问受保护cookie(如果浏览器支持)的风险, ->>>>>>> .merge_file_a01508 +> 注意: 为安全起见[[yii\web\Cookie::httpOnly]] 被设置为true, +这可减少客户端脚本访问受保护cookie(如果浏览器支持)的风险, 更多详情可阅读 [httpOnly wiki article](https://www.owasp.org/index.php/HttpOnly) for more details. ### Cookie验证 -在上两节中,当通过`request` 和 `response` 组件读取和发送cookie时,你会喜欢扩展的cookie验证的保障安全功能,它能 -使cookie不被客户端修改。该功能通过给每个cookie签发一个哈希字符串来告知服务端cookie是否在客户端被修改, +在上两节中,当通过`request` 和 `response` 组件读取和发送cookie时, +你会喜欢扩展的cookie验证的保障安全功能,它能 +使cookie不被客户端修改。该功能通过给每个cookie签发一个 +哈希字符串来告知服务端cookie是否在客户端被修改, 如果被修改,通过`request`组件的[[yii\web\Request::cookies|cookie collection]]cookie集合访问不到该cookie。 -<<<<<<< .merge_file_a02856 -> 注意: Cookie验证只保护cookie值被修改,如果一个cookie验证失败,仍然可以通过`$_COOKIE`来访问该cookie, -======= -> Note: Cookie验证只保护cookie值被修改,如果一个cookie验证失败,仍然可以通过`$_COOKIE`来访问该cookie, ->>>>>>> .merge_file_a01508 +> 注意: Cookie验证只保护cookie值被修改,如果一个cookie验证失败, +仍然可以通过`$_COOKIE`来访问该cookie, 因为这是第三方库对未通过cookie验证自定义的操作方式。 -Cookie验证默认启用,可以设置[[yii\web\Request::enableCookieValidation]]属性为false来禁用它,尽管如此,我们强烈建议启用它。 +Cookie验证默认启用,可以设置[[yii\web\Request::enableCookieValidation]]属性为false来禁用它, +尽管如此,我们强烈建议启用它。 -<<<<<<< .merge_file_a02856 > 注意: 直接通过`$_COOKIE` 和 `setcookie()` 读取和发送的Cookie不会被验证。 -======= -> Note: 直接通过`$_COOKIE` 和 `setcookie()` 读取和发送的Cookie不会被验证。 ->>>>>>> .merge_file_a01508 当使用cookie验证,必须指定[[yii\web\Request::cookieValidationKey]],它是用来生成s上述的哈希值, 可通过在应用配置中配置`request` 组件。 @@ -338,9 +337,5 @@ return [ ]; ``` -<<<<<<< .merge_file_a02856 > 补充: [[yii\web\Request::cookieValidationKey|cookieValidationKey]] 对你的应用安全很重要, -======= -> Info: [[yii\web\Request::cookieValidationKey|cookieValidationKey]] 对你的应用安全很重要, ->>>>>>> .merge_file_a01508 应只被你信任的人知晓,请不要将它放入版本控制中。 diff --git a/docs/guide-zh-CN/security-authorization.md b/docs/guide-zh-CN/security-authorization.md index 0d99715..2a7f6d9 100644 --- a/docs/guide-zh-CN/security-authorization.md +++ b/docs/guide-zh-CN/security-authorization.md @@ -227,6 +227,9 @@ return [ ], ]; ``` +> Note: If you are using yii2-basic-app template, there is a `config/console.php` configuration file where the + `authManager` needs to be declared additionally to `config/web.php`. +> In case of yii2-advanced-app the `authManager` should be declared only once in `common/config/main.php`. `DbManager` 使用4个数据库表存放它的数据: @@ -300,6 +303,9 @@ class RbacController extends Controller } ``` +> Note: If you are using advanced template, you need to put your `RbacController` inside `console/controllers` directory + and change namespace to `console/controllers`. + 在用 `yii rbac/init` 执行了这个命令后,我们将得到下图所示的层次结构: ![Simple RBAC hierarchy](images/rbac-hierarchy-1.png "简单的 RBAC 层次结构示意图") diff --git a/docs/guide-zh-CN/security-best-practices.md b/docs/guide-zh-CN/security-best-practices.md index 4319826..c0f7229 100644 --- a/docs/guide-zh-CN/security-best-practices.md +++ b/docs/guide-zh-CN/security-best-practices.md @@ -14,7 +14,9 @@ ### 过滤输入 -过滤输入的意思是,用户输入不应该认为是安全的,你需要总是验证你获得的输入值是在允许范围内。比如,我们假设 sorting 只能指定为 `title`, `created_at` 和 `status` 三个值,然后,这个值是由用户输入提供的,那么,最好在我们接收参数的时候,检查一下这个值是否是指定的范围。 +过滤输入的意思是,用户输入不应该认为是安全的,你需要总是验证你获得的输入值是在允许范围内。 +比如,我们假设 sorting 只能指定为 `title`, `created_at` 和 `status` 三个值,然后,这个值是由用户输入提供的, +那么,最好在我们接收参数的时候,检查一下这个值是否是指定的范围。 对于基本的 PHP 而言,上述做法类似如下: ```php @@ -29,8 +31,10 @@ if (!in_array($sortBy, ['title', 'created_at', 'status'])) { ### 转义输出 -转义输出的意思是,根据我们使用数据的上下文环境,数据需要被转义。比如:在 HTML 上下文,你需要转义 `<`,`>` 之类的特殊字符。在 JavaScript 或者 SQL 中,也有其他的特殊含义的字符串需要被转义。 -由于手动的给所用的输出转义容易出错,Yii 提供了大量的工具来在不同的上下文执行转义。 +转义输出的意思是,根据我们使用数据的上下文环境,数据需要被转义。比如:在 HTML 上下文, +你需要转义 `<`,`>` 之类的特殊字符。在 JavaScript 或者 SQL 中,也有其他的特殊含义的字符串需要被转义。 +由于手动的给所用的输出转义容易出错, +Yii 提供了大量的工具来在不同的上下文执行转义。 避免 SQL 注入 ----------------------- @@ -49,11 +53,14 @@ $sql = "SELECT * FROM user WHERE username = '$username'"; SELECT * FROM user WHERE username = ''; DROP TABLE user; --' ``` -这是一个合法的查询语句,并将会执行以空的用户名搜索用户操作,然后,删除 `user` 表。这极有可能导致网站出差,数据丢失。(你是否进行了规律的数据备份?) +这是一个合法的查询语句,并将会执行以空的用户名搜索用户操作,然后,删除 `user` 表。 +这极有可能导致网站出差,数据丢失。(你是否进行了规律的数据备份?) -在 Yii 中,大部分的数据查询是通过 [Active Record](db-active-record.md) 进行的,而其是完全使用 PDO 预处理语句执行 SQL 查询的。在预处理语句中,上述示例中,构造 SQL 查询的场景是不可能发生的。 +在 Yii 中,大部分的数据查询是通过 [Active Record](db-active-record.md) 进行的, +而其是完全使用 PDO 预处理语句执行 SQL 查询的。在预处理语句中,上述示例中,构造 SQL 查询的场景是不可能发生的。 -有时,你仍需要使用 [raw queries](db-dao.md) 或者 [query builder](db-query-builder.md)。在这种情况下,你应该使用安全的方式传递参数。如果数据是提供给表列的值,最好使用预处理语句: +有时,你仍需要使用 [raw queries](db-dao.md) 或者 [query builder](db-query-builder.md)。 +在这种情况下,你应该使用安全的方式传递参数。如果数据是提供给表列的值,最好使用预处理语句: ```php // query builder @@ -83,7 +90,8 @@ function actionList($orderBy = null) } ``` -如果上述方法不行,表名或者列名应该被转义。 Yii 针对这种转义提供了一个特殊的语法,这样可以在所有支持的数据库都使用一套方案。 +如果上述方法不行,表名或者列名应该被转义。 Yii 针对这种转义提供了一个特殊的语法, +这样可以在所有支持的数据库都使用一套方案。 ```php $sql = "SELECT COUNT([[$column]]) FROM {{table}}"; @@ -92,10 +100,15 @@ $rowCount = $connection->createCommand($sql)->queryScalar(); 你可以在 [Quoting Table and Column Names](db-dao.md#quoting-table-and-column-names) 中获取更多的语法细节。 + 防止 XSS 攻击 ------------ -XSS 或者跨站脚本发生在输出 HTML 到浏览器时,输出内容没有正确的转义。例如,如果用户可以输入其名称,那么他输入 `` 而非其名字 `Alexander`,所有输出没有转义直接输出用户名的页面都会执行 JavaScript 代码 `alert('Hello!');`,这会导致浏览器页面上出现一个警告弹出框。就具体的站点而言,除了这种无意义的警告输出外,这样的脚本可以以你的名义发送一些消息到后台,甚至执行一些银行交易行为。 +XSS 或者跨站脚本发生在输出 HTML 到浏览器时,输出内容没有正确的转义。 +例如,如果用户可以输入其名称,那么他输入 `` 而非其名字 `Alexander`, +所有输出没有转义直接输出用户名的页面都会执行 JavaScript 代码 `alert('Hello!');`, +这会导致浏览器页面上出现一个警告弹出框。就具体的站点而言,除了这种无意义的警告输出外, +这样的脚本可以以你的名义发送一些消息到后台,甚至执行一些银行交易行为。 避免 XSS 攻击在 Yii 中非常简单,有如下两种一般情况: @@ -104,6 +117,7 @@ XSS 或者跨站脚本发生在输出 HTML 到浏览器时,输出内容没有 如果你需要的是纯文本,你可以如下简单的转义: + ```php ``` @@ -119,9 +133,13 @@ XSS 或者跨站脚本发生在输出 HTML 到浏览器时,输出内容没有 防止 CSRF 攻击 ------------- -CSRF 是跨站请求伪造的缩写。这个攻击思想源自许多应用程序假设来自用户的浏览器请求是由用户自己产生的,而事实并非如此。 +CSRF 是跨站请求伪造的缩写。这个攻击思想源自许多应用程序假设来自用户的浏览器请求是由用户自己产生的, +而事实并非如此。 -比如说:`an.example.com` 站点有一个 `/logout` URL,当以 GET 请求访问时,登出用户。如果它是由用户自己操作的,那么一切都没有问题。但是,有一天坏人在一个用户经常访问的论坛发了一个 `` 内容的帖子。浏览器无法辨别请求一个图片还是一个页面,所以,当用户打开含有上述标签的页面时,他将会从 `an.example.com` 登出。 +比如说:`an.example.com` 站点有一个 `/logout` URL,当以 GET 请求访问时, +登出用户。如果它是由用户自己操作的,那么一切都没有问题。但是, +有一天坏人在一个用户经常访问的论坛发了一个 `` 内容的帖子。 +浏览器无法辨别请求一个图片还是一个页面,所以,当用户打开含有上述标签的页面时,他将会从 `an.example.com` 登出。 上面就是最原始的思想。有人可能会说,登出用户也不是什么严重问题,然而,我们发送一些 POST 数据其实也不是很麻烦的事情。 @@ -130,18 +148,76 @@ CSRF 是跨站请求伪造的缩写。这个攻击思想源自许多应用程序 1. 遵循 HTTP 准则,比如 GET 不应该改变应用的状态。 2. 保证 Yii CSRF 保护开启。 +Sometimes you need to disable CSRF validation per controller and/or action. It could be achieved by setting its property: + +```php +namespace app\controllers; + +use yii\web\Controller; + +class SiteController extends Controller +{ + public $enableCsrfValidation = false; + + public function actionIndex() + { + // CSRF validation will not be applied to this and other actions + } + +} +``` + +To disable CSRF validation per custom actions you can do: + +```php +namespace app\controllers; + +use yii\web\Controller; + +class SiteController extends Controller +{ + public function beforeAction($action) + { + // ...set `$this->enableCsrfValidation` here based on some conditions... + // call parent method that will check CSRF if such property is true. + return parent::beforeAction($action); + } +} +``` + + 防止文件暴露 ---------------------- -默认的服务器 webroot 目录指向包含有 `index.php` 的 `web` 目录。在共享托管环境下,这样是不可能的,这样导致了所有的代码,配置,日志都在webroot目录。 +默认的服务器 webroot 目录指向包含有 `index.php` 的 `web` 目录。在共享托管环境下,这样是不可能的, +这样导致了所有的代码,配置,日志都在webroot目录。 -如果是这样,别忘了拒绝除了 `web` 目录以外的目录的访问权限。如果没法这样做,考虑将你的应用程序托管在其他地方。 +如果是这样,别忘了拒绝除了 `web` 目录以外的目录的访问权限。 +如果没法这样做,考虑将你的应用程序托管在其他地方。 在生产环境关闭调试信息和工具 ------------------------------------------- -在调试模式下, Yii 展示了大量的错误信息,这样是对开发有用的。同样,这些调试信息对于攻击者而言也是方便其用于破解数据结构,配置值,以及你的部分代码。永远不要在生产模式下将你的 `index.php` 中的 `YII_DEBUG` 设置为 `true`。 +在调试模式下, Yii 展示了大量的错误信息,这样是对开发有用的。 +同样,这些调试信息对于攻击者而言也是方便其用于破解数据结构,配置值,以及你的部分代码。 +永远不要在生产模式下将你的 `index.php` 中的 `YII_DEBUG` 设置为 `true`。 + +你同样也不应该在生产模式下开启 Gii。它可以被用于获取数据结构信息, +代码,以及简单的用 Gii 生成的代码覆盖你的代码。 + +调试工具栏同样也应该避免在生产环境出现,除非非常有必要。它将会暴露所有的应用和配置的详情信息。 +如果你确定需要,反复确认其访问权限限定在你自己的 IP。 + +Using secure connection over TLS +-------------------------------- + +Yii provides features that rely on cookies and/or PHP sessions. These can be vulnerable in case your connection is +compromised. The risk is reduced if the app uses secure connection via TLS. -你同样也不应该在生产模式下开启 Gii。它可以被用于获取数据结构信息,代码,以及简单的用 Gii 生成的代码覆盖你的代码。 +Please refer to your webserver documentation for instructions on how to configure it. You may also check example configs +provided by H5BP project: -调试工具栏同样也应该避免在生产环境出现,除非非常有必要。它将会暴露所有的应用和配置的详情信息。如果你确定需要,反复确认其访问权限限定在你自己的 IP。 +- [Nginx](https://github.com/h5bp/server-configs-nginx) +- [Apache](https://github.com/h5bp/server-configs-apache). +- [IIS](https://github.com/h5bp/server-configs-iis). +- [Lighttpd](https://github.com/h5bp/server-configs-lighttpd). \ No newline at end of file diff --git a/docs/guide-zh-CN/security-cryptography.md b/docs/guide-zh-CN/security-cryptography.md new file mode 100644 index 0000000..b5818f9 --- /dev/null +++ b/docs/guide-zh-CN/security-cryptography.md @@ -0,0 +1,66 @@ +Cryptography +============ + +In this section we'll review the following security aspects: + +- Generating random data +- Encryption and Decryption +- Confirming Data Integrity + +Generating Pseudorandom Data +---------------------------- + +Pseudorandom data is useful in many situations. For example when resetting a password via email you need to generate a +token, save it to the database, and send it via email to end user which in turn will allow them to prove ownership of +that account. It is very important that this token be unique and hard to guess, else there is a possibility that attacker +can predict the token's value and reset the user's password. + +Yii security helper makes generating pseudorandom data simple: + + +```php +$key = Yii::$app->getSecurity()->generateRandomString(); +``` + +Encryption and Decryption +------------------------- + +Yii provides convenient helper functions that allow you to encrypt/decrypt data using a secret key. The data is passed through the encryption function so that only the person which has the secret key will be able to decrypt it. +For example, we need to store some information in our database but we need to make sure only the user who has the secret key can view it (even if the application database is compromised): + + +```php +// $data and $secretKey are obtained from the form +$encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey); +// store $encryptedData to database +``` + +Subsequently when user wants to read the data: + +```php +// $secretKey is obtained from user input, $encryptedData is from the database +$data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey); +``` + +It's also possible to use key instead of password via [[\yii\base\Security::encryptByKey()]] and +[[\yii\base\Security::decryptByKey()]]. + +Confirming Data Integrity +------------------------- + +There are situations in which you need to verify that your data hasn't been tampered with by a third party or even corrupted in some way. Yii provides an easy way to confirm data integrity in the form of two helper functions. + +Prefix the data with a hash generated from the secret key and data + + +```php +// $secretKey our application or user secret, $genuineData obtained from a reliable source +$data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey); +``` + +Checks if the data integrity has been compromised + +```php +// $secretKey our application or user secret, $data obtained from an unreliable source +$data = Yii::$app->getSecurity()->validateData($data, $secretKey); +``` diff --git a/docs/guide-zh-CN/security-overview.md b/docs/guide-zh-CN/security-overview.md new file mode 100644 index 0000000..7903a10 --- /dev/null +++ b/docs/guide-zh-CN/security-overview.md @@ -0,0 +1,14 @@ +Security +======== + +Good security is vital to the health and success of any application. Unfortunately, many developers cut corners when it +comes to security, either due to a lack of understanding or because implementation is too much of a hurdle. To make your +Yii powered application as secure as possible, Yii has included several excellent and easy to use security features. + +* [Authentication](security-authentication.md) +* [Authorization](security-authorization.md) +* [Working with Passwords](security-passwords.md) +* [Cryptography](security-cryptography.md) +* [Views security](structure-views.md#security) +* [Auth Clients](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide/README.md) +* [Best Practices](security-best-practices.md) diff --git a/docs/guide-zh-CN/security-passwords.md b/docs/guide-zh-CN/security-passwords.md index 77277fa..4f0178e 100644 --- a/docs/guide-zh-CN/security-passwords.md +++ b/docs/guide-zh-CN/security-passwords.md @@ -1,19 +1,18 @@ 处理密码 ======== -> 注意:本节内容正在开发中。 +大部分开发者知道密码不能以明文形式存储,但是许多开发者仍认为使用 `md5` 或者 `sha1` 来哈希化密码是安全的。 +一度,使用上述的哈希算法是足够安全的,但是, +现代硬件的发展使得短时间内暴力破解上述算法生成的哈希串成为可能。 -好的安全策略对任何应用的健康和成功极其重要。不幸的是,许多开发者在遇到安全问题时,因为认识不够或者实现起来比较麻烦,都不是很注意细节。为了让你的 Yii 应用程序尽可能的安全, Yii 囊括了一些卓越并简单易用的安全特性。 - -密码的哈希与验证 -------------------------------- - -大部分开发者知道密码不能以明文形式存储,但是许多开发者仍认为使用 `md5` 或者 `sha1` 来哈希化密码是安全的。一度,使用上述的哈希算法是足够安全的,但是,现代硬件的发展使得短时间内暴力破解上述算法生成的哈希串成为可能。 - -为了即使在最糟糕的情况下(你的应用程序被破解了)也能给用户密码提供增强的安全性,你需要使用一个能够对抗暴力破解攻击的哈希算法。目前最好的选择是 `bcrypt`。在 PHP 中,你可以通过 [crypt 函数](http://php.net/manual/en/function.crypt.php) 生成 `bcrypt` 哈希。Yii 提供了两个帮助函数以让使用 `crypt` 来进行安全的哈希密码生成和验证更加容易。 +为了即使在最糟糕的情况下(你的应用程序被破解了)也能给用户密码提供增强的安全性, +你需要使用一个能够对抗暴力破解攻击的哈希算法。目前最好的选择是 `bcrypt`。在 PHP 中, +你可以通过 [crypt 函数](http://php.net/manual/en/function.crypt.php) 生成 `bcrypt` 哈希。 +Yii 提供了两个帮助函数以让使用 `crypt` 来进行安全的哈希密码生成和验证更加容易。 当一个用户为第一次使用,提供了一个密码时(比如:注册时),密码就需要被哈希化。 + ```php $hash = Yii::$app->getSecurity()->generatePasswordHash($password); ``` @@ -22,6 +21,7 @@ $hash = Yii::$app->getSecurity()->generatePasswordHash($password); 当一个用户尝试登录时,表单提交的密码需要使用之前的存储的哈希串来验证: + ```php if (Yii::$app->getSecurity()->validatePassword($password, $hash)) { // all good, logging user in @@ -29,104 +29,3 @@ if (Yii::$app->getSecurity()->validatePassword($password, $hash)) { // wrong password } ``` - -生成伪随机数 ------------ - -伪随机数据在许多场景下都非常有用。比如当通过邮件重置密码时,你需要生成一个令牌,将其保存到数据库中,并通过邮件发送到终端用户那里以让其证明其对某个账号的所有权。这个令牌的唯一性和难猜解性非常重要,否则,就存在攻击者猜解令牌,并重置用户的密码的可能性。 - -Yii security helper makes generating pseudorandom data simple: -Yii 安全助手使得生成伪随机数据非常简单: - -```php -$key = Yii::$app->getSecurity()->generateRandomString(); -``` - -注意,你需要安装有 `openssl` 扩展,以生成密码的安全随机数据。 - -加密与解密 -------------------------- - -Yii 提供了方便的帮助函数来让你用一个安全秘钥来加密解密数据。数据通过加密函数进行传输,这样只有拥有安全秘钥的人才能解密。比如,我们需要存储一些信息到我们的数据库中,但是,我们需要保证只有拥有安全秘钥的人才能看到它(即使应用的数据库泄露) - -```php -// $data and $secretKey are obtained from the form -$encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey); -// store $encryptedData to database -``` - -随后,当用户需要读取数据时: - -```php -// $secretKey is obtained from user input, $encryptedData is from the database -$data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey); -``` - -校验数据完整性 --------------------------------- - -有时,你需要验证你的数据没有第三方篡改或者使用某种方式破坏了。Yii 通过两个帮助函数,提供了一个简单的方式来进行数据的完整性校验。 - -首先,将由安全秘钥和数据生成的哈希串前缀到数据上。 - -```php -// $secretKey our application or user secret, $genuineData obtained from a reliable source -$data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey); -``` - -验证数据完整性是否被破坏了。 - -```php -// $secretKey our application or user secret, $data obtained from an unreliable source -$data = Yii::$app->getSecurity()->validateData($data, $secretKey); -``` - -todo: XSS 防范, CSRF 防范, Cookie 保护相关的内容,参考 1.1 文档 - -你同样可以给控制器或者 action 设置它的 `enableCsrfValidation` 属性来单独禁用 CSRF 验证。 - -```php -namespace app\controllers; - -use yii\web\Controller; - -class SiteController extends Controller -{ - public $enableCsrfValidation = false; - - public function actionIndex() - { - // CSRF validation will not be applied to this and other actions - } - -} -``` - -为了给某个定制的 action 关闭 CSRF 验证,你可以: - -```php -namespace app\controllers; - -use yii\web\Controller; - -class SiteController extends Controller -{ - public function beforeAction($action) - { - // ...set `$this->enableCsrfValidation` here based on some conditions... - // call parent method that will check CSRF if such property is true. - return parent::beforeAction($action); - } -} -``` - -安全 Cookie ----------------- - -- validation -- httpOnly is default - -参考 --------- - -- [Views security](structure-views.md#security) diff --git a/docs/guide-zh-CN/start-databases.md b/docs/guide-zh-CN/start-databases.md index 8a81508..194b754 100644 --- a/docs/guide-zh-CN/start-databases.md +++ b/docs/guide-zh-CN/start-databases.md @@ -1,7 +1,10 @@ 使用数据库 ====================== -本章节将介绍如何如何创建一个从数据表 `country` 中读取国家数据并显示出来的页面。为了实现这个目标,你将会配置一个数据库连接,创建一个[活动记录](db-active-record.md)类,并且创建一个[操作](structure-controllers.md)及一个[视图](structure-views.md)。 +本章节将介绍如何如何创建一个从数据表 `country` 中读取国家数据并显示出来的页面。 +为了实现这个目标,你将会配置一个数据库连接,创建一个[活动记录](db-active-record.md)类, +并且创建一个[操作](structure-controllers.md)及一个[视图](structure-views.md)。 + 贯穿整个章节,你将会学到: @@ -10,13 +13,15 @@ * 使用活动记录从数据库中查询数据 * 以分页方式在视图中显示数据 -请注意,为了掌握本章你应该具备最基本的数据库知识和使用经验。尤其是应该知道如何创建数据库,如何通过数据库终端执行 SQL 语句。 +请注意,为了掌握本章你应该具备最基本的数据库知识和使用经验。 +尤其是应该知道如何创建数据库,如何通过数据库终端执行 SQL 语句。 准备数据库 -------------------- -首先创建一个名为 `yii2basic` 的数据库,应用将从这个数据库中读取数据。你可以创建 SQLite,MySQL,PostregSQL,MSSQL 或 Oracle 数据库,Yii 内置多种数据库支持。简单起见,后面的内容将以 MySQL 为例做演示。 +首先创建一个名为 `yii2basic` 的数据库,应用将从这个数据库中读取数据。你可以创建 SQLite,MySQL,PostregSQL,MSSQL 或 Oracle 数据库, +Yii 内置多种数据库支持。简单起见,后面的内容将以 MySQL 为例做演示。 然后在数据库中创建一个名为 `country` 的表并插入简单的数据。可以执行下面的语句: @@ -27,7 +32,6 @@ CREATE TABLE `country` ( `population` INT(11) NOT NULL DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -<<<<<<< .merge_file_a07132 INSERT INTO `country` VALUES ('AU','Australia',18886000); INSERT INTO `country` VALUES ('BR','Brazil',170115000); INSERT INTO `country` VALUES ('CA','Canada',1147000); @@ -38,18 +42,6 @@ INSERT INTO `country` VALUES ('GB','United Kingdom',59623400); INSERT INTO `country` VALUES ('IN','India',1013662000); INSERT INTO `country` VALUES ('RU','Russia',146934000); INSERT INTO `country` VALUES ('US','United States',278357000); -======= -INSERT INTO `country` VALUES ('AU','Australia',24016400); -INSERT INTO `country` VALUES ('BR','Brazil',205722000); -INSERT INTO `country` VALUES ('CA','Canada',35985751); -INSERT INTO `country` VALUES ('CN','China',1375210000); -INSERT INTO `country` VALUES ('DE','Germany',81459000); -INSERT INTO `country` VALUES ('FR','France',64513242); -INSERT INTO `country` VALUES ('GB','United Kingdom',65097000); -INSERT INTO `country` VALUES ('IN','India',1285400000); -INSERT INTO `country` VALUES ('RU','Russia',146519759); -INSERT INTO `country` VALUES ('US','United States',322976000); ->>>>>>> .merge_file_a06196 ``` 此时便有了一个名为 `yii2basic` 的数据库,在这个数据库中有一个包含三个字段的数据表 `country`,表中有十行数据。 @@ -57,9 +49,12 @@ INSERT INTO `country` VALUES ('US','United States',322976000); 配置数据库连接 --------------------------- -开始之前,请确保你已经安装了 PHP [PDO](http://www.php.net/manual/en/book.pdo.php) 扩展和你所使用的数据库的 PDO 驱动(例如 MySQL 的 `pdo_mysql`)。对于使用关系型数据库来讲,这是基本要求。 +开始之前,请确保你已经安装了 PHP [PDO](http://www.php.net/manual/en/book.pdo.php) +扩展和你所使用的数据库的 PDO 驱动(例如 MySQL 的 `pdo_mysql`)。 +对于使用关系型数据库来讲,这是基本要求。 -驱动和扩展安装可用后,打开 `config/db.php` 修改里面的配置参数对应你的数据库配置。该文件默认包含这些内容: +驱动和扩展安装可用后,打开 `config/db.php` 修改里面的配置参数对应你的数据库配置。 +该文件默认包含这些内容: ```php db` 表达式访问。 -<<<<<<< .merge_file_a07132 -> 补充:`config/db.php` 将被包含在应用配置文件 `config/web.php` 中,后者指定了整个[应用](structure-applications.md)如何初始化。请参考[配置](concept-configurations.md)章节了解更多信息。 -======= -> Info: `config/db.php` 将被包含在应用配置文件 `config/web.php` 中,后者指定了整个[应用](structure-applications.md)如何初始化。请参考[配置](concept-configurations.md)章节了解更多信息。 ->>>>>>> .merge_file_a06196 +> 补充:`config/db.php` 将被包含在应用配置文件 `config/web.php` 中, + 后者指定了整个[应用](structure-applications.md)如何初始化。 + 请参考[配置](concept-configurations.md)章节了解更多信息。 + +If you need to work with databases support for which isn't bundled with Yii, check the following extensions: + +- [Informix](https://github.com/edgardmessias/yii2-informix) +- [IBM DB2](https://github.com/edgardmessias/yii2-ibm-db2) +- [Firebird](https://github.com/edgardmessias/yii2-firebird) 创建活动记录 ------------------------- -创建一个继承自[活动记录](db-active-record.md)类的类 `Country`,把它放在 `models/Country.php` 文件,去代表和读取 `country` 表的数据。 +创建一个继承自[活动记录](db-active-record.md)类的类 `Country`, +把它放在 `models/Country.php` 文件,去代表和读取 `country` 表的数据。 ```php 补充:如果类名和数据表名不能直接对应,可以覆写 [[yii\db\ActiveRecord::tableName()|tableName()]] 方法去显式指定相关表名。 -======= -> Info: 如果类名和数据表名不能直接对应,可以覆写 [[yii\db\ActiveRecord::tableName()|tableName()]] 方法去显式指定相关表名。 ->>>>>>> .merge_file_a06196 +> 注意:如果类名和数据表名不能直接对应,可以覆写 [[yii\db\ActiveRecord::tableName()|tableName()]] +方法去显式指定相关表名。 使用 `Country` 类可以很容易地操作 `country` 表数据,就像这段代码: @@ -128,17 +128,17 @@ $country->name = 'U.S.A.'; $country->save(); ``` -<<<<<<< .merge_file_a07132 -> 补充:活动记录是面向对象、功能强大的访问和操作数据库数据的方式。你可以在[活动记录](db-active-record.md)章节了解更多信息。除此之外你还可以使用另一种更原生的被称做[数据访问对象](db-dao)的方法操作数据库数据。 -======= -> Info: 活动记录是面向对象、功能强大的访问和操作数据库数据的方式。你可以在[活动记录](db-active-record.md)章节了解更多信息。除此之外你还可以使用另一种更原生的被称做[数据访问对象](db-dao)的方法操作数据库数据。 ->>>>>>> .merge_file_a06196 +> 补充:活动记录是面向对象、功能强大的访问和操作数据库数据的方式。你可以在[活动记录](db-active-record.md)章节了解更多信息。 +除此之外你还可以使用另一种更原生的被称做[数据访问对象](db-dao)的方法操作数据库数据。 创建操作 ------------------ -为了向最终用户显示国家数据,你需要创建一个操作。相比之前小节掌握的在 `site` 控制器中创建操作,在这里为所有和国家有关的数据新建一个控制器更加合理。新控制器名为 `CountryController`,并在其中创建一个 `index` 操作,如下: +为了向最终用户显示国家数据,你需要创建一个操作。 +相比之前小节掌握的在 `site` 控制器中创建操作, +在这里为所有和国家有关的数据新建一个控制器更加合理。 +新控制器名为 `CountryController`,并在其中创建一个 `index` 操作,如下: ```php --------------- -在 `views` 目录下先创建一个名为 `country` 的子目录。这个目录存储所有由 `country` 控制器渲染的视图。在 `views/country` 目录下创建一个名为 `index.php` 的视图文件,内容如下: +在 `views` 目录下先创建一个名为 `country` 的子目录。这个目录存储所有由 `country` 控制器渲染的视图。 +在 `views/country` 目录下创建一个名为 `index.php` 的视图文件, +内容如下: ```php $pagination]) ?> ``` -这个视图包含两部分用以显示国家数据。第一部分遍历国家数据并以无序 HTML 列表渲染出来。第二部分使用 [[yii\widgets\LinkPager]] 去渲染从操作中传来的分页信息。小部件 `LinkPager` 显示一个分页按钮的列表。点击任何一个按钮都会跳转到对应的分页。 +这个视图包含两部分用以显示国家数据。第一部分遍历国家数据并以无序 HTML 列表渲染出来。 +第二部分使用 [[yii\widgets\LinkPager]] 去渲染从操作中传来的分页信息。 +小部件 `LinkPager` 显示一个分页按钮的列表。 +点击任何一个按钮都会跳转到对应的分页。 试运行 @@ -220,7 +230,9 @@ http://hostname/index.php?r=country/index ![国家列表](images/start-country-list.png) -首先你会看到显示着五个国家的列表页面。在国家下面,你还会看到一个包含四个按钮的分页器。如果你点击按钮 “2”,将会跳转到显示另外五个国家的页面,也就是第二页记录。如果观察仔细点你还会看到浏览器的 URL 变成了: +首先你会看到显示着五个国家的列表页面。在国家下面, +你还会看到一个包含四个按钮的分页器。如果你点击按钮 “2”,将会跳转到显示另外五个国家的页面, +也就是第二页记录。如果观察仔细点你还会看到浏览器的 URL 变成了: ``` http://hostname/index.php?r=country/index&page=2 @@ -228,17 +240,24 @@ http://hostname/index.php?r=country/index&page=2 在这个场景里,[[yii\data\Pagination|Pagination]] 提供了为数据结果集分页的所有功能: -* 首先 [[yii\data\Pagination|Pagination]] 把 SELECT 的子查询 `LIMIT 5 OFFSET 0` 数据表示成第一页。因此开头的五条数据会被取出并显示。 -* 然后小部件 [[yii\widgets\LinkPager|LinkPager]] 使用 [[yii\data\Pagination::createUrl()|Pagination::createUrl()]] 方法生成的 URL 去渲染翻页按钮。URL 中包含必要的参数 `page` 才能查询不同的页面编号。 -* 如果你点击按钮 “2”,将会发起一个路由为 `country/index` 的新请求。[[yii\data\Pagination|Pagination]] 接收到 URL 中的 `page` 参数把当前的页码设为 2。新的数据库请求将会以 `LIMIT 5 OFFSET 5` 查询并显示。 +* 首先 [[yii\data\Pagination|Pagination]] 把 SELECT 的子查询 `LIMIT 5 OFFSET 0` 数据表示成第一页。 + 因此开头的五条数据会被取出并显示。 +* 然后小部件 [[yii\widgets\LinkPager|LinkPager]] 使用 + [[yii\data\Pagination::createUrl()|Pagination::createUrl()]] 方法生成的 URL 去渲染翻页按钮。 + URL 中包含必要的参数 `page` 才能查询不同的页面编号。 +* 如果你点击按钮 “2”,将会发起一个路由为 `country/index` 的新请求。 + [[yii\data\Pagination|Pagination]] 接收到 URL 中的 `page` 参数把当前的页码设为 2。 + 新的数据库请求将会以 `LIMIT 5 OFFSET 5` + 查询并显示。 + 总结 ------- -本章节中你学到了如何使用数据库。你还学到了如何取出并使用 [[yii\data\Pagination]] 和 [[yii\widgets\LinkPager]] 显示数据。 +本章节中你学到了如何使用数据库。你还学到了如何取出并使用 +[[yii\data\Pagination]] 和 [[yii\widgets\LinkPager]] 显示数据。 -<<<<<<< .merge_file_a07132 -下一章中你会学到如何使用 Yii 中强大的代码生成器 [Gii](tool-gii.md),去帮助你实现一些常用的功能需求,例如增查改删(CRUD)数据表中的数据。事实上你之前所写的代码全部都可以由 Gii 自动生成。 -======= -下一章中你会学到如何使用 Yii 中强大的代码生成器 [Gii](tool-gii.md),去帮助你实现一些常用的功能需求,例如增查改删(CRUD)数据表中的数据。事实上你之前所写的代码全部都可以由 Gii 自动生成。 ->>>>>>> .merge_file_a06196 +下一章中你会学到如何使用 Yii 中强大的代码生成器 [Gii](tool-gii.md), +去帮助你实现一些常用的功能需求, +例如增查改删(CRUD)数据表中的数据。 +事实上你之前所写的代码全部都可以由 Gii 自动生成。 diff --git a/docs/guide-zh-CN/start-forms.md b/docs/guide-zh-CN/start-forms.md index ad32514..dd5708a 100644 --- a/docs/guide-zh-CN/start-forms.md +++ b/docs/guide-zh-CN/start-forms.md @@ -1,9 +1,12 @@ 使用表单 ================== -本章节介绍如何创建一个让用户提交数据的表单页。该页将显示一个包含 name 输入框和 email 输入框的表单。当提交这两部分信息后,页面将会显示用户所输入的信息。 +本章节介绍如何创建一个让用户提交数据的表单页。 +该页将显示一个包含 name 输入框和 email 输入框的表单。 +当提交这两部分信息后,页面将会显示用户所输入的信息。 -为了实现这个目标,除了创建一个[操作](structure-controllers.md)和两个[视图](structure-views)外,还需要创建一个[模型](structure-models.md)。 +为了实现这个目标,除了创建一个[操作](structure-controllers.md)和两个[视图](structure-views)外, +还需要创建一个[模型](structure-models.md)。 贯穿整个小节,你将会学到: @@ -15,13 +18,16 @@ 创建模型 ---------------- -模型类 `EntryForm` 代表从用户那请求的数据,该类如下所示并存储在 `models/EntryForm.php` 文件中。请参考[类自动加载](concept-autoloading.md)章节获取更多关于类命名约定的介绍。 +模型类 `EntryForm` 代表从用户那请求的数据, +该类如下所示并存储在 `models/EntryForm.php` 文件中。 +请参考[类自动加载](concept-autoloading.md)章节获取更多关于类命名约定的介绍。 ```php 补充:[[yii\base\Model]] 被用于普通模型类的父类并与数据表**无关**。[[yii\db\ActiveRecord]] 通常是普通模型类的父类但与数据表有关联(译注:[[yii\db\ActiveRecord]] 类其实也是继承自 [[yii\base\Model]],增加了数据库处理)。 -======= -> Info: [[yii\base\Model]] 被用于普通模型类的父类并与数据表**无关**。[[yii\db\ActiveRecord]] 通常是普通模型类的父类但与数据表有关联(译注:[[yii\db\ActiveRecord]] 类其实也是继承自 [[yii\base\Model]],增加了数据库处理)。 ->>>>>>> .merge_file_a05016 +> 补充:[[yii\base\Model]] 被用于普通模型类的父类并与数据表**无关**。[[yii\db\ActiveRecord]] +通常是普通模型类的父类但与数据表有关联(译注:[[yii\db\ActiveRecord]] 类其实也是继承自 [[yii\base\Model]],增加了数据库处理)。 -`EntryForm` 类包含 `name` 和 `email` 两个公共成员,用来储存用户输入的数据。它还包含一个名为 `rules()` 的方法,用来返回数据验证规则的集合。上面声明的验证规则表示: +`EntryForm` 类包含 `name` 和 `email` 两个公共成员, +用来储存用户输入的数据。它还包含一个名为 `rules()` 的方法, +用来返回数据验证规则的集合。上面声明的验证规则表示: * `name` 和 `email` 值都是必须的 * `email` 的值必须满足email规则验证 -如果你有一个处理用户提交数据的 `EntryForm` 对象,你可以调用它的 [[yii\base\Model::validate()|validate()]] 方法触发数据验证。如果有数据验证失败,将把 [[yii\base\Model::hasErrors|hasErrors]] 属性设为 ture,想要知道具体发生什么错误就调用 [[yii\base\Model::getErrors|getErrors]]。 +如果你有一个处理用户提交数据的 `EntryForm` 对象, +你可以调用它的 [[yii\base\Model::validate()|validate()]] 方法触发数据验证。如果有数据验证失败, +将把 [[yii\base\Model::hasErrors|hasErrors]] 属性设为 ture, +想要知道具体发生什么错误就调用 [[yii\base\Model::getErrors|getErrors]]。 ```php validate()) { 创建操作 ------------------ -下面你得在 `site` 控制器中创建一个 `entry` 操作用于新建的模型。操作的创建和使用已经在[说一声你好](start-hello.md)小节中解释了。 +下面你得在 `site` 控制器中创建一个 `entry` 操作用于新建的模型。 +操作的创建和使用已经在[说一声你好](start-hello.md)小节中解释了。 ```php 补充:表达式 `Yii::$app` 代表[应用](structure-applications.md)实例,它是一个全局可访问的单例。同时它也是一个[服务定位器](concept-service-locator.md),能提供 `request`,`response`,`db` 等等特定功能的组件。在上面的代码里就是使用 `request` 组件来访问应用实例收到的 `$_POST` 数据。 +该操作首先创建了一个 `EntryForm` 对象。然后尝试从 `$_POST` 搜集用户提交的数据, +由 Yii 的 [[yii\web\Request::post()]] 方法负责搜集。如果模型被成功填充数据(也就是说用户已经提交了 HTML 表单), +操作将调用 [[yii\base\Model::validate()|validate()]] 去确保用户提交的是有效数据。 -用户提交表单后,操作将会渲染一个名为 `entry-confirm` 的视图去确认用户输入的数据。如果没填表单就提交,或数据包含错误(译者:如 email 格式不对),`entry` 视图将会渲染输出,连同表单一起输出的还有验证错误的详细信息。 -> 注意:在这个简单例子里我们只是呈现了有效数据的确认页面。实践中你应该考虑使用 [[yii\web\Controller::refresh()|refresh()]] 或 [[yii\web\Controller::redirect()|redirect()]] 去避免[表单重复提交问题](http://en.wikipedia.org/wiki/Post/Redirect/Get)。 -======= -> Info: 表达式 `Yii::$app` 代表[应用](structure-applications.md)实例,它是一个全局可访问的单例。同时它也是一个[服务定位器](concept-service-locator.md),能提供 `request`,`response`,`db` 等等特定功能的组件。在上面的代码里就是使用 `request` 组件来访问应用实例收到的 `$_POST` 数据。 +> 补充:表达式 `Yii::$app` 代表[应用](structure-applications.md)实例, + 它是一个全局可访问的单例。同时它也是一个[服务定位器](concept-service-locator.md), + 能提供 `request`,`response`,`db` 等等特定功能的组件。 + 在上面的代码里就是使用 `request` 组件来访问应用实例收到的 `$_POST` 数据。 -用户提交表单后,操作将会渲染一个名为 `entry-confirm` 的视图去确认用户输入的数据。如果没填表单就提交,或数据包含错误(译者:如 email 格式不对),`entry` 视图将会渲染输出,连同表单一起输出的还有验证错误的详细信息。 +用户提交表单后,操作将会渲染一个名为 `entry-confirm` 的视图去确认用户输入的数据。 +如果没填表单就提交,或数据包含错误(译者:如 email 格式不对),`entry` 视图将会渲染输出, +连同表单一起输出的还有验证错误的详细信息。 -> Note: 在这个简单例子里我们只是呈现了有效数据的确认页面。实践中你应该考虑使用 [[yii\web\Controller::refresh()|refresh()]] 或 [[yii\web\Controller::redirect()|redirect()]] 去避免[表单重复提交问题](http://en.wikipedia.org/wiki/Post/Redirect/Get)。 ->>>>>>> .merge_file_a05016 +> 注意:在这个简单例子里我们只是呈现了有效数据的确认页面。 +实践中你应该考虑使用 [[yii\web\Controller::refresh()|refresh()]] 或 [[yii\web\Controller::redirect()|redirect()]] +去避免[表单重复提交问题](http://en.wikipedia.org/wiki/Post/Redirect/Get)。 创建视图 -------------- -最后创建两个视图文件 `entry-confirm` 和 `entry`。他们会被刚才创建的 `entry` 操作渲染。 +最后创建两个视图文件 `entry-confirm` 和 `entry`。 +他们会被刚才创建的 `entry` 操作渲染。 `entry-confirm` 视图简单地显示提交的 name 和 email 数据。视图文件保存在 `views/site/entry-confirm.php`。 @@ -160,7 +173,12 @@ use yii\widgets\ActiveForm; ``` -视图使用了一个功能强大的[小部件](structure-widgets.md) [[yii\widgets\ActiveForm|ActiveForm]] 去生成 HTML 表单。其中的 `begin()` 和 `end()` 分别用来渲染表单的开始和关闭标签。在这两个方法之间使用了 [[yii\widgets\ActiveForm::field()|field()]] 方法去创建输入框。第一个输入框用于 “name”,第二个输入框用于 “email”。之后使用 [[yii\helpers\Html::submitButton()]] 方法生成提交按钮。 +视图使用了一个功能强大的[小部件](structure-widgets.md) [[yii\widgets\ActiveForm|ActiveForm]] 去生成 HTML 表单。 +其中的 `begin()` 和 `end()` 分别用来渲染表单的开始和关闭标签。 +在这两个方法之间使用了 [[yii\widgets\ActiveForm::field()|field()]] 方法去创建输入框。 +第一个输入框用于 “name”,第二个输入框用于 “email”。 +之后使用 [[yii\helpers\Html::submitButton()]] +方法生成提交按钮。 尝试下 @@ -172,11 +190,13 @@ use yii\widgets\ActiveForm; http://hostname/index.php?r=site/entry ``` -你会看到一个包含两个输入框的表单的页面。每个输入框的前面都有一个标签指明应该输入的数据类型。如果什么都不填就点击提交按钮,或填入格式不正确的 email 地址,将会看到在对应的输入框下显示错误信息。 +你会看到一个包含两个输入框的表单的页面。每个输入框的前面都有一个标签指明应该输入的数据类型。 +如果什么都不填就点击提交按钮,或填入格式不正确的 email 地址,将会看到在对应的输入框下显示错误信息。 ![验证错误的表单](images/start-form-validation.png) -输入有效的 name 和 email 信息并提交后,将会看到一个显示你所提交数据的确认页面。 +输入有效的 name 和 email 信息并提交后, +将会看到一个显示你所提交数据的确认页面。 ![输入数据的确认页](images/start-entry-confirmation.png) @@ -184,37 +204,43 @@ http://hostname/index.php?r=site/entry ### 效果说明 -你可能会好奇 HTML 表单暗地里是如何工作的呢,看起来它可以为每个输入框显示文字标签,而当你没输入正确的信息时又不需要刷新页面就能给出错误提示,似乎有些神奇。 +你可能会好奇 HTML 表单暗地里是如何工作的呢,看起来它可以为每个输入框显示文字标签, +而当你没输入正确的信息时又不需要刷新页面就能给出错误提示, +似乎有些神奇。 -是的,其实数据首先由客户端 JavaScript 脚本验证,然后才会提交给服务器通过 PHP 验证。[[yii\widgets\ActiveForm]] 足够智能到把你在 `EntryForm` 模型中声明的验证规则转化成客户端 JavaScript 脚本去执行验证。如果用户浏览器禁用了 JavaScript, 服务器端仍然会像 `actionEntry()` 方法里这样验证一遍数据。这保证了任何情况下用户提交的数据都是有效的。 +是的,其实数据首先由客户端 JavaScript 脚本验证,然后才会提交给服务器通过 PHP 验证。 +[[yii\widgets\ActiveForm]] 足够智能到把你在 `EntryForm` 模型中声明的验证规则转化成客户端 JavaScript 脚本去执行验证。 +如果用户浏览器禁用了 JavaScript, +服务器端仍然会像 `actionEntry()` 方法里这样验证一遍数据。 +这保证了任何情况下用户提交的数据都是有效的。 -<<<<<<< .merge_file_a03416 -> 警告:客户端验证是提高用户体验的手段。无论它是否正常启用,服务端验证则都是必须的,请不要忽略它。 -======= -> Warning: 客户端验证是提高用户体验的手段。无论它是否正常启用,服务端验证则都是必须的,请不要忽略它。 ->>>>>>> .merge_file_a05016 +> 警告:客户端验证是提高用户体验的手段。无论它是否正常启用, + 服务端验证则都是必须的,请不要忽略它。 -输入框的文字标签是 `field()` 方法生成的,内容就是模型中该数据的属性名。例如模型中的 `name` 属性生成的标签就是 `Name`。 +输入框的文字标签是 `field()` 方法生成的,内容就是模型中该数据的属性名。 +例如模型中的 `name` 属性生成的标签就是 `Name`。 -你可以在视图中自定义标签: +你可以按如下方式 +在视图中自定义标签: ```php field($model, 'name')->label('自定义 Name') ?> field($model, 'email')->label('自定义 Email') ?> ``` -<<<<<<< .merge_file_a03416 -> 补充:Yii 提供了相当多类似的小部件去帮你生成复杂且动态的视图。在后面你还会了解到自己写小部件是多么简单。你可能会把自己的很多视图代码转化成小部件以提高重用,加快开发效率。 -======= -> Info: Yii 提供了相当多类似的小部件去帮你生成复杂且动态的视图。在后面你还会了解到自己写小部件是多么简单。你可能会把自己的很多视图代码转化成小部件以提高重用,加快开发效率。 ->>>>>>> .merge_file_a05016 +> 补充:Yii 提供了相当多类似的小部件去帮你生成复杂且动态的视图。 + 在后面你还会了解到自己写小部件是多么简单。 + 你可能会把自己的很多视图代码转化成小部件以提高重用,加快开发效率。 总结 ------- -本章节指南中你接触了 MVC 设计模式的每个部分。学到了如何创建一个模型代表用户数据并验证它的有效性。 +本章节指南中你接触了 MVC 设计模式的每个部分。 +学到了如何创建一个模型代表用户数据并验证它的有效性。 -你还学到了如何从用户那获取数据并在浏览器上回显给用户。这本来是开发应用的过程中比较耗时的任务,好在 Yii 提供了强大的小部件让它变得如此简单。 +你还学到了如何从用户那获取数据并在浏览器上回显给用户。 +这本来是开发应用的过程中比较耗时的任务, +好在 Yii 提供了强大的小部件让它变得如此简单。 下一章你将学习如何使用数据库,几乎每个应用都需要数据库。 diff --git a/docs/guide-zh-CN/start-gii.md b/docs/guide-zh-CN/start-gii.md index bedf720..8389acc 100644 --- a/docs/guide-zh-CN/start-gii.md +++ b/docs/guide-zh-CN/start-gii.md @@ -1,7 +1,8 @@ 使用 Gii 生成代码 ======================== -本章将介绍如何使用 [Gii](tool-gii.md) 去自动生成 Web 站点常用功能的代码。使用 Gii 生成代码非常简单,只要按照 Gii 页面上的介绍输入正确的信息即可。 +本章将介绍如何使用 [Gii](tool-gii.md) 去自动生成 Web 站点常用功能的代码。使用 Gii 生成代码非常简单, +只要按照 Gii 页面上的介绍输入正确的信息即可。 贯穿本章节,你将会学到: @@ -14,20 +15,25 @@ 开始 Gii ------------ -[Gii](tool-gii.md) 是 Yii 中的一个[模块](structure-modules.md)。可以通过配置应用的 [[yii\base\Application::modules|modules]] 属性开启它。通常来讲在 `config/web.php` 文件中会有以下配置代码: +[Gii](tool-gii.md) 是 Yii 中的一个[模块](structure-modules.md)。可以通过配置应用的 [[yii\base\Application::modules|modules]] 属性开启它。 +通常来讲在 `config/web.php` 文件中会有以下配置代码: ```php $config = [ ... ]; if (YII_ENV_DEV) { $config['bootstrap'][] = 'gii'; - $config['modules']['gii'] = 'yii\gii\Module'; + $config['modules']['gii'] = [ + 'class' => 'yii\gii\Module', + ]; } ``` -这段配置表明,如果当前是[开发环境](concept-configurations.md#environment-constants),应用会包含 `gii` 模块,模块类是 [[yii\gii\Module]]。 +这段配置表明,如果当前是[开发环境](concept-configurations.md#environment-constants), +应用会包含 `gii` 模块,模块类是 [[yii\gii\Module]]。 -如果你检查应用的[入口脚本](structure-entry-scripts.md) `web/index.php`,将看到这行代码将 `YII_ENV_DEV` 设为 true: +如果你检查应用的[入口脚本](structure-entry-scripts.md) `web/index.php`, +将看到这行代码将 `YII_ENV_DEV` 设为 true: ```php defined('YII_ENV') or define('YII_ENV', 'dev'); @@ -39,11 +45,8 @@ defined('YII_ENV') or define('YII_ENV', 'dev'); http://hostname/index.php?r=gii ``` -<<<<<<< .merge_file_a06868 -> 补充: 如果你通过本机以外的机器访问 Gii,请求会被出于安全原因拒绝。你可以配置 Gii 为其添加允许访问的 IP 地址: -======= -> Info: 如果你通过本机以外的机器访问 Gii,请求会被出于安全原因拒绝。你可以配置 Gii 为其添加允许访问的 IP 地址: ->>>>>>> .merge_file_a02624 +> 补充: 如果你通过本机以外的机器访问 Gii,请求会被出于安全原因拒绝。 +> 你可以配置 Gii 为其添加允许访问的 IP 地址: > ```php 'gii' => [ @@ -67,13 +70,16 @@ http://hostname/index.php?r=gii 然后点击 “Preview” 按钮。你会看到 `models/Country.php` 被列在将要生成的文件列表中。可以点击文件名预览内容。 -如果你已经创建过同样的文件,使用 Gii 会覆写它,点击文件名旁边的 `diff` 能查看现有文件与将要生成的文件的内容区别。 +如果你已经创建过同样的文件, +使用 Gii 会覆写它, +点击文件名旁边的 `diff` 能查看现有文件与将要生成的文件的内容区别。 ![模型生成器预览](images/start-gii-model-preview.png) 想要覆写已存在文件,选中 “overwrite” 下的复选框然后点击 “Generator”。如果是新文件,只点击 “Generator” 就好。 -接下来你会看到一个包含已生成文件的说明页面。如果生成过程中覆写过文件,还会有一条信息说明代码是重新生成覆盖的。 +接下来你会看到一个包含已生成文件的说明页面。如果生成过程中覆写过文件, +还会有一条信息说明代码是重新生成覆盖的。 生成 CRUD 代码 @@ -91,7 +97,8 @@ CRUD 代表增,查,改,删操作,这是绝大多数 Web 站点常用的 [[NEED THE IMAGE HERE / 等待官方补充图片]] -如果你之前创建过 `controllers/CountryController.php` 和 `views/country/index.php` 文件(在指南的使用数据库章节),选中 “overwrite” 下的复选框覆写它们(之前的文件没能全部支持 CRUD)。 +如果你之前创建过 `controllers/CountryController.php` 和 `views/country/index.php` 文件(在指南的使用数据库章节), +选中 “overwrite” 下的复选框覆写它们(之前的文件没能全部支持 CRUD)。 试运行 @@ -103,28 +110,30 @@ CRUD 代表增,查,改,删操作,这是绝大多数 Web 站点常用的 http://hostname/index.php?r=country/index ``` -可以看到一个栅格显示着从数据表中读取的国家数据。支持在列头对数据进行排序,输入筛选条件进行筛选。 +可以看到一个栅格显示着从数据表中读取的国家数据。 +支持在列头对数据进行排序,输入筛选条件进行筛选。 -可以浏览详情,编辑,或删除栅格中的每个国家。还可以点击栅格上方的 “Create Country” 按钮通过表单创建新国家。 +可以浏览详情,编辑,或删除栅格中的每个国家。 +还可以点击栅格上方的 “Create Country” 按钮通过表单创建新国家。 ![国家的数据栅格](images/start-gii-country-grid.png) ![编辑一个国家](images/start-gii-country-update.png) -下面列出由 Gii 生成的文件,以便你研习功能和实现,或修改它们。 +下面列出由 Gii 生成的文件,以便你研习功能和实现, +或修改它们。 * 控制器:`controllers/CountryController.php` * 模型:`models/Country.php` 和 `models/CountrySearch.php` * 视图:`views/country/*.php` -<<<<<<< .merge_file_a06868 -> 补充:Gii 被设计成高度可定制和可扩展的代码生成工具。使用它可以大幅提高应用开发速度。请参考 [Gii](tool-gii.md) 章节了解更多内容。 -======= -> Info: Gii 被设计成高度可定制和可扩展的代码生成工具。使用它可以大幅提高应用开发速度。请参考 [Gii](tool-gii.md) 章节了解更多内容。 ->>>>>>> .merge_file_a02624 +> 注意:Gii 被设计成高度可定制和可扩展的代码生成工具。 + 使用它可以大幅提高应用开发速度。 + 请参考 [Gii](tool-gii.md) 章节了解更多内容。 总结 ------- -本章学习了如何使用 Gii 去生成为数据表中数据实现完整 CRUD 功能的代码。 +本章学习了如何使用 Gii 去生成为数据表中 +数据实现完整 CRUD 功能的代码。 diff --git a/docs/guide-zh-CN/start-hello.md b/docs/guide-zh-CN/start-hello.md index 5bc9f9e..af39380 100644 --- a/docs/guide-zh-CN/start-hello.md +++ b/docs/guide-zh-CN/start-hello.md @@ -1,7 +1,9 @@ 说声 Hello ============ -本章描述了如何在你的应用中创建一个新的 “Hello” 页面。为了实现这一目标,将会创建一个[操作](structure-controllers.md#creating-actions)和一个[视图](structure-views.md): +本章描述了如何在你的应用中创建一个新的 “Hello” 页面。 +为了实现这一目标, +将会创建一个[操作](structure-controllers.md#creating-actions)和一个[视图](structure-views.md): * 应用将会分派页面请求给操作 * 操作将会依次渲染视图呈现 “Hello” 给最终用户 @@ -16,15 +18,17 @@ 创建操作 ------------------ -为了 “Hello”,需要创建一个 `say` [操作](structure-controllers.md#creating-actions),从请求中接收 `message` 参数并显示给最终用户。如果请求没有提供 `message` 参数,操作将显示默认参数 “Hello”。 +为了 “Hello”,需要创建一个 `say` [操作](structure-controllers.md#creating-actions), +从请求中接收 `message` 参数并显示给最终用户。如果请求没有提供 `message` 参数, +操作将显示默认参数 “Hello”。 -<<<<<<< .merge_file_a05440 -> 补充:[操作](structure-controllers.md#creating-actions)是最终用户可以直接访问并执行的对象。操作被组织在[控制器](structure-controllers.md)中。一个操作的执行结果就是最终用户收到的响应内容。 -======= -> Info: [操作](structure-controllers.md#creating-actions)是最终用户可以直接访问并执行的对象。操作被组织在[控制器](structure-controllers.md)中。一个操作的执行结果就是最终用户收到的响应内容。 ->>>>>>> .merge_file_a02836 +> 注意:[操作](structure-controllers.md#creating-actions)是最终用户可以直接访问并执行的对象。 + 操作被组织在[控制器](structure-controllers.md)中。 + 一个操作的执行结果就是最终用户收到的响应内容。 -操作必须声明在[控制器](structure-controllers.md)中。为了简单起见,你可以直接在 `SiteController` 控制器里声明 `say` 操作。这个控制器是由文件 `controllers/SiteController.php` 定义的。以下是一个操作的声明: +操作必须声明在[控制器](structure-controllers.md)中。为了简单起见, +你可以直接在 `SiteController` 控制器里声明 `say` 操作。这个控制器是由文件 `controllers/SiteController.php` 定义的。 +以下是一个操作的声明: ```php --------------- -[视图](structure-views.md)是你用来生成响应内容的脚本。为了说 “Hello”,你需要创建一个 `say` 视图,以便显示从操作方法中传来的 `message` 参数。 +[视图](structure-views.md)是你用来生成响应内容的脚本。为了说 “Hello”, +你需要创建一个 `say` 视图,以便显示从操作方法中传来的 `message` 参数。 ```php ``` -`say` 视图应该存为 `views/site/say.php` 文件。当一个操作中调用了 [[yii\web\Controller::render()|render()]] 方法时,它将会按 `views/控制器 ID/视图名.php` 路径加载 PHP 文件。 +`say` 视图应该存为 `views/site/say.php` 文件。当一个操作中调用了 [[yii\web\Controller::render()|render()]] 方法时, +它将会按 `views/控制器 ID/视图名.php` 路径加载 PHP 文件。 -注意以上代码,`message` 参数在输出之前被 [[yii\helpers\Html::encode()|HTML-encoded]] 方法处理过。这很有必要,当参数来自于最终用户时,参数中可能隐含的恶意 JavaScript 代码会导致[跨站脚本(XSS)攻击](http://en.wikipedia.org/wiki/Cross-site_scripting)。 +注意以上代码,`message` 参数在输出之前被 [[yii\helpers\Html::encode()|HTML-encoded]] 方法处理过。 +这很有必要,当参数来自于最终用户时, +参数中可能隐含的恶意 JavaScript 代码会导致 +[跨站脚本(XSS)攻击](http://en.wikipedia.org/wiki/Cross-site_scripting)。 -当然了,你大概会在 `say` 视图里放入更多内容。内容可以由 HTML 标签,纯文本,甚至 PHP 语句组成。实际上 `say` 视图就是一个由 [[yii\web\Controller::render()|render()]] 执行的 PHP 脚本。视图脚本输出的内容将会作为响应结果返回给应用。应用将依次输出结果给最终用户。 +当然了,你大概会在 `say` 视图里放入更多内容。内容可以由 HTML 标签,纯文本, +甚至 PHP 语句组成。实际上 `say` 视图就是一个由 [[yii\web\Controller::render()|render()]] 执行的 PHP 脚本。 +视图脚本输出的内容将会作为响应结果返回给应用。应用将依次输出结果给最终用户。 试运行 @@ -85,27 +109,34 @@ http://hostname/index.php?r=site/say&message=Hello+World 这个 URL 将会输出包含 “Hello World” 的页面,页面和应用里的其它页面使用同样的头部和尾部。 -如果你省略 URL 中的 `message` 参数,将会看到页面只显示 “Hello”。这是因为 `message` 被作为一个参数传给 `actionSay()` 方法,当省略它时,参数将使用默认的 `“Hello”` 代替。 +如果你省略 URL 中的 `message` 参数,将会看到页面只显示 “Hello”。 +这是因为 `message` 被作为一个参数传给 `actionSay()` 方法,当省略它时,参数将使用默认的 `“Hello”` 代替。 -<<<<<<< .merge_file_a05440 -> 补充:新页面和其它页面使用同样的头部和尾部是因为 [[yii\web\Controller::render()|render()]] 方法会自动把 `say` 视图执行的结果嵌入称为[布局](structure-views.md#layouts)的文件中,本例中是 `views/layouts/main.php`。 +> 注意:新页面和其它页面使用同样的头部和尾部是因为 [[yii\web\Controller::render()|render()]] 方法会 + 自动把 `say` 视图执行的结果嵌入称为[布局](structure-views.md#layouts)的文件中, + 本例中是 `views/layouts/main.php`。 -上面 URL 中的参数 `r` 需要更多解释。它代表[路由](runtime-routing.md),是整个应用级的,指向特定操作的独立 ID。路由格式是 `控制器 ID/操作 ID`。应用接受请求的时候会检查参数,使用控制器 ID 去确定哪个控制器应该被用来处理请求。然后相应控制器将使用操作 ID 去确定哪个操作方法将被用来做具体工作。上述例子中,路由 `site/say` 将被解析至 `SiteController` 控制器和其中的 `say` 操作。因此 `SiteController::actionSay()` 方法将被调用处理请求。 +上面 URL 中的参数 `r` 需要更多解释。 +它代表[路由](runtime-routing.md),是整个应用级的, +指向特定操作的独立 ID。路由格式是 `控制器 ID/操作 ID`。应用接受请求的时候会检查参数, +使用控制器 ID 去确定哪个控制器应该被用来处理请求。 +然后相应控制器将使用操作 ID 去确定哪个操作方法将被用来做具体工作。 +上述例子中,路由 `site/say` 将被解析至 `SiteController` 控制器和其中的 `say` 操作。 +因此 `SiteController::actionSay()` 方法将被调用处理请求。 -> 补充:与操作一样,一个应用中控制器同样有唯一的 ID。控制器 ID 和操作 ID 使用同样的命名规则。控制器的类名源自于控制器 ID,移除了连字符,每个单词首字母大写,并加上 `Controller` 后缀。例子:控制器 ID `post-comment` 相当于控制器类名 `PostCommentController`。 -======= -> Info: 新页面和其它页面使用同样的头部和尾部是因为 [[yii\web\Controller::render()|render()]] 方法会自动把 `say` 视图执行的结果嵌入称为[布局](structure-views.md#layouts)的文件中,本例中是 `views/layouts/main.php`。 - -上面 URL 中的参数 `r` 需要更多解释。它代表[路由](runtime-routing.md),是整个应用级的,指向特定操作的独立 ID。路由格式是 `控制器 ID/操作 ID`。应用接受请求的时候会检查参数,使用控制器 ID 去确定哪个控制器应该被用来处理请求。然后相应控制器将使用操作 ID 去确定哪个操作方法将被用来做具体工作。上述例子中,路由 `site/say` 将被解析至 `SiteController` 控制器和其中的 `say` 操作。因此 `SiteController::actionSay()` 方法将被调用处理请求。 - -> Info: 与操作一样,一个应用中控制器同样有唯一的 ID。控制器 ID 和操作 ID 使用同样的命名规则。控制器的类名源自于控制器 ID,移除了连字符,每个单词首字母大写,并加上 `Controller` 后缀。例子:控制器 ID `post-comment` 相当于控制器类名 `PostCommentController`。 ->>>>>>> .merge_file_a02836 +> 注意:与操作一样,一个应用中控制器同样有唯一的 ID。 + 控制器 ID 和操作 ID 使用同样的命名规则。 + 控制器的类名源自于控制器 ID,移除了连字符 + ,每个单词首字母大写,并加上 `Controller` 后缀。 + 例子:控制器 ID `post-comment` 相当于控制器类名 `PostCommentController`。 总结 ------- -通过本章节你接触了 MVC 设计模式中的控制器和视图部分。创建了一个操作作为控制器的一部分去处理特定请求。然后又创建了一个视图去构造响应内容。在这个小例子中,没有模型调用,唯一涉及到数据的地方是 `message` 参数。 +通过本章节你接触了 MVC 设计模式中的控制器和视图部分。 +创建了一个操作作为控制器的一部分去处理特定请求。然后又创建了一个视图去构造响应内容。 +在这个小例子中,没有模型调用,唯一涉及到数据的地方是 `message` 参数。 你同样学习了 Yii 路由的相关内容,它是用户请求与控制器操作之间的桥梁。 diff --git a/docs/guide-zh-CN/start-installation.md b/docs/guide-zh-CN/start-installation.md index 24b8920..e8f98b2 100644 --- a/docs/guide-zh-CN/start-installation.md +++ b/docs/guide-zh-CN/start-installation.md @@ -1,67 +1,63 @@ 安装 Yii ============== -<<<<<<< .merge_file_a03504 -<<<<<<< HEAD -你可以通过两种方式安装 Yii:使用 [Composer](http://getcomposer.org/) 或下载一个归档文件。推荐使用前者,这样只需执行一条简单的命令就可以安装新的[扩展](structure-extensions.md)或更新 Yii 了。 -======= -你可以通过两种方式安装 Yii:使用 [Composer](https://getcomposer.org/) 或下载一个归档文件。推荐使用前者,这样只需执行一条简单的命令就可以安装新的[扩展](structure-extensions.md)或更新 Yii 了。 ->>>>>>> yiichina/master -======= -你可以通过两种方式安装 Yii:使用 [Composer](https://getcomposer.org/) 或下载一个归档文件。推荐使用前者,这样只需执行一条简单的命令就可以安装新的[扩展](structure-extensions.md)或更新 Yii 了。 ->>>>>>> .merge_file_a06364 +你可以通过两种方式安装 Yii:使用 [Composer](http://getcomposer.org/) 或下载一个归档文件。 +推荐使用前者,这样只需执行一条简单的命令就可以安装新的[扩展](structure-extensions.md)或更新 Yii 了。 -> Note: 和 Yii 1 不同,以标准方式安装 Yii 2 时会同时下载并安装框架本身和一个应用程序的基本骨架。 +Standard installations of Yii result in both the framework and a project template being downloaded and installed. +A project template is a working Yii project implementing some basic features, such as login, contact form, etc. +Its code is organized in a recommended way. Therefore, it can serve as a good starting point for your projects. + +In this and the next few sections, we will describe how to install Yii with the so-called *Basic Project Template* and +how to implement new features on top of this template. Yii also provides another template called +the [Advanced Project Template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md) which is better used in a team development environment +to develop applications with multiple tiers. + +> Info: The Basic Project Template is suitable for developing 90 percent of Web applications. It differs + from the Advanced Project Template mainly in how their code is organized. If you are new to Yii, we strongly + recommend you stick to the Basic Project Template for its simplicity yet sufficient functionalities. 通过 Composer 安装 ----------------------- -如果还没有安装 Composer,你可以按 [getcomposer.org](https://getcomposer.org/download/) 中的方法安装。在 Linux 和 Mac OS X 中可以运行如下命令: +如果还没有安装 Composer,你可以按 [getcomposer.org](https://getcomposer.org/download/) 中的方法安装。 +在 Linux 和 Mac OS X 中可以运行如下命令: -<<<<<<< .merge_file_a03504 -<<<<<<< HEAD - curl -s http://getcomposer.org/installer | php -======= - curl -sS https://getcomposer.org/installer | php ->>>>>>> yiichina/master -======= - curl -sS https://getcomposer.org/installer | php ->>>>>>> .merge_file_a06364 - mv composer.phar /usr/local/bin/composer +```bash +curl -sS https://getcomposer.org/installer | php +mv composer.phar /usr/local/bin/composer +``` 在 Windows 中,你需要下载并运行 [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe)。 -如果遇到任何问题或者想更深入地学习 Composer,请参考 [Composer 文档(英文)](https://getcomposer.org/doc/),[Composer 中文](https://github.com/5-say/composer-doc-cn)。 +如果遇到任何问题或者想更深入地学习 Composer, +请参考 [Composer 文档(英文)](https://getcomposer.org/doc/),[Composer 中文](https://github.com/5-say/composer-doc-cn)。 -如果你已经安装有 Composer 请确保使用的是最新版本,你可以用 `composer self-update` 命令更新 Composer 为最新版本。 +如果你已经安装有 Composer 请确保使用的是最新版本, +你可以用 `composer self-update` 命令更新 Composer 为最新版本。 Composer 安装后,切换到一个可通过 Web 访问的目录,执行如下命令即可安装 Yii : -<<<<<<< .merge_file_a03504 -<<<<<<< HEAD - composer global require "fxp/composer-asset-plugin:1.0.0" -======= - composer global require "fxp/composer-asset-plugin:~1.0.0" ->>>>>>> yiichina/master -======= - composer global require "fxp/composer-asset-plugin:~1.1.1" ->>>>>>> .merge_file_a06364 - composer create-project --prefer-dist yiisoft/yii2-app-basic basic - -第一条命令安装 [Composer asset plugin](https://github.com/francoispluchino/composer-asset-plugin/),它是通过 Composer 管理 bower 和 npm 包所必须的,此命令全局生效,一劳永逸。 +```bash +composer global require "fxp/composer-asset-plugin:~1.1.1" +composer create-project --prefer-dist yiisoft/yii2-app-basic basic +``` + +第一条命令安装 [Composer asset plugin](https://github.com/francoispluchino/composer-asset-plugin/), +它是通过 Composer 管理 bower 和 npm 包所必须的,此命令全局生效,一劳永逸。 第二条命令会将 Yii 安装在名为 `basic` 的目录中,你也可以随便选择其他名称。 -> Note: 在安装过程中 Composer 可能会询问你 GitHub 账户的登录信息,因为可能在使用中超过了 GitHub API -(对匿名用户的)使用限制。因为 Composer 需要为所有扩展包从 GitHub -中获取大量信息,所以超限非常正常。(译注:也意味着作为程序猿没有 GitHub 账号,就真不能愉快地玩耍了)登陆 GitHub -之后可以得到更高的 API 限额,这样 Composer 才能正常运行。更多细节请参考 [Composer -文档](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens)(该段 Composer -中文文档[期待您的参与](https://github.com/5-say/composer-doc-cn/blob/master/cn-introduction/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens))。 +> Note: 在安装过程中 Composer 可能会询问你 GitHub 账户的登录信息,因为可能在使用中超过了 GitHub API (对匿名用户的)使用限制。因为 Composer 需要为所有扩展包从 GitHub +> 中获取大量信息,所以超限非常正常。(译注:也意味着作为程序猿没有 GitHub 账号,就真不能愉快地玩耍了)登陆 GitHub 之后可以得到更高的 API 限额,这样 Composer 才能正常运行。更多细节请参考 [Composer +> 文档](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens)(该段 Composer中文文档[期待您的参与](https://github.com/5-say/composer-doc-cn/blob/master/cn-introduction/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens))。 -> Tip: 如果你想安装 Yii 的最新开发版本,可以使用以下命令代替,它添加了一个 [stability 选项](https://getcomposer.org/doc/04-schema.md#minimum-stability)([中文版](https://github.com/5-say/composer-doc-cn/blob/master/cn-introduction/04-schema.md#minimum-stability)): +> Tip: 如果你想安装 Yii 的最新开发版本,可以使用以下命令代替, +> 它添加了一个 [stability 选项](https://getcomposer.org/doc/04-schema.md#minimum-stability)([中文版](https://github.com/5-say/composer-doc-cn/blob/master/cn-introduction/04-schema.md#minimum-stability)): > -> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic +> ```bash +> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic +> ``` > > 注意,Yii 的开发版(dev 版)不应该用于生产环境中,它可能会破坏运行中的代码。 @@ -73,7 +69,8 @@ Composer 安装后,切换到一个可通过 Web 访问的目录,执行如下 1. 从 [yiiframework.com](http://www.yiiframework.com/download/) 下载归档文件。 2. 将下载的文件解压缩到 Web 目录中。 -3. 修改 `config/web.php` 文件,给 `cookieValidationKey` 配置项添加一个密钥(若你通过 Composer 安装,则此步骤会自动完成): +3. 修改 `config/web.php` 文件,给 `cookieValidationKey` 配置项 + 添加一个密钥(若你通过 Composer 安装,则此步骤会自动完成): ```php // !!! 在下面插入一段密钥(若为空) - 以供 cookie validation 的需要 @@ -84,28 +81,46 @@ Composer 安装后,切换到一个可通过 Web 访问的目录,执行如下 其他安装方式 -------------------------- -上文介绍了两种安装 Yii 的方法,安装的同时也会创建一个立即可用的 Web 应用程序。对于小的项目或用于学习上手,这都是一个不错的起点。 +上文介绍了两种安装 Yii 的方法, +安装的同时也会创建一个立即可用的 Web 应用程序。 +对于小的项目或用于学习上手,这都是一个不错的起点。 但是其他的安装方式也存在: -* 如果你只想安装核心框架,然后从零开始构建整个属于你自己的应用程序模版,可以参考[从头构建自定义模版](tutorial-start-from-scratch.md)一节的介绍。 -* 如果你要开发一个更复杂的应用,可以更好地适用于团队开发环境的,可以考虑安装[高级应用模版](tutorial-advanced-app.md)。 +* 如果你只想安装核心框架,然后从零开始构建整个属于你自己的应用程序模版, + 可以参考[从头构建自定义模版](tutorial-start-from-scratch.md)一节的介绍。 +* 如果你要开发一个更复杂的应用,可以更好地适用于团队开发环境的, + 可以考虑安装[高级应用模版](tutorial-advanced-app.md)。 验证安装的结果 -------------------------- +After installation is done, either configure your web server (see next section) or use the +[built-in PHP web server](https://secure.php.net/manual/en/features.commandline.webserver.php) by running the following +console command while in the project `web` directory: + +```bash +php yii serve +``` + +> Note: By default the HTTP-server will listen to port 8080. However if that port is already in use or you wish to +serve multiple applications this way, you might want to specify what port to use. Just add the --port argument: + +```bash +php yii serve --port=8888 +``` + 安装完成后,就可以使用浏览器通过如下 URL 访问刚安装完的 Yii 应用了: ``` -http://localhost/basic/web/index.php +http://localhost:8080/ ``` -这个 URL 假设你将 Yii 安装到了一个位于 Web 文档根目录下的 `basic` 目录中,且该 Web 服务器正运行在你自己的电脑上(`localhost`)。你可能需要将其调整为适应自己的安装环境。 - ![Yii 安装成功](images/start-app-installed.png) -你应该可以在浏览器中看到如上所示的 “Congratulations!” 页面。如果没有,请通过以下任意一种方式,检查当前 PHP 环境是否满足 Yii 最基本需求: +你应该可以在浏览器中看到如上所示的 “Congratulations!” 页面。如果没有, +请通过以下任意一种方式,检查当前 PHP 环境是否满足 Yii 最基本需求: * 通过浏览器访问 URL `http://localhost/basic/requirements.php` * 执行如下命令: @@ -115,26 +130,42 @@ http://localhost/basic/web/index.php php requirements.php ``` -你需要配置好 PHP 安装环境,使其符合 Yii 的最小需求。主要是需要 PHP 5.4 以上版本。如果应用需要用到数据库,那还要安装 [PDO PHP 扩展](http://www.php.net/manual/zh/pdo.installation.php) 和相应的数据库驱动(例如访问 MySQL 数据库所需的 `pdo_mysql`)。 +你需要配置好 PHP 安装环境,使其符合 Yii 的最小需求。主要是需要 PHP 5.4 以上版本。 +如果应用需要用到数据库,那还要安装 [PDO PHP 扩展](http://www.php.net/manual/zh/pdo.installation.php) +和相应的数据库驱动(例如访问 MySQL 数据库所需的 `pdo_mysql`)。 配置 Web 服务器 ----------------------- -> Info: 如果你现在只是要试用 Yii 而不是将其部署到生产环境中,本小节可以跳过。 +> Info: 如果你现在只是要试用 Yii 而不是将其部署到生产环境中, + 本小节可以跳过。 -通过上述方法安装的应用程序在 Windows,Max OS X,Linux 中的 [Apache HTTP 服务器](http://httpd.apache.org/)或 [Nginx HTTP 服务器](http://nginx.org/)且PHP版本为5.4或更高都可以直接运行。Yii 2.0 也兼容 Facebook 公司的 [HHVM](http://hhvm.com/),由于 HHVM 和标准 PHP 在边界案例上有些地方略有不同,在使用 HHVM 时需稍作处理。 +通过上述方法安装的应用程序在 Windows,Max OS X, +Linux 中的 [Apache HTTP 服务器](http://httpd.apache.org/) +或 [Nginx HTTP 服务器](http://nginx.org/)且PHP版本为5.4或更高都可以直接运行。 +Yii 2.0 也兼容 Facebook 公司的 [HHVM](http://hhvm.com/), +由于 HHVM 和标准 PHP 在边界案例上有些地方略有不同,在使用 HHVM 时需稍作处理。 -在生产环境的服务器上,你可能会想配置服务器让应用程序可以通过 URL `http://www.example.com/index.php` 访问而不是 `http://www.example.com/basic/web/index.php`。这种配置需要将 Web 服务器的文档根目录指向 `basic/web` 目录。可能你还会想隐藏掉 URL 中的 `index.php`,具体细节在 [URL 解析和生成](runtime-url-handling.md)一章中有介绍,你将学到如何配置 Apache 或 Nginx 服务器实现这些目标。 +在生产环境的服务器上,你可能会想配置服务器让应用程序可以通过 +URL `http://www.example.com/index.php` 访问而不是 `http://www.example.com/basic/web/index.php`。 +这种配置需要将 Web 服务器的文档根目录指向 `basic/web` 目录。 +可能你还会想隐藏掉 URL 中的 `index.php`,具体细节在 [URL 解析和生成](runtime-url-handling.md)一章中有介绍, +你将学到如何配置 Apache 或 Nginx 服务器实现这些目标。 -> Info: 将 `basic/web` 设置为文档根目录,可以防止终端用户访问 `basic/web` 相邻目录中的私有应用代码和敏感数据文件。禁止对其他目录的访问是一个不错的安全改进。 +> Info: 将 `basic/web` 设置为文档根目录,可以防止终端用户访问 `basic/web` 相邻目录中 +的私有应用代码和敏感数据文件。 +禁止对其他目录的访问是一个不错的安全改进。 -> Info: 如果你的应用程序将来要运行在共享虚拟主机环境中,没有修改其 Web 服务器配置的权限,你依然可以通过调整应用的结构来提升安全性。详情请参考[共享主机环境](tutorial-shared-hosting.md) 一章。 +> Info: 如果你的应用程序将来要运行在共享虚拟主机环境中, +没有修改其 Web 服务器配置的权限,你依然可以通过调整应用的结构来提升安全性。 +详情请参考[共享主机环境](tutorial-shared-hosting.md) 一章。 ### 推荐使用的 Apache 配置 -在 Apache 的 `httpd.conf` 文件或在一个虚拟主机配置文件中使用如下配置。注意,你应该将 `path/to/basic/web` 替换为实际的 `basic/web` 目录。 +在 Apache 的 `httpd.conf` 文件或在一个虚拟主机配置文件中使用如下配置。 +注意,你应该将 `path/to/basic/web` 替换为实际的 `basic/web` 目录。 ``` # 设置文档根目录为 “basic/web” @@ -156,36 +187,39 @@ DocumentRoot "path/to/basic/web" ### 推荐使用的 Nginx 配置 -为了使用 [Nginx](http://wiki.nginx.org/),你应该已经将 PHP 安装为 [FPM SAPI](http://php.net/install.fpm) 了。使用如下 Nginx 配置,将 `path/to/basic/web` 替换为实际的 `basic/web` 目录,`mysite.local` 替换为实际的主机名以提供服务。 +为了使用 [Nginx](http://wiki.nginx.org/),你应该已经将 PHP 安装为 [FPM SAPI](http://php.net/install.fpm) 了。 +使用如下 Nginx 配置,将 `path/to/basic/web` 替换为实际的 `basic/web` 目录, +`mysite.local` 替换为实际的主机名以提供服务。 -``` +```nginx server { charset utf-8; client_max_body_size 128M; - listen 80; ## 监听 ipv4 上的 80 端口 - #listen [::]:80 default_server ipv6only=on; ## 监听 ipv6 上的 80 端口 + listen 80; ## listen for ipv4 + #listen [::]:80 default_server ipv6only=on; ## listen for ipv6 server_name mysite.local; root /path/to/basic/web; index index.php; - access_log /path/to/basic/log/access.log main; + access_log /path/to/basic/log/access.log; error_log /path/to/basic/log/error.log; location / { - # 如果找不到真实存在的文件,把请求分发至 index.php - try_files $uri $uri/ /index.php?$args; + # Redirect everything that isn't a real file to index.php + try_files $uri $uri/ /index.php$is_args$args; } - # 若取消下面这段的注释,可避免 Yii 接管不存在文件的处理过程(404) + # uncomment to avoid processing of calls to non-existing static files by Yii #location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ { # try_files $uri =404; #} #error_page 404 /404.html; location ~ \.php$ { - include fastcgi.conf; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass 127.0.0.1:9000; #fastcgi_pass unix:/var/run/php5-fpm.sock; try_files $uri =404; @@ -197,6 +231,8 @@ server { } ``` -使用该配置时,你还应该在 `php.ini` 文件中设置 `cgi.fix_pathinfo=0` ,能避免掉很多不必要的 `stat()` 系统调用。 +使用该配置时,你还应该在 `php.ini` 文件中设置 `cgi.fix_pathinfo=0` , +能避免掉很多不必要的 `stat()` 系统调用。 -还要注意当运行一个 HTTPS 服务器时,需要添加 `fastcgi_param HTTPS on;` 一行,这样 Yii 才能正确地判断连接是否安全。 +还要注意当运行一个 HTTPS 服务器时,需要添加 `fastcgi_param HTTPS on;` 一行, +这样 Yii 才能正确地判断连接是否安全。 diff --git a/docs/guide-zh-CN/start-looking-ahead.md b/docs/guide-zh-CN/start-looking-ahead.md index e462f2b..7ec655a 100644 --- a/docs/guide-zh-CN/start-looking-ahead.md +++ b/docs/guide-zh-CN/start-looking-ahead.md @@ -1,22 +1,35 @@ 更上一层楼 ============= -通篇阅读完整个“入门”部分,你就完成了一个完整 Yii 应用的创建。在此过程中你学到了如何实现一些常用功能,例如通过 HTML 表单从用户那获取数据,从数据库中获取数据并以分页形式显示。你还学到了如何通过 [Gii](tool-gii.md) 去自动生成代码。使用 Gii 生成代码把 Web 开发中多数繁杂的过程转化为仅仅填写几个表单就行。 +通篇阅读完整个“入门”部分,你就完成了一个完整 Yii 应用的创建。 +在此过程中你学到了如何实现一些常用功能,例如通过 HTML 表单从用户那获取数据, +从数据库中获取数据并以分页形式显示。你还学到了如何通过 [Gii](tool-gii.md) 去自动生成代码。 +使用 Gii 生成代码把 Web 开发中多数繁杂的过程转化为仅仅填写几个表单就行。 本章将介绍一些有助于更好使用 Yii 的资源: * 文档 - - 权威指南:顾名思义,指南详细描述了 Yii 的工作原理并提供了如何使用它的常规引导。这是最重要的 Yii 辅助资料,强烈建议在开始写 Yii 代码之前阅读。 - - 类参考手册:描述了 Yii 中每个类的用法。在编码过程中这极为有用,能够帮你理清某个特定类,方法,和属性的用法。类参考手册最好在整个框架的语境下去理解。 - - Wiki 文章:Wiki 文章是 Yii 用户在其自身经验基础上分享出来的。大多数是使用教程或如何使用 Yii 解决特定问题。虽然这些文章质量可能并不如权威指南,但它们往往覆盖了更广泛的话题,并常常提供解决方案,所以它们也很有用。 + - 权威指南:顾名思义, + 指南详细描述了 Yii 的工作原理并提供了如何使用它的常规引导。 + 这是最重要的 Yii 辅助资料, + 强烈建议在开始写 Yii 代码之前阅读。 + - 类参考手册:描述了 Yii 中每个类的用法。在编码过程中这极为有用, + 能够帮你理清某个特定类,方法,和属性的用法。 + 类参考手册最好在整个框架的语境下去理解。 + - Wiki 文章:Wiki 文章是 Yii 用户在其自身经验基础上分享出来的。 + 大多数是使用教程或如何使用 Yii 解决特定问题。 + 虽然这些文章质量可能并不如权威指南, + 但它们往往覆盖了更广泛的话题, + 并常常提供解决方案,所以它们也很有用。 - 书籍 -* [扩展](http://www.yiiframework.com/extensions/):Yii 拥有数以千计用户提供的扩展,这些扩展能非常方便的插入到应用中,使你的应用开发过程更加方便快捷。 +* [扩展](http://www.yiiframework.com/extensions/):Yii 拥有数以千计用户提供的扩展, + 这些扩展能非常方便的插入到应用中,使你的应用开发过程更加方便快捷。 * 社区 - 官方论坛: - - IRC 聊天室:Freenode 网络上的 #yii 频道 ()(使用英文哦,无需反馈上游的问题可以加 -QQ-Yii2中国交流群) + - IRC 聊天室:Freenode 网络上的 #yii 频道 () + - Gitter chat: - GitHub: - Facebook: - Twitter: - LinkedIn: - + - Stackoverflow: diff --git a/docs/guide-zh-CN/start-workflow.md b/docs/guide-zh-CN/start-workflow.md index 4f789f8..dfe4bce 100644 --- a/docs/guide-zh-CN/start-workflow.md +++ b/docs/guide-zh-CN/start-workflow.md @@ -1,13 +1,19 @@ 运行应用 ==================== -安装 Yii 后,就有了一个可运行的 Yii 应用,根据配置的不同,可以通过 `http://hostname/basic/web/index.php` 或 `http://hostname/index.php` 访问。本章节将介绍应用的内建功能,如何组织代码,以及一般情况下应用如何处理请求。 +安装 Yii 后,就有了一个可运行的 Yii 应用, +根据配置的不同,可以通过 `http://hostname/basic/web/index.php` 或 `http://hostname/index.php` 访问。 +本章节将介绍应用的内建功能,如何组织代码, +以及一般情况下应用如何处理请求。 + +> 补充:为简单起见,在整个“入门”板块都假定你已经把 + `basic/web` 设为 Web 服务器根目录并配置完毕, + 你访问应用的地址会是 `http://lostname/index.php` 或类似的。 + 请按需调整 URL。 + +Note that unlike framework itself, after project template is installed it's all yours. You're free to add or delete +code and overall modify it as you need. -<<<<<<< .merge_file_a07004 -> 补充:为简单起见,在整个“入门”板块都假定你已经把 `basic/web` 设为 Web 服务器根目录并配置完毕,你访问应用的地址会是 `http://lostname/index.php` 或类似的。请按需调整 URL。 -======= -> Info: 为简单起见,在整个“入门”板块都假定你已经把 `basic/web` 设为 Web 服务器根目录并配置完毕,你访问应用的地址会是 `http://lostname/index.php` 或类似的。请按需调整 URL。 ->>>>>>> .merge_file_a04932 功能 ------------- @@ -17,11 +23,18 @@ * 主页,当你访问 `http://hostname/index.php` 时显示, * “About” 页, * “Contact” 页, 显示一个联系表单,允许终端用户通过 Email 联系你, -* “Login” 页, 显示一个登录表单,用来验证终端用户。试着用 “admin/admin” 登录,你可以看到当前是登录状态,已经可以“退出登录”了。 +* “Login” 页, 显示一个登录表单,用来验证终端用户。试着用 “admin/admin” 登录, + 你可以看到当前是登录状态,已经可以“退出登录”了。 + +这些页面使用同一个头部和尾部。 +头部包含了一个可以在不同页面间切换的导航栏。 -这些页面使用同一个头部和尾部。头部包含了一个可以在不同页面间切换的导航栏。 +在浏览器底部可以看到一个工具栏。这是 Yii 提供的很有用的[调试工具](tool-debugger.md), +可以记录并显示大量的调试信息,例如日志信息,响应状态,数据库查询等等。 -在浏览器底部可以看到一个工具栏。这是 Yii 提供的很有用的[调试工具](tool-debugger.md),可以记录并显示大量的调试信息,例如日志信息,响应状态,数据库查询等等。 +Additionally to the web application, there is a console script called `yii`, which is located in the applications base directory. +This script can be used to run background and maintenance tasks for the application, which are described +in the [Console Application Section](tutorial-console.md). 应用结构 @@ -47,15 +60,23 @@ basic/ 应用根目录 yii Yii 控制台命令执行脚本 ``` -一般来说,应用中的文件可被分为两类:在 `basic/web` 下的和在其它目录下的。前者可以直接通过 HTTP 访问(例如浏览器),后者不能也不应该被直接访问。 +一般来说,应用中的文件可被分为两类:在 `basic/web` 下的和在其它目录下的。 +前者可以直接通过 HTTP 访问(例如浏览器),后者不能也不应该被直接访问。 -Yii 实现了[模型-视图-控制器 (MVC)](http://wikipedia.org/wiki/Model-view-controller)设计模式,这点在上述目录结构中也得以体现。 `models` 目录包含了所有[模型类](structure-models.md),`views` 目录包含了所有[视图脚本](structure-views.md),`controllers` 目录包含了所有[控制器类](structure-controllers.md)。 +Yii 实现了[模型-视图-控制器 (MVC)](http://wikipedia.org/wiki/Model-view-controller)设计模式,这点在上述目录结构中也得以体现。 +`models` 目录包含了所有[模型类](structure-models.md), +`views` 目录包含了所有[视图脚本](structure-views.md), +`controllers` 目录包含了所有[控制器类](structure-controllers.md)。 以下图表展示了一个应用的静态结构: ![应用静态结构](images/application-structure.png) -每个应用都有一个入口脚本 `web/index.php`,这是整个应用中唯一可以访问的 PHP 脚本。入口脚本接受一个 Web 请求并创建[应用](structure-application.md)实例去处理它。 [应用](structure-applications.md)在它的[组建](concept-components.md)辅助下解析请求,并分派请求至 MVC 元素。[视图](structure-views.md)使用[小部件](structure-widgets.md)去创建复杂和动态的用户界面。 +每个应用都有一个入口脚本 `web/index.php`, +这是整个应用中唯一可以访问的 PHP 脚本。 +入口脚本接受一个 Web 请求并创建[应用](structure-application.md)实例去处理它。 +[应用](structure-applications.md)在它的[组建](concept-components.md)辅助下解析请求, +并分派请求至 MVC 元素。[视图](structure-views.md)使用[小部件](structure-widgets.md)去创建复杂和动态的用户界面。 请求生命周期 @@ -66,8 +87,10 @@ Yii 实现了[模型-视图-控制器 (MVC)](http://wikipedia.org/wiki/Model-vie ![请求生命周期](images/request-lifecycle.png) 1. 用户向[入口脚本](structure-entry-scripts.md) `web/index.php` 发起请求。 -2. 入口脚本加载应用[配置](concept-configurations.md)并创建一个[应用](structure-applications.md)实例去处理请求。 -3. 应用通过[请求](runtime-request.md)组件解析请求的[路由](runtime-routing.md)。 +2. 入口脚本加载应用[配置](concept-configurations.md) + 并创建一个[应用](structure-applications.md)实例去处理请求。 +3. 应用通过[请求](runtime-request.md)组件 + 解析请求的[路由](runtime-routing.md)。 4. 应用创建一个[控制器](structure-controllers.md)实例去处理请求。 5. 控制器创建一个[操作](structure-controllers.md)实例并针对操作执行过滤器。 6. 如果任何一个过滤器返回失败,则操作退出。 diff --git a/docs/guide-zh-CN/structure-application-components.md b/docs/guide-zh-CN/structure-application-components.md index b813ac7..f8310c5 100644 --- a/docs/guide-zh-CN/structure-application-components.md +++ b/docs/guide-zh-CN/structure-application-components.md @@ -1,10 +1,13 @@ 应用组件 ====================== -应用主体是[服务定位器](concept-service-locator.md),它部署一组提供各种不同功能的 *应用组件* 来处理请求。 -例如,`urlManager`组件负责处理网页请求路由到对应的控制器。`db`组件提供数据库相关服务等等。 +应用主体是[服务定位器](concept-service-locator.md), +它部署一组提供各种不同功能的 *应用组件* 来处理请求。 +例如,`urlManager`组件负责处理网页请求路由到对应的控制器。 +`db`组件提供数据库相关服务等等。 -在同一个应用中,每个应用组件都有一个独一无二的 ID 用来区分其他应用组件,你可以通过如下表达式访问应用组件。 +在同一个应用中,每个应用组件都有一个独一无二的 ID 用来区分其他应用组件, +你可以通过如下表达式访问应用组件。 ```php \Yii::$app->componentID @@ -13,9 +16,11 @@ 例如,可以使用 `\Yii::$app->db` 来获取到已注册到应用的 [[yii\db\Connection|DB connection]], 使用 `\Yii::$app->cache` 来获取到已注册到应用的 [[yii\caching\Cache|primary cache]]。 -第一次使用以上表达式时候会创建应用组件实例,后续再访问会返回此实例,无需再次创建。 +第一次使用以上表达式时候会创建应用组件实例, +后续再访问会返回此实例,无需再次创建。 -应用组件可以是任意对象,可以在 [应用主体配置](structure-applications.md#application-configurations)配置 [[yii\base\Application::components]] 属性 . +应用组件可以是任意对象,可以在 +[应用主体配置](structure-applications.md#application-configurations)配置 [[yii\base\Application::components]] 属性 . 例如: ```php @@ -40,17 +45,16 @@ ] ``` -<<<<<<< .merge_file_a05900 -> 补充:请谨慎注册太多应用组件,应用组件就像全局变量,使用太多可能加大测试和维护的难度。 -======= -> Info: 请谨慎注册太多应用组件,应用组件就像全局变量,使用太多可能加大测试和维护的难度。 ->>>>>>> .merge_file_a06508 +> 补充:请谨慎注册太多应用组件, + 应用组件就像全局变量, + 使用太多可能加大测试和维护的难度。 一般情况下可以在需要时再创建本地组件。 ## 引导启动组件 -上面提到一个应用组件只会在第一次访问时实例化,如果处理请求过程没有访问的话就不实例化。 +上面提到一个应用组件只会在第一次访问时实例化, +如果处理请求过程没有访问的话就不实例化。 有时你想在每个请求处理过程都实例化某个组件即便它不会被访问, 可以将该组件ID加入到应用主体的 [[yii\base\Application::bootstrap|bootstrap]] 属性中。 @@ -79,27 +83,37 @@ Yii 定义了一组固定ID和默认配置的 *核心* 组件,例如 [[yii\web 通过这些组件,Yii应用主体能处理用户请求。 下面是预定义的核心应用组件列表,可以和普通应用组件一样配置和自定义它们。 -当你配置一个核心组件,不指定它的类名的话就会使用Yii默认指定的类。 +当你配置一个核心组件, +不指定它的类名的话就会使用Yii默认指定的类。 -* [[yii\web\AssetManager|assetManager]]: 管理资源包和资源发布,详情请参考 [管理资源](output-assets.md) 一节。 +* [[yii\web\AssetManager|assetManager]]: 管理资源包和资源发布, + 详情请参考 [管理资源](output-assets.md) 一节。 * [[yii\db\Connection|db]]: 代表一个可以执行数据库操作的数据库连接, - 注意配置该组件时必须指定组件类名和其他相关组件属性,如[[yii\db\Connection::dsn]]。 + 注意配置该组件时必须指定组件类名和其他相关组件属性, + 如[[yii\db\Connection::dsn]]。 详情请参考 [数据访问对象](db-dao.md) 一节。 * [[yii\base\Application::errorHandler|errorHandler]]: 处理 PHP 错误和异常, 详情请参考 [错误处理](tutorial-handling-errors.md) 一节。 -* [[yii\i18n\Formatter|formatter]]: 格式化输出显示给终端用户的数据,例如数字可能要带分隔符, +* [[yii\i18n\Formatter|formatter]]: 格式化输出显示给终端用户的数据, + 例如数字可能要带分隔符, 日期使用长格式。详情请参考 [格式化输出数据](output-formatting.md) 一节。 -* [[yii\i18n\I18N|i18n]]: 支持信息翻译和格式化。详情请参考 [国际化](tutorial-i18n.md) 一节。 -* [[yii\log\Dispatcher|log]]: 管理日志对象。详情请参考 [日志](tutorial-logging.md) 一节。 -* [[yii\swiftmailer\Mailer|mail]]: 支持生成邮件结构并发送,详情请参考 [邮件](tutorial-mailing.md) 一节。 +* [[yii\i18n\I18N|i18n]]: 支持信息翻译和格式化。 + 详情请参考 [国际化](tutorial-i18n.md) 一节。 +* [[yii\log\Dispatcher|log]]: 管理日志对象。 + 详情请参考 [日志](tutorial-logging.md) 一节。 +* [[yii\swiftmailer\Mailer|mail]]: 支持生成邮件结构并发送, + 详情请参考 [邮件](tutorial-mailing.md) 一节。 * [[yii\base\Application::response|response]]: 代表发送给用户的响应, 详情请参考 [响应](runtime-responses.md) 一节。 * [[yii\base\Application::request|request]]: 代表从终端用户处接收到的请求, 详情请参考 [请求](runtime-requests.md) 一节。 -* [[yii\web\Session|session]]: 代表会话信息,仅在[[yii\web\Application|Web applications]] 网页应用中可用, +* [[yii\web\Session|session]]: 代表会话信息, + 仅在[[yii\web\Application|Web applications]] 网页应用中可用, 详情请参考 [Sessions (会话) and Cookies](runtime-sessions-cookies.md) 一节。 * [[yii\web\UrlManager|urlManager]]: 支持URL地址解析和创建, 详情请参考 [URL 解析和生成](runtime-url-handling.md) 一节。 -* [[yii\web\User|user]]: 代表认证登录用户信息,仅在[[yii\web\Application|Web applications]] 网页应用中可用, +* [[yii\web\User|user]]: 代表认证登录用户信息, + 仅在[[yii\web\Application|Web applications]] 网页应用中可用, 详情请参考 [认证](security-authentication.md) 一节。 -* [[yii\web\View|view]]: 支持渲染视图,详情请参考 [Views](structure-views.md) 一节。 +* [[yii\web\View|view]]: 支持渲染视图, + 详情请参考 [Views](structure-views.md) 一节。 diff --git a/docs/guide-zh-CN/structure-applications.md b/docs/guide-zh-CN/structure-applications.md index 737f835..c958998 100644 --- a/docs/guide-zh-CN/structure-applications.md +++ b/docs/guide-zh-CN/structure-applications.md @@ -2,17 +2,21 @@ ============ 应用主体是管理 Yii 应用系统整体结构和生命周期的对象。 -每个Yii应用系统只能包含一个应用主体,应用主体在 [入口脚本](structure-entry-scripts.md) 中创建并能通过表达式 `\Yii::$app` 全局范围内访问。 +每个Yii应用系统只能包含一个应用主体, +应用主体在 [入口脚本](structure-entry-scripts.md) 中创建并能通过表达式 `\Yii::$app` 全局范围内访问。 -> Info: 当我们说"一个应用",它可能是一个应用主体对象,也可能是一个应用系统,是根据上下文来决定[译:中文为避免歧义,Application翻译为应用主体]。 +> Info: 当我们说"一个应用",它可能是一个应用主体对象,也可能是一个应用系统, + 是根据上下文来决定[译:中文为避免歧义,Application翻译为应用主体]。 Yii有两种应用主体: [[yii\web\Application|网页应用主体]] and -[[yii\console\Application|控制台应用主体]], 如名称所示,前者主要处理网页请求,后者处理控制台请求。 +[[yii\console\Application|控制台应用主体]], +如名称所示,前者主要处理网页请求,后者处理控制台请求。 ## 应用主体配置 -如下所示,当 [入口脚本](structure-entry-scripts.md) 创建了一个应用主体,它会加载一个 [配置](concept-configurations.md) 文件并传给应用主体。 +如下所示,当 [入口脚本](structure-entry-scripts.md) 创建了一个应用主体, +它会加载一个 [配置](concept-configurations.md) 文件并传给应用主体。 ```php require(__DIR__ . '/../vendor/autoload.php'); @@ -25,47 +29,60 @@ $config = require(__DIR__ . '/../config/web.php'); (new yii\web\Application($config))->run(); ``` -类似其他 [配置](concept-configurations.md) 文件, 应用主体配置文件标明如何设置应用对象初始属性。 -由于应用主体配置比较复杂,一般保存在多个类似如上web.php的 [配置文件](concept-configurations.md#configuration-files) 当中。 +类似其他 [配置](concept-configurations.md) 文件, +应用主体配置文件标明如何设置应用对象初始属性。 +由于应用主体配置比较复杂, +一般保存在多个类似如上web.php的 [配置文件](concept-configurations.md#configuration-files) 当中。 + ## 应用主体属性 -应用主体配置文件中有许多重要的属性要配置,这些属性指定应用主体的运行环境。 -比如,应用主体需要知道如何加载 [控制器](structure-controllers.md) ,临时文件保存到哪儿等等。 -以下我们简述这些属性。 +应用主体配置文件中有许多重要的属性要配置, +这些属性指定应用主体的运行环境。 +比如,应用主体需要知道如何加载 [控制器](structure-controllers.md) , +临时文件保存到哪儿等等。以下我们简述这些属性。 + ### 必要属性 -在一个应用中,至少要配置2个属性: [[yii\base\Application::id|id]] 和 [[yii\base\Application::basePath|basePath]]。 +在一个应用中,至少要配置2个属性: +[[yii\base\Application::id|id]] 和 [[yii\base\Application::basePath|basePath]]。 #### [[yii\base\Application::id|id]] -[[yii\base\Application::id|id]] 属性用来区分其他应用的唯一标识ID。主要给程序使用。 -为了方便协作,最好使用数字作为应用主体ID,但不强制要求为数字。 +[[yii\base\Application::id|id]] 属性用来区分其他应用的唯一标识ID。 +主要给程序使用。为了方便协作,最好使用数字作为应用主体ID, +但不强制要求为数字。 #### [[yii\base\Application::basePath|basePath]] - -[[yii\base\Application::basePath|basePath]] 指定该应用的根目录。根目录包含应用系统所有受保护的源代码。 -在根目录下可以看到对应MVC设计模式的`models`, `views`, `controllers`等子目录。 +[[yii\base\Application::basePath|basePath]] 指定该应用的根目录。 +根目录包含应用系统所有受保护的源代码。 +在根目录下可以看到对应MVC设计模式的`models`, +`views`, `controllers`等子目录。 可以使用路径或 [路径别名](concept-aliases.md) 来在配置 [[yii\base\Application::basePath|basePath]] 属性。 -两种格式所对应的目录都必须存在,否则系统会抛出一个异常。 系统会使用 `realpath()` 函数规范化配置的路径. +两种格式所对应的目录都必须存在,否则系统会抛出一个异常。 +系统会使用 `realpath()` 函数规范化配置的路径. -[[yii\base\Application::basePath|basePath]] 属性经常用于派生一些其他重要路径(如runtime路径),因此,系统预定义 `@app` 代表这个路径。 +[[yii\base\Application::basePath|basePath]] 属性经常用于派生一些其他重要路径(如runtime路径), +因此,系统预定义 `@app` 代表这个路径。 派生路径可以通过这个别名组成(如`@app/runtime`代表runtime的路径)。 ### 重要属性 -本小节所描述的属性通常需要设置,因为不同的应用属性不同。 +本小节所描述的属性通常需要设置, +因为不同的应用属性不同。 #### [[yii\base\Application::aliases|aliases]] -该属性允许你用一个数组定义多个 [别名](concept-aliases.md)。数组的key为别名名称,值为对应的路径。例如: +该属性允许你用一个数组定义多个 [别名](concept-aliases.md)。 +数组的key为别名名称,值为对应的路径。 +例如: ```php [ @@ -76,13 +93,16 @@ $config = require(__DIR__ . '/../config/web.php'); ] ``` -使用这个属性来定义别名,代替 [[Yii::setAlias()]] 方法来设置。 +使用这个属性来定义别名, +代替 [[Yii::setAlias()]] 方法来设置。 #### [[yii\base\Application::bootstrap|bootstrap]] -这个属性很实用,它允许你用数组指定启动阶段[[yii\base\Application::bootstrap()|bootstrapping process]]需要运行的组件。 -比如,如果你希望一个 [模块](structure-modules.md) 自定义 [URL 规则](runtime-url-handling.md),你可以将模块ID加入到bootstrap数组中。 +这个属性很实用,它允许你用数组指定启动阶段 +[[yii\base\Application::bootstrap()|bootstrapping process]]需要运行的组件。 +比如,如果你希望一个 [模块](structure-modules.md) 自定义 [URL 规则](runtime-url-handling.md), +你可以将模块ID加入到bootstrap数组中。 属性中的每个组件需要指定以下一项: @@ -117,7 +137,10 @@ $config = require(__DIR__ . '/../config/web.php'); ] ``` -> Info: 如果模块ID和应用组件ID同名,优先使用应用组件ID,如果你想用模块ID,可以使用如下无名称函数返回模块ID。 +> Info: 如果模块ID和应用组件ID同名, +> 优先使用应用组件ID,如果你想用模块ID, +> 可以使用如下无名称函数返回模块ID。 +> > ```php [ function () { @@ -127,9 +150,12 @@ $config = require(__DIR__ . '/../config/web.php'); ``` -在启动阶段,每个组件都会实例化。如果组件类实现接口 [[yii\base\BootstrapInterface]],也会调用 [[yii\base\BootstrapInterface::bootstrap()|bootstrap()]] 方法。 +在启动阶段,每个组件都会实例化。 +如果组件类实现接口 [[yii\base\BootstrapInterface]], +也会调用 [[yii\base\BootstrapInterface::bootstrap()|bootstrap()]] 方法。 -举一个实际的例子,[Basic Application Template](start-installation.md) 应用主体配置中, +举一个实际的例子, +[Basic Application Template](start-installation.md) 应用主体配置中, 开发环境下会在启动阶段运行 `debug` 和 `gii` 模块。 ```php @@ -143,15 +169,18 @@ if (YII_ENV_DEV) { } ``` -> 注: 启动太多的组件会降低系统性能,因为每次请求都需要重新运行启动组件,因此谨慎配置启动组件。 +> 注: 启动太多的组件会降低系统性能, + 因为每次请求都需要重新运行启动组件,因此谨慎配置启动组件。 #### [[yii\web\Application::catchAll|catchAll]] 该属性仅 [[yii\web\Application|Web applications]] 网页应用支持。 -它指定一个要处理所有用户请求的 [控制器方法](structure-controllers.md),通常在维护模式下使用,同一个方法处理所有用户请求。 +它指定一个要处理所有用户请求的 [控制器方法](structure-controllers.md), +通常在维护模式下使用,同一个方法处理所有用户请求。 -该配置为一个数组,第一项指定动作的路由,剩下的数组项(key-value 成对)指定传递给动作的参数,例如: +该配置为一个数组,第一项指定动作的路由, +剩下的数组项(key-value 成对)指定传递给动作的参数,例如: ```php [ @@ -163,10 +192,12 @@ if (YII_ENV_DEV) { ] ``` +> Info: Debug panel on development environment will not work when this property is enabled #### [[yii\base\Application::components|components]] -这是最重要的属性,它允许你注册多个在其他地方使用的[应用组件](#structure-application-components.md). 例如 +这是最重要的属性,它允许你注册多个在其他地方使用的[应用组件](#structure-application-components.md)。 +例如 ```php [ @@ -182,17 +213,21 @@ if (YII_ENV_DEV) { ] ``` -每一个应用组件指定一个key-value对的数组,key代表组件ID,value代表组件类名或 [配置](concept-configurations.md)。 +每一个应用组件指定一个key-value对的数组,key代表组件ID, +value代表组件类名或 [配置](concept-configurations.md)。 -在应用中可以任意注册组件,并可以通过表达式 `\Yii::$app->ComponentID` 全局访问。 +在应用中可以任意注册组件, +并可以通过表达式 `\Yii::$app->ComponentID` 全局访问。 详情请阅读 [应用组件](structure-application-components.md) 一节. #### [[yii\base\Application::controllerMap|controllerMap]] -该属性允许你指定一个控制器ID到任意控制器类。Yii遵循一个默认的 [规则](#controllerNamespace)指定控制器ID到任意控制器类(如`post`对应`app\controllers\PostController`)。 -通过配置这个属性,可以打破这个默认规则,在下面的例子中,`account`对应到`app\controllers\UserController`, +该属性允许你指定一个控制器ID到任意控制器类。 +Yii遵循一个默认的 [规则](#controllerNamespace)指定控制器ID到任意控制器类(如`post`对应`app\controllers\PostController`)。 +通过配置这个属性,可以打破这个默认规则,在下面的例子中, +`account`对应到`app\controllers\UserController`, `article` 对应到 `app\controllers\PostController`。 ```php @@ -209,31 +244,41 @@ if (YII_ENV_DEV) { ] ``` -数组的键代表控制器ID,数组的值代表对应的类名。 +数组的键代表控制器ID, +数组的值代表对应的类名。 #### [[yii\base\Application::controllerNamespace|controllerNamespace]] -该属性指定控制器类默认的命名空间,默认为`app\controllers`。比如控制器ID为 `post` 默认对应 `PostController` (不带命名空间), +该属性指定控制器类默认的命名空间,默认为`app\controllers`。 +比如控制器ID为 `post` 默认对应 `PostController` (不带命名空间), 类全名为 `app\controllers\PostController`。 控制器类文件可能放在这个命名空间对应目录的子目录下, -例如,控制器ID `admin/post` 对应的控制器类全名为 `app\controllers\admin\PostController`。 +例如,控制器ID `admin/post` 对应的控制器类全名为 +`app\controllers\admin\PostController`。 -控制器类全面能被 [自动加载](concept-autoloading.md),这点是非常重要的,控制器类的实际命名空间对应这个属性, +控制器类全面能被 [自动加载](concept-autoloading.md),这点是非常重要的, +控制器类的实际命名空间对应这个属性, 否则,访问时你会收到"Page Not Found"[译:页面找不到]。 -如果你想打破上述的规则,可以配置 [controllerMap](#controllerMap) 属性。 +如果你想打破上述的规则, +可以配置 [controllerMap](#controllerMap) 属性。 #### [[yii\base\Application::language|language]] -该属性指定应用展示给终端用户的语言,默认为 `en` 标识英文。如果需要之前其他语言可以配置该属性。 +该属性指定应用展示给终端用户的语言, +默认为 `en` 标识英文。 +如果需要之前其他语言可以配置该属性。 -该属性影响各种 [国际化](tutorial-i18n.md) ,包括信息翻译、日期格式、数字格式等。 -例如 [[yii\jui\DatePicker]] 小部件会根据该属性展示对应语言的日历以及日期格式。 +该属性影响各种 [国际化](tutorial-i18n.md) , +包括信息翻译、日期格式、数字格式等。 +例如 [[yii\jui\DatePicker]] 小部件会 +根据该属性展示对应语言的日历以及日期格式。 -推荐遵循 [IETF language tag](http://en.wikipedia.org/wiki/IETF_language_tag) 来设置语言,例如 `en` 代表英文, `en-US` 代表英文(美国). +推荐遵循 [IETF language tag](http://en.wikipedia.org/wiki/IETF_language_tag) 来设置语言, +例如 `en` 代表英文, `en-US` 代表英文(美国)。 该属性的更多信息可参考 [国际化](tutorial-i18n.md) 一节. @@ -242,7 +287,8 @@ if (YII_ENV_DEV) { 该属性指定应用所包含的 [模块](structure-modules.md)。 -该属性使用数组包含多个模块类 [配置](concept-configurations.md),数组的键为模块ID,例: +该属性使用数组包含多个模块类 [配置](concept-configurations.md), +数组的键为模块ID,例: ```php [ @@ -264,7 +310,8 @@ if (YII_ENV_DEV) { #### [[yii\base\Application::name|name]] -该属性指定你可能想展示给终端用户的应用名称,不同于需要唯一性的 [[yii\base\Application::id|id]] 属性, +该属性指定你可能想展示给终端用户的应用名称, +不同于需要唯一性的 [[yii\base\Application::id|id]] 属性, 该属性可以不唯一,该属性用于显示应用的用途。 如果其他地方的代码没有用到,可以不配置该属性。 @@ -272,7 +319,9 @@ if (YII_ENV_DEV) { #### [[yii\base\Application::params|params]] -该属性为一个数组,指定可以全局访问的参数,代替程序中硬编码的数字和字符,应用中的参数定义到一个单独的文件并随时可以访问是一个好习惯。 +该属性为一个数组,指定可以全局访问的参数, +代替程序中硬编码的数字和字符, +应用中的参数定义到一个单独的文件并随时可以访问是一个好习惯。 例如用参数定义缩略图的长宽如下: ```php @@ -290,14 +339,17 @@ $size = \Yii::$app->params['thumbnail.size']; $width = \Yii::$app->params['thumbnail.size'][0]; ``` -以后想修改缩略图长宽,只需要修改该参数而不需要相关的代码。 +以后想修改缩略图长宽, +只需要修改该参数而不需要相关的代码。 #### [[yii\base\Application::sourceLanguage|sourceLanguage]] -该属性指定应用代码的语言,默认为 `'en-US'` 标识英文(美国),如果应用不是英文请修改该属性。 +该属性指定应用代码的语言,默认为 `'en-US'` 标识英文(美国), +如果应用不是英文请修改该属性。 -和 [语言](#language) 属性类似,配置该属性需遵循 [IETF language tag](http://en.wikipedia.org/wiki/IETF_language_tag). +和 [语言](#language) 属性类似, +配置该属性需遵循 [IETF language tag](http://en.wikipedia.org/wiki/IETF_language_tag). 例如 `en` 代表英文, `en-US` 代表英文(美国)。 该属性的更多信息可参考 [国际化](tutorial-i18n.md) 一节. @@ -306,7 +358,8 @@ $width = \Yii::$app->params['thumbnail.size'][0]; #### [[yii\base\Application::timeZone|timeZone]] 该属性提供一种方式修改PHP运行环境中的默认时区,配置该属性本质上就是调用PHP函数 -[date_default_timezone_set()](http://php.net/manual/en/function.date-default-timezone-set.php),例如: +[date_default_timezone_set()](http://php.net/manual/en/function.date-default-timezone-set.php), +例如: ```php [ @@ -317,25 +370,31 @@ $width = \Yii::$app->params['thumbnail.size'][0]; #### [[yii\base\Application::version|version]] -该属性指定应用的版本,默认为`'1.0'`,其他代码不使用的话可以不配置。 +该属性指定应用的版本,默认为`'1.0'`, +其他代码不使用的话可以不配置。 ### 实用属性 -本小节描述的属性不经常设置,通常使用系统默认值。如果你想改变默认值,可以配置这些属性。 +本小节描述的属性不经常设置,通常使用系统默认值。 +如果你想改变默认值,可以配置这些属性。 #### [[yii\base\Application::charset|charset]] -该属性指定应用使用的字符集,默认值为 `'UTF-8'`,绝大部分应用都在使用,除非已有的系统大量使用非unicode数据才需要更改该属性。 +该属性指定应用使用的字符集,默认值为 `'UTF-8'`, +绝大部分应用都在使用,除非已有的系统大量使用非unicode数据才需要更改该属性。 #### [[yii\base\Application::defaultRoute|defaultRoute]] -该属性指定未配置的请求的响应 [路由](runtime-routing.md) 规则,路由规则可能包含模块ID,控制器ID,动作ID。 -例如`help`, `post/create`, `admin/post/create`,如果动作ID没有指定,会使用[[yii\base\Controller::defaultAction]]中指定的默认值。 +该属性指定未配置的请求的响应 [路由](runtime-routing.md) 规则, +路由规则可能包含模块ID,控制器ID,动作ID。 +例如`help`, `post/create`, `admin/post/create`,如果动作ID没有指定, +会使用[[yii\base\Controller::defaultAction]]中指定的默认值。 -对于 [[yii\web\Application|Web applications]] 网页应用,默认值为 `'site'` 对应 `SiteController` 控制器,并使用默认的动作。 +对于 [[yii\web\Application|Web applications]] 网页应用, +默认值为 `'site'` 对应 `SiteController` 控制器,并使用默认的动作。 因此你不带路由的访问应用,默认会显示 `app\controllers\SiteController::actionIndex()` 的结果。 对于 [[yii\console\Application|console applications]] 控制台应用, @@ -345,16 +404,9 @@ $width = \Yii::$app->params['thumbnail.size'][0]; #### [[yii\base\Application::extensions|extensions]] -该属性用数组列表指定应用安装和使用的 [扩展](structure-extensions.md),默认使用`@vendor/yiisoft/extensions.php`文件返回的数组。 -<<<<<<< .merge_file_a06168 -<<<<<<< HEAD +该属性用数组列表指定应用安装和使用的 [扩展](structure-extensions.md), +默认使用`@vendor/yiisoft/extensions.php`文件返回的数组。 当你使用 [Composer](http://getcomposer.org) 安装扩展,`extensions.php` 会被自动生成和维护更新。 -======= -当你使用 [Composer](https://getcomposer.org) 安装扩展,`extensions.php` 会被自动生成和维护更新。 ->>>>>>> yiichina/master -======= -当你使用 [Composer](https://getcomposer.org) 安装扩展,`extensions.php` 会被自动生成和维护更新。 ->>>>>>> .merge_file_a06960 所以大多数情况下,不需要配置该属性。 特殊情况下你想自己手动维护扩展,可以参照如下配置该属性: @@ -379,21 +431,25 @@ $width = \Yii::$app->params['thumbnail.size'][0]; ``` 如上所示,该属性包含一个扩展定义数组,每个扩展为一个包含 `name` 和 `version` 项的数组。 -如果扩展要在 [引导启动](runtime-bootstrapping.md) 阶段运行,需要配置 `bootstrap`以及对应的引导启动类名或 [configuration](concept-configurations.md) 数组。 +如果扩展要在 [引导启动](runtime-bootstrapping.md) 阶段运行, +需要配置 `bootstrap`以及对应的引导启动类名或 [configuration](concept-configurations.md) 数组。 扩展也可以定义 [别名](concept-aliases.md) #### [[yii\base\Application::layout|layout]] -该属性指定渲染 [视图](structure-views.md) 默认使用的布局名字,默认值为 `'main'` 对应[布局路径](#layoutPath)下的 `main.php` 文件, -如果 [布局路径](#layoutPath) 和 [视图路径](#viewPath) 都是默认值,默认布局文件可以使用路径别名`@app/views/layouts/main.php` +该属性指定渲染 [视图](structure-views.md) 默认使用的布局名字, +默认值为 `'main'` 对应[布局路径](#layoutPath)下的 `main.php` 文件, +如果 [布局路径](#layoutPath) 和 [视图路径](#viewPath) 都是默认值, +默认布局文件可以使用路径别名`@app/views/layouts/main.php` 如果不想设置默认布局文件,可以设置该属性为 `false`,这种做法比较罕见。 #### [[yii\base\Application::layoutPath|layoutPath]] -该属性指定查找布局文件的路径,默认值为 [视图路径](#viewPath) 下的 `layouts` 子目录。 +该属性指定查找布局文件的路径, +默认值为 [视图路径](#viewPath) 下的 `layouts` 子目录。 如果 [视图路径](#viewPath) 使用默认值,默认的布局路径别名为`@app/views/layouts`。 该属性需要配置成一个目录或 路径 [别名](concept-aliases.md)。 @@ -401,9 +457,11 @@ $width = \Yii::$app->params['thumbnail.size'][0]; #### [[yii\base\Application::runtimePath|runtimePath]] -该属性指定临时文件如日志文件、缓存文件等保存路径,默认值为带别名的 `@app/runtime`。 +该属性指定临时文件如日志文件、缓存文件等保存路径, +默认值为带别名的 `@app/runtime`。 -可以配置该属性为一个目录或者路径 [别名](concept-aliases.md),注意应用运行时有对该路径的写入权限, +可以配置该属性为一个目录或者路径 [别名](concept-aliases.md), +注意应用运行时有对该路径的写入权限, 以及终端用户不能访问该路径因为临时文件可能包含一些敏感信息。 为了简化访问该路径,Yii预定义别名 `@runtime` 代表该路径。 @@ -411,35 +469,32 @@ $width = \Yii::$app->params['thumbnail.size'][0]; #### [[yii\base\Application::viewPath|viewPath]] -该路径指定视图文件的根目录,默认值为带别名的 `@app/views`,可以配置它为一个目录或者路径 [别名](concept-aliases.md). +该路径指定视图文件的根目录,默认值为带别名的 `@app/views`, +可以配置它为一个目录或者路径 [别名](concept-aliases.md). #### [[yii\base\Application::vendorPath|vendorPath]] -<<<<<<< .merge_file_a06168 -<<<<<<< HEAD -该属性指定 [Composer](http://getcomposer.org) 管理的供应商路径,该路径包含应用使用的包括Yii框架在内的所有第三方库。 -======= -该属性指定 [Composer](https://getcomposer.org) 管理的供应商路径,该路径包含应用使用的包括Yii框架在内的所有第三方库。 ->>>>>>> yiichina/master -======= -该属性指定 [Composer](https://getcomposer.org) 管理的供应商路径,该路径包含应用使用的包括Yii框架在内的所有第三方库。 ->>>>>>> .merge_file_a06960 +该属性指定 [Composer](http://getcomposer.org) 管理的供应商路径, +该路径包含应用使用的包括 Yii 框架在内的所有第三方库。 默认值为带别名的 `@app/vendor` 。 -可以配置它为一个目录或者路径 [别名](concept-aliases.md),当你修改时,务必修改对应的 Composer 配置。 +可以配置它为一个目录或者路径 [别名](concept-aliases.md),当你修改时, +务必修改对应的 Composer 配置。 为了简化访问该路径,Yii预定义别名 `@vendor` 代表该路径。 #### [[yii\console\Application::enableCoreCommands|enableCoreCommands]] -该属性仅 [[yii\console\Application|console applications]] 控制台应用支持, 用来指定是否启用Yii中的核心命令,默认值为 `true`。 +该属性仅 [[yii\console\Application|console applications]] 控制台应用支持, +用来指定是否启用Yii中的核心命令,默认值为 `true`。 ## 应用事件 -应用在处理请求过程中会触发事件,可以在配置文件配置事件处理代码,如下所示: +应用在处理请求过程中会触发事件, +可以在配置文件配置事件处理代码,如下所示: ```php [ @@ -449,9 +504,11 @@ $width = \Yii::$app->params['thumbnail.size'][0]; ] ``` -`on eventName` 语法的用法在 [Configurations](concept-configurations.md#configuration-format) 一节有详细描述. +`on eventName` 语法的用法在 [Configurations](concept-configurations.md#configuration-format) +一节有详细描述. -另外,在应用主体实例化后,你可以在[引导启动](runtime-bootstrapping.md) 阶段附加事件处理代码,例如: +另外,在应用主体实例化后,你可以在[引导启动](runtime-bootstrapping.md) 阶段附加事件处理代码, +例如: ```php \Yii::$app->on(\yii\base\Application::EVENT_BEFORE_REQUEST, function ($event) { @@ -463,25 +520,31 @@ $width = \Yii::$app->params['thumbnail.size'][0]; 该事件在应用处理请求*before*之前,实际的事件名为 `beforeRequest`。 -在事件触发前,应用主体已经实例化并配置好了,所以通过事件机制将你的代码嵌入到请求处理过程中非常不错。 +在事件触发前,应用主体已经实例化并配置好了, +所以通过事件机制将你的代码嵌入到请求处理过程中非常不错。 例如在事件处理中根据某些参数动态设置[[yii\base\Application::language]]语言属性。 ### [[yii\base\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] -该事件在应用处理请求*after*之后但在返回响应*before*之前触发,实际的事件名为`afterRequest`。 +该事件在应用处理请求*after*之后但在返回响应*before*之前触发, +实际的事件名为`afterRequest`。 -该事件触发时,请求已经被处理完,可以做一些请求后处理或自定义响应。 +该事件触发时,请求已经被处理完, +可以做一些请求后处理或自定义响应。 -注意 [[yii\web\Response|response]] 组件在发送响应给终端用户时也会触发一些事件,这些事件都在本事件*after*之后触发。 +注意 [[yii\web\Response|response]] 组件在发送响应给终端用户时也会触发一些事件, +这些事件都在本事件*after*之后触发。 ### [[yii\base\Application::EVENT_BEFORE_ACTION|EVENT_BEFORE_ACTION]] -该事件在每个 [控制器动作](structure-controllers.md) 运行*before*之前会被触发,实际的事件名为 `beforeAction`. +该事件在每个 [控制器动作](structure-controllers.md) 运行*before*之前会被触发, +实际的事件名为 `beforeAction`. 事件的参数为一个 [[yii\base\ActionEvent]] 实例, -事件处理中可以设置[[yii\base\ActionEvent::isValid]] 为 `false` 停止运行后续动作,例如: +事件处理中可以设置[[yii\base\ActionEvent::isValid]] 为 `false` 停止运行后续动作, +例如: ```php [ @@ -496,14 +559,17 @@ $width = \Yii::$app->params['thumbnail.size'][0]; 注意 [模块](structure-modules.md) 和 [控制器](structure-controllers.md) 都会触发 `beforeAction` 事件。 应用主体对象首先触发该事件,然后模块触发(如果存在模块),最后控制器触发。 -任何一个事件处理中设置 [[yii\base\ActionEvent::isValid]] 设置为 `false` 会停止触发后面的事件。 +任何一个事件处理中设置 [[yii\base\ActionEvent::isValid]] +设置为 `false` 会停止触发后面的事件。 ### [[yii\base\Application::EVENT_AFTER_ACTION|EVENT_AFTER_ACTION]] -该事件在每个 [控制器动作](structure-controllers.md) 运行*after*之后会被触发,实际的事件名为 `afterAction`. +该事件在每个 [控制器动作](structure-controllers.md) 运行*after*之后会被触发, +实际的事件名为 `afterAction`. -该事件的参数为 [[yii\base\ActionEvent]] 实例,通过[[yii\base\ActionEvent::result]]属性, +该事件的参数为 [[yii\base\ActionEvent]] 实例, +通过[[yii\base\ActionEvent::result]]属性, 事件处理可以访问和修改动作的结果。例如: ```php @@ -517,23 +583,31 @@ $width = \Yii::$app->params['thumbnail.size'][0]; ] ``` -注意 [模块](structure-modules.md) 和 [控制器](structure-controllers.md) 都会触发 `afterAction` 事件。 -这些对象的触发顺序和 `beforeAction` 相反,也就是说,控制器最先触发,然后是模块(如果有模块),最后为应用主体。 +注意 [模块](structure-modules.md) 和 [控制器](structure-controllers.md) +都会触发 `afterAction` 事件。 +这些对象的触发顺序和 `beforeAction` 相反,也就是说,控制器最先触发, +然后是模块(如果有模块),最后为应用主体。 ## 应用主体生命周期 -当运行 [入口脚本](structure-entry-scripts.md) 处理请求时,应用主体会经历以下生命周期: +![Application Lifecycle](images/application-lifecycle.png) + +当运行 [入口脚本](structure-entry-scripts.md) 处理请求时, +应用主体会经历以下生命周期: 1. 入口脚本加载应用主体配置数组。 2. 入口脚本创建一个应用主体实例: - * 调用 [[yii\base\Application::preInit()|preInit()]] 配置几个高级别应用主体属性,比如[[yii\base\Application::basePath|basePath]]。 + * 调用 [[yii\base\Application::preInit()|preInit()]] 配置几个高级别应用主体属性, + 比如[[yii\base\Application::basePath|basePath]]。 * 注册 [[yii\base\Application::errorHandler|error handler]] 错误处理方法. * 配置应用主体属性. - * 调用 [[yii\base\Application::init()|init()]] 初始化,该函数会调用 [[yii\base\Application::bootstrap()|bootstrap()]] 运行引导启动组件. + * 调用 [[yii\base\Application::init()|init()]] 初始化, + 该函数会调用 [[yii\base\Application::bootstrap()|bootstrap()]] 运行引导启动组件. 3. 入口脚本调用 [[yii\base\Application::run()]] 运行应用主体: * 触发 [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] 事件。 - * 处理请求:解析请求 [路由](runtime-routing.md) 和相关参数;创建路由指定的模块、控制器和动作对应的类,并运行动作。 + * 处理请求:解析请求 [路由](runtime-routing.md) 和相关参数; + 创建路由指定的模块、控制器和动作对应的类,并运行动作。 * 触发 [[yii\base\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] 事件。 * 发送响应到终端用户. 4. 入口脚本接收应用主体传来的退出状态并完成请求的处理。 diff --git a/docs/guide-zh-CN/structure-assets.md b/docs/guide-zh-CN/structure-assets.md index 83824b0..2721a6f 100644 --- a/docs/guide-zh-CN/structure-assets.md +++ b/docs/guide-zh-CN/structure-assets.md @@ -4,21 +4,26 @@ Yii中的资源是和Web页面相关的文件,可为CSS文件,JavaScript文件,图片或视频等, 资源放在Web可访问的目录下,直接被Web服务器调用。 -通过程序自动管理资源更好一点,例如,当你在页面中使用 [[yii\jui\DatePicker]] 小部件时, +通过程序自动管理资源更好一点,例如, +当你在页面中使用 [[yii\jui\DatePicker]] 小部件时, 它会自动包含需要的CSS和JavaScript文件,而不是要求你手工去找到这些文件并包含, -当你升级小部件时,它会自动使用新版本的资源文件,在本教程中,我们会详述Yii提供的强大的资源管理功能。 +当你升级小部件时,它会自动使用新版本的资源文件, +在本教程中,我们会详述Yii提供的强大的资源管理功能。 ## 资源包 Yii在*资源包*中管理资源,资源包简单的说就是放在一个目录下的资源集合, -当在[视图](structure-views.md)中注册一个资源包,在渲染Web页面时会包含包中的CSS和JavaScript文件。 +当在[视图](structure-views.md)中注册一个资源包, +在渲染Web页面时会包含包中的CSS和JavaScript文件。 ## 定义资源包 -资源包指定为继承[[yii\web\AssetBundle]]的PHP类,包名为可[自动加载](concept-autoloading.md)的PHP类名, -在资源包类中,要指定资源所在位置,包含哪些CSS和JavaScript文件以及和其他包的依赖关系。 +资源包指定为继承[[yii\web\AssetBundle]]的PHP类, +包名为可[自动加载](concept-autoloading.md)的PHP类名, +在资源包类中,要指定资源所在位置, +包含哪些CSS和JavaScript文件以及和其他包的依赖关系。 如下代码定义[基础应用模板](start-installation.md)使用的主要资源包: @@ -61,67 +66,73 @@ class AppAsset extends AssetBundle [路径别名](concept-aliases.md) 可在此处使用。 * [[yii\web\AssetBundle::baseUrl|baseUrl]]: 指定对应到[[yii\web\AssetBundle::basePath|basePath]]目录的URL, 和 [[yii\web\AssetBundle::basePath|basePath]] 类似,如果你指定 [[yii\web\AssetBundle::sourcePath|sourcePath]] 属性, - [资源管理器](#asset-manager) 会发布这些资源并覆盖该属性,[路径别名](concept-aliases.md) 可在此处使用。 -* [[yii\web\AssetBundle::js|js]]: 一个包含该资源包JavaScript文件的数组,注意正斜杠"/"应作为目录分隔符, + [资源管理器](#asset-manager) 会发布这些资源并覆盖该属性, + [路径别名](concept-aliases.md) 可在此处使用。 +* [[yii\web\AssetBundle::js|js]]: 一个包含该资源包JavaScript文件的数组, + 注意正斜杠"/"应作为目录分隔符, 每个JavaScript文件可指定为以下两种格式之一: - - 相对路径表示为本地JavaScript文件 (如 `js/main.js`),文件实际的路径在该相对路径前加上 + - 相对路径表示为本地JavaScript文件 (如 `js/main.js`), + 文件实际的路径在该相对路径前加上 [[yii\web\AssetManager::basePath]],文件实际的URL在该路径前加上[[yii\web\AssetManager::baseUrl]]。 - 绝对URL地址表示为外部JavaScript文件,如 `http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` 或 `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`. -* [[yii\web\AssetBundle::css|css]]: 一个包含该资源包CSS文件的数组,该数组格式和 [[yii\web\AssetBundle::js|js]] 相同。 -* [[yii\web\AssetBundle::depends|depends]]: 一个列出该资源包依赖的其他资源包(后两节有详细介绍)。 +* [[yii\web\AssetBundle::css|css]]: 一个包含该资源包CSS文件的数组, + 该数组格式和 [[yii\web\AssetBundle::js|js]] 相同。 +* [[yii\web\AssetBundle::depends|depends]]: 一个列出该资源包 + 依赖的其他资源包(后两节有详细介绍)。 * [[yii\web\AssetBundle::jsOptions|jsOptions]]: 当调用[[yii\web\View::registerJsFile()]]注册该包 *每个* JavaScript文件时, 指定传递到该方法的选项。 * [[yii\web\AssetBundle::cssOptions|cssOptions]]: 当调用[[yii\web\View::registerCssFile()]]注册该包 *每个* css文件时, 指定传递到该方法的选项。 * [[yii\web\AssetBundle::publishOptions|publishOptions]]: 当调用[[yii\web\AssetManager::publish()]]发布该包资源文件到Web目录时 - 指定传递到该方法的选项,仅在指定了[[yii\web\AssetBundle::sourcePath|sourcePath]]属性时使用。 + 指定传递到该方法的选项, + 仅在指定了[[yii\web\AssetBundle::sourcePath|sourcePath]]属性时使用。 ### 资源位置 资源根据它们的位置可以分为: -* 源资源: 资源文件和PHP源代码放在一起,不能被Web直接访问,为了使用这些源资源,它们要拷贝到一个可Web访问的Web目录中 +* 源资源: 资源文件和PHP源代码放在一起,不能被Web直接访问, + 为了使用这些源资源,它们要拷贝到一个可Web访问的Web目录中 成为发布的资源,这个过程称为*发布资源*,随后会详细介绍。 * 发布资源: 资源文件放在可通过Web直接访问的Web目录中; -* 外部资源: 资源文件放在与你的Web应用不同的Web服务器上; +* 外部资源: 资源文件放在与你的Web应用不同的 + Web服务器上; -当定义资源包类时候,如果你指定了[[yii\web\AssetBundle::sourcePath|sourcePath]] 属性,就表示任何使用相对路径的资源会被 -当作源资源;如果没有指定该属性,就表示这些资源为发布资源(因此应指定[[yii\web\AssetBundle::basePath|basePath]] 和 +当定义资源包类时候,如果你指定了[[yii\web\AssetBundle::sourcePath|sourcePath]] 属性, +就表示任何使用相对路径的资源会被当作源资源;如果没有指定该属性, +就表示这些资源为发布资源(因此应指定[[yii\web\AssetBundle::basePath|basePath]] 和 [[yii\web\AssetBundle::baseUrl|baseUrl]] 让Yii知道它们的位置)。 -推荐将资源文件放到Web目录以避免不必要的发布资源过程,这就是之前的例子:指定 +推荐将资源文件放到Web目录以避免不必要的发布资源过程, +这就是之前的例子:指定 [[yii\web\AssetBundle::basePath|basePath]] 而不是 [[yii\web\AssetBundle::sourcePath|sourcePath]]. -对于 [扩展](structure-extensions.md)来说,由于它们的资源和源代码都在不能Web访问的目录下, +对于 [扩展](structure-extensions.md)来说, +由于它们的资源和源代码都在不能Web访问的目录下, 在定义资源包类时必须指定[[yii\web\AssetBundle::sourcePath|sourcePath]]属性。 > Note: [[yii\web\AssetBundle::sourcePath|source path]] 属性不要用`@webroot/assets`,该路径默认为 - [[yii\web\AssetManager|asset manager]]资源管理器将源资源发布后存储资源的路径,该路径的所有内容会认为是临时文件, + [[yii\web\AssetManager|asset manager]]资源管理器将源资源发布后存储资源的路径, + 该路径的所有内容会认为是临时文件, 可能会被删除。 ### 资源依赖 -当Web页面包含多个CSS或JavaScript文件时,它们有一定的先后顺序以避免属性覆盖, +当Web页面包含多个CSS或JavaScript文件时, +它们有一定的先后顺序以避免属性覆盖, 例如,Web页面在使用jQuery UI小部件前必须确保jQuery JavaScript文件已经被包含了, 我们称这种资源先后次序称为资源依赖。 资源依赖主要通过[[yii\web\AssetBundle::depends]] 属性来指定, -在`AppAsset` 示例中,资源包依赖其他两个资源包: [[yii\web\YiiAsset]] 和 [[yii\bootstrap\BootstrapAsset]] +在`AppAsset` 示例中,资源包依赖其他两个资源包: +[[yii\web\YiiAsset]] 和 [[yii\bootstrap\BootstrapAsset]] 也就是该资源包的CSS和JavaScript文件要在这两个依赖包的文件包含 *之后* 才包含。 -<<<<<<< .merge_file_a00784 -<<<<<<< HEAD 资源依赖关系是可传递,也就是人说A依赖B,B依赖C,那么A也依赖C。 -======= -资源依赖关系是可传递,也就是说A依赖B,B依赖C,那么A也依赖C。 ->>>>>>> yiichina/master -======= -资源依赖关系是可传递,也就是说A依赖B,B依赖C,那么A也依赖C。 ->>>>>>> .merge_file_a03924 ### 资源选项 @@ -131,7 +142,8 @@ class AppAsset extends AssetBundle 这些属性值会分别传递给 [[yii\web\View::registerCssFile()]] 和 [[yii\web\View::registerJsFile()]] 方法, 在[视图](structure-views.md) 调用这些方法包含CSS和JavaScript文件时。 -> Note: 在资源包类中设置的选项会应用到该包中 *每个* CSS/JavaScript 文件,如果想对每个文件使用不同的选项, +> Note: 在资源包类中设置的选项会应用到该包中 *每个* CSS/JavaScript 文件, + 如果想对每个文件使用不同的选项, 应创建不同的资源包并在每个包中使用一个选项集。 例如,只想IE9或更高的浏览器包含一个CSS文件,可以使用如下选项: @@ -154,30 +166,59 @@ public $cssOptions = ['condition' => 'lte IE9']; public $cssOptions = ['noscript' => true]; ``` -为使JavaScript文件包含在页面head区域(JavaScript文件默认包含在body的结束处)使用以下选项: +为使JavaScript文件包含在页面head区域 +(JavaScript文件默认包含在body的结束处)使用以下选项: ```php public $jsOptions = ['position' => \yii\web\View::POS_HEAD]; ``` +By default, when an asset bundle is being published, all contents in the directory specified by [[yii\web\AssetBundle::sourcePath]] +will be published. You can customize this behavior by configuring the [[yii\web\AssetBundle::publishOptions|publishOptions]] +property. For example, to publish only one or a few subdirectories of [[yii\web\AssetBundle::sourcePath]], +you can do the following in the asset bundle class: + +```php + [ + 'fonts/', + 'css/', + ] + ]; +} +``` + +The above example defines an asset bundle for the ["fontawesome" package](http://fontawesome.io/). By specifying +the `only` publishing option, only the `fonts` and `css` subdirectories will be published. + ### Bower 和 NPM 资源 -<<<<<<< HEAD 大多数 JavaScript/CSS 包通过[Bower](http://bower.io/) 和/或 [NPM](https://www.npmjs.org/)管理, -======= -大多数 JavaScript/CSS 包通过[Bower](http://bower.io/) 和(或) [NPM](https://www.npmjs.org/)管理, ->>>>>>> yiichina/master -如果你的应用或扩展使用这些包,推荐你遵循以下步骤来管理库中的资源: +如果你的应用或扩展使用这些包, +推荐你遵循以下步骤来管理库中的资源: 1. 修改应用或扩展的 `composer.json` 文件将包列入`require` 中, - 应使用`bower-asset/PackageName` (Bower包) 或 `npm-asset/PackageName` (NPM包)来对应库。 + 应使用`bower-asset/PackageName` (Bower包) + 或 `npm-asset/PackageName` (NPM包)来对应库。 2. 创建一个资源包类并将你的应用或扩展要使用的JavaScript/CSS 文件列入到类中, 应设置 [[yii\web\AssetBundle::sourcePath|sourcePath]] 属性为`@bower/PackageName` 或 `@npm/PackageName`, 因为根据别名Composer会安装Bower或NPM包到对应的目录下。 > Note: 一些包会将它们分布式文件放到一个子目录中,对于这种情况,应指定子目录作为 - [[yii\web\AssetBundle::sourcePath|sourcePath]]属性值,例如,[[yii\web\JqueryAsset]]使用 `@bower/jquery/dist` 而不是 `@bower/jquery`。 + [[yii\web\AssetBundle::sourcePath|sourcePath]]属性值, + 例如,[[yii\web\JqueryAsset]]使用 `@bower/jquery/dist` 而不是 `@bower/jquery`。 ## 使用资源包 @@ -190,12 +231,18 @@ use app\assets\AppAsset; AppAsset::register($this); // $this 代表视图对象 ``` +> Info: The [[yii\web\AssetBundle::register()]] method returns an asset bundle object containing the information + about the published assets, such as [[yii\web\AssetBundle::basePath|basePath]] or [[yii\web\AssetBundle::baseUrl|baseUrl]]. + 如果在其他地方注册资源包,应提供视图对象,如在 [小部件](structure-widgets.md) 类中注册资源包, 可以通过 `$this->view` 获取视图对象。 -当在视图中注册一个资源包时,在背后Yii会注册它所依赖的资源包,如果资源包是放在Web不可访问的目录下,会被发布到可访问的目录, -后续当视图渲染页面时,会生成这些注册包包含的CSS和JavaScript文件对应的`` 和 `