intelfric/n8n-php-automation

A visual automation engine for Laravel, similar to n8n, Zapier, or Huginn

Installs: 1

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/intelfric/n8n-php-automation

v1.0.0 2025-11-13 13:36 UTC

This package is not auto-updated.

Last update: 2025-12-26 12:24:37 UTC


README

Latest Version on Packagist Total Downloads

A powerful visual automation engine for Laravel, similar to n8n, Zapier, or Huginn. Create, connect, and execute workflows made of modular nodes (triggers, actions, conditions) directly within your Laravel projects.

Features

  • 🧩 Modular Architecture: Build workflows with reusable, composable nodes
  • Flow Execution Engine: Automatically resolves node dependencies and execution order
  • 📊 Execution Logging: Track all flow executions with detailed logs
  • 🔌 Built-in Nodes: HTTP requests, emails, delays, conditions, database queries
  • 🎨 Extensible: Easy to create and register custom nodes
  • 🚀 RESTful API: Full API for managing flows, nodes, and connections
  • 📝 Event System: Hook into flow lifecycle with Laravel events
  • ⚙️ Queue Support: Run flows asynchronously (optional)

Requirements

  • PHP 8.2 or higher
  • Laravel 10.x, 11.x, or 12.x

Installation

Install the package via Composer:

composer require intelfric/n8n-php-automation

Publish the configuration file:

php artisan vendor:publish --tag=automation-config

Publish and run the migrations:

php artisan vendor:publish --tag=automation-migrations
php artisan migrate

Quick Start

1. Create a Flow

use DrMsigwa\Automation\Models\Flow;
use DrMsigwa\Automation\Nodes\HttpRequestNode;
use DrMsigwa\Automation\Nodes\EmailNode;

// Create a new flow
$flow = Flow::create([
    'name' => 'Send Weather Alert',
    'description' => 'Fetch weather data and send email alert',
    'status' => 'active',
]);

// Add nodes to the flow
$httpNode = $flow->nodes()->create([
    'type' => HttpRequestNode::class,
    'name' => 'Fetch Weather',
    'config' => [
        'method' => 'GET',
        'url' => 'https://api.weather.com/current',
    ],
]);

$emailNode = $flow->nodes()->create([
    'type' => EmailNode::class,
    'name' => 'Send Alert',
    'config' => [
        'to' => 'user@example.com',
        'subject' => 'Weather Update',
        'body' => 'Current weather: {{http_body}}',
    ],
]);

// Connect nodes
$flow->connections()->create([
    'source_node_id' => $httpNode->id,
    'target_node_id' => $emailNode->id,
]);

2. Execute the Flow

use DrMsigwa\Automation\Engine\FlowRunner;

$runner = new FlowRunner($flow);
$result = $runner->run(['city' => 'New York']);

dd($result);

Or via Artisan command:

php artisan automation:run 1
php artisan automation:run 1 --data='{"city":"New York"}'

3. Use the API

The package provides RESTful API endpoints:

# List all flows
GET /api/automation/flows

# Get a specific flow
GET /api/automation/flows/{id}

# Create a flow
POST /api/automation/flows

# Execute a flow
POST /api/automation/flows/{id}/execute

# Get available nodes
GET /api/automation/available-nodes

Built-in Nodes

HTTP Request Node

Makes HTTP requests to external APIs.

[
    'type' => HttpRequestNode::class,
    'config' => [
        'method' => 'POST',
        'url' => 'https://api.example.com/data',
        'headers' => ['Authorization' => 'Bearer token'],
        'body' => ['key' => 'value'],
        'timeout' => 30,
    ],
]

Email Node

Sends emails using Laravel's Mail system.

[
    'type' => EmailNode::class,
    'config' => [
        'to' => 'recipient@example.com',
        'subject' => 'Hello World',
        'body' => 'Email content here',
        'from' => 'sender@example.com', // optional
    ],
]

Delay Node

Adds delays between tasks.

[
    'type' => DelayNode::class,
    'config' => [
        'seconds' => 5,
    ],
]

Condition Node

Evaluates conditional logic.

[
    'type' => ConditionNode::class,
    'config' => [
        'left' => '{{http_status}}',
        'operator' => '==',
        'right' => 200,
    ],
]

Supported operators: ==, ===, !=, !==, >, >=, <, <=, contains, starts_with, ends_with, in

Database Query Node

Executes database queries using Laravel's Query Builder.

[
    'type' => DatabaseQueryNode::class,
    'config' => [
        'table' => 'users',
        'operation' => 'select',
        'conditions' => [
            ['column' => 'status', 'operator' => '=', 'value' => 'active'],
        ],
        'fields' => ['id', 'name', 'email'],
        'limit' => 10,
    ],
]

Creating Custom Nodes

Create a new node by implementing the NodeInterface:

<?php

namespace App\Automation\Nodes;

use DrMsigwa\Automation\Contracts\NodeInterface;

class SlackNotificationNode implements NodeInterface
{
    public function execute(array $data): array
    {
        $config = $data['config'] ?? [];
        $message = $config['message'] ?? '';
        
        // Your implementation here
        // Send message to Slack
        
        return [
            'slack_message_sent' => true,
            'slack_timestamp' => now()->toISOString(),
        ];
    }

    public function getDescription(): array
    {
        return [
            'name' => 'Slack Notification',
            'description' => 'Sends notifications to Slack',
            'icon' => 'message-square',
            'category' => 'notification',
            'inputs' => [
                ['name' => 'message', 'type' => 'string', 'required' => true],
                ['name' => 'channel', 'type' => 'string', 'default' => '#general'],
            ],
            'outputs' => [
                ['name' => 'slack_message_sent', 'type' => 'boolean'],
                ['name' => 'slack_timestamp', 'type' => 'string'],
            ],
        ];
    }
}

Register your custom node in config/automation.php:

'nodes' => [
    // Built-in nodes
    \DrMsigwa\Automation\Nodes\HttpRequestNode::class,
    \DrMsigwa\Automation\Nodes\EmailNode::class,
    // ... others
    
    // Your custom nodes
    \App\Automation\Nodes\SlackNotificationNode::class,
],

Or register dynamically using the facade:

use DrMsigwa\Automation\Facades\Automation;

Automation::register(\App\Automation\Nodes\SlackNotificationNode::class);

Variable Substitution

Use double curly braces {{variable}} to reference data from previous nodes:

$emailNode = $flow->nodes()->create([
    'type' => EmailNode::class,
    'config' => [
        'to' => '{{user_email}}',
        'subject' => 'Order #{{order_id}} Confirmed',
        'body' => 'Thank you {{user_name}}! Your order status: {{http_status}}',
    ],
]);

Events

Listen to flow lifecycle events:

// In your EventServiceProvider
protected $listen = [
    \DrMsigwa\Automation\Events\FlowStarted::class => [
        \App\Listeners\LogFlowStart::class,
    ],
    \DrMsigwa\Automation\Events\NodeExecuted::class => [
        \App\Listeners\LogNodeExecution::class,
    ],
    \DrMsigwa\Automation\Events\FlowCompleted::class => [
        \App\Listeners\NotifyFlowCompletion::class,
    ],
];

Queue Support

Enable async flow execution in config/automation.php:

'queue' => [
    'enabled' => true,
    'connection' => 'redis',
    'queue' => 'automation',
],

Flow Execution Model

How Flows Execute

Important: Flows do NOT run endlessly or continuously by default. Each flow execution is:

  • One-time: Runs once from start to finish
  • Linear: Executes nodes sequentially in dependency order
  • Finite: Completes after all nodes are executed
  • Manual trigger required: Must be explicitly started each time

Triggering Flow Execution

Flows can be triggered in three ways:

1. Via Artisan Command

php artisan automation:run {flow_id}
php artisan automation:run 1 --data='{"user_id":123}'

2. Via API

POST /api/automation/flows/{id}/execute

3. Programmatically

use DrMsigwa\Automation\Engine\FlowRunner;
use DrMsigwa\Automation\Models\Flow;

$flow = Flow::find(1);
$runner = new FlowRunner($flow);
$result = $runner->run(['key' => 'value']);

Setting Up Continuous/Scheduled Execution

To run flows automatically on a schedule, use Laravel's Task Scheduler:

Option 1: Schedule Specific Flow

In app/Console/Kernel.php:

use DrMsigwa\Automation\Models\Flow;
use DrMsigwa\Automation\Engine\FlowRunner;

protected function schedule(Schedule $schedule)
{
    // Run a specific flow every minute
    $schedule->call(function () {
        $flow = Flow::find(1);
        (new FlowRunner($flow))->run();
    })->everyMinute();
    
    // Run daily at midnight
    $schedule->call(function () {
        $flow = Flow::find(2);
        (new FlowRunner($flow))->run();
    })->daily();
    
    // Run every hour
    $schedule->call(function () {
        $flow = Flow::find(3);
        (new FlowRunner($flow))->run();
    })->hourly();
}

Option 2: Run All Active Flows on Schedule

protected function schedule(Schedule $schedule)
{
    $schedule->call(function () {
        $activeFlows = Flow::where('status', 'active')->get();
        
        foreach ($activeFlows as $flow) {
            try {
                (new FlowRunner($flow))->run();
            } catch (\Exception $e) {
                \Log::error("Flow {$flow->id} failed: " . $e->getMessage());
            }
        }
    })->everyFiveMinutes();
}

Option 3: Using Queued Jobs for Better Performance

Create a job:

<?php

namespace App\Jobs;

use DrMsigwa\Automation\Models\Flow;
use DrMsigwa\Automation\Engine\FlowRunner;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;

class ExecuteFlowJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable;
    
    public function __construct(public int $flowId, public array $data = [])
    {
    }
    
    public function handle()
    {
        $flow = Flow::find($this->flowId);
        if ($flow) {
            (new FlowRunner($flow))->run($this->data);
        }
    }
}

Then schedule it:

protected function schedule(Schedule $schedule)
{
    $schedule->job(new ExecuteFlowJob(1))->everyMinute();
}

Don't forget to run the Laravel scheduler:

# Add this to your crontab (cron on Linux/Mac)
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

Webhook Triggers

For event-driven flows, use webhooks to trigger executions:

// In your routes/api.php or controller
Route::post('/webhook/trigger-flow/{flowId}', function ($flowId) {
    $flow = Flow::find($flowId);
    
    if (!$flow) {
        return response()->json(['error' => 'Flow not found'], 404);
    }
    
    $data = request()->all();
    $runner = new FlowRunner($flow);
    $result = $runner->run($data);
    
    return response()->json(['success' => true, 'result' => $result]);
});

Loop Node Behavior

The LoopNode iterates through collections but has built-in safety limits:

[
    'type' => LoopNode::class,
    'config' => [
        'items' => [1, 2, 3, 4, 5],
        'max_iterations' => 1000, // Safety limit (default: 1000)
    ],
]
  • Loops through finite collections only
  • Maximum 1000 iterations by default
  • Does NOT create infinite loops

Configuration

The package configuration file (config/automation.php) allows you to:

  • Register custom nodes
  • Configure queue settings
  • Set execution timeouts
  • Enable/disable API routes
  • Customize route prefixes and middleware

Testing

Run the test suite:

composer test

Architecture

Core Components

Component Description
Flow Represents a complete workflow
Node Individual building block (trigger, condition, or action)
Connection Defines data flow between nodes
FlowRunner Orchestrates node execution and data passing
NodeRegistry Handles node discovery and instantiation
ExecutionLogger Logs all flow executions for tracking

Database Schema

-- flows table
id | name | description | status | created_at | updated_at

-- nodes table
id | flow_id | type | name | config (json) | position_x | position_y

-- connections table
id | flow_id | source_node_id | source_output | target_node_id | target_input

-- executions table
id | flow_id | status | started_at | finished_at | logs (json)

Advanced Usage

Programmatic Flow Execution

use DrMsigwa\Automation\Models\Flow;
use DrMsigwa\Automation\Engine\FlowRunner;

$flow = Flow::find(1);
$runner = new FlowRunner($flow);

try {
    $result = $runner->run([
        'user_id' => 123,
        'action' => 'purchase',
    ]);
    
    $execution = $runner->getExecution();
    $logger = $runner->getLogger();
    
    echo "Execution ID: {$execution->id}\n";
    echo "Status: {$execution->status}\n";
    
} catch (\Exception $e) {
    echo "Flow failed: " . $e->getMessage();
}

Accessing Execution Logs

use DrMsigwa\Automation\Models\Execution;

$execution = Execution::find(1);

foreach ($execution->logs as $log) {
    echo "[{$log['level']}] {$log['message']}\n";
}

Conditional Flow Execution

// Only execute flows with specific status
$activeFlows = Flow::where('status', 'active')->get();

foreach ($activeFlows as $flow) {
    $runner = new FlowRunner($flow);
    $runner->run();
}

Roadmap

  • Visual flow builder UI (Vue/React + Inertia.js or Livewire)
  • Trigger nodes (webhooks, scheduled tasks, database events)
  • Loop and iteration nodes
  • Error handling and retry mechanisms
  • Flow versioning
  • Import/export flows as JSON
  • Metrics and analytics dashboard

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Security

If you discover any security-related issues, please email constantinomsigwa@intelfric.com instead of using the issue tracker.

License

The MIT License (MIT). Please see License File for more information.

Credits

Support

Made with ❤️ for the Laravel community