voskobovich / yii2-many-many-behavior
此行为使您在ActiveRecord模型中维护多对多关系变得简单。
Requires
- php: >=5.4.0
- yiisoft/yii2: ~2.0.0
Requires (Dev)
- yiisoft/yii2-codeception: ~2.0
This package is not auto-updated.
Last update: 2024-09-14 17:03:52 UTC
README
此行为将很快被弃用。
查看新版本 Yii2 Linker Behavior。
关于
此行为使您在ActiveRecord模型中轻松维护多对多和一对一关系。
支持
用法
- 在您的模型中,添加行为并配置它
- 在您的模型中,为行为创建的属性添加验证规则
- 在您的视图中,为属性创建表单字段
添加和配置行为
例如,假设您正在处理类似 Book
、Author
和 Review
的实体。该 Book
模型具有以下关系
public function getAuthors() { return $this->hasMany(Author::className(), ['id' => 'author_id']) ->viaTable('book_has_author', ['book_id' => 'id']); } public function getReviews() { return $this->hasMany(Review::className(), ['id' => 'review_id']); }
在相同的模型中,行为可以如此配置
public function behaviors() { return [ [ 'class' => \voskobovich\behaviors\ManyToManyBehavior::className(), 'relations' => [ 'author_ids' => 'authors', 'review_ids' => 'reviews', ], ], ]; }
关系名称不需要以 _ids
结尾,您可以使用任何名称作为关系名称。尽管如此,建议使用有意义的名称。
添加验证规则
属性会自动创建。但是,您必须为它们提供验证规则(通常是 safe
验证器)
public function rules() { return [ [['author_ids', 'review_ids'], 'each', 'rule' => ['integer']] ]; }
创建表单字段
默认情况下,行为将接受来自多选字段的输入
<?= $form->field($model, 'author_ids') ->dropDownList($authorsAsArray, ['multiple' => true]) ?> ... <?= $form->field($model, 'review_ids') ->dropDownList($reviewsAsArray, ['multiple' => true]) ?>
已知问题和限制
- 不支持复合主键。
- 多对多链接的连接表使用主模型的连接进行更新。
- 在计算默认值时使用函数时,请注意,该函数仅在关系保存之前被调用一次,然后其结果用于通过一个查询更新所有相关行。
- 关系是通过DAO(即直接操作表)保存的。
自定义获取器和设置器
例如,在 Book
模型中自动创建了类似于 author_ids
和 review_ids
的属性。默认情况下,它们被配置为接受来自标准选择输入的数据(见下文)。但是,可以使用自定义获取器和设置器函数,这对于与更复杂的客户端脚本交互可能很有用。可以为特定属性定义许多不同的获取器和设置器
//... 'author_ids' => [ 'authors', 'fields' => [ 'json' => [ 'get' => function($value) { //from internal representation (array) to user type return JSON::encode($value); }, 'set' => function($value) { //from user type to internal representation (array) return JSON::decode($value); }, ], 'string' => [ 'get' => function($value) { //from internal representation (array) to user type return implode(',', $value); }, 'set' => function($value) { //from user type to internal representation (array) return explode(',', $value); }, ], ], ] //...
字段名称与属性名称以下划线连接。在此示例中,访问 $model->author_ids
将得到ID数组,$model->author_ids_json
将返回JSON字符串,而 $model->author_ids_string
将返回ID的逗号分隔字符串。设置器的工作方式类似。
可以省略获取器和设置器以回退到默认行为(ID数组)。
注意
设置器函数接收通过 $_REQUEST
传递的任何数据,并期望返回相关模型ID的数组。获取器函数接收相关模型ID的数组。
兼容性注意
指定主属性(如上面示例中的 author_ids
)的获取器和设置器仍然受支持,但不再推荐。最佳实践是使用主属性以ID数组的形式获取和设置值,并创建 fields
以使用其他获取器和设置器。
自定义连接表值
要在连接表中设置除关系所需列之外的其他值,请使用 viaTableValues
... 'author_ids' => [ 'authors', 'viaTableValues' => [ 'status_key' => BookHasAuthor::STATUS_ACTIVE, 'created_at' => function() { return new \yii\db\Expression('NOW()'); }, 'is_main' => function($model, $relationName, $attributeName, $relatedPk) { return array_search($relatedPk, $model->author_ids) === 0; }, ], ] ...
为孤儿模型设置默认值
当保存一对多关系时,旧链接会被删除,新链接会被创建。为了删除一个旧链接,相应的外键列会被设置为某个值。默认值是 NULL
,但可以配置为不同的值。请注意,您的数据库必须支持您选择的默认值,所以如果您使用 NULL
作为默认值,该字段必须是可空的。
您可以提供如下常量值
... 'review_ids' => [ 'reviews', 'default' => 17, ], ...
还可以显式地将默认值分配给 NULL
,如下所示:'default' => null
。另一个选项是提供一个计算默认值的函数
... 'review_ids' => [ 'reviews', 'default' => function($model, $relationName, $attributeName) { //default value calculation //... return $defaultValue; }, ], ...
该函数接受3个参数。在我们的例子中,$model
是 Book
类(行为的拥有者)的实例,$relationName
是 'reviews'
,而 $attributeName
是 'review_ids'
。
如果在这个函数中需要数据库连接,建议从主模型(Book
)或次模型(Review
)获取。
function($model, $relationName, $attributeName) { //get db connection from primary model (Book) $connection = $model::getDb(); ... //OR get db connection from secondary model (Review) $secondaryModelClass = $model->getRelation($relationName)->modelClass; $connection = $secondaryModelClass::getDb(); ... //further value calculation logic (db query)
将行为应用于单个关系多次
在单个模型中,您可以使用此行为对单个关系多次使用。然而,这并不推荐。
与使用相同连接表的关系一起使用行为
当您在同一模型中实现多个多对多关系,并且它们使用相同的连接表时,您可能会遇到问题,即您的连接记录将无法正确保存。
这是因为每次保存新的关系时,旧连接记录都会被删除。为了避免删除刚刚保存的记录,您需要设置 customDeleteCondition
参数。
此删除条件将与主删除条件合并,并可用于微调您的删除查询。
例如,让我们设想我们为植物实验室开发了一个科学数据库。我们有一个名为“Sample”的模型,用于不同植物的样本,一个名为“Attachment”的模型,用于相关文件(照片或文档),还有一个名为“sample_attachments”的连接表。我们希望通过在连接表中引入字段“type”将所有这些文件划分为“Sample”模型中的单独字段(原材料图片、分子结构等)。在这种情况下,结果“Sample”模型将如下所示
public function behaviors() { return [ 'manyToMany' => [ 'class' => ManyToManyBehavior::className(), 'relations' => [ 'rawMaterialPicturesList' => [ 'rawMaterialPictures', 'viaTableValues' => [ 'type_key' => 'RAW_MATERIAL_PICTURES', ], 'customDeleteCondition' => [ 'type_key' => 'RAW_MATERIAL_PICTURES', ], ], 'molecularStructureList' => [ 'molecularStructure', 'viaTableValues' => [ 'type_key' => 'MOLECULAR_STRUCTURE', ], 'customDeleteCondition' => [ 'type_key' => 'MOLECULAR_STRUCTURE', ], ], ], ], ]; } public function getRawMaterialPictures() { return $this->hasMany(Attachment::className(), ['id' => 'related_id']) ->viaTable('sample_attachments', ['current_id' => 'id'], function ($query) { $query->andWhere([ 'type_key' => 'RAW_MATERIAL_PICTURES', ]); return $query; }); } public function getMolecularStructure() { return $this->hasMany(Attachment::className(), ['id' => 'related_id']) ->viaTable('sample_attachments', ['current_id' => 'id'], function ($query) { $query->andWhere([ 'type_key' => 'MOLECULAR_STRUCTURE', ]); return $query; }); }
安装
安装此扩展的首选方式是通过 composer。
运行
php composer.phar require --prefer-dist voskobovich/yii2-many-many-behavior "^3.0"
或将
"voskobovich/yii2-many-many-behavior": "^3.0"
添加到您的 composer.json
文件的 require 部分中。