›
byrcsc/laravel-assignment · 1.x
Every package failure throws a typed exception that extends one base class, and each type names one situation.
The package throws rather than returning false, because filling the wrong slot
quietly is worse than an error. Every exception extends
ByRcsc\LaravelAssignment\Exceptions\AssignmentException, which extends
RuntimeException, so one catch block covers all of them.
use ByRcsc\LaravelAssignment\Exceptions\AssignmentException;
try {
$enquiry->assign($tradie);
} catch (AssignmentException $e) {
report($e);
}| Exception | Thrown when |
|---|---|
SlotOccupied | The slot already holds an open assignment, or a concurrent writer won the race |
AlreadyAssigned | The assignee already holds the slot, or a queued assignable is already assigned |
NotAssigned | unassign() or reassign() found no open row to act on |
NotTheAssignee | A model other than the one an offer names tried to answer it |
OfferNotOpen | The offer was already answered, or its expiry has passed |
InvalidTransition | complete() ran on an assignment that is not active |
NoProfile | autoAssign() ran on a model with no registered profile |
SlotOccupied is the most important one to handle. It means the slot is taken,
either because it already held an open row or because another request claimed
it between your read and your write.
use ByRcsc\LaravelAssignment\Exceptions\SlotOccupied;
try {
$callout->assign($crew, role: 'crew');
} catch (SlotOccupied) {
// pick again, queue the work, or tell the user
}The package throws it from:
assign() and offer() on an occupied slot.The package does not convert unrelated integrity errors, so a foreign key failure still throws its own exception.
Retrying is often right, particularly for autoAssign(), where the profile
selects again and can pick a different candidate.
AlreadyAssigned means that the requested state is already true:
reassign() was given the assignee who already holds the slot. The package
throws instead of treating a reassignment to the same assignee as a
successful no-op.queue() was called for a slot that already holds an open assignment. There
is nothing to wait for.unassign() or reassign() found an empty slot. Check with isAssigned()
first when an empty slot is an expected state rather than a bug.
Somebody other than the offer's target called accept() or decline(). Treat
it as an authorization failure and report it, rather than catching and
ignoring it. The offer is still open and still belongs to the assignee it
named.
Nobody can answer the offer any more. It was accepted, declined, or its
expires_at passed. In a race between two answers, the loser sees this.
The offer's own row tells you which it was:
catch (OfferNotOpen) {
$offer->refresh();
$offer->ended_reason; // declined, expired, or null when it was accepted
}complete() applies only to an active assignment. Completing an offer that
nobody has accepted, or a row that already ended, throws.
autoAssign() was called on a model with no entry in assignment.profiles,
and none of its parent classes had one either. The message names the class,
which is usually enough to spot a missing config entry or a typo.
Programming errors raise plain PHP exceptions rather than package ones:
UnexpectedValueException when a candidate is not a persisted Eloquent
model, when a policy returns a model that was not among the candidates, when
a registered profile is not a class name or does not extend
AssignmentProfile, or when a policy class does not implement
SelectionPolicy.InvalidArgumentException when a role is an empty string, when an offer
TTL is below one second, or, from the migration, when a configured key type
is not id, uuid, ulid, or string.An AssignmentException block catches neither of them, by design. They mean
the wiring is wrong, not that another request got there first.
Not every empty outcome is an error:
assign() and offer() on the builder return null when the candidate
list is empty or the policy picks nobody.autoAssign() returns null for the same reason, after queueing the
assignable when the profile allows it.assignee(), assignment(), and queuedAssignment() return null when
there is nothing to read.