| name | sequence-development |
| description | Build and work with tenthfeet/laravel-sequence features including defining sequence classes, generating concurrency-safe formatted values (invoice numbers, ticket IDs, serials), composing pattern tokens, applying reset policies, and scoping counters per model. |
Sequence Development
Overview
Use tenthfeet/laravel-sequence to generate concurrency-safe, format-based sequential values such as invoice numbers, reference codes, serial numbers, and ticket IDs. Values are produced under a DB transaction with lockForUpdate(), formatted from pattern tokens, and grouped by configurable reset policies (yearly, monthly, daily, financial year) and optional per-model scope.
When to Activate
- Activate when the user wants to build invoice numbers, reference codes, serial numbers, ticket IDs, or any auto-incrementing formatted string.
- Activate when code references
SequenceDefinition, the Sequence entry point, ResetPolicy, or the make:sequence command.
- Activate when the user needs counters that reset per period (year/month/day/financial year) or per model record.
Scope
- In scope: publishing config/migrations, defining sequence classes, generating/previewing/rolling back values, pattern tokens, reset policies, per-model counters, runtime overrides.
- Out of scope: ad-hoc
max()+1 numbering, non-Laravel frameworks, managing the sequences table by hand.
Workflow
- Identify the task (setup, defining a sequence, generating values, choosing tokens/reset policy, per-model scope).
- Read
references/sequence-guide.md and focus on the relevant section.
- Apply the patterns from the reference, keeping code minimal and driving every operation through
Sequence::using($definition).
Core Concepts
Setup
Publish config and migration, then migrate:
php artisan vendor:publish --provider="Tenthfeet\Sequence\SequenceServiceProvider" --tag="config"
php artisan vendor:publish --provider="Tenthfeet\Sequence\SequenceServiceProvider" --tag="migrations"
php artisan migrate
Config lives at config/sequences.php (table, default_pattern, default_padding_character, default_reset_policy, financial_year.start_month).
Defining a Sequence
Always scaffold with the artisan command; never hand-write the class location:
php artisan make:sequence InvoiceSequence
This creates app/Sequences/InvoiceSequence.php extending Tenthfeet\Sequence\SequenceDefinition. A definition MUST implement key() and configures itself in the constructor via the fluent API:
namespace App\Sequences;
use Tenthfeet\Sequence\SequenceDefinition;
use Tenthfeet\Sequence\Enums\ResetPolicy;
use Carbon\Month;
final class InvoiceSequence extends SequenceDefinition
{
public function __construct()
{
$this->pattern('INV-{FY:YYYY-YY}/{SEQ:4}')
->resetPolicy(ResetPolicy::FinancialYear)
->financialYearStartsIn(Month::April);
}
public function key(): string
{
return 'invoice';
}
}
Generating Values
Drive every operation through Sequence::using($definition):
use Tenthfeet\Sequence\Sequence;
use App\Sequences\InvoiceSequence;
$definition = new InvoiceSequence();
$next = Sequence::using($definition)->next();
$preview = Sequence::using($definition)->previewNext();
Sequence::using($definition)->rollback();
Sequence::using($definition)->rollback(3);
Pattern Tokens
{YYYY}/{YY} year, {MM} month, {DD} day, {H}/{M}/{S} time, {SEQ} raw counter, {SEQ:N} counter padded to width N, {FY} / {FY:YYYY-YY} / {FY:YY-YY} / {FY:YYYY-YYYY} financial year. Everything else is literal.
Reset Policies
Tenthfeet\Sequence\Enums\ResetPolicy: None, Yearly, Monthly, Daily, FinancialYear. The policy determines the scope key that groups counter rows, so the counter restarts at 1 for each new period.
Per-Record Sequences
forModel($model) gives each model instance its own independent counter (stored via model_type + model_id):
$definition = (new ProjectTaskSequence())->forModel($project);
$taskNumber = Sequence::using($definition)->next();
Do and Don't
Do:
- Always scaffold definitions with
php artisan make:sequence <Name>Sequence.
- Extend
SequenceDefinition and implement a stable snake_case key().
- Configure via the fluent methods (
pattern, padWith, resetPolicy, financialYearStartsIn, forModel, usingDate).
- Use
next() (the only consuming method) — it is transaction- and lock-protected, so it is safe under concurrency.
- Use
previewNext() for display-only "next number will be…" UI.
- Pair a
{FY...} token with ResetPolicy::FinancialYear, and match other date tokens to the reset policy.
Don't:
- Don't add your own locking or
max()+1 logic around next().
- Don't extend a
SequenceGenerator class or use protected $pattern properties — the base class is SequenceDefinition.
- Don't persist a previewed value as if it were reserved.
- Don't use a
{FY...} token with any policy other than FinancialYear (throws InvalidArgumentException).
- Don't change a definition's
key() after go-live — it starts a brand-new counter.
- Don't manage the
sequences table directly.
References
references/sequence-guide.md