darwinnatha / laravel-process
A Laravel micro-package based on the pipeline-task pattern for sequential business processes.
Requires
- php: ^8.0
- illuminate/console: ^8.0|^9.0|^10.0|^11.0|^12.0
- illuminate/contracts: *
- illuminate/http: ^8.0|^9.0|^10.0|^11.0|^12.0
- illuminate/support: ^8.0|^9.0|^10.0|^11.0|^12.0
Requires (Dev)
- mockery/mockery: ^1.6
- orchestra/testbench: *
- pestphp/pest: *
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.