Update One-To-Many Records Without Touching Unaffected Rows

We have two models “Foo” and “Bar”, as in the picture below (one Foo can have many bars).

database table table

Let's say that we want to update the model Foowith values ​​from $_POST. Due to the relationship Foowith, Barwe also want to update the model Barwith values ​​from the same $_POST. Foothe update process is the same as usual (i.e., it loads the model Foofrom the database using foo ID, then loads the model $_POSTinto Fooand saves the model). But it Foocan have many Bars, so we delete all the records from the table table with $fooIdand create new records for the table table from $_POST.

, , Foo // . , foo - "foo", - "bar1", "bar2" "bar3". foo "fooChanged" "bar1" "bar10" "bar3". : "bar2" . Foo, foo ( "foo" "fooChanged" ) . Bar . -, , $fooId, batchInsert (. ).

:

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,
    ]);
}

:

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();
}

, , , Bar . , , , , , . ( , Bar , "bar2" ).

, ( )?

+4
2

, . , . , , , .

actionUpdateFoo($fooId = null):

Foo . Bars, Foo. foreach(), Bar ID ($dependantBars). , () 2 ( , - ). if() Foo, .

/**
 * Let 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
}

:

/**
 * 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),
    ];
}

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;
}
+2

Source: https://habr.com/ru/post/1677142/


All Articles