›
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 holds that work. 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 profileThe queue is the package's own table, unrelated to Laravel's job queue. The package dispatches nothing to a worker.
An entry is added in these cases.
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 the package creates an entry, not when it updates an existing one.
Assignment::flushQueue(); // every entry
Assignment::flushQueue(Enquiry::class); // one assignable typeflushQueue() returns how many entries it assigned.
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.
The flush processes each entry in its own transaction and claims it 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.autoAssign() wrote an assignment, and leaves it in
place when it did not.Because step 4 re-reads the profile, the next flush finds a crew who became available or a tradie who was reactivated. Nothing needs to be recorded against the entry.
An entry that still has no candidates stays queued. The same flush does not retry it.
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.
Run the flush from any of these places, or from all of them:
// 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 assigns the work as soon as somebody becomes available. The tick handles every case that 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 that no candidates are becoming available. Look for the cause in your eligibility query, not in the package.
AssignableQueued carries the entry, so you can alert on a backlog from a
listener:
public function handle(AssignableQueued $event): void
{
$event->entry->assignable;
$event->entry->priority;
}queueWhenEmpty() and queuePriority().