madebyclowd/laravel-auto-sequence
Composer 安装命令:
composer require madebyclowd/laravel-auto-sequence
包简介
A concurrency-safe and customizable sequence number generator for Laravel Eloquent models, with support for database/Redis locking and soft deletes.
关键字:
README 文档
README
A concurrency-safe and customizable sequence number generator for Laravel Eloquent models (e.g., Invoices, Orders, CRM records, etc.).
Features
- Concurrency Safety: Utilizes pessimistic database locking (
SELECT ... FOR UPDATE) or Redis-based distributed locks to prevent duplicate number generation. - Hi/Lo Pre-Allocation Caching: Optionally allocates sequence numbers in blocks (e.g., 50 at a time) and increments them in-memory to reduce database lock contention.
- Composite Key Partitioning: Segregates counters using composite primary keys
['module', 'type_code', 'period', 'scope']. - Period Resets: Automatically resets counters on date boundaries (daily, weekly, monthly, yearly) or custom fiscal periods.
- Dynamic Rules & Scopes: Resolves type prefixes from model relations (e.g.
$invoice->branch->code) and scopes sequences by organizational units (e.g.$invoice->tenant_id). - Flexible Format Placeholders: Supports template placeholders like date tokens (
{YYYY},{MM},{date:d-M-Y}), dynamic model attributes ({attribute:customer_code}), and random strings ({rand:8}). - Verification & Repair Commands: Includes Artisan commands to audit model records, detect counter drift, and repair sequence state.
Installation
Install the package via Composer:
composer require madebyclowd/laravel-auto-sequence
Run the interactive installation wizard to publish the configuration file, database migrations, AI developer skills, and run the database migrations:
php artisan sequence:install
Basic Usage
1. Implement and Configure Your Model
Add the AutoSequence contract and use the HasSequenceNumber trait on your Eloquent model:
use MadeByClowd\AutoSequence\Contracts\AutoSequence; use MadeByClowd\AutoSequence\Traits\HasSequenceNumber; use Illuminate\Database\Eloquent\Model; class Invoice extends Model implements AutoSequence { use HasSequenceNumber; /** * Return the sequence configurations for the model. */ public function getSequenceConfig(): array { return [ 'number' => [ 'module' => 'invoice', 'type_code' => 'INV', 'period' => 'monthly', // daily, weekly, monthly, yearly, never (or custom date formats) 'format_template' => '{type_code}-{YYYY}-{MM}-{seq:5}', // Outputs: INV-2026-06-00001 'pad_length' => 5, ] ]; } }
2. Manual Override Protection
If you manually assign a value to the sequenced attribute before saving, the package will respect it and skip generation:
$invoice = new Invoice(); $invoice->number = 'MANUAL-999'; $invoice->save(); // Bypasses sequence generator, keeping 'MANUAL-999'
Advanced Usage
1. Dynamic Type Code Resolution (type_relation)
Resolve the type code prefix from a model relationship dynamically (e.g., Bali branch DPS vs Jakarta branch JKT):
'number' => [ 'module' => 'invoice', 'type_relation' => 'branch', // Calls $model->branch->code 'default_type' => 'HQ', // Fallback if relation is missing 'period' => 'yearly', 'format_template' => '{type_code}-{YYYY}-{seq:5}', ]
To use a relationship column other than code:
'type_relation' => [ 'relation' => 'category', 'column' => 'id_code', ]
2. Multi-Tenant Scoping (scope)
Isolate sequence pools across tenants or branches by specifying a scoping model attribute:
'number' => [ 'module' => 'invoice', 'type_code' => 'INV', 'scope' => 'tenant_id', // Evaluates $model->tenant_id dynamically to separate counters 'format_template' => '{type_code}-{YYYY}-{seq:5}', ]
3. Custom Reset Callables (Fiscal Calendar)
Provide a closure or a custom class string to partition sequences by custom dates (e.g., fiscal years starting in April):
'number' => [ 'module' => 'invoice', 'type_code' => 'INV', 'period' => function ($model) { $createdAt = $model->created_at ?? now(); $year = $createdAt->month >= 4 ? $createdAt->year : $createdAt->year - 1; return "FY{$year}"; }, 'format_template' => '{type_code}-{period}-{seq:5}', ]
4. Custom PHP Date Formatting ({date:FORMAT})
Format the template using standard PHP date character parameters:
'format_template' => '{type_code}-{date:d-M-Y}-{seq:5}' // INV-16-Jun-2026-00001
5. Multi-Column Sequences
Generate sequences for multiple attributes on a single model:
public function getSequenceConfig(): array { return [ 'invoice_number' => [ 'module' => 'invoice', 'type_code' => 'INV', 'format_template' => '{type_code}-{YYYY}-{seq:5}', ], 'internal_ref' => [ 'module' => 'internal', 'type_code' => 'REF', 'period' => 'never', 'format_template' => '{type_code}-{seq:8}', ] ]; }
6. Closure Format Templates & Nested Relation Placeholders
Customize your formatting templates dynamically using closures or fetch nested relation attributes using dot-notation:
'number' => [ 'module' => 'invoice', 'type_code' => 'INV', // 1. Fetching a nested relationship attribute: 'format_template' => 'INV-{attribute:branch.company.code}-{seq:5}', // 2. Closure-based template: 'format_template' => function ($model) { return 'INV-' . ($model->is_priority ? 'URGENT' : 'NORMAL') . '-{seq:5}'; } ]
7. Additional Configuration Options
The following additional configuration options are supported within the model sequence settings:
'number' => [ 'module' => 'invoice', 'type_code' => 'INV', 'format_template' => '{type_code}-{seq:5}', // Set a custom starting value (defaults to 1) 'start_value' => 1000, // Set a custom increment step size (defaults to 1) 'step' => 2, // Set a maximum limit. Throws a SequenceExhaustedException if exceeded 'max_value' => 99999, // Enforce sequence integrity. Throws a AutoSequenceException if a manual value is set before saving 'allow_manual' => false, // Enable D365 continuous sequence (recycles deleted numbers automatically) 'continuous' => true, // Database connection override for this specific sequence 'connection' => 'tenant_db_connection', ]
8. Soft Deletes & Concurrency Best Practices
To ensure data integrity and prevent service disruption in high-volume production systems, the package implements the following behaviors:
- Soft Deletes Protection: If your Eloquent model uses Laravel's
SoftDeletestrait, deleting a record (soft delete) will not trigger sequence number recycling. The sequence number remains reserved on the soft-deleted record in the database. This allows restoring the model via$model->restore()without causing duplicate sequence collisions. The sequence number is only recycled when the model is permanently deleted via$model->forceDelete(). - Preventing PHP-FPM Thread Exhaustion: Under the
databaselocking driver, the package automatically sets session-level or local transaction-level lock wait timeouts on the database connection (using MySQL'sinnodb_lock_wait_timeout, Postgres'slock_timeout, SQL Server'sLOCK_TIMEOUT, or SQLite'sbusy_timeout). If a transaction holds a sequence lock for too long, subsequent requests fail fast with aSequenceLockExceptionafter the configured timeout (default 5s) instead of blocking indefinitely, preventing worker exhaustion and gateway 502/504 errors. - Pre-Allocation & Transaction Modes: High-performance pre-allocation (
pre_allocation.enabled => true) cannot be used together with the'gapless'transaction mode. In'gapless'mode, a rolled-back transaction would roll back the database counter but keep the pre-allocated block in memory/cache, resulting in duplicate sequence collisions. To use pre-allocation, settransaction_mode => 'gap_tolerant'.
Manual Generation (Facade)
Inject sequence values programmatically (e.g. in custom jobs, observers, or seeds):
use MadeByClowd\AutoSequence\Facades\Sequence; // Fetch and increment next sequence value (with optional connection, start value, step, continuous, max value) $number = Sequence::generate( 'order', 'SO', '202606', '{type_code}-{YYYY}-{seq:5}', 5, 'tenant_1', null, // $model (optional) null, // $connection (optional override) 1, // $startValue (optional, default 1) 1, // $step (optional, default 1) false, // $continuous (optional, default false) 99999 // $maxValue (optional, default null) ); // Recycle a sequence number manually (inserts it back into sequence_recycled table) Sequence::recycle('order', 'SO', '202606', 'tenant_1', 105); // Get current value without incrementing $current = Sequence::getCurrent('order', 'SO', '202606', 'tenant_1'); // Reset or offset the counter Sequence::reset('order', 'SO', '202606', 'tenant_1', 100);
Artisan Commands
List Sequences
Display a table of all active sequence counters in the database:
php artisan sequence:list php artisan sequence:list --module=invoice
Reset Counters
Reset or set a specific sequence counter manually:
php artisan sequence:reset invoice INV --value=100
Verify and Repair
Scan actual model tables for sequence column values, identify any counter drift, and optionally align the database sequence counters to prevent key collisions:
php artisan sequence:verify "App\Models\Invoice" number --type=INV --module=invoice # To automatically repair: php artisan sequence:verify "App\Models\Invoice" number --type=INV --module=invoice --repair
Configuration (config/auto-sequence.php)
Publishing configuration gives you full architectural control:
return [ 'table' => 'sequences', 'recycled_table' => 'sequence_recycled', 'connection' => null, // Concurrency Locking Strategy 'locking' => [ 'driver' => 'database', // 'database' (Pessimistic lock), 'cache' (Atomic lock), or 'none' 'cache_store' => null, // cache connection name for atomic locks 'timeout' => 5, // seconds to block waiting for a lock (applies native DB lock timeout for MySQL, Postgres, SQL Server, SQLite) 'retry_interval' => 100, // milliseconds between retry attempts (applies if locking driver is cache) ], // Transaction Mode: // 'gapless': increments within model transaction (rolls back on failure; no gaps) // 'gap_tolerant': increments in isolated transaction (commits immediately; minimizes lock duration) // Note: 'gapless' is incompatible with pre-allocation. 'transaction_mode' => 'gapless', // Hi/Lo Pre-Allocation Caching 'pre_allocation' => [ 'enabled' => false, 'block_size' => 50, // Grab 50 numbers at a time 'store' => null, // dedicated cache store name (e.g. 'redis') to prevent gaps from LRU eviction/flushes ], // Audit Tracking 'audit' => [ 'enabled' => false, // Toggle created_by / updated_by tracking columns 'user_model' => 'App\Models\User', 'created_by_column' => 'created_by', 'updated_by_column' => 'updated_by', 'user_id_type' => 'bigInteger', // Options: 'bigInteger', 'uuid', 'ulid', 'string' ], ];
License
The MIT License (MIT). Please see the LICENSE file for more information.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 4
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-16