Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Autonumber Laravel Package

marjose123/laravel-autonumber

A Laravel package for generating automatic sequential numbers, such as invoice or document codes, for your Eloquent models. Simple to configure and use, it helps keep numbering consistent without manual tracking.

View on GitHub
Deep Wiki
Context7

Getting Started

v1.0.1 is the first release with Laravel 10 support and introduces a configurable starting value for a counter, which makes the package viable for projects that already have a numbering history. Both changes matter on day one: confirm your app is on Laravel 10 (this release raises the framework constraint — if you are still on Laravel 9, pin ^1.0.0 or upgrade the app first), then install:

composer require marjose123/laravel-autonumber
php artisan vendor:publish --tag=autonumber-config   # confirm tag in README
php artisan migrate                                   # if the package ships a counter table

The first thing to look at is the published config file: it is where the document "types" (invoice, quotation, receipt, PO) and their prefix/suffix/padding live, and in v1.0.1 it is also where you now set the starting number for each sequence. Read the config before writing any code — most of the package's behaviour is configuration, not API.

The canonical first use case is an invoice number:

$invoiceNo = AutoNumber::generate('invoice'); // e.g. INV-000001

Wire it into the model that actually needs the number (Invoice) rather than calling it ad hoc from controllers, so every creation path gets a number and none can accidentally skip it. Confirm the exact method signature against the README for this version — it is a small package and the call surface has changed between releases.

Implementation Patterns

Generate on model creation, not in the controller. Put the call in the model's creating hook or a dedicated observer. This guarantees every path (HTTP, console command, seeder, imported job) produces a number, and it keeps the generation call in exactly one place when the package's API shifts:

protected static function booted(): void
{
    static::creating(function (self $model) {
        $model->number ??= AutoNumber::generate('invoice');
    });
}

The ??= matters: it lets a seeder or a data-migration explicitly set a historical number without the package overwriting it.

Wrap generation in the same transaction as the insert. A number you generated but never persisted is a wasted sequence value — or worse, a duplicate if the counter store is transactional and the insert rolls back. Generate and save together:

return DB::transaction(function () use ($data) {
    $invoice = Invoice::create($data);   // number assigned in creating()
    return $invoice;
});

Use the new starting value for legacy cut-overs, not for padding. When migrating from an old system whose last invoice was INV-10432, set the invoice sequence's starting value to 10433 (or seed the counter row) before the first new invoice is issued. Do this as an explicit deployment step — a seeder or a one-off artisan command — so the value is versioned and reproducible across environments. Do not rely on it being set ad hoc in tinker on production.

Isolate the package behind one class. Even for a small package, wrap it:

final class NumberSequence
{
    public function next(string $type): string
    {
        return AutoNumber::generate($type);
    }
}

Six months from now, when the package is abandoned or you need per-tenant formats it cannot express, you change one file instead of grepping the codebase.

Per-type config, one key per document. Define invoice, quotation, receipt, purchase_order as separate sequences with their own prefixes. Do not reuse a single counter for different document types; you will not be able to unpick it later.

Multi-tenant: put the tenant in the key. If tenants must each have their own INV-000001, the sequence key has to include the tenant id (e.g. invoice:{$tenant->id}). Check whether the package supports arbitrary keys or only keys defined in config — if the latter, you need a config entry per tenant, which does not scale, and the wrapper class above is where you handle it.

Testing. In tests, reset the counter between cases (a RefreshDatabase migration truncation or an explicit reset in setUp) or accept that numbers differ per run and assert only on the shape (regex) rather than the exact value. Asserting INV-000001 exactly will make your suite order-dependent and flaky the moment you add a test.

Gotchas and Tips

  • Concurrency is the real risk. Two simultaneous requests can read the same counter and issue the same number. Always keep a unique index on the number column as the backstop, and be prepared to catch the resulting QueryException and retry once. If the package's counter store is a DB table, verify it uses lockForUpdate() (or an atomic increment) inside the transaction; if it does not, wrap the generation in DB::transaction(...) yourself and test with parallel requests before go-live.
  • Numbers are not gap-free. Rollbacks, failed jobs, and deleted drafts leave holes. If finance or an auditor expects a gapless series, that is a compliance requirement, not a package feature — raise it early, because it changes the whole design (you need a locked, serialised issuance step).
  • The starting value only affects future generation. It does not rewrite existing rows and will not take effect if the counter row already exists — v1.0.x likely only reads the start value when initialising a sequence. Verify the behaviour on a fresh sequence before assuming it applies to a live one, and check the counter table's current value if a change appears to be ignored.
  • Config caching. After editing prefixes or starting values in production, run php artisan config:cache as part of deploy — a stale cached config silently produces the old format for as long as the cache lives. This is the single most common "the package ignores my config" cause.
  • Cache-backed counters reset. If the counter lives in the cache store rather than the database, cache:clear, a Redis eviction, or a failover to a fresh node will restart the sequence and produce duplicates. Prefer the database-backed store for anything that must be unique forever, and if you must use cache, use a persistent store with eviction disabled.
  • Queued jobs and retries. A job that fails after generating a number and is retried will consume another value, or collide if the model was partially persisted. Generate inside the job's transaction, and make the job idempotent on the model's natural key rather than on the number.
  • Debugging. When a number looks wrong, check in this order: (1) php artisan config:show for the actual merged config, (2) the counter table/row for that key, (3) whether the record was created inside a rolled-back transaction, (4) your model's creating hook firing twice (mass-assignment plus explicit assignment). DB::listen() in a local environment will show you the counter UPDATE and its value at the moment of generation.
  • Extension points. The facade is the obvious seam (swap the binding in a service provider for a test double or your own implementation). If the package fires events around generation, hook them for an audit log of every number issued — that log is invaluable when two invoices end up with the same number.
  • Pin the version. This is a 1.0.x package with a single contributor and one release. Use an exact or ~1.0.1 constraint, commit the lock file, and read the diff before bumping. The Laravel 10 change in v1.0.1 is evidence that minor bumps here can carry framework-constraint changes.
  • Have an exit plan. The package's whole job is ~40 lines of code (a locked counter read, increment, and format). If it becomes unmaintained, the wrapper class means you can reimplement it in an afternoon without touching call sites.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support