eg-mohamed/referenceable
Laravel package to make Eloquent models referenceable with customizable reference numbers. Supports random, sequential and template-based formats (YEAR/MONTH/SEQ/RANDOM), collision handling, validation, reset rules (daily/monthly/yearly), multi-tenancy, artisan tools, caching and Laravel 10–13.
Install the package:
composer require eg-mohamed/referenceable
php artisan referenceable:install
This creates the necessary migration tables and publishes the config file.
Add the reference column to your model migration:
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->string('reference')->unique()->index(); // Required column
$table->timestamps();
});
Apply the trait to your model:
use MohamedSaid\Referenceable\Traits\HasReference;
class Order extends Model
{
use HasReference;
}
Generate a reference automatically when creating a record:
$order = Order::create(['customer_id' => 1]);
echo $order->reference; // Outputs: "ORD-123456" (default random strategy)
Configure a model for sequential invoice numbering with yearly reset:
class Invoice extends Model
{
use HasReference;
protected $referenceStrategy = 'sequential';
protected $referencePrefix = 'INV';
protected $referenceSequential = [
'start' => 1000,
'min_digits' => 6,
'reset_frequency' => 'yearly',
];
}
Now every Invoice::create() will auto-generate INV-001000, INV-001001, etc., resetting yearly.
Automatic Reference Generation
referencePrefix, referenceLength).class Ticket extends Model
{
use HasReference;
protected $referenceStrategy = 'template';
protected $referenceTemplate = [
'format' => '{YEAR}{SEQ}',
'sequence_length' => 5,
];
protected $referencePrefix = 'TKT-';
}
Manual Reference Control
$reference = $order->generateReference(); // Without saving
$order->reference = $reference;
$order->save();
$order->regenerateReference(save: true);
Batch Operations
php artisan referenceable:generate App\Models\Order --batch=500
$references = app(\MohamedSaid\Referenceable\ModelReference::class)
->generateBatch(Order::class, 100);
Validation Integration
$request->validate([
'reference' => 'required|referenceable:App\Models\Order',
]);
if (!$order->validateReference()) {
return back()->withErrors(['reference' => 'Invalid format']);
}
Query Scopes
$order = Order::findByReference('ORD-123456');
$todayOrders = Order::referenceStartsWith('ORD-2024')->get();
Multi-Tenancy
class Order extends Model
{
use HasReference;
protected $referenceUniquenessScope = 'tenant';
protected $referenceTenantColumn = 'company_id';
}
Custom Placeholders
getReferenceTemplateVariables() method:
public function getReferenceTemplateVariables()
{
return array_merge(parent::getReferenceTemplateVariables(), [
'{CUSTOM}' => $this->customField,
]);
}
Collision Handling
retry) may cause performance issues with high contention.referenceMaxRetries higher or switch to append strategy:
protected $referenceCollisionStrategy = 'append';
protected $referenceMaxRetries = 3;
Sequential Reset Timing
public function getReferenceResetDate()
{
return Carbon::now()->startOfMonth()->setTimezone('America/New_York');
}
Database Indexes
Schema::table('orders', function (Blueprint $table) {
$table->index('reference');
});
$table->index(['company_id', 'reference']);
Configuration Overrides
config/referenceable.php is merged with model-specific settings.config('referenceable.strategy') to check the active strategy dynamically.Reference Generation Issues
referenceable_log table for failed generations.config/referenceable.php:
'debug' => [
'log_failures' => true,
'log_level' => 'debug',
],
Validation Errors
if (!$model->validateReference()) {
dd($model->getReferenceErrors());
}
Performance Bottlenecks
'performance' => [
'use_transactions' => false,
],
'performance' => [
'cache_ttl' => 3600, // 1 hour
],
Custom Strategies
MohamedSaid\Referenceable\Contracts\ReferenceStrategy:
class CustomStrategy implements ReferenceStrategy
{
public function generate(Model $model): string
{
return 'CUSTOM-' . Str::random(8);
}
}
config/referenceable.php:
'strategies' => [
'custom' => \App\Strategies\CustomStrategy::class,
],
Dynamic Configuration
getReferenceConfig() to fetch settings from a database or API:
public function getReferenceConfig()
{
return Cache::remember("ref_config_{$this->id}", 60, function () {
return $this->referenceSettings->firstOrFail();
});
}
Artisan Command Extensions
php artisan vendor:publish --tag=referenceable-commands
app/Console/Commands/ReferenceableGenerateCommand.php.Template Debugging
{DEBUG} placeholder to inspect variables:
protected $referenceTemplate = [
'format' => '{PREFIX}-{DEBUG}',
];
Reference Prefix Validation
class OrderObserver
{
public function saving(Order $order)
{
if (Str::startsWith($order->reference, 'ORD-')) {
return;
}
$order->reference = 'ORD-' . Str::after($order->reference, '-');
}
}
Sequential Numbering for Soft Deletes
public static function bootHasReference()
{
static::deleted(function (Model $model) {
if ($model->usesReferenceable()) {
$model->resetReferenceSequence();
}
});
}
How can I help you explore Laravel packages today?