monzer/filament-workflows

Automation made easy!

0.2.4 2025-09-30 12:26 UTC

This package is auto-updated.

Last update: 2025-09-30 12:29:06 UTC


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

Create workflow Action 1 Action 2

βš™οΈ 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.
      

🎭 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.
      

🎯 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:

β˜• Buy Me a Coffee

Your support helps improve and maintain this package! πŸ™Œ

πŸ“œ License

This package is licensed under the MIT License. See the LICENSE file for details.