ringlesoft/laravel-process-approval
Multi-level approval workflows for Laravel Eloquent models. Define configurable, role-based approval steps and track review status across multiple approvers before execution. Supports Laravel 10+, publishable config/migrations/views, with optional UUID support.
Installation
composer require ringlesoft/laravel-process-approval
php artisan process-approval:install
php artisan migrate
Define an Approvable Model
use RingleSoft\LaravelProcessApproval\Traits\Approvable;
use RingleSoft\LaravelProcessApproval\Contracts\ApprovableModel;
class FundRequest extends Model implements ApprovableModel
{
use Approvable;
public function onApprovalCompleted(ProcessApproval $approval): bool
{
// Logic to execute on final approval
return true;
}
}
Create a Flow & Steps
php artisan process-approval:flow add FundRequest
php artisan process-approval:step add
(Follow CLI prompts to configure roles/steps)
Add UI Component
<x-ringlesoft-approval-actions :model="$fundRequest" />
Scenario: A "Leave Request" model needs 3-tier approval (Manager → HR → Director).
ApprovableModel on LeaveRequest.onApprovalCompleted() to finalize the request (e.g., update status).php artisan process-approval:flow add LeaveRequest
php artisan process-approval:step add
(Select roles: manager, hr, director with actions approve/reject).<x-ringlesoft-approval-actions :model="$leaveRequest" />
Model Submission
$autoSubmit = true or implement enableAutoSubmit().Approvable trait’s default UI.bypassApprovalProcess() to skip workflows for specific cases.Approval Routing
pauseApprovals() for custom validation.UI Integration
<x-ringlesoft-approval-status-summary> in lists:
<x-ringlesoft-approval-status-summary :model="$request" />
<x-ringlesoft-approval-actions> component adapts to the current step (e.g., shows "Approve/Reject" only for the next approver).Event-Driven Extensions
ProcessSubmittedEvent to notify approvers:
public function handle(ProcessSubmittedEvent $event)
{
$nextApprover = $event->approvable->getNextApprover();
Notification::send($nextApprover, new AwaitingApprovalNotification($event->approvable));
}
ProcessApprovedEvent/ProcessRejectedEvent to log actions.manager) exist in your roles table (e.g., Spatie’s laravel-permission).multi_tenancy_field in process_approval.php to scope approvals by tenant.approval-actions.blade.php) for theming.Approvals facade to mock approvals in tests:
$approval = Approvals::approve($request, $user);
Migration Conflicts
php artisan migrate twice if load_migrations is true in config.load_migrations: false after publishing migrations manually or use process-approval:install.UUID Mismatches
--uuids flag).Missing Roles
php artisan role:create manager --permissions=approve,reject
Circular Dependencies
onApprovalCompleted() failing causes stuck approvals.public function onApprovalCompleted(ProcessApproval $approval): bool
{
DB::transaction(function () use ($approval) {
$this->update(['status' => 'approved']);
// Other logic...
});
return true;
}
UI Styling Quirks
css_library in config.Check Approval Flow:
php artisan process-approval:flow list
Verify steps are correctly ordered and roles are assigned.
Inspect Model State:
Use the getApprovalStatus() method to debug:
$request->getApprovalStatus(); // Returns ['step' => 1, 'status' => 'pending']
Event Debugging: Temporarily log events in a listener:
public function handle(ApprovalNotificationEvent $event)
{
Log::info('Approval Event:', $event->toArray());
}
Database Inspection:
Check the approvals and approval_steps tables for orphaned records:
SELECT * FROM approvals WHERE model_type = 'App\Models\FundRequest';
Custom Approval Actions
Extend the ApproveAction class to add custom logic (e.g., auto-approve if amount < $100):
class CustomApproveAction extends ApproveAction
{
public function handle(ProcessApproval $approval)
{
if ($approval->approvable->amount < 100) {
return $this->approve($approval);
}
return parent::handle($approval);
}
}
Dynamic Role Assignment
Override getNextApprover() in your model:
public function getNextApprover(): ?User
{
return User::where('department', $this->department)->first();
}
API Integration
Use the Approvals facade in controllers:
$approval = Approvals::approve($request, auth()->user());
return response()->json(['status' => 'approved']);
Localization Publish translations and override messages:
php artisan vendor:publish --tag="approvals-translations"
Then modify resources/lang/en/approval.php.
Performance
with(['nextApprover']) in queries.approval_steps query in AppServiceProvider:
Cache::remember('approval_flows', now()->addHours(1), function () {
return ApprovalFlow::with('steps')->get();
});
How can I help you explore Laravel packages today?