testmonitor/eloquent-incrementable
Installation:
composer require testmonitor/eloquent-incrementable
Add the service provider to config/app.php:
'providers' => [
// ...
TestMonitor\Incrementable\IncrementableServiceProvider::class,
],
First Use Case:
Define an Incrementable trait in your Eloquent model:
use TestMonitor\Incrementable\Incrementable;
class Bug extends Model
{
use Incrementable;
protected $incrementable = [
'field' => 'code', // Custom auto-increment field
'group_by' => 'project_id', // Reset counter per project_id
];
}
Now, Bug::create() will auto-generate sequential code values per project_id.
Basic Incrementation:
// Auto-increments 'code' per 'project_id'
$bug = Bug::create(['project_id' => 1, 'title' => 'Fix login']);
// $bug->code = 1 (for project_id=1)
Manual Incrementation:
// Force a specific value (bypasses auto-increment)
$bug = Bug::create(['project_id' => 1, 'code' => 99, 'title' => 'Manual']);
Group-Based Logic:
// Reset counter for a group (e.g., after bulk deletes)
Bug::where('project_id', 1)->resetIncrement();
code field is nullable and has no default value:
$table->integer('code')->nullable()->comment('Auto-incremented per project');
incrementable:generate to pre-generate codes for bulk inserts:
$bugs = collect([...])->map(fn($data) => [
...$data,
'code' => Bug::generateIncrement('project_id' => $data['project_id']),
]);
use Illuminate\Validation\Rule;
$validator->rule('code', Rule::unique('bugs')->where('project_id', $data['project_id']));
Race Conditions:
DB::transaction(function () {
Bug::create([...]);
Bug::create([...]);
});
lock field or using database-level sequences as a fallback.Group Changes:
group_by after data exists will break increment sequences. Document this in your schema.Soft Deletes:
getIncrementableKey() to exclude soft-deleted records:
protected function getIncrementableKey()
{
return $this->fresh()->group_by . '|' . ($this->deleted_at ? 'deleted' : 'active');
}
Log Increment Values:
Enable debug mode in config/incrementable.php:
'debug' => env('INCREMENTABLE_DEBUG', false),
Logs will show generated values and group queries.
Manual Overrides: If increments fail, manually set the field and bypass auto-generation:
$bug = new Bug(['project_id' => 1, 'code' => null]);
$bug->forceFill(['code' => 5])->save();
Custom Generators:
Override generateIncrement() for non-sequential logic (e.g., UUIDs):
protected function generateIncrement($attributes)
{
return Str::upper(Str::random(6));
}
Dynamic Groups: Use closures for runtime group determination:
protected $incrementable = [
'field' => 'ticket',
'group_by' => function ($model) {
return $model->project_id . '-' . $model->priority;
},
];
Event Hooks:
Listen for incrementable.generated events to log or audit increments:
Bug::incrementableGenerated(function ($model, $value) {
\Log::info("Generated {$model->field}={$value} for group {$model->group_by}");
});
Cache: The package caches increment counts per group. Clear cache with:
\TestMonitor\Incrementable\Incrementable::clearCache();
Or disable caching in config/incrementable.php:
'cache' => false,
Field Types: Supports integers, strings, and UUIDs. For strings, ensure your DB column matches (e.g., VARCHAR(255)).
How can I help you explore Laravel packages today?