›
byrcsc/laravel-assignment · 1.x
A profile holds the candidates, policy, scope, and mode for one assignable model, so assignment takes no arguments.
The fluent builder needs a candidate list at the call site. That works in a controller. It fails in a queued job, a scheduled command, or the queue flush, because none of them has a chain to build.
A profile is a class that supplies the candidates, policy, scope, and mode for one assignable model. Once you register it, assignment takes no arguments:
$enquiry->autoAssign();
$callout->autoAssign('crew');candidates() and policy() are required. The other methods have defaults.
use ByRcsc\LaravelAssignment\AssignmentProfile;
use ByRcsc\LaravelAssignment\Policies\RoundRobin;
use Illuminate\Database\Eloquent\Model;
final class EnquiryProfile extends AssignmentProfile
{
public function candidates(Model $assignable, ?string $role): iterable
{
return Tradie::query()->where('available', true)->get();
}
public function policy(Model $assignable, ?string $role): string
{
return RoundRobin::class;
}
public function scope(Model $assignable, ?string $role): ?string
{
return 'trade:'.$assignable->trade;
}
}candidates() returns any iterable of persisted Eloquent models. policy()
returns a class name, a policy instance, or a closure, which is the same set
using() accepts.
Every method receives the assignable and the role, so one profile can serve
several roles on the same model by branching on $role.
The contract types the assignable as
Model. The engine does not know which model a profile was registered for. Narrow it yourself when you need the model's own properties, either with aninstanceofcheck or by accepting the wider type and throwing an exception when the model is wrong.
// config/assignment.php
'profiles' => [
App\Models\Enquiry::class => App\Assignment\EnquiryProfile::class,
App\Models\Callout::class => App\Assignment\CalloutProfile::class,
],The key is the assignable class, and the value is the profile class name. The container resolves profiles, so a profile can take constructor dependencies.
The package checks the class and then its parents, so a profile registered against a base class serves every subclass that has no profile of its own.
autoAssign() on a model with no registered profile throws NoProfile.
| Method | Default | Controls |
|---|---|---|
scope() | null | The rotation scope; null uses the derived one |
mode() | Mode::Assign | Whether to assign outright or offer |
offerTtl() | null | Seconds an offer stands (at least 1), or null |
queueWhenEmpty() | true | Queue the assignable when nobody is available |
queuePriority() | 0 | Its position in the queue, highest first |
use ByRcsc\LaravelAssignment\Enums\Mode;
public function mode(Model $assignable, ?string $role): Mode
{
return $role === 'crew' ? Mode::Offer : Mode::Assign;
}
public function offerTtl(Model $assignable, ?string $role): ?int
{
return 120;
}Only offers from a profile cascade. An offer created through the fluent builder has no cascade, so it ends when the assignee declines it or it expires. See offer cascades.
public function queueWhenEmpty(Model $assignable, ?string $role): bool
{
return true;
}
public function queuePriority(Model $assignable, ?string $role): int
{
return $assignable->emergency ? 10 : 0;
}With queueWhenEmpty() returning true, an autoAssign() that finds no
candidates adds the assignable to the queue rather than doing nothing. Return
false and autoAssign() returns null and writes nothing. See the
queue.
candidates(), scope(), policy(), and mode().queueWhenEmpty() is true, the package
queues the assignable at queuePriority().The return value is the assignment, or null when the policy selected nobody.
The facade runs the same steps. Use it when you do not want to call through the model's trait:
use ByRcsc\LaravelAssignment\Facades\Assignment;
Assignment::autoAssign($enquiry);
Assignment::autoAssign($callout, 'crew');autoAssign() on an unregistered model
throws NoProfile.candidates() runs on every call. A queue flush minutes
later therefore sees a tradie who has since become available.