›
byrcsc/laravel-assignment · 1.x
Laravel Assignment routes work to people and teams, and records who held what.
An enquiry needs a tradie. A callout needs a crew. A lead needs a sales agent. A ticket needs whoever is on shift. Two requests arrive at once and both pick the same person, so one of them silently overwrites the other. Nobody can answer who held the record last week, because the only column that ever held that answer was overwritten too.
Laravel Assignment gives you the slot and the history. Any Eloquent model can be assigned, any Eloquent model can take assignments, and at most one open assignment per slot is enforced by a database constraint rather than by an application check.
$enquiry->assign($tradie); // active immediately
$enquiry->assignee(); // the Tradie, or null
$enquiry->reassign($otherTradie); // ends the old row, opens the new one
$enquiry->assignments; // every row, oldest firstYour application keeps ownership of its users, its teams, its eligibility rules, and whatever being assigned actually means.
| Requirement | Supported versions |
|---|---|
| PHP | 8.3, 8.4 |
| Laravel | 12.x, 13.x |
| Databases | MySQL, PostgreSQL, SQLite |
The package follows semantic versioning: upgrading within 1.x is safe.
Source and issues live on
GitHub.
An assignable is anything that gets assigned: an enquiry, an order, a ticket, a
callout. Add the Assignable trait to that model.
An assignee is anything that takes an assignment: a user, a crew, a team.
Add the Assignee trait to that model.
A role is an optional label that lets one assignable hold more than one assignee at a time. A callout can hold a crew and a tradie at once, because each role is its own slot. Leave the role off and an assignable has one slot.
assign() writes an active assignment, offer() writes one the
assignee has to accept or decline.RoundRobin, LeastWorkload, Random,
FirstAvailable) and any closure, behind one SelectionPolicy contract.The package draws its edges deliberately. What follows describes what it sets out to do, not a list of planned work.
assign() and offer() are ordinary method calls with no
policy behind them.Four decisions shape most of the rest.
The open-slot rule is a database constraint. An internal slot column
holds the role while a row is open and null once it ends, under a unique index
on the assignable and that column. The loser of a race gets an exception, never
a duplicate. Moving the check into PHP would break the package's central
promise.
Assignments end, they are not deleted. An ended row records ended_at and
an ended_reason of unassigned, reassigned, declined, expired, or
completed. The rows are the history, and there is no separate audit table.
Timestamps decide expiry. An offer whose expires_at has passed stops
holding its slot immediately. assignment:tick advances cascades and flushes
the queue; it is never required for a read to be correct.
Eligibility and selection are separate jobs. Your application answers who can take the work. The engine answers who gets it, among the candidates you passed, fairly and atomically.