Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Approval Laravel Package

byrcsc/laravel-approval

byrcsc/laravel-approval is a Laravel package for adding approval workflows to your application. Attach approvals to Eloquent models, manage pending and approved states, and keep approval logic organized within your existing Laravel app.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install the package with Composer, then publish and run its migrations. This creates the eight approval tables; decide on your identity strategy (integer, UUID, ULID, or string) before migrating, because the schema is built around it.
  • Publish config/approval.php first. It is the control surface for workflows, resolvers, notification events, SLA strategies, and guard mode.
  • If you plan to use built-in notifications, also publish approval-translations and approval-views. They are intentionally separate so you can customize copy and mail templates independently.
  • Start with the workbench demo: workbench/README.md is the fastest way to see every integration seam in a bootable Laravel app. Treat it as reference, not production runtime; it is excluded from the dist archive.
  • First real use case: add the Approvable trait to one model, define a simple workflow with one or two stages, bind an approver resolver (start with StaticUsersResolver or CallbackResolver), run approval:sync, submit a record, and approve/reject it through the request model and policy. This exercises schema, workflow snapshotting, approver resolution, authorization, and audit in one pass.
  • Look next at ApprovalRequestPolicy, ApproverResolver, StageCondition, and WorkflowCondition. Those are the main extension points you will reach for as the workflow grows beyond a demo.

Implementation Patterns

  • Version workflows in code, reconcile with approval:sync. Define workflows as arrays or fluent builders, commit them, and use approval:sync for idempotent reconciliation. Run dry-run mode in CI or before deployment. In-flight requests keep their snapshots, so workflow edits do not rewrite live approvals.
  • Use multiple workflows deliberately. Register workflows against a model type through approvable_type, use container-resolved WorkflowCondition classes to select candidates for a given submission, and use priority to order the remainder. Use Approval::workflows() and Approval::workflowsFor() to drive a workflow picker. Avoid ambiguous candidates; the package refuses equally specific workflows rather than guessing.
  • Treat submission as a validation boundary. The submit path validates the workflow and snapshots stages before writing rows. If a workflow cannot reach a decision, it fails before any approval records are written. This is a good place to surface user-facing validation early.
  • Resolve approvers through the contract, not ad hoc queries. Implement ApproverResolver for dynamic org data, or use StaticUsersResolver / CallbackResolver for simpler cases. Stage conditions are container-resolved StageCondition classes evaluated once at submission and snapshotted as visible skipped stages.
  • Use the policy as the day-to-day authorization layer. The convention-discovered ApprovalRequestPolicy covers approve, reject, return, cancel, delegate, and view. It is backed by the same checks as the engine, so UI and engine stay aligned.
  • Use read models for inboxes and status screens. Current stage, pending approvers, model/request booleans, eager-loadable timelines, and approvable scopes are designed for state and approver inboxes. Prefer these over hand-rolled queries.
  • Draft apply and guard mode belong together. If approvables can be edited while a request is pending, enable guard mode for Eloquent protection of drafted attributes, use cast-aware stale checking, and handle the conflicted state explicitly. Use withoutApprovalGuard() only as a re-entrant, record-scoped, audited escape hatch; the final stale check remains the backstop for writes outside model events.
  • Plan delegation and recovery as normal operations. Delegate or reassign assignments, deliberately resync a live request when ownership changes, and rely on orphaned-work closure when an approvable is hard-deleted. Preserve living assignment coverage during ownership changes.
  • Schedule escalation and verification. Configure per-stage deadlines and one of the four escalation strategies via the swappable config map. Schedule approval:escalate idempotently and use approval:doctor for stranded-stage detection. Use approval:status for operational visibility and approval:verify for global or request-scoped audit-chain verification.
  • Opt into notifications explicitly. The event map is disabled by default. Enable only the events you need; notifications are deferred until transaction commit. For custom notifications, use ApprovalNotification::mailFor() so replacement notifications follow the same path. Keep translations and views aligned with application locale and record-name placeholders.
  • Use Approval::fake() and exported factories for host tests. Fake records every public mutation with matching assertions and avoids approval-state writes. Use real database integration tests where transactions, row locks, and per-request behavior matter.
  • Treat the action history as append-only evidence. Application-enforced hash chaining is useful for tamper evidence within the documented trust boundary, but not a substitute for database-level immutability. Bind host-owned attachment references into the chain and verify with approval:verify.

Gotchas and Tips

  • Do not edit in-flight snapshots. Workflow reconciliation is intentionally non-destructive. If you need a different workflow for an existing record, use resubmission or a new request; changing the definition will not rewrite live approvals.
  • Watch for ambiguous workflow candidates. Equally specific workflows are refused rather than resolved arbitrarily. Set priority and conditions carefully, and test Approval::workflowsFor() with real records.
  • Dry-run approval:sync before deploying workflow changes. It is the cheapest way to catch destructive drift and accidental stage removals before they hit a live environment.
  • Guard mode is opt-in, not a database constraint. It protects pending drafted attributes on Eloquent events, but writes outside model events are only caught by the final stale check. Keep the escape hatch narrow and audited.
  • Conflicted drafts need explicit UX. The package exposes the state and conflict resolution; your UI still needs to explain why a stale write was rejected and what the user can do next.
  • Notifications are disabled by default. If nothing sends, check the event map before debugging mail. Also remember they are deferred until commit, so tests may need transaction-aware assertions.
  • Translations and views publish separately. The views read the translations, so customizing one half should not freeze the other, but republishing only one side can still surprise you. Keep both in source control and review them together.
  • Escalation is idempotent, but scheduling still matters. approval:escalate is safe to rerun, but stranded stages only surface if the command runs often enough and your bounded scans cover the relevant population. Use approval:doctor when stages look stuck.
  • Hard deletes close orphaned work. That is usually right, but confirm it matches your retention policy before relying on it for cleanup.
  • Identity customization is easiest before the first migration. Changing integer/UUID/ULID/string identity after data exists is a migration project, not a config toggle.
  • Bulk decisions are still per-request transactions. Large batches can be heavy; chunk where possible and monitor locks.
  • The workbench is development tooling only. Use it to learn integration seams, but do not ship it as part of your application or assume it is covered by CI.
  • Check platform compatibility early. The release targets PHP 8.3/8.4 and Laravel 12/13 with database-aware tests for SQLite, MySQL, and PostgreSQL. If your app is on older versions, verify before adopting.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin