Laravel Running Number v3.0 is finally here, and this release introduces the kind of improvements that make a difference in real systems—where concurrency matters, audit trails need to be trustworthy, and developers just want a clean, predictable API to generate running numbers.
If you haven't seen the release notes yet, you can check them out here:
Before we dive in, one quick reminder:
Make sure your types are declared in config/running-number.php or exposed via your enum.
More in the Configuration Overview
Now, let’s talk about what’s new and why these updates actually matter in day-to-day application development.
Native PHP Enums — Cleaner, Safer, Less Guesswork
I’ve wanted proper enum support for a long time, and v3.0 finally brings it in.
Enums help reduce typos, give you IDE autocompletion, and make your code more expressive.
With Traitify behind the scenes, you also get helpers like values(), labels(), and options() automatically.
use CleaniqueCoders\RunningNumber\Enums\Organization;
// config/running-number.php
'types' => Organization::values();
$number = running_number()
->type(Organization::PROFILE->value)
->generate(); // PROFILE001
UUID Identifiers — Because Debugging Shouldn’t Be a Guessing Game
Every generated sequence now comes with a UUID.
This is incredibly helpful when:
- You’re syncing with external services
- You want stronger audit trails
- You need to debug inconsistencies
$rn = RunningNumber::where('type', 'PROFILE')->first();
echo $rn->uuid;
It’s small, but it’s the kind of practical addition you appreciate once you need it.
Reset Periods — Daily, Monthly, Yearly, or Fully Manual
Reset periods make your identifiers predictable.
Think invoices that restart monthly or reports that refresh every year.
'reset_period' => [
'default' => ResetPeriod::MONTHLY->value,
'types' => [
'invoice' => ResetPeriod::YEARLY->value,
],
];
And if you ever need to reset manually:
php artisan running-number:reset INVOICE --scope=retail
This gives you full control without cluttering your code.
Date-Based Presenters — Human-Readable, Business-Friendly Numbers
Sometimes the business team wants to see the date inside the number.
Presenters let you customise formatting cleanly:
use CleaniqueCoders\RunningNumber\Presenters\YearMonthPresenter;
$number = running_number()
->type('invoice')
->formatter(new YearMonthPresenter('-'))
->generate();
// INVOICE-2025-11-001
Readable, structured, and consistent.
Scopes — Multiple Independent Sequences for the Same Type
One of my favourite additions.
Scopes let you maintain separate sequences for different "segments"—branches, stores, departments, etc.
running_number()->type('invoice')->scope('retail')->generate(); // INVOICE001
running_number()->type('invoice')->scope('wholesale')->generate(); // INVOICE001
No more juggling prefixes or custom logic just to keep numbers separated.
Custom Starting Numbers — Perfect for Migrations
For teams migrating from legacy systems, this is a lifesaver.
$number = running_number()
->type('ticket')
->startFrom(1000)
->generate(); // TICKET1001
CLI option is also available:
php artisan running-number:create ticket --start=1000
Max Number Limits — Fail Fast, Fail Safe
If a sequence should never go beyond a certain range, you can enforce it:
try {
running_number()->type('voucher')->maxNumber(5)->generate();
} catch (MaxNumberReachedException $e) {
// graceful handling
}
Especially useful for voucher books, serial allocations, and compliance-driven systems.
Preview Mode — Know the Next Number Without Updating Anything
Great for UI previews and displaying "Upcoming Order Number".
$next = running_number()->type('order')->preview();
No DB writes. No side effects.
Batch Generation — Atomic, Fast, and Fully Safe
Sometimes you need to generate multiple numbers in one go—shipping labels, voucher sheets, batch allocations.
$numbers = running_number()
->type('shipment')
->generateBatch(5);
This happens inside a single transaction with locking, so everything remains safe.
Model Trait — Zero Boilerplate Number Generation
If you want a field to auto-populate when a model is created, this trait does all the heavy lifting.
class Invoice extends Model
{
use InteractsWithRunningNumber;
protected string $runningNumberField = 'invoice_number';
protected string $runningNumberType = 'invoice';
protected ?string $runningNumberScope = '$store_id';
}
Clean. Minimal. Reliable.
Artisan Commands — Handy Tools for Ops and Developers
You can create, list, and reset sequences directly from the CLI.
php artisan running-number:create invoice --scope=retail --start=100 --reset=yearly
php artisan running-number:list
php artisan running-number:reset INVOICE --scope=retail --force
Ops teams will appreciate this.
Optional REST API — Use It Across Services
If you’re running microservices or integrating with external apps, the optional API is a straightforward solution.
curl -X POST http://localhost/api/running-numbers/generate \
-d '{"type":"invoice","scope":"retail"}'
Events — Integrate with Logging, Notifications, or External Systems
Hook into RunningNumberGenerated to push updates where you need them.
Event::listen(RunningNumberGenerated::class, function ($event) {
logger()->info('Number generated', $event->toArray());
});
Simple and effective.
Documentation
See full documentation here.
Upgrade Guide
Refer to upgrade guide for upgrading from version 2.3 to 3.0.
Final Thoughts
v3.0 is a solid upgrade. It’s the kind of release that doesn’t just add features—it improves the fundamentals of how running numbers should behave in a production-grade system.
Whether you're building a billing engine, logistics workflow, multi-tenant SaaS, or internal ERP module, this version gives you the flexibility and reliability you need.
Photo by Francesco Ungaro: https://www.pexels.com/photo/blue-sky-281260/
Top comments (0)