›
byrcsc/laravel-hold · 1.x
Install Laravel Hold, choose key types, migrate, and add the model traits.
The package ships one migration and one config file. The config decides the shape of the migration's identity columns, so publish it first when your models do not use integer keys.
composer require byrcsc/laravel-holdThe service provider is discovered automatically. It registers the config, the migration, and the two console commands.
Skip this step when your table can be called holds and both sides of a hold
use integer primary keys. Those are the defaults.
php artisan vendor:publish --tag="hold-config"That writes config/hold.php:
return [
'table' => 'holds',
'holdable_key_type' => env('HOLD_HOLDABLE_KEY_TYPE', 'int'),
'holder_key_type' => env('HOLD_HOLDER_KEY_TYPE', 'int'),
];Set the key types to int, uuid, ulid, or string to match the models on
each side:
HOLD_HOLDABLE_KEY_TYPE=uuid
HOLD_HOLDER_KEY_TYPE=ulidSet these before you migrate. The two key types shape the
holdable_id,holder_id, andreleased_by_idcolumns. Changing them after the table exists takes a migration of your own. Full details in configuration.
php artisan vendor:publish --tag="hold-migrations"
php artisan migrateThe published file creates one table. It reads config/hold.php at migration
time for the table name and both key types, and throws
InvalidArgumentException if a key type is not one of the four supported
values. See database schema for the columns and indexes.
Put Holdable on the resource:
use ByRcsc\LaravelHold\Concerns\Holdable;
class Seat extends Model
{
use Holdable;
}Put HasHolds on the holder, which does not need to be a user:
use ByRcsc\LaravelHold\Concerns\HasHolds;
class Cart extends Model
{
use HasHolds;
}HasHolds is optional. A holder works without it, because the hold row is
written from the holdable side. Add the trait when you want to read holds from
the holder, as $cart->holds and $cart->activeHolds.
A model that is both a resource and a holder needs one resolution block. See holdables and holders.
$seat = Seat::first();
$seat->availableSlots(); // 1
$seat->isFullyHeld(); // false
$hold = $seat->acquireHold($user, expiresAt: now()->addMinutes(15));
$seat->fresh()->isFullyHeld(); // trueNothing here is required for correctness. Availability reads the clock, so an expired hold stops blocking its slot with no command running.
Schedule hold:expire only when something in your application listens for the
HoldExpired event:
use Illuminate\Support\Facades\Schedule;
Schedule::command('hold:expire')->everyMinute();See scheduling expiry for what the command does and does not guarantee, and pruning history for bounding the table over time.