From d5db45574781de02097cbb3feb12f7fed250687c Mon Sep 17 00:00:00 2001 From: lizhenju Date: Thu, 12 Oct 2017 18:13:48 +0800 Subject: [PATCH] =?UTF-8?q?complete=20=20AR=20=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide-zh-CN/db-active-record-bak.md | 230 +++++++++++------------ 1 file changed, 115 insertions(+), 115 deletions(-) diff --git a/docs/guide-zh-CN/db-active-record-bak.md b/docs/guide-zh-CN/db-active-record-bak.md index 97fb657384..225119b363 100644 --- a/docs/guide-zh-CN/db-active-record-bak.md +++ b/docs/guide-zh-CN/db-active-record-bak.md @@ -1169,10 +1169,10 @@ $query->joinWith(['orders o' => function($q) { ->where('o.amount > 100'); ``` -### Inverse Relations +### 反向关联 -Relation declarations are often reciprocal between two Active Record classes. For example, `Customer` is related -to `Order` via the `orders` relation, and `Order` is related back to `Customer` via the `customer` relation. +两个 AR 类之间的关联声明往往像 *倒数* 那样。例如,`Customer` 是 +通过 `orders` 关联到 `Order` ,而`Order` 通过 `customer` 又关联回到了 `Customer`。 ```php class Customer extends ActiveRecord @@ -1192,7 +1192,7 @@ class Order extends ActiveRecord } ``` -Now consider the following piece of code: +现在开一下脑洞: ```php // SELECT * FROM `customer` WHERE `id` = 123 @@ -1204,17 +1204,17 @@ $order = $customer->orders[0]; // SELECT * FROM `customer` WHERE `id` = 123 $customer2 = $order->customer; -// displays "not the same" +// 显示 "not the same" echo $customer2 === $customer ? 'same' : 'not the same'; ``` -We would think `$customer` and `$customer2` are the same, but they are not! Actually they do contain the same -customer data, but they are different objects. When accessing `$order->customer`, an extra SQL statement -is executed to populate a new object `$customer2`. +我们原本认为 `$customer` 和 `$customer2` 是一样的,但不是!其实他们确实包含相同的 +客户数据,但它们是不同的对象。 访问 `$order->customer` 时,需要执行额外的 SQL 语句, +以填充出一个新对象 `$customer2`。 -To avoid the redundant execution of the last SQL statement in the above example, we should tell Yii that -`customer` is an *inverse relation* of `orders` by calling the [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] method -like shown below: +为了避免上述例子中最后一个 SQL 语句被冗余执行,我们应该告诉 Yii +`customer` 是 `orders` 的 *反向关联*,可以通过调用 [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] 方法声明, +如下所示: ```php class Customer extends ActiveRecord @@ -1226,7 +1226,7 @@ class Customer extends ActiveRecord } ``` -With this modified relation declaration, we will have: +这样修改关联声明后: ```php // SELECT * FROM `customer` WHERE `id` = 123 @@ -1238,20 +1238,20 @@ $order = $customer->orders[0]; // No SQL will be executed $customer2 = $order->customer; -// displays "same" +// 输出 "same" echo $customer2 === $customer ? 'same' : 'not the same'; ``` -> Note: Inverse relations cannot be defined for relations involving a [junction table](#junction-table). - That is, if a relation is defined with [[yii\db\ActiveQuery::via()|via()]] or [[yii\db\ActiveQuery::viaTable()|viaTable()]], - you should not call [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] further. +> 提示:反向关联不能用在有 [连接表](#junction-table) 关联声明中。 + 也就是说,如果一个关联关系通过 [[yii\db\ActiveQuery::via()|via()]] 或 [[yii\db\ActiveQuery::viaTable()|viaTable()]] 声明, + 你就不能再调用 [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] 了。 -## Saving Relations +## 保存关联数据 -When working with relational data, you often need to establish relationships between different data or destroy -existing relationships. This requires setting proper values for the columns that define the relations. Using Active Record, -you may end up writing the code like the following: +在使用关联数据时,您经常需要建立不同数据之间的关联或销毁 +现有关联。这需要为定义的关联的列设置正确的值。通过使用 AR 活动记录, +你就可以编写如下代码: ```php $customer = Customer::findOne(123); @@ -1259,12 +1259,12 @@ $order = new Order(); $order->subtotal = 100; // ... -// setting the attribute that defines the "customer" relation in Order +// 为 Order 设置属性以定义与 "customer" 的关联关系 $order->customer_id = $customer->id; $order->save(); ``` -Active Record provides the [[yii\db\ActiveRecord::link()|link()]] method that allows you to accomplish this task more nicely: +AR 提供了 [[yii\db\ActiveRecord::link()|link()]] 方法,可以更好地完成此任务: ```php $customer = Customer::findOne(123); @@ -1275,53 +1275,53 @@ $order->subtotal = 100; $order->link('customer', $customer); ``` -The [[yii\db\ActiveRecord::link()|link()]] method requires you to specify the relation name and the target Active Record -instance that the relationship should be established with. The method will modify the values of the attributes that -link two Active Record instances and save them to the database. In the above example, it will set the `customer_id` -attribute of the `Order` instance to be the value of the `id` attribute of the `Customer` instance and then save it -to the database. +[[yii\db\ActiveRecord::link()|link()]] 方法需要指定关联名 +和要建立关联的目标 AR 实例。该方法将修改属性的值 +以连接两个 AR 实例,并将其保存到数据库。在上面的例子中,它将设置 `Order` 实例的 `customer_id` 属性 +为 `Customer` 实例的 `id` 属性的值,然后保存 +到数据库。 -> Note: You cannot link two newly created Active Record instances. +> 提示:你不能 link() 两个新的 AR 实例。(译者注:其中的一个必须是数据库中查询出来的) -The benefit of using [[yii\db\ActiveRecord::link()|link()]] is even more obvious when a relation is defined via -a [junction table](#junction-table). For example, you may use the following code to link an `Order` instance -with an `Item` instance: +当一个关联关系通过 [连接表](#junction-table) 定义时,此 [[yii\db\ActiveRecord::link()|link()]] 方法更能体现在党的领导下的中国特色社会主义的优越性。 +例如,你可以使用以下代码 link() `Order` 实例 +和 `Item` 实例: ```php $order->link('items', $item); ``` -The above code will automatically insert a row in the `order_item` junction table to relate the order with the item. +上述代码会自动在 `order_item` 关联表中插入一行,以关联 order 和 item 这两个数据记录。 -> Info: The [[yii\db\ActiveRecord::link()|link()]] method will NOT perform any data validation while - saving the affected Active Record instance. It is your responsibility to validate any input data before - calling this method. +> 提示:[[yii\db\ActiveRecord::link()|link()]] 方法在保存相应的 AR 实例时, + 将不会执行任何数据验证。在调用此方法之前, + 您应当验证所有的输入数据。 -The opposite operation to [[yii\db\ActiveRecord::link()|link()]] is [[yii\db\ActiveRecord::unlink()|unlink()]] -which breaks an existing relationship between two Active Record instances. For example, +[[yii\db\ActiveRecord::link()|link()]] 方法的反向操作是 [[yii\db\ActiveRecord::unlink()|unlink()]] 方法, +这将可以断掉两个 AR 实例间的已经存在了的关联关系。例如, ```php $customer = Customer::find()->with('orders')->where(['id' => 123])->one(); $customer->unlink('orders', $customer->orders[0]); ``` -By default, the [[yii\db\ActiveRecord::unlink()|unlink()]] method will set the foreign key value(s) that specify -the existing relationship to be `null`. You may, however, choose to delete the table row that contains the foreign key value -by passing the `$delete` parameter as `true` to the method. +默认情况下,[[yii\db\ActiveRecord::unlink()|unlink()]] 方法将设置指定的外键值, +以把现有的关联指定为 `null`。此外,你可以选择通过将 `$delete` 参数设置为`true` 传递给方法, +删除包含此外键值的表记录行。 -When a junction table is involved in a relation, calling [[yii\db\ActiveRecord::unlink()|unlink()]] will cause -the foreign keys in the junction table to be cleared, or the deletion of the corresponding row in the junction table -if `$delete` is `true`. +当关联关系中有连接表时,调用 [[yii\db\ActiveRecord::unlink()|unlink()]] 时, +如果 `$delete` 参数是 `true` 的话,将导致 +连接表中的外键或相应的行被删除。 -## Cross-Database Relations +## 跨数据库关联 -Active Record allows you to declare relations between Active Record classes that are powered by different databases. -The databases can be of different types (e.g. MySQL and PostgreSQL, or MS SQL and MongoDB), and they can run on -different servers. You can use the same syntax to perform relational queries. For example, +AR 活动记录允许您在不同数据库驱动的 AR 类之间声明关联关系。 +这些数据库可以是不同的类型(例如 MySQL 和 PostgreSQL ,或是 MS SQL 和 MongoDB),它们也可以运行在 +不同的服务器上。你可以使用相同的语法来执行关联查询。例如, ```php -// Customer is associated with the "customer" table in a relational database (e.g. MySQL) +// Customer 对应的表是关系数据库中(比如 MySQL)的 "customer" 表 class Customer extends \yii\db\ActiveRecord { public static function tableName() @@ -1331,12 +1331,12 @@ class Customer extends \yii\db\ActiveRecord public function getComments() { - // a customer has many comments + // 一个 customer 有很多条评论(comments) return $this->hasMany(Comment::className(), ['customer_id' => 'id']); } } -// Comment is associated with the "comment" collection in a MongoDB database +// Comment 对应的是 MongoDB 数据库中的 "comment" 集合(译者注:MongoDB 中的集合相当于 MySQL 中的表) class Comment extends \yii\mongodb\ActiveRecord { public static function collectionName() @@ -1346,7 +1346,7 @@ class Comment extends \yii\mongodb\ActiveRecord public function getCustomer() { - // a comment has one customer + // 一条评论对应一位 customer return $this->hasOne(Customer::className(), ['id' => 'customer_id']); } } @@ -1354,17 +1354,17 @@ class Comment extends \yii\mongodb\ActiveRecord $customers = Customer::find()->with('comments')->all(); ``` -You can use most of the relational query features that have been described in this section. +本节中描述的大多数关联查询功能,你都可以抄一抄。 -> Note: Usage of [[yii\db\ActiveQuery::joinWith()|joinWith()]] is limited to databases that allow cross-database JOIN queries. - For this reason, you cannot use this method in the above example because MongoDB does not support JOIN. +> 提示:[[yii\db\ActiveQuery::joinWith()|joinWith()]] 这个功能限制于某些数据库是否支持跨数据库 JOIN 查询。 + 因此,你再上述的代码里就不能用此方法了,因为 MongoDB 不知道 JOIN 查询。 -## Customizing Query Classes +## 自定义查询类 -By default, all Active Record queries are supported by [[yii\db\ActiveQuery]]. To use a customized query class -in an Active Record class, you should override the [[yii\db\ActiveRecord::find()]] method and return an instance -of your customized query class. For example, +默认情况下,[[yii\db\ActiveQuery]] 支持所有 AR 查询。要在 AR 类中使用自定义的查询类, +您应该重写 [[yii\db\ActiveRecord::find()]] 方法并返回一个你自定义查询类的实例。 +例如, ```php // file Comment.php @@ -1381,10 +1381,10 @@ class Comment extends ActiveRecord } ``` -Now whenever you are performing a query (e.g. `find()`, `findOne()`) or defining a relation (e.g. `hasOne()`) -with `Comment`, you will be calling an instance of `CommentQuery` instead of `ActiveQuery`. +现在,对于 `Comment` 类,不管你执行查询(比如 `find()`、`findOne()`),还是定义一个关联(比如 `hasOne()`), +你都将调用到 `CommentQuery` 实例,而不再是 `ActiveQuery` 实例。 -You now have to define the `CommentQuery` class, which can be customized in many creative ways to improve your query building experience. For example, +现在你可以定义 `CommentQuery` 类了,发挥你的奇技淫巧,以改善查询构建体验。例如, ```php // file CommentQuery.php @@ -1394,14 +1394,14 @@ use yii\db\ActiveQuery; class CommentQuery extends ActiveQuery { - // conditions appended by default (can be skipped) + // 默认加上一些条件(可以跳过) public function init() { $this->andOnCondition(['deleted' => false]); parent::init(); } - // ... add customized query methods here ... + // ... 在这里加上自定义的查询方法 ... public function active($state = true) { @@ -1410,20 +1410,20 @@ class CommentQuery extends ActiveQuery } ``` -> Note: Instead of calling [[yii\db\ActiveQuery::onCondition()|onCondition()]], you usually should call - [[yii\db\ActiveQuery::andOnCondition()|andOnCondition()]] or [[yii\db\ActiveQuery::orOnCondition()|orOnCondition()]] to append additional conditions when defining new query building methods so that any existing conditions are not overwritten. +> 提示:作为 [[yii\db\ActiveQuery::onCondition()|onCondition()]] 方法的替代方案,你应当 + 调用 [[yii\db\ActiveQuery::andOnCondition()|andOnCondition()]] 或 [[yii\db\ActiveQuery::orOnCondition()|orOnCondition()]] 方法来附加新增的条件,不然在一个新定义的查询方法,已存在的条件可能会被覆盖。 -This allows you to write query building code like the following: +然后你就可以先下面这样构建你的查询了: ```php $comments = Comment::find()->active()->all(); $inactiveComments = Comment::find()->active(false)->all(); ``` -> Tip: In big projects, it is recommended that you use customized query classes to hold most query-related code - so that the Active Record classes can be kept clean. +> 提示:在大型项目中,建议您使用自定义查询类来容纳大多数与查询相关的代码, + 以使 AR 类保持简洁。 -You can also use the new query building methods when defining relations about `Comment` or performing relational query: +您还可以在 `Comment` 关联关系的定义中或在执行关联查询时,使用刚刚新建查询构建方法: ```php class Customer extends \yii\db\ActiveRecord @@ -1436,7 +1436,7 @@ class Customer extends \yii\db\ActiveRecord $customers = Customer::find()->joinWith('activeComments')->all(); -// or alternatively +// 或者这样 class Customer extends \yii\db\ActiveRecord { public function getComments() @@ -1452,22 +1452,22 @@ $customers = Customer::find()->joinWith([ ])->all(); ``` -> Info: In Yii 1.1, there is a concept called *scope*. Scope is no longer directly supported in Yii 2.0, - and you should use customized query classes and query methods to achieve the same goal. +> 提示:在 Yii 1.1 中,有个概念叫做 *命名范围*。命名范围在 Yii 2.0 中不再支持, + 你依然可以使用自定义查询类、查询方法来达到一样的效果。 -## Selecting extra fields +## 选择额外的字段 -When Active Record instance is populated from query results, its attributes are filled up by corresponding column -values from received data set. +当 AR 实例从查询结果中填充时,从数据结果集中, +其属性的值将被相应的列填充。 -You are able to fetch additional columns or values from query and store it inside the Active Record. -For example, assume we have a table named `room`, which contains information about rooms available in the hotel. -Each room stores information about its geometrical size using fields `length`, `width`, `height`. -Imagine we need to retrieve list of all available rooms with their volume in descendant order. -So you can not calculate volume using PHP, because we need to sort the records by its value, but you also want `volume` -to be displayed in the list. -To achieve the goal, you need to declare an extra field in your `Room` Active Record class, which will store `volume` value: +你可以从查询中获取其他列或值,并将其存储在 AR 活动记录中。 +例如,假设我们有一个名为 `room` 的表,其中包含有关酒店可用房间的信息。 +每个房间使用字段 `length`,`width`,`height` 存储有关其空间大小的信息。 +想象一下,我们需要检索出所有可用房间的列表,并按照体积大小倒序排列。 +你不可能使用 PHP 来计算体积,但是,由于我们需要按照它的值对这些记录进行排序,你依然需要 `volume` (体积) +来显示在这个列表中。 +为了达到这个目标,你需要在你的 `Room` 活动记录类中声明一个额外的字段,它将存储 `volume` 的值: ```php class Room extends \yii\db\ActiveRecord @@ -1478,25 +1478,25 @@ class Room extends \yii\db\ActiveRecord } ``` -Then you need to compose a query, which calculates volume of the room and performs the sort: +然后,你需要撰写一个查询,它可以计算房间的大小并执行排序: ```php $rooms = Room::find() ->select([ '{{room}}.*', // select all columns - '([[length]] * [[width]] * [[height]]) AS volume', // calculate a volume + '([[length]] * [[width]] * [[height]]) AS volume', // 计算体积 ]) - ->orderBy('volume DESC') // apply sort + ->orderBy('volume DESC') // 使用排序 ->all(); foreach ($rooms as $room) { - echo $room->volume; // contains value calculated by SQL + echo $room->volume; // 包含了由 SQL 计算出的值 } ``` -Ability to select extra fields can be exceptionally useful for aggregation queries. -Assume you need to display a list of customers with the count of orders they have made. -First of all, you need to declare a `Customer` class with `orders` relation and extra field for count storage: +额外字段的特性对于聚合查询非常有用。 +假设您需要显示一系列客户的订单数量。 +首先,您需要使用 `orders` 关系声明一个 `Customer` 类,并指定额外字段来存储 count 结果: ```php class Customer extends \yii\db\ActiveRecord @@ -1512,22 +1512,22 @@ class Customer extends \yii\db\ActiveRecord } ``` -Then you can compose a query, which joins the orders and calculates their count: +然后你可以编写一个查询来 JOIN 订单表,并计算订单的总数: ```php $customers = Customer::find() ->select([ - '{{customer}}.*', // select all customer fields - 'COUNT({{order}}.id) AS ordersCount' // calculate orders count + '{{customer}}.*', // select customer 表所有的字段 + 'COUNT({{order}}.id) AS ordersCount' // 计算订单总数 ]) - ->joinWith('orders') // ensure table junction - ->groupBy('{{customer}}.id') // group the result to ensure aggregation function works + ->joinWith('orders') // 连接表 + ->groupBy('{{customer}}.id') // 分组查询,以确保聚合函数生效 ->all(); ``` -A disadvantage of using this method would be that, if the information isn't loaded on the SQL query - it has to be calculated -separately. Thus, if you have found particular record via regular query without extra select statements, it -will be unable to return actual value for the extra field. Same will happen for the newly saved record. +使用此方法的一个缺点是,如果数据不是从 SQL 查询上加载的,它必须再单独计算一遍。 +因此,如果你通过常规查询获取个别的数据记录时,它没有额外的 select 语句,那么它 +将无法返回额外字段的实际值。新保存的记录一样会发生这种情。 ```php $room = new Room(); @@ -1535,11 +1535,11 @@ $room->length = 100; $room->width = 50; $room->height = 2; -$room->volume; // this value will be `null`, since it was not declared yet +$room->volume; // 为 `null`, 因为它没有被声明(赋值) ``` -Using the [[yii\db\BaseActiveRecord::__get()|__get()]] and [[yii\db\BaseActiveRecord::__set()|__set()]] magic methods -we can emulate the behavior of a property: +通过 [[yii\db\BaseActiveRecord::__get()|__get()]] 和 [[yii\db\BaseActiveRecord::__set()|__set()]] 魔术方法 +我们可以将属性赋予行为特性: ```php class Room extends \yii\db\ActiveRecord @@ -1570,10 +1570,10 @@ class Room extends \yii\db\ActiveRecord } ``` -When the select query doesn't provide the volume, the model will be able to calculate it automatically using -the attributes of the model. +当 select 查询不提供 volume 体积时,这模型将能够自动计算体积的值出来 +,当访问模型的属性的时候。 -You can calculate the aggregation fields as well using defined relations: +当定义关联关系的时候,你也可以计算聚合字段: ```php class Customer extends \yii\db\ActiveRecord @@ -1588,11 +1588,11 @@ class Customer extends \yii\db\ActiveRecord public function getOrdersCount() { if ($this->isNewRecord) { - return null; // this avoid calling a query searching for null primary keys + return null; // 这样可以避免调用空主键进行查询 } if ($this->_ordersCount === null) { - $this->setOrdersCount($this->getOrders()->count()); // calculate aggregation on demand from relation + $this->setOrdersCount($this->getOrders()->count()); // 根据关联关系按需计算聚合字段 } return $this->_ordersCount; @@ -1607,29 +1607,29 @@ class Customer extends \yii\db\ActiveRecord } ``` -With this code, in case 'ordersCount' is present in 'select' statement - `Customer::ordersCount` will be populated -by query results, otherwise it will be calculated on demand using `Customer::orders` relation. +使用此代码,如果 'select' 语句中存在 'ordersCount' - 它会从查询结果集获取数据填充 `Customer::ordersCount` 属性, +,否则它会在被访问的时候,使用 `Customer::orders` 关联按需计算。 -This approach can be as well used for creation of the shortcuts for some relational data, especially for the aggregation. -For example: +这种方法也适用于创建一些关联数据的快捷访问方式,特别是对于聚合。 +例如: ```php class Customer extends \yii\db\ActiveRecord { /** - * Defines read-only virtual property for aggregation data. + * 为聚合数据定义一个只读的虚拟属性 */ public function getOrdersCount() { if ($this->isNewRecord) { - return null; // this avoid calling a query searching for null primary keys + return null; // 这样可以避免调用空主键进行查询 } return empty($this->ordersAggregation) ? 0 : $this->ordersAggregation[0]['counted']; } /** - * Declares normal 'orders' relation. + * 声明一个常规的 'orders' 关联 */ public function getOrders() { @@ -1637,7 +1637,7 @@ class Customer extends \yii\db\ActiveRecord } /** - * Declares new relation based on 'orders', which provides aggregation. + * 基于 'orders' 关联,声明一个用于查询聚合的新关联 */ public function getOrdersAggregation() { @@ -1651,9 +1651,9 @@ class Customer extends \yii\db\ActiveRecord } foreach (Customer::find()->with('ordersAggregation')->all() as $customer) { - echo $customer->ordersCount; // outputs aggregation data from relation without extra query due to eager loading + echo $customer->ordersCount; // 输出关联的聚合数据,而不需要额外的查询,因为我们用了即时加载 } $customer = Customer::findOne($pk); -$customer->ordersCount; // output aggregation data from lazy loaded relation +$customer->ordersCount; // 从延迟加载的关联中,输出聚合数据 ```