›
›
›
  1. docs
  2. ›
  3. byrcsc/laravel-assignment
1.x
Browse documentationOpenClose

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Assignments and slots
  • Assigning and offering
  • Selection policies
  • Scopes and rotation
  • Profiles
  • The queue
  • Offer cascades

Operations

  • Expiry and scheduling
  • Reading assignments
  • Notifications
  • Events and listeners
  • Concurrency guarantees

Reference

  • Configuration
  • API reference
  • Console commands
  • Exceptions
  • Testing
  • Troubleshooting

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Assignments and slots
  • Assigning and offering
  • Selection policies
  • Scopes and rotation
  • Profiles
  • The queue
  • Offer cascades

Operations

  • Expiry and scheduling
  • Reading assignments
  • Notifications
  • Events and listeners
  • Concurrency guarantees

Reference

  • Configuration
  • API reference
  • Console commands
  • Exceptions
  • Testing
  • Troubleshooting

byrcsc/laravel-assignment · 1.x

The queue.

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 profile

This is the package's own queue table, unrelated to Laravel's job queue. Nothing is dispatched to a worker.

What goes in

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.

What comes out

Assignment::flushQueue();                 // every entry
Assignment::flushQueue(Enquiry::class);      // one assignable type

flushQueue() 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:

  1. Claims the row.
  2. Skips and deletes the entry when the slot has since been filled by something else.
  3. Runs autoAssign() for that assignable and role, which re-reads the profile from scratch.
  4. Deletes the entry when an assignment was written, and leaves it in place when one was not.

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.

Clearing without a 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.

Running the flush

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.

Watching the queue

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 backlog

A 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;
}

What it does not do

  • Time entries out. An entry waits until it is assigned or deleted. There is no maximum age and no dead-letter queue.
  • Retry with different candidates. A flush asks the profile again, and the profile answers with whatever its query returns at that moment.
  • Dispatch jobs. Flushing happens in the process that calls it.
  • Preserve why an entry failed. An entry that finds nobody is left in place with no error recorded.

What to read next

  • Profiles for queueWhenEmpty() and queuePriority().
  • Offer cascades for the other way an assignable ends up queued.
  • Expiry and scheduling for the tick that flushes it.
PreviousProfilesNextOffer cascades
View source

On this page

  1. What goes in
  2. What comes out
  3. Clearing without a flush
  4. Running the flush
  5. Watching the queue
  6. What it does not do
  7. What to read next