Update multiple Laravel Model records, each with it's own set of values, sending a single query to your database!

Overview

Laravel Mass Update

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

Update multiple Laravel Model records, each with its own set of values, sending a single query to your database!

Installation

You can install the package via composer:

composer require iksaku/laravel-mass-update

Usage

In your model class, add the Iksaku\Laravel\MassUpdate\MassUpdatable trait:

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

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

And that's all! Your model is now ready to update multiple records with varying values in a single query!

Let's take a look at some possible use cases for this new query:

Simple use case: Update the values of multiple records

Imagine that you have the following users table:

id name username
1 Jorge Gonzales iksaku
2 Gladys Martines gm_mtz

But, we want to update both records since those users have told us that their legal last name was misspelled:

  • González is written with an accent on the letter a, and only uses z, never an s.
  • Martínez is written with an accent on the letter i, and last letter should be a z, not an s

Well, we can mass update those specific records:

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

Now, both records will be updated with their corresponding values in a single query, resulting in:

id name username
1 Jorge González iksaku
2 Gladys Martínez gm_mtz

By default, the massUpdate query will grab your model's primary key name and apply it as part of the query to not affect other records.

If you want to use another column as an index to separate value types, you could pass it as a second argument to the function call:

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

Simple use case #2: Updating multiple Eloquent Models

If you need to update the values in some Model classes and want to automatically mass update those changes, then this is for you!

The existing masUpdate query is capable of identifying the dirty attributes of Eloquent model classes and compile them properly. You don't need to manually convert the models into an array, you just pass the list of models you want to update, and it takes care of the rest.

Tip: If you pass a full list of Eloquent models, only those with dirty values will be updated, so you don't actually need to filter the unchanged ones manually.

Let's recreate the previous example, but using Eloquent models...

// 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]
);

Pretty cool, right?

Note: It is only possible to mass update instances of the same Eloquent model, it is not possible to mix the Query Builder with different Eloquent model classes.

Complicated use case: Using multiple indexes to differentiate records

Let's say that we just created expenses table to track how much we spend across time, and we manually filled the following values:

id year quarter total_expenses
.. .. .. ..
.. 2019 Q3 216.70
.. 2019 Q4 216.70
.. 2020 Q1 416.70
.. 2020 Q2 211.12
.. 2020 Q3 113.17
.. 2020 Q4 422.89
.. 2021 Q1 431.35

Above information is not real, I don't track my expenses quarterly.

Oops... We made a little mistake... Expenses from Q1 of 2020 and 2021 are switched, and in order to fix it we could only pass the quarter column as an index, but if we only pass down the quarter column as an index, we'll modify ALL Q1 records. So, for this, we should also pass down the year column as an index:

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

Tip: If you ever need to specify more than one or two indexes, just include all of them in the values and uniqueBy parameters.

The result in the table will be properly updated:

id year quarter total_expenses
.. .. .. ..
.. 2020 Q1 431.35
.. .. .. ..
.. 2021 Q1 416.70

NOTE: It is important that you always include the uniqueBy columns in your values array, exceptions will be thrown otherwise.

NOTE #2: It is not possible to update the values of the uniqueBy columns. Every column specified in this parameter will be filtered from the ones that are going to be updated.

This prevents unexpected side effects from happening while updating values in array shape and passed as Eloquent models.

Advanced use case: Chaining with other query statements

Let's try to keep things simple and imagine a system that tracks To-Do items for multiple users:

id user_id content order
1 1 Pick up my daughter 2
2 1 Buy a new ThinkPad 1
3 1 Drink water 3

Like every To-Do system, we let our users order their To-Do items to see the most important ones at the top of the list, and to do this, we may be using a simple sorting package that allows the user to drag items up and down the list.

Once the user moves one item in the list, in the backend we may receive an array with a specific key-value shape: ['position' => 'id']. With this, we're going to update the records' position based on the given id.

We can simply call our massUpdate query function and everything will be done... Well, sort of...

In this specific scenario, we're dealing with multiple lists for multiple users, that means that we may not be always able to control which id columns are sent to the server, maybe some malicious actor wants to hijack our To-Do list and lower the priority for buying ThinkPads. This is a pretty serious security concern.

There are many ways to solve this kind of issues, and a simple one is to chain query statements to our massUpdate function.

In this case, we're going to add a where() statement to only update those items that belong to the currently logged in user. And it's as simple as in any other Laravel query builder:

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

Tip: Did you know you can pass Collections, LazyCollections, Eloquent Collections and basically any Arrayable instance as values to the massUpdate query function?

This can be used as an extra layer to ensure data integrity when dealing with User-provided input that affects multiple records in the database.

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

You might also like...
Laravel-model-mapper - Map your model attributes to class properties with ease.
Laravel-model-mapper - Map your model attributes to class properties with ease.

Laravel Model-Property Mapper This package provides functionality to map your model attributes to local class properties with the same names. The pack

A MongoDB based Eloquent model and Query builder for Laravel (Moloquent)

Laravel MongoDB This package adds functionalities to the Eloquent model and Query builder for MongoDB, using the original Laravel API. This library ex

A DynamoDB based Eloquent model and Query builder for Laravel.

Laravel DynamoDB A DynamoDB based Eloquent model and Query builder for Laravel. You can find an example implementation in kitar/simplechat. Motivation

Easily add all the 58 Algerian Wilayas and its Dairas to your cool Laravel project (Migrations, Seeders and Models).
Easily add all the 58 Algerian Wilayas and its Dairas to your cool Laravel project (Migrations, Seeders and Models).

Laravel-Algereography Laravel-Algereography allows you to add Migrations, Seeders and Models of Algerian Wilayas and Dairas to your existing or new co

A simple laravel package to handle multiple key based model route binding

Laravel Model UUID A simple package to handle the multiple key/column based route model binding for laravel package Installation Require the package u

A laravel package to generate model hashid based on model id column.

Laravel Model Hashid A package to generate model hash id from the model auto increment id for laravel models Installation Require the package using co

A Laravel package that allows you to validate your config values and environment.
A Laravel package that allows you to validate your config values and environment.

Table of Contents Overview Installation Requirements Install the Package Publishing the Default Rulesets Usage Creating a Validation Ruleset Using the

Blacksmith is a code generation tool which automates the creation of common files that you'd typically create for each entity in your application.
Blacksmith is a code generation tool which automates the creation of common files that you'd typically create for each entity in your application.

Blacksmith is a code generation tool which automates the creation of common files that you'd typically create for each entity in your application.

This is a Laravel package for msegat. Its goal is to remove the complexity

laravel-msegat package This is a Laravel package for msegat. Its goal is to remove the complexity Laravel Msegat Package This is a package for msegat.

Releases(1.0.4)
Owner
Jorge González
Self-taught developer, love sports and pizza!
Jorge González
A laravel Livewire Dynamic Selects with multiple selects depending on each other values, with infinite levels and totally configurable.

Livewire Combobox: A dynamic selects for Laravel Livewire A Laravel Livewire multiple selects depending on each other values, with infinite levels of

Damián Aguilar 25 Oct 30, 2022
webtrees module: enhanced clippings cart with more functions to add records to the clippings cart and to start actions on these records

webtrees module hh_clippings_cart_enhanced !!! This is an alpha version! Do not use it in a productive webtrees system! !!! This webtrees custom modul

Hermann Hartenthaler 1 Sep 18, 2022
A Laravel wrapper for spatie/dns. Allows to query and validate DNS records.

A Laravel wrapper for spatie/dns. Allows to query and validate DNS records.

Astrotomic 22 Nov 17, 2022
A package to filter laravel model based on query params or retrieved model collection

Laravel Filterable A package to filter laravel model based on query params or retrived model collection. Installation Require/Install the package usin

Touhidur Rahman 17 Jan 20, 2022
This Package helps you in laravel application to log all desired activity for each request from request entry point to generate response at a single snapshot.

Laravel Scenario Logger This Package helps you in laravel application to log all desired activity for each request from request entry point to generat

Mehrdad Mahdian 6 Sep 27, 2021
Allow your model to record the creation, update and deletion of user fingerprints in laravel packages

This laravel package will allow your models to record the the created, updated and deleted by User FingerPrints

Managemize 4 Mar 11, 2022
Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes and constraints by adding them as a subquery.

Laravel Eloquent Scope as Select Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes an

Protone Media 75 Dec 7, 2022
Make your own custom cast type for Laravel model attributes

Laravel Custom Casts Make your own cast type for Laravel model attributes Laravel custom casts works similarly to Eloquent attribute casting, but with

Vladimir Ković 220 Oct 28, 2022
Easily integrate single-database multi tenant features into your Laravel application

Laravel Tenant Aware Easily integrate single-database multi tenant features into your Laravel application. Installation You can install the package vi

H-FARM Innovation 9 Dec 21, 2022
Effortlessly streamline tables and records printing in PDF/XLSX in your FilamentPHP application.

Filament Printables: a package to generate reports and form printables for your app. This is a work in progress thing Installation You can install the

fastOFI Corp 6 Jun 15, 2023