DEV Community

Cover image for Laravel Running Number v3.0 — A Practical, Powerful Upgrade for Real-World Systems
Nasrul Hazim Bin Mohamad
Nasrul Hazim Bin Mohamad

Posted on

Laravel Running Number v3.0 — A Practical, Powerful Upgrade for Real-World Systems

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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,
    ],
];
Enter fullscreen mode Exit fullscreen mode

And if you ever need to reset manually:

php artisan running-number:reset INVOICE --scope=retail
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

CLI option is also available:

php artisan running-number:create ticket --start=1000
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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';
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"}'
Enter fullscreen mode Exit fullscreen mode

Full API docs


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());
});
Enter fullscreen mode Exit fullscreen mode

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)