›
byrcsc/laravel-approval · 1.x
Assign a specific approver or resolve several approvers from an application rule.
An approver entry points to one saved model. Only that model can respond to the assignment.
$stage->approvers()->create([
'approver_type' => $financeUser->getMorphClass(),
'approver_id' => $financeUser->getKey(),
]);The package does not expand a Team, Role, Position, or other container
model into its members. Assigning a team assigns the team record itself, and
only something acting as that team can approve. To assign the members, use an
approver resolver.
Approver columns are polymorphic, so an assignment can name any model. The
package never assumes your users are App\Models\User.
A resolver converts an application rule into a list of approver models. It runs when you submit a request:
use ByRcsc\LaravelApproval\Contracts\ApproverResolver;
use ByRcsc\LaravelApproval\Data\StageSnapshot;
use Illuminate\Database\Eloquent\Model;
final class FinanceTeamMembers implements ApproverResolver
{
public function resolve(
StageSnapshot $stage,
Model $approvable,
Model $requester,
array $config,
): iterable {
return Team::findOrFail($config['team'])->members;
}
}Register it on the stage instead of a concrete approver:
$stage->approvers()->create([
'resolver' => FinanceTeamMembers::class,
'resolver_config' => ['team' => $financeTeam->getKey()],
]);An approver entry names either a model or a resolver, never both. The package enforces this rule in code, because no portable database constraint can express it.
InvalidWorkflowException.Laravel Approval resolves resolvers from the container, so constructor injection works.
A resolver runs once, at submission. It is not a live query. The package writes its result into the stage's assignments and never runs the resolver again for that request. If the approver list changes tomorrow, today's assignments stay the same.
To fix a stale assignment, delegate it, reassign it, or resync the request.
Two entries that name the same record produce one assignment, even when they come from different sources. The same person returned by two resolvers on one stage produces one assignment. Duplicate assignments do not increase the number of approvals available to the stage.
StaticUsersResolverStaticUsersResolver assigns a fixed list of primary keys. Use it when several
stages share one list and you want to edit the list in one place:
use ByRcsc\LaravelApproval\Resolvers\StaticUsersResolver;
$stage->approvers()->create([
'resolver' => StaticUsersResolver::class,
'resolver_config' => ['ids' => [4, 11], 'model' => Team::class],
]);model is optional and defaults to approval.user_model.
CallbackResolverCallbackResolver calls a method on a class your application already has. An
existing ManagerLookup::forEmployee() becomes an approver rule, and you do not
rewrite it around the package's interface:
use ByRcsc\LaravelApproval\Resolvers\CallbackResolver;
$stage->approvers()->create([
'resolver' => CallbackResolver::class,
'resolver_config' => ['callback' => 'App\Org\ManagerLookup@forEmployee'],
]);The package resolves the class from the container, so the container injects
its dependencies. The method receives the same four arguments as
ApproverResolver::resolve(). Omit @method to call __invoke. The package
drops any returned value that is not an Eloquent model.
Use CallbackResolver when the logic already exists. Otherwise, write a
resolver. An implementation of the interface is about fifteen lines, and its
class name says what it does.
Every assignment records how its current holder received it:
| Source | Meaning |
|---|---|
assigned | Copied from a concrete approver entry at submission |
resolved | Produced by a resolver at submission |
delegated | An approver handed the assignment on themselves |
reassigned | An administrator rewrote who the assignment belongs to |
escalated | An overdue stage's escalation policy added the approver |
resynced | An administrative resync added the approver |
The submit-time snapshot produces assigned and resolved. The other sources
record a deliberate change to a request already in progress.
A requester may not approve their own request unless the policy allows it. Enable it globally:
APPROVAL_ALLOW_SELF_APPROVAL=trueYou can also enable it per workflow. The workflow setting takes precedence:
$workflow->update(['settings' => ['allow_self_approval' => true]]);An eligibility check reports a denied attempt as Ineligibility::SelfApproval.
A decision throws SelfApprovalException.
An approval rule counts assignments. If assigned models are deleted, a stage can require more approvals than the remaining people can provide. The stage can no longer finish without administrative action.
approval:doctor finds stranded stages:
php artisan approval:doctorThe package allows a delegation or reassignment when it does not make the stage's shortage of approvers worse. The change does not have to remove the shortage. An already stranded stage can still receive the delegations and reassignments that try to repair it.