gpt4 book ai didi

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

转载 作者:可可西里 更新时间:2023-11-01 08:24:34 24 4
gpt4 key购买 nike

我们有两个模型“Foo”和“Bar”,如下图所示(一个 Foo 可以有多个 Bar)。

database tables scheme

假设我们想用 $_POST 中的值更新 Foo 模型。由于 FooBar 的关系,我们还想用来自相同 $_POST 的值更新 Bar 模型。 Foo 更新过程与通常相同(即通过 foo ID 从数据库加载 Foo 模型,然后将 $_POST 加载到 Foo 模型并保存模型)。但是 Foo 可以有很多 Bar,这就是为什么我们从 bar 表中删除所有具有 $fooId 的条目并为 bar 表创建新条目的原因$_POST

例如,用户打开表单,他可以在其中更改 Foo 名称并添加/更改/删除许多栏名称。假设当前的 foo 名称是“foo”,当前的条形图是“bar1”、“bar2”和“bar3”。用户将 foo 名称更改为“fooChanged”,同时将“bar1”更改为“bar10”并删除“bar3”。 注意:“bar2”没有被触及。提交表单后, Controller 加载 Foo 模型,加载 foo 更改(现在“foo”已更改为“fooChanged”)并保存模型。 Bar 模型有点不同。首先, Controller 删除所有具有 $fooId 的条目,并使用 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,
]);
}

酒吧模型:

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”)。

有没有比这个更好的方法(我们想忽略未更改的行,只更改受影响的行)?

最佳答案

不必先删除所有行再添加。我们正在使用一种简单的方法来检测更改并仅更新更新的行。虽然这可能不会减少代码中编写的行数,但会减少使用的查询量,从而提高加载速度。

actionUpdateFoo($fooId = null) 的简短摘要:

我们正在用新值加载 Foo。我们还选择了分配给 Foo 模型的所有 Bars。我们使用 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
}

Controller 中的分离方法来获取差异:

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

关于php - 更新一对多关系条目而不触及未受影响的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43972693/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com