简体   繁体   English

更新一对多关系条目,而不触及未受影响的行

[英]Update one-to-many relation entries without touching unaffected rows

We have two models "Foo" and "Bar" like in the picture below (one Foo can have many Bars). 我们有两个模型“Foo”和“Bar”,如下图所示(一个Foo可以有很多Bars)。

数据库表方案

Let's say that we want to update Foo model with values from $_POST . 假设我们想要使用$_POST值更新Foo模型。 Because of Foo relation with Bar , we also want to update Bar model with values from same $_POST . 由于与BarFoo关系,我们还希望使用来自相同$_POST值更新Bar模型。 Foo updating process is the same as usual (ie load Foo model from database by foo ID, then load $_POST to Foo model and save the model). Foo更新过程与通常相同(即通过foo ID从数据库加载Foo模型,然后将$_POST加载到Foo模型并保存模型)。 But Foo can have many Bar s, thats why we delete all entries from bar table that have $fooId and create new entries to bar table from $_POST . 但是Foo可以有很多Bar ,这就是为什么我们从bar表中删除所有带有$fooId条目并从$_POST创建条形表的新条目。

For example, user opens form where he can change Foo name and add/change/remove many bar names. 例如,用户打开表单,他可以在其中更改Foo名称并添加/更改/删除许多栏名称。 Let's say current foo name is "foo" and current bars are "bar1", "bar2" and "bar3". 假设当前foo名称为“foo”,当前条形为“bar1”,“bar2”和“bar3”。 User changes foo name to "fooChanged" and at the same time changes "bar1" to "bar10" and deletes "bar3". 用户将foo名称更改为“fooChanged”,同时将“bar1”更改为“bar10”并删除“bar3”。 NOTE : that "bar2" was not touched. 注意 :未触及“bar2”。 After submitting the form, controller loads Foo model, loads to it foo changes (now "foo" has been changed to "fooChanged") and saves the model. 提交表单后,控制器加载Foo模型,加载foo更改(现在“foo”已更改为“fooChanged”)并保存模型。 With Bar model it's a little bit different. 有了Bar模型,它有点不同。 Firstly, controller removes all entries, that have $fooId , and creates new ones with batchInsert (see the code below). 首先,控制器删除所有具有$fooId条目,并使用batchInsert创建新的batchInsert (请参阅下面的代码)。

Controller: 控制器:

public function actionUpdateFoo($fooId = null)
{
    $foo = Foo::findOne($fooId);
    $foo->load(Yii::$app->request->post());

    $transaction = Yii::$app->db->beginTransaction();
    if ($foo->save() && Bar::deleteAll(['foo_id' => $fooId]) && Bar::create($fooId, Yii::$app->request->post())) {
        $transaction->commit();
    } else {
        $transaction->rollBack();
    }

    return $this->render('foo', [
        'foo' => $foo,
    ]);
}

Bar model: 酒吧型号:

public static function create($fooId, $post)
{
    $array = [];
    foreach ($post['Bar'] as $item) {
        array_push($array, [
            'foo_id' => $fooId,
            'name' => $item['name'],
        ]);
    }

    return Yii::$app->db->createCommand()->batchInsert(self::tableName(), ['foo_id', 'name'], $array)->execute();
}

The problem we face is that in order to update many Bar entries, we must remove the old ones and add new ones. 我们面临的问题是,为了更新许多Bar条目,我们必须删除旧条目并添加新条目。 We think this approach is not optimal because if we have a lot of entries and user changes just one, we have to delete all of them and insert again the same ones plus updated one. 我们认为这种方法不是最优的,因为如果我们有很多条目和用户更改只有一个,我们必须删除所有条目并再次插入相同的条目和更新的条目。 (Like in example above, all three Bar entries will be removed even though "bar2" was not touched). (如上例所示,即使未触及“bar2”,也会删除所有三个Bar条目)。

Is there any other better approach than this one (we want to ignore unchanged rows and only change the affected rows)? 有没有比这更好的方法(我们想忽略未更改的行,只更改受影响的行)?

It is not necessary to delete all rows first and then add again. 没有必要先删除所有行,然后再次添加。 We are using a simple method that detects changes and updates only updated rows. 我们使用一种简单的方法来检测更改并仅更新更新的行。 While this might not reduce the amount of lines written in code, but it reduces the amount of queries used which could improve loading speed. 虽然这可能不会减少代码中写入的行数,但它会减少使用的查询量,从而提高加载速度。

Short summary for actionUpdateFoo($fooId = null) : actionUpdateFoo($fooId = null)简短摘要actionUpdateFoo($fooId = null)

We are loading Foo with new values. 我们正在使用新值加载Foo We are also selecting all Bars that are assigned to Foo model. 我们还选择了分配给Foo模型的所有Bars Using foreach() we iterate through Bar and place ID of each found row to one variable ( $dependantBars ). 使用foreach()我们遍历Bar并将每个找到的行的ID放到一个变量( $dependantBars )。 Using method, we (always) get an array with a size of 2 (first element is an array of old values and second element is an array of new values). 使用方法,我们(总是)获得一个大小为2的数组(第一个元素是一个旧值数组,第二个元素是一个新值数组)。 In if() we save updated Foo model and also check if deletions and insertions were successful. 在if()中,我们保存更新的Foo模型,并检查删除和插入是否成功。

/**
 * Let's say, we have in this example:
 * $dependantBars = [0, 1, 2, 3]; (old choices)
 * $foo['choices'] = [0, 1, 5, 7]; (loaded from Yii::$app->request->post()['Foo']['choices'])
 */
public function actionUpdateFoo($fooId = null)
{
    $foo = Foo::findOne($fooId);
    $foo->load(Yii::$app->request->post());

    $subFoo = Bar::findAll($fooId);
    $dependantBars = [];
    foreach ($subFoo as $foo) {
        $dependantBars[] = $foo->id;
    }

    $distinction = self::getDifferencesInArrays($dependantBars, $foo['choices']);

    $transaction = Yii::$app->db->beginTransaction();
    if ($foo->save() && Bar::updateUserChoices($distinction)) {
        $transaction->commit();
    } else {
        $transaction->rollBack();
    }

    // Do something else
}

Separate method in controller to get differences: 控制器中的单独方法可以获得差异:

/**
 * Checks the difference between 2 arrays.
 *
 * @param array $array1 Old values that are still saved in database.
 * @param array $array2 New values that are selected by user.
 * @return array
 */
public static function getDifferencesInArrays($array1 = [], $array2 = [])
{
    return [
        array_diff($array1, $array2),
        array_diff($array2, $array1),
    ];
}

And in Bar class we can write this method to do both things in the same method (delete and insert): 在Bar类中,我们可以编写这个方法来在同一个方法中执行这两个方法(删除和插入):

public static function updateUserChoices($distinction)
{
    $deletedRows = true;
    $insertedRows = true;

    if (!empty($distinction[0])) {
        $deletedRows = self::deleteAll(['foo_id' => $distinction[0]]);
    }

    if (!empty($distinction[1])) {
        $insertedRows = Bar::create(); // Something here
    }

    return $deletedRows && $insertedRows;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM