arogachev/yii2-many-to-many

为 Yii 2 框架提供的多对多 ActiveRecord 关系

安装: 30,906

依赖项: 5

建议者: 0

安全: 0

星标: 36

关注者: 5

分支: 16

公开问题: 5

类型:yii2-extension

0.3.0 2018-03-13 08:16 UTC

This package is not auto-updated.

Last update: 2024-09-19 14:06:34 UTC


README

实现 Yii 2 框架的多对多关系

Latest Stable Version Total Downloads Latest Unstable Version License

安装

安装此扩展的首选方式是通过 composer

运行以下命令之一:

php composer.phar require --prefer-dist arogachev/yii2-many-to-many

或添加

"arogachev/yii2-many-to-many": "0.2.*"

到您的 composer.json 文件的 require 部分中。

功能

  • 使用现有的 hasMany 关系配置
  • 多个关系
  • 无需额外查询。例如,如果最初模型有 100 个相关记录,添加一个后,恰好插入一行。如果没有变化,则不会执行查询。
  • 自动填充可编辑属性
  • 用于检查接收到的列表是否有效的验证器

创建可编辑属性

只需在您的 ActiveRecord 模型中添加公共属性即可,例如

/**
 * @var array
 */
public $editableUsers = [];

它将存储更新期间相关记录的主键。

附加和配置行为

第一种方法是显式指定所有参数

use arogachev\ManyToMany\behaviors\ManyToManyBehavior;

/**
 * @inheritdoc
 */
public function behaviors()
{
    return [
        [
            'class' => ManyToManyBehavior::className(),
            'relations' => [
                [
                    'editableAttribute' => 'editableUsers', // Editable attribute name
                    'table' => 'tests_to_users', // Name of the junction table
                    'ownAttribute' => 'test_id', // Name of the column in junction table that represents current model
                    'relatedModel' => User::className(), // Related model class
                    'relatedAttribute' => 'user_id', // Name of the column in junction table that represents related model
                ],
            ],
        ],
    ];
}

但更常见的是我们需要显示相关模型,因此最好为该模型定义关系,并用于显示和行为配置。两种方式(viaviaTable)都认为是有效的

使用 viaTable

/**
 * @return \yii\db\ActiveQuery
 */
public function getUsers()
{
    return $this->hasMany(User::className(), ['id' => 'user_id'])
        ->viaTable('tests_to_users', ['test_id' => 'id'])
        ->orderBy('name');
}

使用 via(需要额外的模型来处理连接表)

/**
 * @return \yii\db\ActiveQuery
 */
public function getTestUsers()
{
    return $this->hasMany(TestUser::className(), ['test_id' => 'id']);
}

/**
 * @return \yii\db\ActiveQuery
 */
public function getUsers()
{
    return $this->hasMany(User::className(), ['id' => 'user_id'])
        ->via('testUsers')
        ->orderBy('name');
}

顺序不是必需的。

然后只需传递此关系的名称,所有其他参数将自动获取。

/**
 * @inheritdoc
 */
public function behaviors()
{
    return [
        [
            'class' => ManyToManyBehavior::className(),
            'relations' => [
                [
                    'name' => 'users',
                    // This is the same as in previous example
                    'editableAttribute' => 'editableUsers',
                ],
            ],
        ],
    ];
}

可以通过完全相同的方式添加额外的多对多关系。请注意,即使对于单个关系,也应将其声明为 relations 部分的一部分。

填充关系

默认情况下,每个找到的模型的 editableAttribute 将用相关模型 id 填充(使用预加载)。如果您想有更多的手动控制,防止额外的查询,禁用 autoFill 选项

'autoFill' => false,

并仅在需要时填充它,例如在控制器的 update 动作中。这是推荐的使用方式。

public function actionUpdate($id)
{
    $model = $this->findModel($id);
    $model->getManyToManyRelation('users')->fill();
    // ...
}

或者您可以在闭包中指定填充条件

'autoFill' => function ($model) {
    return $model->scenario == Test::SCENARIO_UPDATE; // boolean value
}

甚至可以这样做

'autoFill' => function ($model) {
    return Yii::$app->controller->route == 'tests/default/update';
}

但出于某种原因,这不建议使用,因为模型不是处理路由的合适位置。

在不进行大量赋值的情况下保存关系

在创建模型时

$model = new Test;
$model->editableUsers = [1, 2];
$model->save();

在更新模型('autoFill' => true)时

$model = new Test;
$model->editableUsers = [1, 2];
$model->save();

在更新模型('autoFill' => false,手动填充)时

$model = new Test;
$model->getManyToManyRelation('users')->fill();
var_dump($model->editableUsers) // [1, 2]
$model->editableUsers = [1, 2, 3];
$model->save();

在更新模型('autoFill' => false,不手动填充)时

$model = new Test;
var_dump($model->editableUsers) // empty array
$model->save();

在这种情况下,多对多关系将保持不变。

将属性添加为安全

将可编辑属性添加到模型规则中,以进行大量赋值。

至少将其标记为安全

public function rules()
{
    ['editableUsers', 'safe'],
}

或使用自定义验证器

use arogachev\ManyToMany\validators\ManyToManyValidator;

public function rules()
{
    ['editableUsers', ManyToManyValidator::className()],
}

验证器检查列表是否为数组,并且仅包含在相关模型中呈现的主键。它不能在没有附加 ManyToManyBehavior 的情况下使用。

将控件添加到视图

将控件添加到视图以管理相关列表。没有扩展,可以使用多选列表完成此操作

<?= $form->field($model, 'editableUsers')->dropDownList(User::getList(), ['multiple' => true]) ?>

示例 getList() 方法内容(需要放置在 User 模型中)

use yii\helpers\ArrayHelper;

/**
 * @return array
 */
public static function getList()
{
    $models = static::find()->orderBy('name')->all();

    return ArrayHelper::map($models, 'id', 'name');
}

关系功能

您可以像这样访问多对多关系

$relation = $model->getManyToManyRelation('users');

users 可以是配置中指定的 nametable 关系属性之一的值。

您可以这样用相关记录的 id 填充 editableAttribute

$model->getManyToManyRelation('users')->fill();

您可以如此获取特定关系的相关模型的添加和删除主键。

$addedPrimaryKeys = $model->getManyToManyRelation('users')->getAddedPrimaryKeys();
$deletedPrimaryKeys = $model->getManyToManyRelation('users')->getDeletedPrimaryKeys();

注意,它们仅在模型保存后可用,因此您可以在调用$model->save()后或afterSave()事件处理器中访问它们。

运行测试

安装依赖项

composer install

添加数据库配置(tests/config/db-local.php文件)如下内容

<?php

return [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=localhost;dbname=yii2_many_to_many',
    'username' => 'root',
    'password' => '',
];

您可以按需更改dbnameusernamepassword。确保在运行测试之前创建数据库和用户。

运行测试

vendor/bin/phpunit