We have two models "Foo" and "Bar" like in the picture below (one Foo can have many Bars).
Let's say that we want to update Foo
model with values from $_POST
. Because of Foo
relation with Bar
, we also want to update Bar
model with values from same $_POST
. Foo
updating process is the same as usual (i.e. load Foo
model from database by foo ID, then load $_POST
to Foo
model and save the model). 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
.
For example, user opens form where he can change Foo
name and add/change/remove many bar names. Let's say current foo name is "foo" and current bars are "bar1", "bar2" and "bar3". User changes foo name to "fooChanged" and at the same time changes "bar1" to "bar10" and deletes "bar3". NOTE: that "bar2" was not touched. After submitting the form, controller loads Foo
model, loads to it foo changes (now "foo" has been changed to "fooChanged") and saves the model. With Bar
model it's a little bit different. Firstly, controller removes all entries, that have $fooId
, and creates new ones with batchInsert
(see the code below).
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. 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).
Is there any other better approach than this one (we want to ignore unchanged rows and only change the affected rows)?