Weave Code
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

Referenceable Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Install the package:

    composer require eg-mohamed/referenceable
    php artisan referenceable:install
    

    This creates the necessary migration tables and publishes the config file.

  2. 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();
    });
    
  3. Apply the trait to your model:

    use MohamedSaid\Referenceable\Traits\HasReference;
    
    class Order extends Model
    {
        use HasReference;
    }
    
  4. Generate a reference automatically when creating a record:

    $order = Order::create(['customer_id' => 1]);
    echo $order->reference; // Outputs: "ORD-123456" (default random strategy)
    

First Use Case: Sequential Invoice Numbers

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.


Implementation Patterns

Core Workflows

  1. Automatic Reference Generation

    • Use the trait out-of-the-box with default settings.
    • Override model properties for customization (e.g., referencePrefix, referenceLength).
    • Example:
      class Ticket extends Model
      {
          use HasReference;
      
          protected $referenceStrategy = 'template';
          protected $referenceTemplate = [
              'format' => '{YEAR}{SEQ}',
              'sequence_length' => 5,
          ];
          protected $referencePrefix = 'TKT-';
      }
      
  2. Manual Reference Control

    • Generate references on demand:
      $reference = $order->generateReference(); // Without saving
      $order->reference = $reference;
      $order->save();
      
    • Regenerate existing references:
      $order->regenerateReference(save: true);
      
  3. Batch Operations

    • Use Artisan commands for bulk processing:
      php artisan referenceable:generate App\Models\Order --batch=500
      
    • Programmatically generate batches:
      $references = app(\MohamedSaid\Referenceable\ModelReference::class)
          ->generateBatch(Order::class, 100);
      

Integration Tips

  1. Validation Integration

    • Validate references during form submission:
      $request->validate([
          'reference' => 'required|referenceable:App\Models\Order',
      ]);
      
    • Or use the model method:
      if (!$order->validateReference()) {
          return back()->withErrors(['reference' => 'Invalid format']);
      }
      
  2. Query Scopes

    • Filter models by reference:
      $order = Order::findByReference('ORD-123456');
      $todayOrders = Order::referenceStartsWith('ORD-2024')->get();
      
  3. Multi-Tenancy

    • Configure tenant-aware uniqueness:
      class Order extends Model
      {
          use HasReference;
      
          protected $referenceUniquenessScope = 'tenant';
          protected $referenceTenantColumn = 'company_id';
      }
      
  4. Custom Placeholders

    • Extend template placeholders by overriding the getReferenceTemplateVariables() method:
      public function getReferenceTemplateVariables()
      {
          return array_merge(parent::getReferenceTemplateVariables(), [
              '{CUSTOM}' => $this->customField,
          ]);
      }
      

Gotchas and Tips

Pitfalls

  1. Collision Handling

    • Default strategy (retry) may cause performance issues with high contention.
    • Fix: Set referenceMaxRetries higher or switch to append strategy:
      protected $referenceCollisionStrategy = 'append';
      protected $referenceMaxRetries = 3;
      
  2. Sequential Reset Timing

    • Yearly/monthly resets occur at midnight UTC. For timezone-specific resets, override:
      public function getReferenceResetDate()
      {
          return Carbon::now()->startOfMonth()->setTimezone('America/New_York');
      }
      
  3. Database Indexes

    • Must-have: Add an index to the reference column:
      Schema::table('orders', function (Blueprint $table) {
          $table->index('reference');
      });
      
    • For multi-tenancy, add a composite index:
      $table->index(['company_id', 'reference']);
      
  4. Configuration Overrides

    • Global config in config/referenceable.php is merged with model-specific settings.
    • Tip: Use config('referenceable.strategy') to check the active strategy dynamically.

Debugging

  1. Reference Generation Issues

    • Check the referenceable_log table for failed generations.
    • Enable debug logging in config/referenceable.php:
      'debug' => [
          'log_failures' => true,
          'log_level' => 'debug',
      ],
      
  2. Validation Errors

    • Validate manually before saving:
      if (!$model->validateReference()) {
          dd($model->getReferenceErrors());
      }
      
  3. Performance Bottlenecks

    • Disable transactions for bulk operations:
      'performance' => [
          'use_transactions' => false,
      ],
      
    • Monitor cache TTL for configuration:
      'performance' => [
          'cache_ttl' => 3600, // 1 hour
      ],
      

Extension Points

  1. Custom Strategies

    • Implement MohamedSaid\Referenceable\Contracts\ReferenceStrategy:
      class CustomStrategy implements ReferenceStrategy
      {
          public function generate(Model $model): string
          {
              return 'CUSTOM-' . Str::random(8);
          }
      }
      
    • Register in config/referenceable.php:
      'strategies' => [
          'custom' => \App\Strategies\CustomStrategy::class,
      ],
      
  2. Dynamic Configuration

    • Override 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();
          });
      }
      
  3. Artisan Command Extensions

    • Extend existing commands by publishing and overriding:
      php artisan vendor:publish --tag=referenceable-commands
      
    • Modify app/Console/Commands/ReferenceableGenerateCommand.php.

Pro Tips

  1. Template Debugging

    • Use {DEBUG} placeholder to inspect variables:
      protected $referenceTemplate = [
          'format' => '{PREFIX}-{DEBUG}',
      ];
      
  2. Reference Prefix Validation

    • Enforce prefix consistency with a model observer:
      class OrderObserver
      {
          public function saving(Order $order)
          {
              if (Str::startsWith($order->reference, 'ORD-')) {
                  return;
              }
              $order->reference = 'ORD-' . Str::after($order->reference, '-');
          }
      }
      
  3. Sequential Numbering for Soft Deletes

    • Reset sequences when deleting records:
      public static function bootHasReference()
      {
          static::deleted(function (Model $model) {
              if ($model->usesReferenceable()) {
                  $model->resetReferenceSequence();
              }
          });
      }
      
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport