yii2tech / ar-eagerjoin
为 Yii2 提供通过连接而不进行额外查询的 ActiveRecord 关联预加载支持
Requires
- yiisoft/yii2: ~2.0.0
This package is auto-updated.
Last update: 2022-01-10 10:34:07 UTC
README
为 Yii 2 的 ActiveRecord 预连接扩展
此扩展提供了通过 '连接' 而不进行额外查询的 ActiveRecord 关联预加载支持。
有关许可信息,请参阅 LICENSE 文件。
安装
安装此扩展的首选方式是通过 composer。
运行以下命令:
php composer.phar require --prefer-dist yii2tech/ar-eagerjoin
或添加以下内容到你的 composer.json 中的 require 部分:
"yii2tech/ar-eagerjoin": "*"
用法
此扩展提供了通过 '连接' 而不进行额外查询的 ActiveRecord 关联预加载支持。假设我们有一个以下数据库结构
CREATE TABLE `Group` ( `id` integer NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL, `code` varchar(10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE InnoDB; CREATE TABLE `Item` ( `id` integer NOT NULL AUTO_INCREMENT, `groupId` integer NOT NULL, `name` varchar(64) NOT NULL, `price` float, PRIMARY KEY (`id`) FOREIGN KEY (`groupId`) REFERENCES `Group` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, ) ENGINE InnoDB;
如果您需要显示项目的列表,以及它们所属的组,按组名或代码排序,您将不得不使用 JOIN
SQL 语句,因此需要使用 \yii\db\ActiveQuery::joinWith()
方法
$items = Item::find() ->joinWith('group') ->orderBy(['{{group}}.[[name]]' => SORT_ASC]) ->all();
然而,上面的代码将执行 2 个 SQL 查询:一个用于获取项目(包括 JOIN
和 ORDER BY
语句),另一个用于获取组。虽然第二个查询将非常简单且快速,但它仍然是多余的且效率低下,因为所有组列都可能与项目列一起被选择。
此扩展提供了 [[yii2tech\ar\eagerjoin\EagerJoinTrait]] 特性,一旦在 ActiveRecord 类中使用,就可以在不执行额外 SQL 查询的情况下选择相关记录。
设置示例
use yii\db\ActiveRecord; use yii2tech\ar\eagerjoin\EagerJoinTrait; class Item extends ActiveRecord { use EagerJoinTrait; public function getGroup() { return $this->hasOne(Group::className(), ['id' => 'groupId']); } }
为了通过 '连接' 查询填充相关记录,您需要手动将它们的列追加到 SELECT
查询部分,并使用以下格式的名称对它们进行别名
{relationName}{boundary}{columnName}
其中
- 'relationName' - 要填充的关联名称
- 'columnName' - 要填充的相关记录的列(属性)名称
- 'boundary' - 由 [[yii2tech\ar\eagerjoin\EagerJoinTrait::eagerJoinBoundary()]] 配置的分隔符
例如
$items = Item::find() ->select(['Item.*', 'group__name' => 'Group.name', 'group__code' => 'Group.code']) ->joinWith('group', false) // disable regular eager loading!!! ->all(); foreach ($items as $item) { var_dump($item->isRelationPopulated('group')); // outputs `true`!!! echo $item->group->name; // no extra query performed! echo $item->group->code; // no extra query performed! echo get_class($item->group); // outputs 'Group'! }
在这里,查询结果集的 'group__name' 列传递给 $item->group->name
,'group__code' - 到 $item->group->code
等等。
请注意!不要忘记禁用预加载,通过将 false
作为 joinWith()
方法的第二个参数传递,否则您将不会获得任何好处。
注意:请仔细选择
boundary
:它不应作为非预期传递给相关记录的列(或别名)的一部分存在。因此,双下划线 ('__') 被用作默认值。
提示:如果您使用 'camelCase' 语法表示表列,则可以使用单下划线 ('_') 作为边界,以便使选择语句更加清晰。
您可以使用 [[\yii2tech\ar\eagerjoin\EagerJoinQueryTrait]] 特性来加快查询的编写速度。此特性应在 [[\yii\db\ActiveQuery]] 实例中使用
use yii\db\ActiveQuery; use yii2tech\ar\eagerjoin\EagerJoinQueryTrait; use yii\db\ActiveRecord; use yii2tech\ar\eagerjoin\EagerJoinTrait; class ItemQuery extends ActiveQuery { use EagerJoinQueryTrait; // ... } class Item extends ActiveRecord { use EagerJoinTrait; /** * @inheritdoc * @return ItemQuery the active query used by this AR class. */ public static function find() { return new ItemQuery(get_called_class()); } // ... }
在构建查询时,您将能够使用 eagerJoinWith()
方法。
$items = Item::find()->eagerJoinWith('group')->all();
适当的 'select' 和 'join' 语句的组合将自动执行。
限制和缺点
虽然减少了执行的查询数量,但此扩展有几个限制和缺点。
-
仅支持 'has-one' 关系。扩展无法处理 'has-many' 关系。您应该使用常规的
joinWith()
和懒加载来处理 'has-many' 关系。 -
如果所有选择的相关模型字段都将为
null
,则整个相关记录将被设置为null
。您应该始终选择至少一个 '非 null' 列,以避免不适当的结果。 -
尽管移除了额外的查询,但此扩展实际上可能不会提高整体性能。常规 Yii 懒加载查询非常简单且快速,而此扩展会消耗额外的内存并执行额外的计算。因此,性能几乎保持不变。在大多数情况下,使用此扩展是一种权衡:它减少了数据库端的负载,同时增加了 PHP 端的负载。