Installation:
composer require alfa6661/laravel-autonumber
Register the service provider in config/app.php:
Alfa6661\AutoNumber\AutoNumberServiceProvider::class,
Publish the config:
php artisan vendor:publish --provider="Alfa6661\AutoNumber\AutoNumberServiceProvider"
Run migrations:
php artisan migrate
First Use Case:
Add the AutoNumberTrait to your Eloquent model and implement getAutoNumberOptions():
use Alfa6661\AutoNumber\AutoNumberTrait;
class Invoice extends Model
{
use AutoNumberTrait;
public function getAutoNumberOptions()
{
return [
'prefix' => 'INV-',
'length' => 8,
'padding' => '0',
'field' => 'invoice_number',
'format' => 'Y-m-d-His', // Optional: for dynamic prefixes
];
}
}
Now, every time you create an Invoice, the invoice_number field will auto-populate with a formatted number (e.g., INV-20230815).
Basic Auto-Numbering: Use static configurations for simple sequential numbering:
public function getAutoNumberOptions()
{
return [
'prefix' => 'ORD-',
'length' => 6,
'padding' => '0',
'field' => 'order_code',
];
}
Output: ORD-000001, ORD-000002, etc.
Dynamic Prefixes with Dates:
Leverage the format option for time-based prefixes:
public function getAutoNumberOptions()
{
return [
'prefix' => 'INV-',
'length' => 6,
'padding' => '0',
'field' => 'invoice_number',
'format' => 'Y', // Year-based prefix (e.g., "INV-20230001")
];
}
Custom Logic via Closures:
Override getAutoNumber() to inject custom logic:
use Alfa6661\AutoNumber\AutoNumberTrait;
class Project extends Model
{
use AutoNumberTrait;
public function getAutoNumberOptions()
{
return [
'field' => 'project_id',
];
}
public function getAutoNumber()
{
$base = parent::getAutoNumber();
return 'PROJ-' . strtoupper(substr($base, 0, 3)) . '-' . substr($base, 3);
}
}
Output: PROJ-ABC-1234.
Batch Processing:
Use AutoNumber::generate() for bulk operations:
$numbers = AutoNumber::generate(Invoice::class, 5); // Generates 5 invoice numbers
Soft Deletes Handling:
Ensure deleted_at is respected by configuring the query in getAutoNumberOptions():
public function getAutoNumberOptions()
{
return [
'prefix' => 'DEL-',
'field' => 'reference',
'query' => function ($query) {
return $query->whereNull('deleted_at');
},
];
}
Database Constraints: Add a unique constraint to the auto-number field in migrations:
Schema::table('invoices', function (Blueprint $table) {
$table->string('invoice_number')->unique();
});
API Responses: Format auto-numbers in API responses using accessors:
public function getFormattedInvoiceNumberAttribute()
{
return 'Invoice #: ' . $this->invoice_number;
}
Testing: Mock the auto-number generator in tests:
$invoice = Invoice::factory()->create();
$this->assertMatchesRegularExpression('/^INV-\d{8}$/', $invoice->invoice_number);
Multi-Tenant Support:
Scope auto-numbers to tenants via query in getAutoNumberOptions():
public function getAutoNumberOptions()
{
return [
'field' => 'ticket_number',
'query' => function ($query) {
return $query->where('tenant_id', auth()->user()->tenant_id);
},
];
}
Race Conditions: Auto-number generation is not thread-safe by default. For high-concurrency apps, use database transactions or a queue:
DB::transaction(function () {
$invoice = Invoice::create([...]);
});
Field Name Conflicts:
Ensure the field in getAutoNumberOptions() matches the database column exactly (case-sensitive).
Dynamic format Quirks:
The format option uses PHP’s date() syntax. Test edge cases (e.g., Y vs. Y-m-d for leap years).
Migration Dependencies:
The package adds a auto_number table. If you skip migrations, auto-numbers will fail silently.
Soft Deletes Misconfiguration:
Forgetting to include whereNull('deleted_at') in the query option can cause duplicate numbers for soft-deleted records.
Log Generation: Temporarily log the auto-number logic:
public function getAutoNumber()
{
$number = parent::getAutoNumber();
\Log::debug("Generated auto-number: {$number}", ['model' => static::class]);
return $number;
}
Check the auto_number Table:
Inspect auto_number entries for your model:
SELECT * FROM auto_number WHERE model = 'App\Models\Invoice';
Override getNextNumber():
Debug the counter logic:
public function getNextNumber()
{
$next = parent::getNextNumber();
\Log::debug("Next number for {$this->getAutoNumberField()}: {$next}");
return $next;
}
Custom Storage:
Override getAutoNumberTable() to use a custom table:
protected function getAutoNumberTable()
{
return 'custom_auto_numbers';
}
Event Hooks: Trigger events before/after auto-number generation:
use Alfa6661\AutoNumber\Events\AutoNumberGenerated;
AutoNumberGenerated::dispatch($this, $this->{$this->getAutoNumberField()});
Fallback Logic: Handle generation failures gracefully:
public function getAutoNumber()
{
try {
return parent::getAutoNumber();
} catch (\Exception $e) {
return 'MAN-' . Str::upper(Str::random(6));
}
}
Multi-Field Auto-Numbers: Combine multiple fields for complex numbering:
public function getAutoNumber()
{
$base = parent::getAutoNumber();
return $this->category->code . '-' . $base;
}
Localization: Support non-Latin characters in prefixes/padding:
public function getAutoNumberOptions()
{
return [
'prefix' => 'FACT-',
'padding' => '۰', // Arabic zero
];
}
How can I help you explore Laravel packages today?