darwinnatha/laravel-process

A Laravel micro-package based on the pipeline-task pattern for sequential business processes.

v2.0.0-beta 2025-06-22 20:34 UTC

This package is auto-updated.

Last update: 2025-06-22 20:59:43 UTC


README

A completely flexible and customizable process orchestration package for Laravel.
Structure your business logic as processes composed of tasks with total control over the base implementation.

โœจ Key Philosophy

Ultimate Flexibility - Publish and customize everything:

  • ๐Ÿ“ฅ Universal Input - Accept any type: Arrays, Objects, Collections, Requests, DTOs, etc.
  • ๐Ÿ“ค Universal Output - Return any type: Boolean, Array, DTO, Resource, Collection, null, etc.
  • ๐ŸŽ›๏ธ Customizable Base Class - Publish AbstractProcess and modify it as needed
  • ๐Ÿš€ Zero Constraints - No forced patterns, complete developer freedom

โœจ Features

  • Publishable Base Class - Customize AbstractProcess to your needs
  • Universal Input/Output - mixed types for maximum flexibility
  • Pipeline-Based - Leverages Laravel's Pipeline for task chaining
  • Automatic Transactions - Built-in DB transaction handling with rollback
  • Optional API Formatting - Use FormatsApiResponse trait when needed
  • Artisan Commands - Generate processes and tasks easily

๐Ÿ“ฆ Installation

composer require darwinnatha/laravel-process

๐Ÿš€ Quick Start

1. Publish the AbstractProcess (Recommended)

php artisan process:publish

This creates app/Processes/AbstractProcess.php that you can customize:

  • Add logging, monitoring, caching
  • Modify transaction handling
  • Add middleware or custom validation
  • Implement your error handling strategy

2. Create a Process

php artisan make:process CreateUserProcess --group=User

3. Create Tasks

php artisan make:task ValidateUserData --group=User
php artisan make:task SaveUserToDatabase --group=User

๐Ÿง  How It Works

After publishing, your AbstractProcess becomes completely yours to customize. All processes extend from your base class.

Example: Customized AbstractProcess

// app/Processes/AbstractProcess.php
abstract class AbstractProcess
{
    public array $tasks = [];

    public function handle(mixed $input): mixed
    {
        // Your custom logging
        Log::info('Process started', ['process' => static::class]);

        // Your custom validation
        $this->validateInput($input);

        // Your custom transaction handling
        DB::beginTransaction();
        try {
            $result = Pipeline::send($input)
                ->through($this->getMiddleware())  // Your middleware
                ->through($this->tasks)
                ->thenReturn();
                
            DB::commit();
            
            // Your custom success handling
            $this->onSuccess($result);
            return $result;
            
        } catch (Throwable $e) {
            DB::rollBack();
            
            // Your custom error handling
            $this->onError($e);
            throw $e;
        }
    }

    // Your custom methods
    protected function validateInput(mixed $input): void { /* ... */ }
    protected function getMiddleware(): array { return []; }
    protected function onSuccess(mixed $result): void { /* ... */ }
    protected function onError(Throwable $e): void { /* ... */ }
}

Example: Your Process

namespace App\Processes\User;

use App\Processes\AbstractProcess;  // Your custom base class

class CreateUserProcess extends AbstractProcess
{
    public array $tasks = [
        Tasks\ValidateUserData::class,
        Tasks\SaveUserToDatabase::class,
        Tasks\SendWelcomeEmail::class,
    ];
}

Example: Flexible Task

final class ValidateUserData implements TaskInterface
{
    public function __invoke(mixed $input, callable $next): mixed
    {
        // Handle any input type
        $data = match(true) {
            $input instanceof Request => $input->validated(),
            is_array($input) => $input,
            is_object($input) => (array) $input,
            default => throw new InvalidArgumentException('Unsupported input')
        };

        // Your validation logic
        if (!$this->isValid($data)) {
            throw new ValidationException('Invalid data');
        }

        return $next($input);
    }
}

๐Ÿš€ Usage Examples

With Different Input Types

$process = new CreateUserProcess();

// With an array
$result = $process->handle(['name' => 'John', 'email' => 'john@test.com']);

// With a Request
$result = $process->handle($request);

// With a DTO  
$result = $process->handle($userDto);

// With any custom object
$result = $process->handle($customObject);

๐ŸŽ›๏ธ Optional API Response Formatting

If you want standardized API responses, use the optional FormatsApiResponse trait:

use DarwinNatha\Process\Traits\FormatsApiResponse;

final class CreateUserTask implements TaskInterface
{
    use FormatsApiResponse; // Optional!

    public function __invoke(mixed $input, callable $next): mixed
    {
        try {
            $user = User::create($this->extractData($input));
            return $this->created($user, 'User created successfully');
        } catch (Exception $e) {
            return $this->error('User creation failed', ['error' => $e->getMessage()]);
        }
    }
}

This is completely optional - by default, return whatever you want!

โš™๏ธ Commands

Publish AbstractProcess

php artisan process:publish          # Publish for customization
php artisan process:publish --force  # Overwrite existing

Generate Classes

php artisan make:process LoginProcess --group=Auth
php artisan make:task ValidateCredentials --group=Auth

๐ŸŽฏ Advanced Customization Examples

Add Caching to Your AbstractProcess

public function handle(mixed $input): mixed
{
    $cacheKey = $this->getCacheKey($input);
    
    if ($cacheKey && Cache::has($cacheKey)) {
        return Cache::get($cacheKey);
    }
    
    $result = $this->executeProcess($input);
    
    if ($cacheKey) {
        Cache::put($cacheKey, $result, $this->getCacheTtl());
    }
    
    return $result;
}

๐Ÿงช Testing

Run tests:

vendor/bin/pest

The package includes comprehensive tests covering:

  • AbstractProcess publication and customization
  • Flexible input/output handling
  • Different data types (arrays, objects, DTOs, Requests)
  • Transaction rollback on failures
  • Console command generation

๐Ÿง‘โ€๐Ÿ’ป Author

Darwin Piegue (DarwinNatha) ๐Ÿ”— github.com/darwinnatha

โš–๏ธ License

MIT License โ€” free to use, modify, and distribute.