iksaku/laravel-mass-update

使用单个查询更新多个Laravel模型记录,每个记录有不同的值!

1.0.7 2024-03-23 20:48 UTC

This package is auto-updated.

Last update: 2024-09-21 08:54:29 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

更新多个Laravel模型记录,每个记录有自己的值集合,发送单个查询到数据库!

安装

您可以通过composer安装此包

composer require iksaku/laravel-mass-update

用法

在您的模型类中添加Iksaku\Laravel\MassUpdate\MassUpdatable特质

use Illuminate\Database\Eloquent\Model;
use Iksaku\Laravel\MassUpdate\MassUpdatable;

class User extends Model
{
    use MassUpdatable;
    
    // ...
}

这就完成了!您的模型现在可以单个查询更新具有不同值的多个记录!

让我们看看这个新查询的一些可能的用例

简单用例:更新多个记录的值

假设您有以下users

但是,我们想要更新这两条记录,因为那些用户告诉我们他们的法定姓氏拼写错误

  • González在字母a上带有一个重音符号,并且只使用z,从不使用s
  • Martínez在字母i上有一个重音符号,最后一个字母应该是z,而不是s

嗯,我们可以批量更新这些特定的记录

User::massUpdate(
    values: [
        ['id' => 1, 'name' => 'Jorge González'],
        ['id' => 2, 'name' => 'Gladys Martínez'],
    ]
);

现在,这两条记录将通过单个查询更新为它们的相应值,结果如下

默认情况下,massUpdate查询将获取您的模型的主键名称并将其作为查询的一部分应用,以不影响其他记录。

如果您想使用另一列作为索引来区分值类型,您可以将它作为函数调用的第二个参数传递

User::massUpdate(
    values: [
        ['username' => 'iksaku', 'name' => 'Jorge González'],
        ['username' => 'gm_mtz', 'name' => 'Gladys Martínez'],
    ],
    uniqueBy: 'username'
);

简单用例 #2:更新多个Eloquent模型

如果您需要更新一些模型类中的值,并希望自动批量更新这些更改,那么这个包就是为您准备的!

现有的masUpdate查询能够识别Eloquent模型类的属性并正确编译。您不需要手动将模型转换为数组,您只需传递要更新的模型列表,它就会处理剩下的。

提示:如果您传递了完整的Eloquent模型列表,只有那些具有值的模型会被更新,因此您实际上不需要手动过滤未更改的模型。

让我们重新创建前面的例子,但使用Eloquent模型...

// Say we already pulled our user models previously... Something like this:
$jorge = User::where('name', 'Jorge Gonzales')->first();
$gladys = User::where('name', 'Gladys Martines')->first();

// And let's say we already made changes to those models... Like this:
$jorge->name = 'Jorge González';
$gladys->name = 'Gladys Martínez';

// And now, let's update both models in a single query:
User::massUpdate(
    values: [$jorge, $gladys]
);

很酷,对吧?

注意:只能批量更新相同Eloquent模型的实例,不能混合不同的Eloquent模型类。

复杂用例:使用多个索引来区分记录

假设我们刚刚创建了expenses表来跟踪我们在时间上的花费,并且我们手动填充了以下值

以上信息是虚构的,我不跟踪我的季度开支。

哎呀...我们犯了一个小错误...2020年和2021年第一季度的开支被颠倒了,为了修复它,我们只能将quarter列作为索引传递,但如果我们只传递quarter列作为索引,我们将修改所有的Q1记录。因此,为此,我们还应该将year列作为索引传递

Expense::massUpdate(
    values: [
        ['year' => 2020, 'quarter' => 'Q1', 'total_expenses' => 431.35],
        ['year' => 2021, 'quarter' => 'Q1', 'total_expenses' => 416.70],
    ],
    uniqueBy: ['year', 'quarter']
);

提示:如果您需要指定一个以上的一个或两个索引,只需在valuesuniqueBy参数中包含所有索引即可。

表中的结果将被正确更新

注意:您始终需要在您的 values 数组中包含 uniqueBy 列,否则会抛出异常。

注意 #2:无法更新 uniqueBy 列的值。此参数中指定的每个列都将从将要更新的列中过滤掉。

这可以防止在以 array 形式更新 values 并作为 Eloquent 模型传递时出现意外的副作用。

高级用例:与其他查询语句链式使用

让我们尽量保持简单,想象一个跟踪多个用户的 待办事项 系统的体系结构

像每个 待办事项 系统,我们允许用户 order 他们的 待办事项 项,以便将最重要的项显示在列表的顶部,为此,我们可能会使用一个允许用户拖动项在列表上下移动的 简单的排序包

一旦用户移动列表中的某个项,在后端我们可能会收到一个具有特定键值形状的数组:['position' => 'id']。有了这个,我们将根据给定的 id 更新记录的 position

我们可以简单地调用我们的 massUpdate 查询函数,一切都会完成... 嗯,差不多...

在这个特定的场景中,我们处理的是 多个用户的多列表,这意味着我们可能无法总是控制发送到服务器的 id 列表,也许有些恶意行为者想要劫持我们的 待办事项 列表并降低购买 ThinkPads 的优先级。这是一个相当严重的安全问题。

有很多种方法可以解决这个问题,其中一种简单的方法是将查询语句链式到我们的 massUpdate 函数中。

在这种情况下,我们将添加一个 where() 语句,只更新属于当前登录用户的项目。这和任何其他 Laravel 查询构建器一样简单

TodoItem::query()
    ->where('user_id', auth()->id())
    ->massUpdate(
        values: collect($request->input('item_order'))
            ->mapWithKeys(
                fn ($id, int $position) => ['id' => $id, 'order' => $position]
            )
    );

提示:你知道你可以将集合、延迟集合、Eloquent 集合以及基本上任何 Arrayable 实例作为 values 传递给 massUpdate 查询函数吗?

这可以用作额外的一层来确保处理影响数据库中多个记录的用户提供的输入时的数据完整性。

测试

composer test

变更日志

请参阅 CHANGELOG 了解最近更改的更多信息。

贡献

请参阅 CONTRIBUTING 了解详细信息。

安全漏洞

如果您发现任何与安全相关的问题,请通过电子邮件发送到 yo@jorgeglz.io 而不是使用问题跟踪器。

鸣谢

许可证

MIT 许可证 (MIT)。请参阅 许可证文件 了解更多信息。