devaspid/laravel-id-generator
Composer 安装命令:
composer require devaspid/laravel-id-generator
包简介
Concurrency-safe document and identifier number generation for Laravel.
README 文档
README
Generate business document numbers such as invoices, transactions, procurement records, and purchase orders without scanning their transaction tables.
The package keeps only mutable counter state in id_sequences; formatting and reset behaviour remain version-controlled in config/id-generator.php. Counters are incremented inside a database transaction with a row lock, making the generator safe for concurrent requests.
Features
- Laravel 12 and 13 support, PHP 8.3+
- One compact state row per configured generator
- Atomic, concurrency-safe sequence increments
- Global, yearly, monthly, and daily scopes
- Prefix, suffix, left padding, and date placeholders
id-generator:synccommand to create missing sequence rows- No scheduler and no lookup of invoice/transaction tables
Requirements
- PHP 8.3 or newer
- Laravel 12 or Laravel 13
- A database connection that supports transactions and row locks in production
Installation
Install the package:
composer require devaspid/laravel-id-generator
Publish its configuration and migration, then migrate:
php artisan vendor:publish --tag=id-generator-config php artisan vendor:publish --tag=id-generator-migrations php artisan migrate
Define your generators in config/id-generator.php, then create their state rows:
php artisan id-generator:sync
Run id-generator:sync on every environment after adding a generator. It creates missing rows only; it never resets or overwrites an existing counter.
Configuration
<?php return [ 'invoice' => [ 'prefix' => 'INV-{Y}{m}-', 'padding' => 6, 'scope' => 'monthly', ], 'transaction' => [ 'prefix' => 'TRX-', 'padding' => 8, 'scope' => 'never', ], 'procurement' => [ 'prefix' => 'PRC-{Y}-', 'suffix' => '-ID', 'padding' => 5, 'scope' => 'yearly', ], 'purchase-order' => [ 'prefix' => 'PO-', 'padding' => 5, 'scope' => 'daily', ], ];
| Option | Required | Default | Description |
|---|---|---|---|
prefix |
No | '' |
Text before the sequence number. |
suffix |
No | '' |
Text after the sequence number. |
padding |
No | 0 |
Minimum number width, padded on the left with zeroes. |
scope |
No | never |
Counter lifecycle: never, yearly, monthly, or daily. |
Supported placeholders in prefix and suffix are {Y}, {m}, {d}, and {His}. For example, INV-{Y}{m}- becomes INV-202607- in July 2026.
Usage
Use the facade wherever a document number is needed:
use Devaspid\LaravelIdGenerator\Facades\IdGenerator; $invoice->number = IdGenerator::generate('invoice'); // INV-202607-000001
Generate the other configured numbers in exactly the same way:
$transaction->reference = IdGenerator::generate('transaction'); $procurement->number = IdGenerator::generate('procurement'); $purchaseOrder->number = IdGenerator::generate('purchase-order');
The Laravel alias IdGenerator is also auto-discovered:
$number = IdGenerator::generate('invoice');
Call the generator and save the document in the same application-level database transaction when the number must not be consumed after a failed business write:
DB::transaction(function () use ($request) { $invoice = new Invoice($request->validated()); $invoice->number = IdGenerator::generate('invoice'); $invoice->save(); });
Scope and reset behaviour
Reset behaviour is driven by a scope, not a scheduler and not the date of the last invoice. The database stores current and last_scope in one row for each generator.
For a monthly invoice generator, a July state might be current = 1523 and last_scope = '2026-07'. The first generation in August detects the new scope while holding the row lock, changes the state to current = 0 and last_scope = '2026-08', then increments to 1. No business table is scanned, and no extra sequence rows are created for idle periods.
never resolves to a global scope and never resets. yearly, monthly, and daily resolve respectively to YYYY, YYYY-MM, and YYYY-MM-DD.
Concurrency
generate() starts a database transaction, locks the single row for the requested generator (SELECT ... FOR UPDATE), evaluates its scope, increments the counter, and saves it before committing. Concurrent requests for the same generator wait briefly for that row instead of receiving the same number. Different generators use different rows and can proceed independently.
Use a unique database index on your document-number column as a final application-level integrity safeguard.
API reference
string IdGenerator::generate(string $name)
Throws InvalidGeneratorConfiguration if the generator is absent or invalid, and SequenceNotFound if id-generator:sync has not created its state row.
Upgrade guide
Before upgrading this package, commit your composer.lock, application config, and database backup. Run Composer's update, review the package release notes, then run:
php artisan id-generator:sync
The command is safe to rerun and preserves current and last_scope. Do not truncate id_sequences in a live system unless restarting every document sequence is intentional.
FAQ
Why not scan the invoice table for its highest number?
Scanning grows more expensive as the table grows and has a race condition: two concurrent requests can read the same maximum. This package locks one small state row instead.
Does monthly reset require a scheduler?
No. The first generation in a new period resets atomically. Months with no documents create no data and require no work.
Will a failed transaction leave a number gap?
When generate() runs inside the same outer transaction as the document insert, a rollback rolls back both changes. Database engines and other application workflows may still create gaps in some circumstances; document sequences should generally be unique and ordered, not assumed gapless.
Can I add custom scopes?
The ScopeResolverRegistry is intentionally separated from the manager so a future release can expose custom resolver registration without changing storage or generation semantics.
License
This package is open-sourced software licensed under the MIT license.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-11