Installation
composer require wezlo/filament-approval
php artisan vendor:publish --tag=filament-approval-migrations
php artisan migrate
Verify the approvals, approval_actions, and approval_notifications tables exist.
First Use Case: Basic Approval Chain
Add the HasApprovals trait to your Eloquent model:
use Wezlo\FilamentApproval\Traits\HasApprovals;
class Invoice extends Model
{
use HasApprovals;
}
Publish the config and views:
php artisan vendor:publish --tag=filament-approval-config
php artisan vendor:publish --tag=filament-approval-views
Define an Approval Flow
Create a Filament resource for your model (e.g., InvoiceResource). In the resource’s getRelations() method, include:
public static function getRelations(): array
{
return [
ApprovalRelationManager::make(),
];
}
Access the Approval Workflow tab in the Filament admin panel to configure your first approval chain.
Single Approver Flow
User::where('role', 'finance')->first()).config/filament-approval.php:
'resolvers' => [
'finance' => function ($record) {
return User::where('role', 'finance')->first();
},
],
Sequential Approval Chain
'next_approver' => function ($record, $currentApprover) {
return User::where('department', $record->department)
->where('role', 'department_head')
->first();
},
Parallel Approval
Dynamic Approval Assignment
public function getApprovalResolvers(): array
{
return [
'legal' => function ($record) {
return User::where('email', 'legal@company.com')->first();
},
];
}
Filament Resource Integration
Add the ApprovalStatusBadge column to your resource table:
public static function getTableColumns(): array
{
return [
ApprovalStatusBadge::make(),
// ... other columns
];
}
Infolist for Quick Status Check Display approval status in the model’s detail view:
public static function getInfolists(): array
{
return [
'default' => [
ApprovalStatusSection::make(),
// ... other sections
],
];
}
Notifications
Customize notification triggers in config/filament-approval.php:
'notifications' => [
'requested' => true,
'approved' => true,
'rejected' => true,
'escalated' => true,
'sla_warning' => true,
],
SLA and Escalation Configure SLAs per approval step in the Filament UI (e.g., "24 hours for Team Lead"). Define escalation actions (e.g., auto-reject after 48 hours):
'escalation' => [
'auto_reject_after_hours' => 48,
],
Delegation Allow approvers to delegate via the Filament UI or programmatically:
$approval->delegateTo(User::find(123));
Audit Trail
Access the full history via the ApprovalRelationManager:
$record->approvals()->with('actions')->latest()->get();
Migration Conflicts
notifications table, merge the published migrations carefully. The package expects standard notification columns (type, data, etc.).php artisan migrate --pretend to preview conflicts before applying.Resolver Caching
next_approver) are called on every action. Cache results if resolvers are expensive:
'next_approver' => function ($record, $currentApprover) {
return Cache::remember("next_approver_{$record->id}", now()->addHours(1), function () {
return User::where(...)->first();
});
},
SLA Processing
RunSlaChecks command must run periodically (e.g., via Laravel’s scheduler):
* * * * * php artisan approval:sla-check
storage/logs/laravel.log for resolver errors.Polymorphic Model Issues
approvals() relation is correctly defined:
public function approvals()
{
return $this->morphMany(Approval::class, 'approvable');
}
php artisan route:list | grep approval to verify webhook routes.Filament Plugin Isolation
config/filament-approval.php:
'panel_group' => 'admin', // or 'tenant', etc.
Approval State Transitions
Approval::observe(ApprovalObserver::class);
namespace App\Observers;
use Wezlo\FilamentApproval\Models\Approval;
class ApprovalObserver
{
public function saved(Approval $approval)
{
\Log::info("Approval {$approval->id} transitioned to {$approval->status}");
}
}
Resolver Failures
'resolver' => function ($record) {
try {
return User::where(...)->firstOrFail();
} catch (\Exception $e) {
\Log::error("Resolver failed for {$record->id}: " . $e->getMessage());
return null; // Fallback to next resolver or reject
}
},
SLA Not Triggering
created_at and updated_at timestamps on approvals and approval_actions tables.updated_at to a past date and re-run the SLA command.Custom Approval Actions
ApprovalAction model or create a custom action type:
namespace App\Models;
use Wezlo\FilamentApproval\Models\ApprovalAction;
class CustomApprovalAction extends ApprovalAction
{
protected $table = 'custom_approval_actions';
}
config/filament-approval.php:
'action_models' => [
\App\Models\CustomApprovalAction::class,
],
Custom Notifications
ApprovalNotification class:
php artisan vendor:publish --tag=filament-approval-views
namespace App\Notifications;
use Wezlo\FilamentApproval\Notifications\ApprovalNotification;
class CustomApprovalNotification extends ApprovalNotification
{
public function via($notifiable)
{
return ['database', 'mail']; // Add Slack, etc.
}
}
config/filament-approval.php:
'notification_class' => \App\Notifications\CustomApprovalNotification::class,
Custom UI Components
php artisan vendor:publish --tag=filament-approval-assets
ApprovalRelationManager:
namespace App\Filament\Resources;
use Wezlo\FilamentApproval\Filament\Resources\ApprovalRelationManager;
class CustomApprovalRelationManager
How can I help you explore Laravel packages today?