Repository navigation
Laravel Batch v3.2.0
New bulk operations for large tables and files, JSON column updates, and two JSON bug fixes.
Everything is additive, so upgrading from 3.1 needs no code changes:
composer update mavinoo/laravel-batch
Added
Update keys inside JSON columns (#64, #83, #111)
Batch::update(new User, [
['id' => 1, 'settings->theme' => 'dark', 'settings->notify->email' => false],
]);- Works in every update method, on MySQL, MariaDB, PostgreSQL and SQLite.
- Other keys are kept, missing objects are created, and values keep their JSON type.
updated_atonly changes when the document changes.- Arrays and collections for columns the model casts to JSON (
array,json,collection,
AsCollection, ...) are encoded the way the model stores them, in update, insert and upsert.
insertGetIds(): insert rows and get their ids (#48)
$ids = Batch::insertGetIds(new User, $rows); // [101, 102, ...] in row order- Checked with 8 concurrent writers and 78,720 rows on MySQL 8.4, MariaDB, PostgreSQL and SQLite,
with 0 wrong ids.
import(): import files and iterables with constant memory
Batch::import(new User, storage_path('users.csv'), ['mode' => 'upsert', 'uniqueBy' => ['email']]);- Reads CSV, TSV and JSON Lines files (also
.gz), open streams, or any iterable such as
Model::cursor(). - Supports column mapping, null values, transforms, and progress and error callbacks.
| 500,000-row CSV | import() |
array + insertRows() |
|---|---|---|
| Memory | 7 MB | 765 MB |
| Time | 30–45% faster |
splitFile(): split large files
Batch::splitFile($path, lines: 50000); // users-001.csv, users-002.csv, ...- Splits by number of records and / or size.
- Never cuts a record or a multi-line CSV field in half, and repeats the header in every part.
deleteInChunks(), updateInChunks(), archive()
Log::where('created_at', '<', now()->subYear())->deleteInChunks(10000);
User::where('active', false)->updateInChunks(['status' => 'archived']);
Order::where('created_at', '<', '2020-01-01')->archiveTo('orders_archive');- Each runs a chunk at a time, in primary key order, so no lock is held for long.
- Deleting 200k rows on MySQL 8.4: the longest chunk took 0.10 s, against 0.8 s for one
DELETE. archive()copies and deletes each chunk in one transaction.
sync(): make a table match a list
Product::where('supplier_id', 5)->syncRows($feedRows, ['sku']);
// ['inserted' => 120, 'updated' => 4800, 'deleted' => 35]- Inserts, updates and deletes in one transaction.
- The database compares the keys itself, so its collation decides which are equal. On MySQL,
"ABC"and"abc"are the same key. - Soft-deleted rows that are in the list are restored.
- Syncing 100,000 rows takes 1.4–4.8 s with about 15 MB of memory.
Fixed
- Updating a PostgreSQL
jsoncolumn failed withoperator does not exist: json <> unknown. - MySQL
jsoncolumns touchedupdated_ateven when their value didn't change.
Also
- 219 tests on SQLite, MySQL, MariaDB and PostgreSQL, PHP 8.1 – 8.5 and Laravel 10 – 13.
- PHPStan level 9 with Larastan.
Full changelog: https://github.com/mavinoo/laravelBatch/blob/master/CHANGELOG.md
Pull request: #126