›
›
›
  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 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 profile

The queue is the package's own table, unrelated to Laravel's job queue. The package dispatches nothing to a worker.

What goes in

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.

What comes out

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

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

  1. Claims the row.
  2. Deletes the entry when its assignable model no longer exists.
  3. Deletes the entry when something else has filled the slot since.
  4. Runs autoAssign() for that assignable and role, which re-reads the profile from scratch.
  5. Deletes the entry when 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.

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

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.

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

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. The flush runs in the process that calls it.
  • Preserve why an entry failed. An entry that finds nobody stays in place, and the package records no error.

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