›
byrcsc/laravel-assignment · 1.x
Assignables with no available candidate wait in a priority queue instead of failing silently.
Auto-assignment can find nobody. Every crew is offline, every tradie is deactivated, or the policy declined to pick. Without somewhere to put the work, the call returns null and the enquiry is lost.
The queue is that somewhere. A queued assignable waits until a flush finds it a candidate.
use ByRcsc\LaravelAssignment\Facades\Assignment;
$enquiry->autoAssign(); // no candidates, so the enquiry is queued
$enquiry->queuedAssignment(); // the waiting entry
Assignment::flushQueue(); // re-resolve everything through its profileThis is the package's own queue table, unrelated to Laravel's job queue. Nothing is dispatched to a worker.
Two paths add an entry.
Auto-assignment that finds nobody, when the profile's queueWhenEmpty()
returns true, which is the default. Its position comes from
queuePriority().
An explicit call through the builder, when you want to reserve a place before any candidate exists:
Assignment::for($callout)->role('supervisor')->queue(priority: 10);Queueing an assignable whose slot already holds an open assignment throws
AlreadyAssigned. There is nothing to wait for.
Entries are unique per assignable and role, so queueing twice updates the
existing entry rather than adding a second one. AssignableQueued fires only
when an entry is created, not when an existing one is touched again.
Assignment::flushQueue(); // every entry
Assignment::flushQueue(Enquiry::class); // one assignable typeflushQueue() returns how many entries it managed to assign.
Order is priority descending, then queued_at ascending, then insertion order.
A priority 10 entry queued this morning goes before a priority 0 entry queued
last week, and two entries at the same priority keep their arrival order.
Each entry is processed in its own transaction and claimed with a row lock, so two flushes running at once never assign the same entry twice.
For each entry the flush:
autoAssign() for that assignable and role, which re-reads the profile
from scratch.Because step 3 re-resolves the profile, a crew becoming available or a tradie being reactivated is picked up by the next flush without anything being recorded against the entry.
An entry that still has no candidates stays queued, and is not retried again inside the same flush.
Creating an assignment for a slot deletes any queue entry for that slot, so a
direct assign() cleans up after a queued attempt. You do not need to check
the queue before assigning by hand.
Three places, and you can use all three:
// The scheduled tick, alongside expiry
Schedule::command('assignment:tick')->everyMinute();// A domain event of your own
public function handle(CrewBecameAvailable $event): void
{
Assignment::flushQueue(Callout::class);
}// By hand, in a console command or a controller
Assignment::flushQueue();Flushing on your own events is the responsive option, since the work is picked up the moment somebody becomes available. The tick is the safety net for everything your events do not cover.
use ByRcsc\LaravelAssignment\Models\QueuedAssignment;
$entry = $enquiry->queuedAssignment(); // or null
$entry->role;
$entry->priority;
$entry->queued_at;
$entry->assignable; // back to the model
QueuedAssignment::query()->count(); // how deep is the backlogA queue that only grows means candidates are never becoming available. That is a signal about your eligibility query, not about the package.
AssignableQueued carries the entry, so an alert on a backlog is a listener:
public function handle(AssignableQueued $event): void
{
$event->entry->assignable;
$event->entry->priority;
}queueWhenEmpty() and queuePriority().