cjmellor/approval
Laravel package to stage new model changes for review before they’re persisted. Add approvals to your workflow, store pending records in the database, and promote them once approved. Supports PHP 8.3+ and Laravel 12.4+/13.
ApprovalCreated, ModelApproved, etc.) enable seamless integration with Laravel’s event system, allowing for side effects (e.g., notifications, auditing) without coupling logic to the approval process.ApprovalStatus enum for state transitions (pending, approved, rejected, custom states), which is clean and extensible. The #[Scope] attribute (Laravel 12.4+) improves query readability and maintainability.new_data/original_data as JSON, preserving schema flexibility while enabling rollbacks and diffing. The approvalAttributes property allows granular control over which fields require approval.approvals table with polymorphic relationships (approvalable_type, approvalable_id). Migration is straightforward but requires schema changes (backward-incompatible in v2.x).MustBeApproved trait to models. Customization (e.g., foreign keys, approval attributes) is explicit and well-documented.approve(), reject(), rollback()) and query scopes (pending(), approved()), reducing friction for existing Laravel patterns.approvalable_type/id collide with other tables).new_data/original_data is efficient for small models but could bloat storage for large payloads (e.g., nested relationships). Consider indexing state, approvalable_id, and creator_id for query performance.thenCustom() handlers) could lead to edge cases.rollback(bypass: true) skips re-approval, which may not align with all workflows (e.g., requiring manual re-review). Requires explicit opt-in for re-approval.Workflow Alignment:
pending → approved/rejected) match your business workflow? For example, do you need intermediate states like in_review or needs_revision?thenReject, thenPostpone) interact with your SLA requirements?Data Sensitivity:
new_data/original_data that should be redacted or encrypted (e.g., PII)? The package doesn’t handle this natively.Concurrency:
rollback or approve simultaneously? The package lacks explicit locking mechanisms.$version)?Auditability:
audited_by field tracks state changes, but does your compliance require a full audit log (e.g., who viewed/edited approvals)? Consider pairing with Laravel’s audit packages.Post is deleted while pending)?Testing:
incrementing IDs disabled?fillable/guarded rules conflicting with approvalAttributes?expires_at/actioned_at?Scaling:
approvals table could grow large. Will you partition it by approvalable_type or use archiving?process-expired command runs every minute—is this frequency sufficient?#[Scope]), and typed properties, reducing runtime overhead.Post, UserProfile, FinancialTransaction).MustBeApproved trait.php artisan vendor:publish --tag="approval-migrations" and php artisan migrate.MustBeApproved to target models, starting with low-risk ones (e.g., non-critical content).getApprovalForeignKeyName()) and approval attributes ($approvalAttributes) as needed.Approval model’s query scopes.ModelApproved, ModelRejected, etc., to trigger downstream actions (e.g., notifications, webhooks).approval:process-expired command in your scheduler (everyMinute() or less frequent if SLAs permit).approvalable_type/id doesn’t clash with existing polymorphic tables (e.g., taggable).saving/saved observers, ensure they don’t interfere with the package’s MustBeApproved logic.expires_at in the past).in_review) via config/approval.php.approvals table for performance-critical queries.ModelApproved) to a monitoring tool.How can I help you explore Laravel packages today?