›
›
›
  1. docs
  2. ›
  3. byrcsc/laravel-approval
1.x
Browse documentationOpenClose

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Workflows and stages
  • Choosing the workflow
  • Approvers and resolvers
  • Requests and state
  • Conditional stages

Approval flow

  • Submitting for approval
  • Recording decisions
  • Attribute drafts
  • Returns and resubmission
  • Bulk decisions

Assignments and deadlines

  • Delegation and reassignment
  • SLAs and escalation

Reading and authorization

  • Eligibility and authorization
  • Queries and timelines
  • Events and listeners
  • Notifications
  • Notification content

Operations

  • Workflow definitions
  • Audit trail and evidence
  • Console commands
  • Testing
  • Troubleshooting

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Workflows and stages
  • Choosing the workflow
  • Approvers and resolvers
  • Requests and state
  • Conditional stages

Approval flow

  • Submitting for approval
  • Recording decisions
  • Attribute drafts
  • Returns and resubmission
  • Bulk decisions

Assignments and deadlines

  • Delegation and reassignment
  • SLAs and escalation

Reading and authorization

  • Eligibility and authorization
  • Queries and timelines
  • Events and listeners
  • Notifications
  • Notification content

Operations

  • Workflow definitions
  • Audit trail and evidence
  • Console commands
  • Testing
  • Troubleshooting

byrcsc/laravel-approval · 1.x

Conditional stages.

Skip a stage when the proposed model values do not meet its condition.

A condition decides whether a stage applies to a submission. You can require board approval above ₱100,000 or legal review only for external suppliers.

use ByRcsc\LaravelApproval\Contracts\StageCondition;
use ByRcsc\LaravelApproval\Data\StageSnapshot;
use Illuminate\Database\Eloquent\Model;

final class AmountExceeds implements StageCondition
{
    public function passes(
        StageSnapshot $stage,
        Model $approvable,
        Model $requester,
        array $config,
    ): bool {
        return $approvable->amount > ($config['threshold'] ?? 0);
    }
}

Attach it to the stage:

$workflow->stages()->create([
    'sequence' => 2,
    'name' => 'Board',
    'required_approvals' => 2,
    'condition' => AmountExceeds::class,
    'condition_config' => ['threshold' => 100_000],
]);

Laravel Approval resolves conditions from the container, so constructor injection works.

Evaluated against the proposal

The $approvable a condition receives is the proposed record, not the persisted one. When a request has an attribute draft, the package fills the drafted values onto a clone before the condition runs:

// Persisted amount is 4_000; the draft proposes 150_000.
$order->submitForApproval('purchase-order', ['amount' => 150_000]);

// AmountExceeds sees 150_000 and the board stage applies.

The package discards the clone after the condition runs. It never changes the real model, and approver resolvers still receive the persisted record.

Evaluated once, then frozen

Conditions run during submission, before the package writes the request. Each stage stores its result in condition_result. The package creates a stage whose condition did not pass directly in the skipped state.

Nothing re-evaluates a condition afterwards. Editing the record while the request is in progress does not switch a skipped stage back on. Approver snapshots do not update for the same reason. The request follows the rules it was submitted under.

The audit trail records a skipped stage as a stage_skipped action that names the condition class. The history then shows why a stage never ran.

Validation

A missing condition class, or one that does not implement StageCondition, raises InvalidWorkflowException before submission writes any rows. Workflow definitions run the same check when the package loads the definition.

Every stage skipped

If every stage in a workflow is skipped, the request has no stage to wait on. The package submits the request and fires ApprovalCompleted at once. It applies the draft after the usual stale check, and the request reaches approved without any human decision.

In that case, the workflow configuration required no human approval. Add an unconditional first stage when every request must receive at least one decision.

Conditions and required approvals

A skipped stage produces no assignments, but Laravel Approval still validates its configuration. Resolvers run for every stage. If required_approvals exceeds the number of resolved approvers, submission throws InvalidWorkflowException::approvalRuleUnreachable(). This validation also applies when the stage is skipped.

One check depends on the condition. Only a stage whose condition passes must resolve at least one approver. A stage that does not apply may have no approvers. If a stage applies and has no approvers, the workflow can never finish, so submission fails with InvalidWorkflowException::stageHasNoApprovers().

Reading the outcome

foreach ($request->timeline() as $entry) {
    $entry->state; // TimelineState::ConditionSkipped for condition skips
}

TimelineState separates condition_skipped from an ordinary skipped. A stage is skipped when the request ended before reaching it. The underlying StageStatus is skipped in both cases. The timeline tells them apart by reading condition_result.

What to read next

  • Workflows and stages to configure stage order and approval thresholds.
  • Choosing the workflow to compare workflow and stage conditions.
  • Approvers and resolvers to understand when stage approvers are resolved.
  • Queries and timelines to display skipped stages.
PreviousRequests and stateNextSubmitting for approval
View source

On this page

  1. Evaluated against the proposal
  2. Evaluated once, then frozen
  3. Validation
  4. Every stage skipped
  5. Conditions and required approvals
  6. Reading the outcome
  7. What to read next