我们有两个模型“Foo”和“Bar”,如下图所示(一个Foo可以有很多Bars).
假设我们想要使用$_POST中的值更新Foo模型.由于与Bar的Foo关系,我们还希望使用来自相同$_POST的值更新BAR模型. Foo更新过程与通常相同(即通过foo ID从数据库加载Foo模型,然后将$_POST加载到Foo模型并保存模型).但是Foo可以有很多Bars,这就是为什么我们从bar表中删除所有带有$fooId的条目并从$_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条目,我们必须删除旧条目并添加新条目.我们认为这种方法不是最优的,因为如果我们有很多条目和用户更改只有一个,我们必须删除所有条目并再次插入相同的条目和更新的条目. (如上例所示,即使未触及“bar2”,也会删除所有三个条形条目).
有没有比这更好的方法(我们想忽略未更改的行,只更改受影响的行)?
解决方法:
没有必要先删除所有行,然后再次添加.我们使用一种简单的方法来检测更改并仅更新更新的行.虽然这可能不会减少代码中写入的行数,但它会减少使用的查询量,从而提高加载速度.
actionUpdateFoo的简短摘要($fooId = null):
我们正在使用新值加载Foo.我们还选择了分配给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;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。