voskobovich/yii2-many-many-behavior

此行为使您在ActiveRecord模型中维护多对多关系变得简单。

安装: 164 090

依赖: 17

建议者: 0

安全: 0

星星: 112

关注者: 13

分支: 31

类型:yii2-behavior

v3.2.2 2016-01-14 09:39 UTC

This package is not auto-updated.

Last update: 2024-09-14 17:03:52 UTC


README

此行为将很快被弃用
查看新版本 Yii2 Linker Behavior

关于

此行为使您在ActiveRecord模型中轻松维护多对多和一对一关系。

License Latest Stable Version Latest Unstable Version Total Downloads Build Status

支持

GutHuB问题.

用法

  1. 在您的模型中,添加行为并配置它
  2. 在您的模型中,为行为创建的属性添加验证规则
  3. 在您的视图中,为属性创建表单字段

添加和配置行为

例如,假设您正在处理类似 BookAuthorReview 的实体。该 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_idsreview_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个参数。在我们的例子中,$modelBook 类(行为的拥有者)的实例,$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 部分中。