diff --git a/README.md b/README.md index b5f38ba..c52fa19 100644 --- a/README.md +++ b/README.md @@ -1,512 +1,402 @@ # Laravel Statecraft -
- Laravel Statecraft -

Elegant and testable state machines for Laravel applications — Define entity workflows declaratively (YAML), and control transitions with guards, actions, and events.

- - [![Latest Version](https://img.shields.io/packagist/v/grazulex/laravel-statecraft)](https://packagist.org/packages/grazulex/laravel-statecraft) - [![Total Downloads](https://img.shields.io/packagist/dt/grazulex/laravel-statecraft)](https://packagist.org/packages/grazulex/laravel-statecraft) - [![License](https://img.shields.io/github/license/grazulex/laravel-statecraft)](LICENSE.md) - [![PHP Version](https://img.shields.io/badge/php-%5E8.3-blue)](https://php.net) - [![Laravel Version](https://img.shields.io/badge/laravel-%5E12.19-red)](https://laravel.com) - [![Tests](https://github.com/Grazulex/laravel-statecraft/workflows/Tests/badge.svg)](https://github.com/Grazulex/laravel-statecraft/actions) - [![Code Style](https://img.shields.io/badge/code%20style-pint-orange)](https://github.com/laravel/pint) -
- -## Overview - -
- -**Laravel Statecraft** est la solution élégante pour gérer les **machines d'état** dans vos applications Laravel. Définissez vos workflows de manière **déclarative** avec YAML, contrôlez les transitions avec des **guards**, des **actions**, et des **événements**. +Laravel Statecraft -✨ **Parfait pour** : workflows de commandes, validation de contenu, gestion d'utilisateurs, processus d'approbation -🚀 **Simplicité** : Configuration YAML intuitive + méthodes auto-générées -🔧 **Flexibilité** : Guards complexes, actions personnalisées, historique des transitions +Advanced State Machine implementation for Laravel applications. Declarative state management with support for conditions, actions, and complex workflows through YAML configuration. -
+[![Latest Version](https://img.shields.io/packagist/v/grazulex/laravel-statecraft.svg?style=flat-square)](https://packagist.org/packages/grazulex/laravel-statecraft) +[![Total Downloads](https://img.shields.io/packagist/dt/grazulex/laravel-statecraft.svg?style=flat-square)](https://packagist.org/packages/grazulex/laravel-statecraft) +[![License](https://img.shields.io/github/license/grazulex/laravel-statecraft.svg?style=flat-square)](https://github.com/Grazulex/laravel-statecraft/blob/main/LICENSE.md) +[![PHP Version](https://img.shields.io/packagist/php-v/grazulex/laravel-statecraft.svg?style=flat-square)](https://php.net/) +[![Laravel Version](https://img.shields.io/badge/laravel-12.x-ff2d20?style=flat-square&logo=laravel)](https://laravel.com/) +[![Tests](https://img.shields.io/github/actions/workflow/status/grazulex/laravel-statecraft/tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/Grazulex/laravel-statecraft/actions) +[![Code Style](https://img.shields.io/badge/code%20style-pint-000000?style=flat-square&logo=laravel)](https://github.com/laravel/pint) -## Table of Contents +## 📖 Table of Contents -- [🚀 Features](#-features) +- [Overview](#overview) +- [✨ Features](#-features) - [📦 Installation](#-installation) -- [✨ Example: Order Workflow](#-example-order-workflow) -- [🧩 Guard Expressions](#-guard-expressions) -- [⚙️ Custom Guard](#-custom-guard) -- [🔍 Custom Action](#-custom-action) -- [📜 Transition History](#-transition-history-optional) -- [✅ Artisan Commands](#-artisan-commands) -- [🧪 Testing](#-testing) -- [🔔 Events](#-events) +- [🚀 Quick Start](#-quick-start) +- [🔄 State Transitions](#-state-transitions) +- [🎯 Guards & Actions](#-guards--actions) +- [📋 YAML Configuration](#-yaml-configuration) +- [⚙️ Configuration](#️-configuration) - [📚 Documentation](#-documentation) -- [🎯 Next Steps](#-next-steps) +- [💡 Examples](#-examples) +- [🧪 Testing](#-testing) +- [🔧 Requirements](#-requirements) +- [🚀 Performance](#-performance) - [🤝 Contributing](#-contributing) - [🔒 Security](#-security) - [📄 License](#-license) -## 🚀 Features - -- 🔁 **Declarative state machines** for Eloquent models -- 🛡️ **Guard conditions** with AND/OR/NOT logic expressions -- ⚙️ **Lifecycle actions** on transitions -- 📦 **Auto-generated methods** like `canPublish()` and `publish()` -- 🧪 **Built-in test support** for transitions -- 🔔 **Laravel event support** (`Transitioning`, `Transitioned`) -- 🧾 **Optional transition history tracking** -- ⚙️ **Comprehensive Artisan commands** for YAML definitions and PHP classes -- 🔧 **Configurable** paths, events, and history tracking -- 🎯 **Dynamic resolution** of guards and actions via Laravel container -- 🧩 **Complex guard expressions** with nested conditional logic -- 📊 **Export capabilities** (JSON, Mermaid, Markdown) -- ✅ **Validation system** for YAML definitions -- 📝 **Comprehensive documentation** and examples - -## 📦 Installation - -Install via Composer: - -```bash -composer require grazulex/laravel-statecraft -``` - -### Configuration (Optional) +## Overview -
+Laravel Statecraft is a powerful state machine implementation for Laravel that provides declarative state management through YAML configuration. Build complex workflows with conditional transitions, guards, actions, and comprehensive state tracking. -Publish the configuration file and migrations: +**Perfect for order processing, user workflows, approval systems, and any application requiring sophisticated state management.** -```bash -# Publish configuration -php artisan vendor:publish --tag=statecraft-config +### 🎯 Use Cases -# Publish migrations (if using history tracking) -php artisan vendor:publish --tag=statecraft-migrations -php artisan migrate -``` +Laravel Statecraft is perfect for: -The configuration file will be published to `config/statecraft.php` where you can customize: -- **State machine definitions path** -- **Default state field name** -- **Event system settings** -- **History tracking options** +- **Order Processing** - Complex e-commerce order workflows +- **User Registration** - Multi-step user onboarding flows +- **Approval Systems** - Document or request approval workflows +- **Content Management** - Publishing and moderation workflows +- **Business Processes** - Any multi-state business logic -
+## ✨ Features -## ✨ Example: Order Workflow +- 🚀 **Declarative Configuration** - Define state machines in YAML files +- 🔄 **Flexible Transitions** - Conditional transitions with guards and actions +- 🎯 **Event System** - Built-in events for state changes and transitions +- 📊 **State History** - Track all state changes with timestamps +- 🛡️ **Guards & Actions** - Pre/post transition validation and processing +- 🔗 **Model Integration** - Seamless Eloquent model integration +- 📋 **YAML Support** - Human-readable state machine definitions +- 🎨 **Artisan Commands** - CLI tools for state machine management +- ✅ **Validation** - Comprehensive state machine validation +- 📈 **Visualization** - Export state machines to Mermaid diagrams +- 🧪 **Test-Friendly** - Built-in testing utilities +- ⚡ **Performance** - Optimized for speed with caching support -**YAML Definition** +## 📦 Installation -
+Install the package via Composer: -```yaml -state_machine: - name: OrderWorkflow - model: App\Models\Order - states: [draft, pending, approved, rejected] - initial: draft - transitions: - - from: draft - to: pending - guard: canSubmit - action: notifyReviewer - - from: pending - to: approved - guard: isManager - - from: pending - to: rejected - action: refundCustomer +```bash +composer require grazulex/laravel-statecraft ``` -
- -## 🧩 Guard Expressions +> **💡 Auto-Discovery** +> The service provider will be automatically registered thanks to Laravel's package auto-discovery. -### AND Logic - All conditions must be true -
+Publish configuration: -```yaml -- from: pending - to: approved - guard: - and: - - IsManager - - HasMinimumAmount +```bash +php artisan vendor:publish --tag=statecraft-config ``` -
- -### OR Logic - At least one condition must be true -
+Publish migrations (if using history tracking): -```yaml -- from: pending - to: approved - guard: - or: - - IsManager - - IsVIP +```bash +php artisan vendor:publish --tag=statecraft-migrations +php artisan migrate ``` -
+## 🚀 Quick Start -### NOT Logic - Condition must be false -
+### 1. Create a State Machine Definition -```yaml -- from: pending - to: approved - guard: - not: IsBlacklisted +```bash +php artisan statecraft:make OrderStateMachine --model=Order ``` -
- -### Nested Expressions - Complex combinations -
+### 2. Define Your State Machine in YAML ```yaml -- from: pending - to: approved - guard: - and: - - IsManager - - or: - - IsVIP - - IsUrgent +# state-machines/OrderStateMachine.yaml +name: OrderStateMachine +model: App\Models\Order +initial_state: pending + +states: + - name: pending + description: Order is pending payment + - name: paid + description: Order has been paid + - name: processing + description: Order is being processed + - name: shipped + description: Order has been shipped + - name: delivered + description: Order has been delivered + - name: cancelled + description: Order was cancelled + +transitions: + - name: pay + from: pending + to: paid + guard: PaymentGuard + action: ProcessPayment + + - name: process + from: paid + to: processing + action: StartProcessing + + - name: ship + from: processing + to: shipped + guard: InventoryGuard + action: CreateShipment ``` -
- -**Key Features:** -- 🔄 **Backward Compatible** - Simple string guards still work -- 🎯 **Dynamic Evaluation** - Guards resolved at runtime -- 🧩 **Nested Logic** - Complex business rules supported -- 📊 **Event Integration** - Expressions serialized in events and history -- ⚡ **Boolean Logic** - AND/OR/NOT operations with short-circuit evaluation - -### Basic Model Setup - -
- -Add the trait to your model: +### 3. Add the Trait to Your Model ```php -use Grazulex\LaravelStatecraft\Traits\HasStateMachine; +use Grazulex\LaravelStatecraft\HasStateMachine; class Order extends Model { use HasStateMachine; - protected function getStateMachineDefinitionName(): string - { - return 'order-workflow'; // YAML file name - } + protected $stateMachine = 'OrderStateMachine'; } ``` -
+### 4. Use State Transitions -### Using the State Machine +```php +// Create a new order (starts in 'pending' state) +$order = Order::create(['total' => 100.00]); -
+// Check current state +echo $order->currentState(); // 'pending' -```php -$order = Order::find(1); +// Transition to next state +$order->transition('pay'); // Moves to 'paid' state -// Check if transitions are allowed -if ($order->canApprove()) { - $order->approve(); // Executes guard + action + state change -} +// Check available transitions +$availableTransitions = $order->availableTransitions(); -// Get current state and available transitions -$currentState = $order->getCurrentState(); -$availableTransitions = $order->getAvailableTransitions(); +// Get state history +$history = $order->stateHistory(); ``` -
- -### With History Tracking +## 🔄 State Transitions -
+Laravel Statecraft provides flexible transition management: ```php -use Grazulex\LaravelStatecraft\Traits\HasStateHistory; +// Basic transition +$order->transition('pay'); -class Order extends Model -{ - use HasStateMachine, HasStateHistory; - - // ... rest of your model +// Transition with context data +$order->transition('ship', ['tracking_number' => 'ABC123']); + +// Check if transition is possible +if ($order->canTransition('process')) { + $order->transition('process'); } -// Access transition history -$history = $order->stateHistory(); -$lastTransition = $order->latestStateTransition(); +// Bulk state operations +$orders = Order::inState('pending')->get(); +foreach ($orders as $order) { + if ($order->canTransition('pay')) { + $order->transition('pay'); + } +} ``` -
- ---- +## 🎯 Guards & Actions -## ⚙️ Custom Guard - -
+### Guards (Pre-transition Validation) ```php -class IsManager implements \Grazulex\LaravelStatecraft\Contracts\Guard +use Grazulex\LaravelStatecraft\Contracts\Guard; + +class PaymentGuard implements Guard { - public function check(Model $model, string $from, string $to): bool + public function passes($model, string $transition, array $context = []): bool { - return auth()->user()?->is_manager; + // Check if payment is valid + return $model->payment_status === 'completed'; + } + + public function message(): string + { + return 'Payment must be completed before processing order.'; } } ``` -
- ---- - -## 🔍 Custom Action - -
+### Actions (Post-transition Processing) ```php -class NotifyReviewer implements \Grazulex\LaravelStatecraft\Contracts\Action +use Grazulex\LaravelStatecraft\Contracts\Action; + +class ProcessPayment implements Action { - public function execute(Model $model, string $from, string $to): void + public function execute($model, string $transition, array $context = []): void { - Notification::route('mail', 'review@team.com') - ->notify(new OrderPendingNotification($model)); + // Process payment logic + $model->update([ + 'payment_processed_at' => now(), + 'payment_id' => $context['payment_id'] ?? null, + ]); + + // Send confirmation email + Mail::to($model->user)->send(new PaymentConfirmed($model)); } } ``` -
- ---- +## 📋 YAML Configuration -## 📜 Transition History (optional) +Advanced state machine configuration: -
- -```php -$order->stateHistory(); // → returns a collection of past transitions -``` - -
- ---- - -## ✅ Artisan Commands - -### Generate YAML Definition - -
- -```bash -php artisan statecraft:make order-workflow -php artisan statecraft:make article-status --states=draft,review,published --initial=draft +```yaml +# state-machines/AdvancedOrderStateMachine.yaml +name: AdvancedOrderStateMachine +model: App\Models\Order +initial_state: draft + +states: + - name: draft + description: Order being prepared + - name: pending_payment + description: Waiting for payment + +transitions: + - name: submit_order + from: draft + to: pending_payment + guard: OrderValidationGuard + action: NotifyCustomer + + - name: process_payment + from: pending_payment + to: [paid, failed] # Conditional transitions + conditions: + - condition: "payment.status == 'success'" + to: paid + action: ProcessSuccessfulPayment ``` -
- -### Generate PHP Classes from YAML +## ⚙️ Configuration -
+Laravel Statecraft works out of the box, but you can customize it: -```bash -php artisan statecraft:generate database/state_machines/order-workflow.yaml +```php +// config/statecraft.php +return [ + 'state_machines_path' => base_path('state-machines'), + 'cache_enabled' => true, + 'history_enabled' => true, +]; ``` -This generates: -- **Guard classes** in `app/StateMachines/Guards/` -- **Action classes** in `app/StateMachines/Actions/` -- **Model examples** in `app/StateMachines/` +## 📚 Documentation -
+For detailed documentation, examples, and advanced usage: -### List and Inspect Definitions +- 📚 [Full Documentation](docs/README.md) +- 🎯 [Examples](examples/README.md) +- 🔧 [Configuration](docs/configuration.md) +- 🧪 [Testing](docs/testing.md) +- 🎨 [Guards & Actions](docs/guards-actions.md) -
- -```bash -# List all YAML definitions -php artisan statecraft:list +## 💡 Examples -# Show definition details -php artisan statecraft:show order-workflow +### Order Processing State Machine -# Validate definitions -php artisan statecraft:validate --all -``` - -
- -### Export to Different Formats - -
- -```bash -# Export to JSON, Mermaid, or Markdown -php artisan statecraft:export order-workflow json -php artisan statecraft:export order-workflow mermaid -php artisan statecraft:export order-workflow md --output=docs/workflow.md -``` - -
+```php +// Check order state and available actions +$order = Order::find(1); -### Command Options +if ($order->inState('pending')) { + // Show payment form + return view('orders.payment', compact('order')); +} -**statecraft:make** supports additional options: +if ($order->inState('paid') && $order->canTransition('process')) { + // Start processing + $order->transition('process'); +} -```bash -php artisan statecraft:make order-workflow --model=App\\Models\\Order --states=draft,pending,approved --initial=draft +// Get transition history +$history = $order->stateHistory(); +foreach ($history as $entry) { + echo "{$entry->from_state} → {$entry->to_state} at {$entry->created_at}"; +} ``` -**statecraft:generate** uses configurable output paths: -- Configure output directory via `statecraft.generated_code_path` -- Defaults to `app/StateMachines/` if not configured - ---- - -## 🧪 Testing - -## 🧪 Testing - -Use the built-in test utilities: - -
+### User Registration Flow ```php -use Grazulex\LaravelStatecraft\Testing\StateMachineTester; - -// Test transitions -StateMachineTester::assertTransitionAllowed($order, 'approved'); -StateMachineTester::assertTransitionBlocked($order, 'rejected'); - -// Test states -StateMachineTester::assertInState($order, 'pending'); -StateMachineTester::assertHasAvailableTransitions($order, ['approved', 'rejected']); +class UserRegistration extends Model +{ + use HasStateMachine; + + protected $stateMachine = 'UserRegistrationStateMachine'; +} -// Test methods -StateMachineTester::assertCanExecuteMethod($order, 'approve'); -StateMachineTester::assertCannotExecuteMethod($order, 'reject'); +// Registration workflow +$registration = UserRegistration::create(['email' => 'user@example.com']); +$registration->transition('send_verification'); // pending → email_sent +$registration->transition('verify_email'); // email_sent → verified +$registration->transition('complete'); // verified → completed ``` -
- -### Testing Guard Expressions +Check out the [examples directory](examples) for more examples. -
+## 🧪 Testing -Test **complex guard expressions** by setting up your models and authentication: +Laravel Statecraft includes comprehensive testing utilities: ```php -// Test AND logic with actual conditions -$manager = User::factory()->create(['is_manager' => true]); -$order = Order::factory()->create(['amount' => 1000]); -$this->actingAs($manager); - -// Both conditions true: IsManager AND HasMinimumAmount -StateMachineTester::assertTransitionAllowed($order, 'approved'); - -// Make one condition false -$nonManager = User::factory()->create(['is_manager' => false]); -$this->actingAs($nonManager); -StateMachineTester::assertTransitionBlocked($order, 'approved'); - -// Test OR logic with different conditions -$vipOrder = Order::factory()->create(['is_vip' => true]); -StateMachineTester::assertTransitionAllowed($vipOrder, 'approved'); - -// Test NOT logic -$blacklistedOrder = Order::factory()->create(['customer_blacklisted' => true]); -StateMachineTester::assertTransitionBlocked($blacklistedOrder, 'approved'); -``` - -
- -## 🔔 Events - -Laravel Statecraft dispatches **events during transitions**: - -
+use Grazulex\LaravelStatecraft\Testing\StateMachineTester; -```php -use Grazulex\LaravelStatecraft\Events\StateTransitioning; -use Grazulex\LaravelStatecraft\Events\StateTransitioned; - -// Listen to state changes -Event::listen(StateTransitioning::class, function ($event) { - // Before transition - $event->model; // The model - $event->from; // From state - $event->to; // To state - $event->guard; // Guard class (if any) - $event->action; // Action class (if any) -}); - -Event::listen(StateTransitioned::class, function ($event) { - // After transition - Log::info("Order {$event->model->id} transitioned from {$event->from} to {$event->to}"); -}); +public function test_order_payment_flow() +{ + $order = Order::factory()->create(); + + // Test state machine flow + StateMachineTester::make($order) + ->assertCurrentState('pending') + ->assertCanTransition('pay') + ->assertCannotTransition('ship') + ->transition('pay') + ->assertCurrentState('paid') + ->assertTransitionCount(1); +} ``` -
+## 🔧 Requirements ---- +- PHP: ^8.3 +- Laravel: ^12.0 +- Carbon: ^3.10 -## 📚 Documentation +## 🚀 Performance -For **comprehensive documentation**, examples, and **advanced usage**: +Laravel Statecraft is optimized for performance: -
+- **State Caching**: State machines are cached for better performance +- **Lazy Loading**: Guards and actions are loaded only when needed +- **Efficient Queries**: Optimized database queries for state operations +- **Memory Efficient**: Minimal memory footprint -### 📖 Getting Started -- **[Documentation Overview](docs/README.md)** - Complete documentation index -- **[Configuration](docs/CONFIGURATION.md)** - Configuration options +## 🤝 Contributing -### 🎯 Advanced Usage -- **[Console Commands](docs/CONSOLE_COMMANDS.md)** - Console commands reference -- **[Guards and Actions](docs/GUARDS_AND_ACTIONS.md)** - Dynamic guards and actions -- **[Guard Expressions](docs/GUARD_EXPRESSIONS.md)** - AND/OR/NOT logic for guards -- **[Events](docs/EVENTS.md)** - Event system usage -- **[Testing](docs/TESTING.md)** - Testing utilities -- **[History](docs/HISTORY.md)** - State transition history - -### 💡 Examples -- **[Examples Overview](examples/README.md)** - Practical examples and use cases - -
+We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. -## 🎯 Next Steps +## 🔒 Security -
+If you discover a security vulnerability, please review our [Security Policy](SECURITY.md) before disclosing it. -1. **Quick Start**: Check out the [OrderWorkflow example](examples/OrderWorkflow/) -2. **Console Commands**: Explore the [console commands](docs/CONSOLE_COMMANDS.md) -3. **Guard Expressions**: See [guard-expressions-workflow.yaml](examples/OrderWorkflow/guard-expressions-workflow.yaml) for comprehensive examples -4. **Advanced Usage**: Read the [Guards and Actions documentation](docs/GUARDS_AND_ACTIONS.md) -5. **Configuration**: Review the [Configuration guide](docs/CONFIGURATION.md) -6. **Testing**: Learn about [Testing utilities](docs/TESTING.md) +## 📄 License -
+Laravel Statecraft is open-sourced software licensed under the [MIT license](LICENSE.md). --- -## 🤝 Contributing - -We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. - -## 🔒 Security +**Made with ❤️ for the Laravel community** -If you discover a security vulnerability, please review our [Security Policy](SECURITY.md) before disclosing it. - -## 📄 License +### Resources -Laravel Statecraft is open-sourced software licensed under the [MIT license](LICENSE.md). +- [📖 Documentation](docs/README.md) +- [💬 Discussions](https://github.com/Grazulex/laravel-statecraft/discussions) +- [🐛 Issue Tracker](https://github.com/Grazulex/laravel-statecraft/issues) +- [📦 Packagist](https://packagist.org/packages/grazulex/laravel-statecraft) ---- +### Community Links -
- Made with ❤️ for the Laravel community -
\ No newline at end of file +- [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) - Our code of conduct +- [CONTRIBUTING.md](CONTRIBUTING.md) - How to contribute +- [SECURITY.md](SECURITY.md) - Security policy +- [RELEASES.md](RELEASES.md) - Release notes and changelog