【发布时间】:2019-01-19 04:37:41
【问题描述】:
这可能是并发用户在同一应用中工作时最常见的问题之一。
用户 A 从数据库中获取模型,并在屏幕上显示给用户。他执行一些操作,并更新该对象的值。
在某个时间,它会保存模型。用户 A 几乎不知道,在他获取模型并保存修改后的模型之间,用户 B 已经获取了相同的模型,并将他的更改提交到数据库。结果:用户 B 的更改丢失。
我已经多次解决了同样的问题,通常是这样解决的:
1/给模型添加一个属性$version
2/ 在创建新模型时,我们设置$version = 1
3/ 取模型的时候,版本属性明显也取了。
4/保存修改后的模型时,我只保存... where id = $model->id AND WHERE version = $model->version,同时我正在更新$version = $version + 1
当该更新从数据库中抛出“没有更新记录”时,我只使用 id 搜索模型;如果我可以获取,我知道该模型存在,但版本不再匹配。然后我向用户提出一个异常(说'某人或某事已经修改了模型')
我相信这是确保您始终更新对象的最新版本的唯一正确方法。您显然也可以使用 updated_at 属性来比较您仍然拥有相同版本的对象,但理论上另一个用户可能已经更新了它。
现在我正在努力解决如何在 Laravel 中设置这个逻辑。我想创建一个特征,我可以在一个模型上使用它,该模型基本上可以完成我上面描述的工作,但我正在努力解决我真正应该做的事情。
我知道我可以覆盖 protected function setKeysForSaveQuery($query) 让它更新 where id = $this->id and version = $this->version 但是当我这样做时,laravel 没有更新任何东西,我没有得到允许我确定更新失败的异常,因为版本不匹配...
我想避免编写将保存封装在事务中的代码,并且每次保存模型时都必须验证版本;寻找一种方法来将其放入我的基础模型中,方法是包含一个特征,以便它自动运行。
任何意见将不胜感激。
--添加示例:
想象一下:
$modelcopy1 = App\Model::find(1); // version = 1
$modelcopy2 = App\Model::find(1); // version = 1
$modelcopy1->save(); // this should update version in database to 2
// also $modelcopy1->version should now hold 2
$modelcopy2->save(); // this should now throw an exception
// because copy 1 still references version 1
// database version is already at 2
所以基本上,我需要->save() 中的逻辑来验证版本属性的模型值是否与当前数据库版本匹配,并且我需要同步,以便在更新完成时,模型版本属性更新为最新版本。
保存应该如下所示:
UPDATE Model
SET version = version + 1, attr1 = :attr1, attr2 = :attr2, ...
WHERE version = $model->version
AND id = $model->id
IF UPDATE 成功 --> 确保 $model->version 更新为新版本值
IF UPDATE 不成功 THEN SELECT * FROM Model WHERE id = $model->id
IF 找到模型,则 THROW ERROR '模型已被其他人更新' ELSE THROW ERROR '模型不存在'
我希望这是有道理的。在我看来,这样的锁定防止机制应该是任何“成人”框架的一部分,但似乎很难将其绑定到 Laravel 框架中......
【问题讨论】:
-
我还没有要查看的代码 - 我只是找不到合适的地方将其插入 laravel 框架...
-
您是否在添加 trait 或让 trait 在子模型更新时遇到问题?
-
添加特征不是问题。问题是我看不出在 Laravel 框架的哪个位置插入是最好的选择。我唯一看到的是编写自己的
performUpdatemethod 并覆盖 eloquent 的方法,但这听起来不像是一个持久的解决方案,因为它对未来版本的不兼容性非常敏感,我相信... -
我正在编辑问题以添加一些示例...