litepie/actions

A comprehensive Laravel Actions package for implementing the Action pattern with validation, events, and caching

v1.0.0 2025-08-20 15:27 UTC

This package is auto-updated.

Last update: 2025-08-20 16:07:38 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

A comprehensive Laravel package for implementing the Action pattern with advanced features including authorization, validation, caching, sub-action orchestration, form management, event handling, and comprehensive logging.

โœจ Features

๐ŸŽฏ Core Functionality

  • Action Pattern Implementation - Clean, testable business logic organization
  • Multiple Action Types - BaseAction, StandardAction, CompleteAction
  • Trait-Based Architecture - Mix and match functionality as needed
  • Result Objects - Standardized success/failure handling

๐Ÿ” Authorization & Security

  • Gate-Based Authorization - Laravel gate integration
  • User Context Tracking - Full user authentication support
  • Permission Checking - Automatic authorization validation

โœ… Validation & Forms

  • Laravel Validator Integration - Built-in validation support
  • Dynamic Form Generation - Auto-generate forms from actions
  • Custom Validation Rules - Flexible validation configuration
  • Form-Action Integration - Seamless form and validation flow

๐Ÿ”„ Sub-Actions & Orchestration

  • Sub-Action Execution - Execute related actions automatically
  • Conditional Logic - Run sub-actions based on conditions
  • Before/After Hooks - Control execution timing
  • Error Handling - Continue or fail based on configuration

๐Ÿ“ง Notifications & Events

  • Automatic Notifications - Send notifications on action completion
  • Laravel Events - Fire events during action lifecycle
  • Multiple Recipients - Notify different user groups
  • Queue Integration - Async notification delivery

๐Ÿ“Š Logging & Auditing

  • Comprehensive Logging - Full action execution tracking
  • ActionLog Model - Database storage with relationships
  • Rich Context - User, IP, timestamps, properties
  • Searchable History - Query and filter action logs

โšก Performance & Scalability

  • Result Caching - Cache action results for performance
  • Async Execution - Queue-based background processing
  • Batch Processing - Execute multiple actions efficiently
  • Pipeline Support - Chain actions with middleware
  • Retry Logic - Automatic retry with exponential backoff

๐ŸŽ›๏ธ Advanced Features

  • Action Manager - Centralized action registration and execution
  • Middleware Support - Rate limiting, logging, custom middleware
  • Conditional Actions - Execute different actions based on conditions
  • Metrics Collection - Execution time and memory tracking
  • Helper Functions - Convenient global helper functions

๐Ÿ“ฆ Installation

Install the package via Composer:

composer require litepie/actions

Publish the configuration file:

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

Run the migrations to create the action logs table:

php artisan migrate

๐Ÿš€ Quick Start

Creating Your First Action

Generate a new action using the Artisan command:

php artisan make:action CreateUserAction --validation --cacheable

This creates a basic action class:

<?php

namespace App\Actions;

use App\Models\User;
use Litepie\Actions\StandardAction;

class CreateUserAction extends StandardAction
{
    protected function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'password' => 'required|string|min:8',
        ];
    }

    protected function handle(): User
    {
        return User::create([
            'name' => $this->data['name'],
            'email' => $this->data['email'],
            'password' => bcrypt($this->data['password']),
        ]);
    }
}

Basic Usage

// Execute the action
$result = CreateUserAction::execute(null, [
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'password' => 'password123'
], $currentUser);

if ($result->isSuccess()) {
    $user = $result->getData();
    echo "User created: " . $user->name;
} else {
    echo "Error: " . $result->getMessage();
}

Fluent Interface

$result = CreateUserAction::make(null, $userData, $currentUser)
    ->cached('user-creation', 1800)
    ->retries(3)
    ->timeout(60)
    ->executeWithRetry();

๐Ÿ“š Action Types

BaseAction - Foundation Class

The minimal action class with core functionality:

use Litepie\Actions\BaseAction;

class SimpleAction extends BaseAction
{
    protected function handle(): mixed
    {
        return ['message' => 'Action completed'];
    }
}

StandardAction - Common Use Case

Includes authorization, validation, and logging:

use Litepie\Actions\StandardAction;

class StandardUserAction extends StandardAction
{
    protected function rules(): array
    {
        return ['email' => 'required|email'];
    }

    protected function handle(): mixed
    {
        // Your business logic here
    }
}

CompleteAction - Full-Featured

Includes all available traits for complex workflows:

use Litepie\Actions\CompleteAction;

class ComplexOrderAction extends CompleteAction
{
    protected function rules(): array
    {
        return ['order_id' => 'required|exists:orders,id'];
    }

    protected function getSubActions(string $timing): array
    {
        return $timing === 'after' ? [
            ['action' => SendInvoiceAction::class],
            ['action' => UpdateInventoryAction::class],
        ] : [];
    }

    protected function getNotifications(): array
    {
        return [
            [
                'recipients' => User::admins()->get(),
                'class' => OrderProcessedNotification::class
            ]
        ];
    }

    protected function handle(): Order
    {
        $order = Order::find($this->data['order_id']);
        $order->update(['status' => 'processed']);
        return $order;
    }
}

๐ŸŽญ Traits System

Mix and match functionality using traits:

Available Traits

  • AuthorizesActions - Gate-based authorization
  • ExecutesSubActions - Sub-action orchestration
  • HandlesNotificationsAndEvents - Notification and event management
  • LogsActions - Comprehensive action logging
  • ManagesForms - Dynamic form generation
  • HasValidation - Laravel validation integration
  • HasCaching - Result caching capabilities
  • HasEvents - Event firing support
  • HasMetrics - Performance metrics collection

Custom Action with Specific Traits

use Litepie\Actions\BaseAction;
use Litepie\Actions\Traits\AuthorizesActions;
use Litepie\Actions\Traits\LogsActions;

class CustomAction extends BaseAction
{
    use AuthorizesActions, LogsActions;

    protected function handle(): mixed
    {
        // Your custom logic
    }
}

๐Ÿ”„ Sub-Actions

Execute related actions automatically with conditional logic:

class ProcessOrderAction extends CompleteAction
{
    protected function getSubActions(string $timing): array
    {
        if ($timing === 'before') {
            return [
                [
                    'action' => ValidateInventoryAction::class,
                    'data' => ['check_stock' => true],
                ],
            ];
        }

        if ($timing === 'after') {
            return [
                [
                    'action' => SendInvoiceAction::class,
                    'condition' => fn($data) => $data['send_invoice'] ?? false,
                ],
                [
                    'action' => UpdateInventoryAction::class,
                    'continue_on_failure' => true,
                ],
                [
                    'action' => NotifyCustomerAction::class,
                    'data' => ['template' => 'order_confirmation'],
                ],
            ];
        }

        return [];
    }
}

๐Ÿ“ Form Management

Generate dynamic forms from your actions:

class CreateUserAction extends CompleteAction
{
    protected function getFormFields(): array
    {
        return [
            [
                'name' => 'name',
                'type' => 'text',
                'label' => 'Full Name',
                'required' => true,
            ],
            [
                'name' => 'email',
                'type' => 'email',
                'label' => 'Email Address',
                'required' => true,
            ],
            [
                'name' => 'role',
                'type' => 'select',
                'label' => 'User Role',
                'options' => [
                    'user' => 'Regular User',
                    'admin' => 'Administrator',
                ],
            ],
        ];
    }

    protected function getFormValidationRules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
        ];
    }
}

// Check if action requires a form
$action = new CreateUserAction();
if ($action->requiresForm()) {
    $formConfig = $action->renderForm();
    // Use $formConfig to render your form
}

๐Ÿ“Š Action Manager

Centrally manage and execute actions:

use Litepie\Actions\Facades\ActionManager;

// Register actions
ActionManager::register('create-user', CreateUserAction::class);
ActionManager::register('process-order', ProcessOrderAction::class);

// Execute registered actions
$result = ActionManager::execute('create-user', $userData);

// Chain multiple actions
$results = ActionManager::chain([
    ['name' => 'create-user', 'data' => $userData],
    ['name' => 'send-welcome-email', 'use_previous_result' => true],
]);

// Execute actions in parallel
$results = ActionManager::parallel([
    ['name' => 'create-user', 'data' => $user1Data],
    ['name' => 'create-user', 'data' => $user2Data],
]);

// Get execution statistics
$stats = ActionManager::getStatistics();

๐Ÿ”„ Batch Processing

Execute multiple actions efficiently:

use Litepie\Actions\Batch\ActionBatch;

$batch = ActionBatch::make()
    ->add(new CreateUserAction(null, $user1Data))
    ->add(new CreateUserAction(null, $user2Data))
    ->add(new CreateUserAction(null, $user3Data))
    ->concurrent(3)
    ->stopOnFailure(false)
    ->execute();

$successful = $batch->getSuccessful();
$failed = $batch->getFailed();
$allSuccessful = $batch->isAllSuccessful();

๐Ÿ”— Pipeline Processing

Chain actions with middleware:

use Litepie\Actions\Pipeline\ActionPipeline;

$result = ActionPipeline::make()
    ->send($initialData)
    ->through([
        ValidateDataAction::class,
        ProcessDataAction::class,
        SaveDataAction::class,
    ])
    ->via([
        LoggingMiddleware::class,
        RateLimitMiddleware::class,
    ])
    ->execute();

๐ŸŽฏ Conditional Actions

Execute different actions based on conditions:

use Litepie\Actions\Conditional\ConditionalAction;

$result = ConditionalAction::make()
    ->when(fn($data) => $data['user_type'] === 'premium')
    ->then(CreatePremiumUserAction::class)
    ->otherwise(CreateRegularUserAction::class)
    ->with($userData)
    ->execute();

// Or using helper function
$result = action_when(fn($data) => $data['amount'] > 1000)
    ->then(ProcessLargeOrderAction::class)
    ->otherwise(ProcessRegularOrderAction::class)
    ->with($orderData)
    ->execute();

๐Ÿ›ก๏ธ Middleware

Add middleware for cross-cutting concerns:

use Litepie\Actions\Middleware\LoggingMiddleware;
use Litepie\Actions\Middleware\RateLimitMiddleware;

// Built-in middleware
class MyAction extends BaseAction
{
    // Middleware will be applied automatically in pipelines
}

// Custom middleware
class CustomMiddleware extends ActionMiddleware
{
    public function handle(ActionContract $action, Closure $next): mixed
    {
        // Before action execution
        $result = $next($action);
        // After action execution
        return $result;
    }
}

๐Ÿ“ˆ Caching

Cache action results for improved performance:

// Enable caching for specific executions
$result = CreateUserAction::make($model, $data)
    ->cached('user-creation-' . $data['email'], 3600)
    ->execute();

// Cache with custom key and TTL
$result = ProcessReportAction::make()
    ->with($reportData)
    ->cached('report-' . $reportId, 7200)
    ->execute();

// Clear cached results
$action->clearCache();

// Disable caching for this execution
$result = $action->fresh()->execute();

๐Ÿ” Retry Logic

Handle failures with automatic retry:

// Configure retries
$result = UnstableApiAction::make()
    ->with($apiData)
    ->retries(5)
    ->timeout(30)
    ->executeWithRetry();

// Exponential backoff is applied automatically
// Retry delays: 1s, 2s, 4s, 8s, 16s

โšก Async Execution

Execute actions in the background:

// Execute asynchronously
CreateUserAction::make($model, $data, $user)->executeAsync();

// Or use Laravel's dispatch
CreateUserAction::dispatch($model, $data, $user);

// Delayed execution
CreateUserAction::executeLater(now()->addMinutes(30), $model, $data, $user);

๐Ÿ“Š Logging & Auditing

Track all action executions:

use Litepie\Actions\Models\ActionLog;

// Query action logs
$logs = ActionLog::inLog('actions')
    ->where('action', 'CreateUser')
    ->where('created_at', '>=', now()->subDays(7))
    ->get();

// Get logs for specific model
$userLogs = ActionLog::forSubject($user)->get();

// Get logs by user
$adminLogs = ActionLog::causedBy($admin)->get();

// Get log properties
$log = ActionLog::first();
$executionTime = $log->getExtraProperty('execution_time');
$userAgent = $log->getExtraProperty('user_agent');

๐Ÿ”ง Configuration

Customize the package behavior:

// config/actions.php
return [
    'cache' => [
        'enabled' => true,
        'ttl' => 3600,
        'prefix' => 'action',
    ],
    
    'logging' => [
        'enabled' => true,
        'default_log_name' => 'actions',
        'delete_records_older_than_days' => 365,
    ],
    
    'authorization' => [
        'enabled' => true,
        'require_authenticated_user' => true,
    ],
    
    'sub_actions' => [
        'enabled' => true,
        'max_depth' => 5,
        'continue_on_failure' => false,
    ],
    
    // ... more configuration options
];

๐ŸŽจ Helper Functions

Convenient global functions:

// Execute registered action
$result = action('create-user', $userData);

// Create action batch
$batch = action_batch()
    ->add($action1)
    ->add($action2)
    ->execute();

// Create action pipeline
$result = action_pipeline()
    ->send($data)
    ->through($actions)
    ->execute();

// Conditional action execution
$result = action_when($condition)
    ->then(ActionA::class)
    ->otherwise(ActionB::class)
    ->execute();

๐Ÿงช Testing

The package includes comprehensive tests and testing utilities:

// Test your actions
class CreateUserActionTest extends TestCase
{
    /** @test */
    public function it_creates_a_user_successfully()
    {
        $userData = ['name' => 'John', 'email' => 'john@example.com'];
        $result = CreateUserAction::execute(null, $userData, $this->user);
        
        $this->assertTrue($result->isSuccess());
        $this->assertInstanceOf(User::class, $result->getData());
    }

    /** @test */
    public function it_validates_required_fields()
    {
        $this->expectException(ValidationException::class);
        CreateUserAction::execute(null, [], $this->user);
    }
}

๐Ÿ“– Advanced Examples

Complex E-commerce Order Processing

class ProcessOrderAction extends CompleteAction
{
    protected function rules(): array
    {
        return [
            'order_id' => 'required|exists:orders,id',
            'payment_method' => 'required|in:card,bank,paypal',
        ];
    }

    protected function getSubActions(string $timing): array
    {
        if ($timing === 'before') {
            return [
                [
                    'action' => ValidateInventoryAction::class,
                    'data' => ['strict_check' => true],
                ],
                [
                    'action' => CalculateShippingAction::class,
                    'condition' => fn($data) => $data['shipping_required'] ?? true,
                ],
            ];
        }

        if ($timing === 'after') {
            return [
                [
                    'action' => ProcessPaymentAction::class,
                    'data' => ['method' => $this->data['payment_method']],
                ],
                [
                    'action' => UpdateInventoryAction::class,
                    'continue_on_failure' => false,
                ],
                [
                    'action' => SendOrderConfirmationAction::class,
                    'continue_on_failure' => true,
                ],
                [
                    'action' => CreateShippingLabelAction::class,
                    'condition' => fn($data) => $data['shipping_required'] ?? true,
                    'continue_on_failure' => true,
                ],
            ];
        }

        return [];
    }

    protected function getNotifications(): array
    {
        return [
            [
                'recipients' => [$this->model->customer],
                'class' => OrderProcessedNotification::class,
            ],
            [
                'recipients' => User::role('warehouse')->get(),
                'class' => NewOrderNotification::class,
            ],
        ];
    }

    protected function getEvents(): array
    {
        return [
            OrderProcessedEvent::class,
            InventoryUpdatedEvent::class,
        ];
    }

    protected function handle(): Order
    {
        $order = Order::find($this->data['order_id']);
        
        $order->update([
            'status' => 'processing',
            'processed_at' => now(),
            'processed_by' => $this->user->id,
        ]);

        return $order;
    }

    protected function getDescription(string $status): string
    {
        $orderId = $this->data['order_id'];
        return "Order #{$orderId} processing " . ($status === 'success' ? 'completed' : 'failed');
    }
}

Batch User Import with Error Handling

$users = collect($csvData);
$batch = ActionBatch::make()->concurrent(10);

foreach ($users->chunk(100) as $userChunk) {
    foreach ($userChunk as $userData) {
        $batch->add(new CreateUserAction(null, $userData, $admin));
    }
}

$results = $batch->execute();

// Handle results
$successful = $batch->getSuccessful();
$failed = $batch->getFailed();

Log::info("Batch import completed", [
    'total' => $results->count(),
    'successful' => $successful->count(),
    'failed' => $failed->count(),
]);

๐Ÿค Contributing

We welcome contributions! Please see CONTRIBUTING.md for details.

๐Ÿ“„ License

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

๐Ÿ™ Credits

๐Ÿ“ž Support

Built with โค๏ธ by the Litepie Team