jdavidbakr/replaceable-model

为Eloquent模型添加'RePLACE'和'INSERT IGNORE'查询能力

1.4 2022-03-22 13:56 UTC

This package is auto-updated.

Last update: 2024-08-26 10:39:38 UTC


README

Latest Version on Packagist Software License Total Downloads Travis CI

Eloquent默认模型在大多数情况下都很不错,但如果你的数据库表有额外的约束,你可能会遇到竞争条件,这时标准的update()调用会失败。例如,想象以下表结构

id auto increment
user_id
widget_id
date

其中每个用户每天只能有一个小部件,因此在user_id和date上有一个唯一约束。现在在你的界面中,你有一个使用ajax调用更新此表中条目的表单,这可能包括根据选择的条目删除项目。因为表单提交的是,比如,整整一个月的小部件,你不想逐个插入 - 你想执行单个插入查询。所以你的PHP代码中可能如下所示

Model::where('user_id',$user_id)->delete();
Model::insert($inserts);

这实际上在内部执行以下操作

delete from table where user_id = A;
insert into table (user_id, widget_id, date) values (A, B, C) ...

如果你陷入竞争条件,你可能会得到以下结果

delete from table where user_id = A; -- process #1
delete from table where user_id = A; -- process #2
insert into table (user_id, widget_id, date) values (A, B, C) ... -- process #1
insert into table (user_id, widget_id, date) values (A, B, C) ... -- process #2 - Exception!

第二个插入将导致异常。如果第二个查询比第一个查询多一行,则该插入将永远丢失。

在Laravel之前,我通常会使用REPLACE或INSERT IGNORE命令来处理这种情况。REPLACE将在查询中的任何约束基础上执行删除和插入,而INSERT IGNORE将执行插入,但如果存在导致约束冲突的行,则这些行将不会被更新。如果你希望最后一个查询覆盖任何现有行,请使用REPLACE;如果你只想插入新行,请使用INSERT IGNORE。

因为这是MySQL的一个特定特性,Laravel不支持在Eloquent中使用它。标准的解决方案是执行原始查询。这是可以接受的,但是每次都要用绑定等构建这个查询有点麻烦,所以我决定创建一个Eloquent的特质,为我处理所有这些,并且可以通过与insert()相同的方式访问。

请注意,我这里没有使用Builder类。我直接扩展了Model类,因此你将无法像使用常规的insert()命令那样链式调用这些函数。这实际上只是一个用于解决我遇到的问题的宏。我欢迎任何可以解决你在此包中遇到的其他问题的pull requests。

'saving'和'saved'事件都会为这两个命令触发。

安装

通过Composer

$ composer require jdavidbakr/replaceable-model

使用

将特质应用到你的模型上以激活使用replace和insertIgnore的能力

class model extends Model {
	...	
	use \jdavidbakr\ReplaceableModel\ReplaceableModel
	...
}

然后构建你的插入数组,就像为insert()调用构建数组一样,并调用这两个函数中的一个

$inserts = [...];
\App\Model::replace($inserts);
\App\Model::insertIgnore($inserts);

贡献

请参阅CONTRIBUTINGCONDUCT以获取详细信息。

安全性

如果你发现任何与安全性相关的问题,请通过电子邮件me@jdavidbaker.com联系,而不是使用问题跟踪器。

鸣谢

许可证

MIT许可证(MIT)。有关更多信息,请参阅许可证文件