This package is a FilamentPHP plugin designed to provide a workflow automation system within FilamentPHP applications. It enables users to create and manage workflows triggered by model events, custom events, or scheduled tasks. The package integrates seamlessly with FilamentPHP, offering a Filament Resource for managing workflows.

Ensure your Laravel application meets the following requirements:
composer require monzer/filament-workflows
php artisan vendor:publish --provider="Monzer\FilamentWorkflows\FilamentWorkflowsServiceProvider" --tag="migrations"
php artisan vendor:publish --provider="Monzer\FilamentWorkflows\FilamentWorkflowsServiceProvider" --tag="config"
php artisan migrate
Users must manually register the plugin in their PanelProvider.php:
use Filament\Facades\Filament;use Monzer\FilamentWorkflows\WorkflowsPlugin; public function panel(Panel $panel): Panel{ return $panel ->plugin(WorkflowsPlugin::make());}
To integrate a model with the model event workflow system, the model must implement the following trait:
use Monzer\FilamentWorkflows\Traits\TrackWorkflowModelEvents; class Order extends Model{ use TrackWorkflowModelEvents;}
To change the model display name you can use the getModelName() static function:
use Monzer\FilamentWorkflows\Traits\TrackWorkflowModelEvents; class Order extends Model{ use TrackWorkflowModelEvents; public static function getModelName(): string { return __("order.plural"); //for example }}
To change the attributes display name you can use the getAttributeName() static function:
use Monzer\FilamentWorkflows\Traits\TrackWorkflowModelEvents; class Order extends Model{ use TrackWorkflowModelEvents; public static function getAttributeName(string $attribute): ?string { switch ($attribute) { case 'id': return __("order.fields.id"); case 'type': return __("order.fields.type"); //... extra default: return null; } }}
You need to run php artisan schedule:work command to run the workflows.
Example configuration in config/workflows.php:
return [ 'actions' => [ \Monzer\FilamentWorkflows\Actions\SendFilamentNotification::class, \Monzer\FilamentWorkflows\Actions\SendEmail::class, \Monzer\FilamentWorkflows\Actions\SendSmsViaTwilio::class, \Monzer\FilamentWorkflows\Actions\CreateRecord::class, \Monzer\FilamentWorkflows\Actions\UpdateRecord::class, \Monzer\FilamentWorkflows\Actions\SendWebhook::class, \Monzer\FilamentWorkflows\Actions\PushFirebaseNotification::class, \Monzer\FilamentWorkflows\Actions\BackupMySqlDBUsingMySqlDump::class, \Monzer\FilamentWorkflows\Actions\SendWhatsAppMessageViaWassenger::class, \Monzer\FilamentWorkflows\Actions\SendTelegramMessage::class ], //scan the following directories for models 'models_directory' => [ 'App\\Models', ], 'services' => [ 'firebase' => [ 'server_key' => env('FIREBASE_SERVER_KEY'), 'model_token_attribute_name' => env('FIREBASE_MODEL_TOKEN_ATTRIBUTE_NAME', 'fcm_token'), 'icon' => env('FIREBASE_ICON'), ], 'telegram' => [ 'bot_token' => env('TELEGRAM_BOT_TOKEN'), ], 'wassenger' => [ 'api_key' => env('WASSENGER_API_KEY'), ], 'twilio' => [ 'sid' => env('TWILIO_SID'), 'token' => env('TWILIO_TOKEN'), 'from' => env('TWILIO_FROM'), ], ], /* |-------------------------------------------------------------------------- | Maximum Log Entries |-------------------------------------------------------------------------- | | This value determines the maximum number of log entries to keep for | each workflow. When this limit is exceeded, older entries will be | automatically removed to prevent database overflow. Set to null to | disable log rotation (not recommended for production). | */ 'max_log_entries' => env('WORKFLOWS_MAX_LOG_ENTRIES', 100),];
Starting from version 0.3.0, this package includes automatic log rotation to prevent database overflow. By default, only the last 100 log entries are kept for each workflow.
You can customize the maximum number of log entries by setting the WORKFLOWS_MAX_LOG_ENTRIES environment variable:
WORKFLOWS_MAX_LOG_ENTRIES=200
Or modify it directly in the config file:
'max_log_entries' => 200, // Keep last 200 entries
To disable log rotation (not recommended):
'max_log_entries' => null, // Disable rotation
If you have existing workflows with large log histories, you can clean them up using the provided artisan command:
# Clean up logs using the configured limitphp artisan workflows:cleanup-logs # Clean up logs with a custom limitphp artisan workflows:cleanup-logs --limit=50 # Preview what would be cleaned without making changesphp artisan workflows:cleanup-logs --dry-run
For existing installations that experience database errors due to large logs, run the optional migration to increase column size:
php artisan migrate --path=vendor/monzer/filament-workflows/database/migrations/2024_01_01_000000_update_workflows_logs_column_size.php
Magic attributes are placeholders that get dynamically replaced with actual data from the model or event triggering the workflow.
@email@ → Replaced by the model's email attribute.
Hello @email@, your order has been processed.
email = user@example.com, the message will be:
Hello user@example.com, your order has been processed.
@event->name@ → Replaced by the event’s name attribute.
A new event named @event->name@ has been created.
name = System Update, the message will be:
A new event named System Update has been created.
Users can create custom actions by implementing the Action interface. Below is an example implementation of the *
SendEmail* action:
namespace Monzer\FilamentWorkflows\Actions; use Filament\Forms\Components\Textarea;use Filament\Forms\Components\TextInput;use Illuminate\Database\Eloquent\Model;use Illuminate\Support\Facades\Mail;use Monzer\FilamentWorkflows\Contracts\Action;use Monzer\FilamentWorkflows\Models\WorkflowActionExecution; class SendEmail extends Action{ public function getId(): string { return 'send-email'; } public function getName(): string { return 'Send Email'; } public function getFields(): array { return [ TextInput::make('data.email') ->helperText("Supports magic attributes") ->required(), TextInput::make('data.subject') ->helperText("Supports magic attributes") ->required(), Textarea::make('data.message') ->helperText("Supports magic attributes") ->required() ->rows(5), ]; } public function getMagicAttributeFields(): array { return ['email', 'subject', 'message']; } public function execute(array $data, WorkflowActionExecution $actionExecution, ?Model $model, array $custom_event_data, array &$sharedData) { Mail::raw($data['message'], function ($message) use ($data) { $message->to($data['email'])->subject($data['subject']); }); $actionExecution->log("Email successfully sent to: {$data['email']} regarding: {$data['subject']}"); } public function canBeUsedWithScheduledWorkflows(): bool { return true; } public function canBeUsedWithRecordEventWorkflows(): bool { return true; } public function canBeUsedWithCustomEventWorkflows(): bool { return true; } public function requireInstalledPackages(): array { return []; }}
Then add your custom action
use Filament\Facades\Filament;use Monzer\FilamentWorkflows\WorkflowsPlugin; public function panel(Panel $panel): Panel{ return $panel ->plugin(WorkflowsPlugin::make()->actions([CustomAction::class]));}
To allow actions to be aware of each other and share data, a shared data array is passed between actions in
the execute function. This enables actions to store and retrieve information dynamically as they execute.
Let's say we need to:
1️⃣ Generate an Invoice and store the invoice_id.
2️⃣ Send an Email using that invoice_id.
class GenerateInvoice extends Action{ public function execute(array $data, WorkflowActionExecution $execution, ?Model $model, array $custom_event_data, array &$sharedData) { // Generate invoice $invoiceId = Str::uuid(); $sharedData['invoice_id'] = $invoiceId; $execution->log("Generated Invoice ID: $invoiceId"); }}
class SendEmail extends Action{ public function execute(array $data, WorkflowActionExecution $execution, ?Model $model, array $custom_event_data, array &$sharedData) { $invoiceId = $sharedData['invoice_id'] ?? 'Unknown'; Mail::raw("Invoice ID: $invoiceId", function ($message) use ($data) { $message->to($data['email'])->subject("Your Invoice"); }); $execution->log("Email sent with Invoice ID: $invoiceId"); }}
Create a middleware to setup tenancy
namespace App\Http\Middleware; use Monzer\FilamentWorkflows\Models\Workflow; class ApplyTenantScopes{ /** * Handle an incoming request. * * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ public function handle(Request $request, Closure $next): Response { Workflow::resolveRelationUsing('team', function ($model) { return $model->belongsTo(Team::class, 'team_id'); }); return $next($request); }}
Then, add the middleware to the panel
use Filament\Facades\Filament;use Monzer\FilamentWorkflows\WorkflowsPlugin; public function panel(Panel $panel): Panel{ return $panel ->tenantMiddleware([ ApplyTenantScopes::class, ], isPersistent: true);}
Currently, automated tests are not available for this package. Future updates may include unit tests and integration tests to ensure workflow stability and execution accuracy.
For issues and feature requests, please visit the GitHub repository and create an issue.
Pull requests are welcome. Make sure to follow the contribution guidelines.
If you find this package helpful and would like to support its development, consider making a donation:
Your support helps improve and maintain this package! 🙌
This package is licensed under the MIT License. See the LICENSE file for details.