我们有两个模型“Foo”和“Bar”,如下图所示(一个Foo可以有很多条)。
假设我们要使用Foo
中的值更新$_POST
模型。由于与Foo
的{{1}}关系,我们还希望使用来自相同Bar
的值更新Bar
模型。 $_POST
更新过程与通常相同(即通过foo ID从数据库加载Foo
模型,然后将Foo
加载到$_POST
模型并保存模型)。但是Foo
可以包含多个Foo
,这就是为什么我们会从条形图表中删除Bar
的所有条目,并从$fooId
创建条形表的新条目。
例如,用户打开表单,他可以在其中更改$_POST
名称并添加/更改/删除许多栏名称。假设当前foo名称为“foo”,当前条形为“bar1”,“bar2”和“bar3”。用户将foo名称更改为“fooChanged”,同时将“bar1”更改为“bar10”并删除“bar3”。 注意:未触及“bar2”。提交表单后,控制器加载Foo
模型,加载foo更改(现在“foo”已更改为“fooChanged”)并保存模型。使用Foo
模型,它有点不同。首先,控制器删除所有具有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();
}
条目,我们必须删除旧条目并添加新条目。我们认为这种方法不是最优的,因为如果我们有很多条目和用户更改只有一个,我们必须删除所有条目并再次插入相同的条目和更新的条目。 (如上例所示,即使未触及“bar2”,也会删除所有三个Bar
条目。
有没有比这更好的方法(我们想忽略未更改的行,只更改受影响的行)?
答案 0 :(得分:2)
不必先删除所有行,然后再重新添加。我们使用一种简单的方法来检测更改并仅更新更新的行。虽然这可能不会减少代码中写入的行数,但它会减少使用的查询量,从而提高加载速度。
actionUpdateFoo($fooId = null)
的简短摘要:
我们正在使用新值加载Foo
。我们还选择了分配给Bars
模型的所有Foo
。使用foreach()
,我们遍历Bar并将每个找到的行的ID放到一个变量($dependantBars
)。使用方法,我们(总是)获得一个大小为2的数组(第一个元素是一个旧值数组,第二个元素是一个新值数组)。在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
}
控制器中的单独方法以获得差异:
/**
* 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;
}