monzer / filament-workflows
Automation made easy!
Installs: 1 739
Dependents: 0
Suggesters: 0
Security: 0
Stars: 58
Watchers: 4
Forks: 12
Open Issues: 5
Requires
- filament/support: ^3.0
README
β¨ Introduction
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.
π Features
- π Workflow automation via model events, custom events, or scheduling.
- π οΈ Filament Resource for CRUD workflow management.
- ποΈ Supports custom workflow actions.
- π Execution logs viewable through Filament.
- π Chaining of multiple actions.
- π Webhook sending as an external integration.
- β¨ Magic Attributes enable dynamic replacement of placeholders with model attributes or event data, allowing seamless data binding and automation within the system.
Screenshots
βοΈ Installation & Setup
π₯οΈ Requirements
Ensure your Laravel application meets the following requirements:
- Laravel 10+
- FilamentPHP 3.2
- PHP 8.1+
π₯ Install the Package
composer require monzer/filament-workflows
β‘ Publish Migration
php artisan vendor:publish --provider="Monzer\FilamentWorkflows\FilamentWorkflowsServiceProvider" --tag="migrations"
β‘ Publish Configuration (Optional)
php artisan vendor:publish --provider="Monzer\FilamentWorkflows\FilamentWorkflowsServiceProvider" --tag="config"
π Migrate Database
php artisan migrate
π§ Registering the Plugin
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()); }
π Setting Up Model Event Workflows
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; } } }
NOTE:
You need to run php artisan schedule:work command to run the workflows.
π§ Configuration
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), ];
π Log Management
Automatic Log Rotation
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.
Configuration
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
Cleaning Up Existing Logs
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 limit php artisan workflows:cleanup-logs # Clean up logs with a custom limit php artisan workflows:cleanup-logs --limit=50 # Preview what would be cleaned without making changes php artisan workflows:cleanup-logs --dry-run
Database Migration for Large Logs
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
Magic attributes are placeholders that get dynamically replaced with actual data from the model or event triggering the workflow.
π Model Event Workflows
@email@
β Replaced by the model's email attribute.- Example:
Hello @email@, your order has been processed.
- If the model contains
email = user@example.com
, the message will be:Hello user@example.com, your order has been processed.
- Example:
π Custom Event Workflows
@event->name@
β Replaced by the eventβs name attribute.- Example:
A new event named @event->name@ has been created.
- If the event contains
name = System Update
, the message will be:A new event named System Update has been created.
- Example:
π― Defining Custom Workflow Actions
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])); }
π Sharing Data Between Actions
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.
π How It Works:
- Each action receives a shared data array.
- Actions can store values inside this array to be used by subsequent actions.
- The shared data persists throughout the workflow execution.
π Example: Sharing Data Between Actions
Let's say we need to:
1οΈβ£ Generate an Invoice and store the invoice_id
.
2οΈβ£ Send an Email using that invoice_id
.
π οΈ Action 1: Generate Invoice
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"); } }
π§ Action 2: Send Email with Invoice ID
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"); } }
Using workflows with tenancy
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); }
π§ͺ Tests
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.
β€οΈ Support & Contributions
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.
π° Support the Project
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! π
π License
This package is licensed under the MIT License. See the LICENSE
file for details.