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

Assignments and slots.

A slot is one assignable and one role, and the database allows one open assignment in it.

An assignment binds one assignee to one assignable, optionally under a role. The rule that shapes everything else is that a slot holds at most one open assignment, and the database is what enforces it.

What counts as open

An assignment is open when it is active, or when it is offered and its expires_at has not passed.

StatusOpen?
offeredYes, until expires_at passes
activeYes
endedNo

An offer holds its slot while it stands. That is the point of offering: nobody else can be given the work while somebody is deciding whether to take it.

$assignment->isOpen();      // active, or offered and not past expiry
$assignment->isActive();
$assignment->isOffered();
$assignment->isEnded();
$assignment->isExpired();   // offered, has an expires_at, and it has passed

Roles make more than one slot

Leave the role off and an assignable has one slot:

$enquiry->assign($tradie);
$enquiry->assignee();          // the Tradie

Pass a role and each role is its own slot, so one callout holds two assignees at once:

$callout->assign($crew, role: 'crew');
$callout->assign($tradie, role: 'supervisor');

$callout->assignee('crew');   // the Crew
$callout->assignee('supervisor');   // the Tradie

Every reader takes the same optional role, and the role-less slot is a distinct slot rather than a wildcard. $callout->assignee() with no argument reads the slot with no role, which in the example above is empty.

Roles are free-form strings. The package neither validates them nor keeps a list of the ones you use.

The constraint behind the rule

The assignments table carries an internal slot column. While a row is open, slot holds the role, or an empty string when there is no role. When the row ends, slot becomes null. A unique index covers the assignable type, the assignable id, and slot.

Null values repeat freely in a unique index, so ended rows never collide, and two open rows for the same slot cannot both exist. Two concurrent callers racing for one slot end with one assignment and one SlotOccupied exception, on MySQL, PostgreSQL, and SQLite alike.

slot is internal. It is hidden from the model's array and JSON output, and nothing in the public API takes or returns it.

History is the table

Assignments end, they are not deleted. An ended row records when and why:

ended_reasonWritten when
unassignedunassign() ended an active assignment
reassignedreassign() replaced this row with another
declinedThe assignee declined an offer
expiredAn offer passed its expires_at
completedcomplete() ended the work as finished

Every row also carries offered_at, accepted_at, expires_at, ended_at, and a polymorphic assigned_by. A null assigned_by means the engine created the row rather than an actor you named.

$callout->assignments()->ended()->get();   // past holders, oldest first

There is no separate transition table. A row is one holder's whole story, and the sequence of rows is the assignable's.

The rows are a record, not an audit trail. They are ordinary Eloquent models with no append-only enforcement. Anything that can write to the database can change ended_at or expires_at. If you need attributable, tamper-evident history, listen to the events and write it yourself.

What it does not do

  • Track a slot's history separately from an assignee's. Both are the same rows, read from different sides.
  • Reopen an ended assignment. Ending is final; a new holder is a new row.
  • Validate roles. Any string works, and a typo creates a second slot rather than an error.

What to read next

  • Assigning and offering for the two verbs and the handshake between them.
  • Reading assignments for every reader on both traits.
  • Concurrency guarantees for what the constraint promises under load.
PreviousQuick startNextAssigning and offering
View source

On this page

  1. What counts as open
  2. Roles make more than one slot
  3. The constraint behind the rule
  4. History is the table
  5. What it does not do
  6. What to read next