diff --git a/README.md b/README.md index 3f584e5..386fdf3 100644 --- a/README.md +++ b/README.md @@ -1,138 +1,172 @@ -# LumoSolutions - Actionable +# ⚡ Actionable -A Laravel package that provides a clean, elegant way to create dispatchable and runnable actions with built-in array conversion capabilities. Simplify your Laravel application's business logic with reusable, testable action classes. +> **Transform your Laravel code into clean, testable, and reusable actions.** Say goodbye to bloated controllers and hello to elegantly organized business logic! -## Features +
-- **Runnable Actions**: Execute actions synchronously with dependency injection support -- **Dispatchable Actions**: Queue actions for asynchronous execution -- **Array Conversion**: Convert objects to/from arrays with attribute-based customization -- **Artisan Commands**: Generate action and DTO stubs quickly -- **Flexible Attributes**: Control serialization behavior with custom attributes +[![CI Pipeline](https://github.com/LumoSolutions/actionable/actions/workflows/build.yml/badge.svg)](https://github.com/LumoSolutions/actionable/actions/workflows/build.yml) +[![codecov](https://codecov.io/gh/LumoSolutions/actionable/branch/main/graph/badge.svg)](https://codecov.io/gh/LumoSolutions/actionable) +[![Latest Stable Version](https://poser.pugx.org/lumosolutions/actionable/v/stable)](https://packagist.org/packages/lumosolutions/actionable) +[![Total Downloads](https://poser.pugx.org/lumosolutions/actionable/downloads)](https://packagist.org/packages/lumosolutions/actionable) +[![License](https://img.shields.io/github/license/LumoSolutions/actionable)](LICENSE) -## Installation +**[Installation](#-installation)** • **[Quick Start](#-quick-start)** • **[Features](#-key-features)** • **[Documentation](#-documentation)** • **[Examples](#-real-world-examples)** -Install the package via Composer: +
-```bash -composer require lumo-solutions/actionable +## 💡 Why Actionable? + +Ever found yourself writing the same business logic patterns over and over? Controllers getting too fat? Service classes becoming a mess? **Actionable is here to save the day!** + +```php +// ❌ The old way - Fat controllers, messy code +class UserController extends Controller +{ + public function register(Request $request) + { + // Validation logic... + // User creation logic... + // Email sending logic... + // Queue processing... + // 200 lines later... + } +} + +// ✅ The Actionable way - Clean, focused, reusable +RegisterUser::run($userData); ``` -## Quick Start +## 🎯 Key Features -### Creating a Basic Action +### 🏃‍♂️ **Runnable Actions** +Execute business logic with a single, expressive call. No more hunting through service classes! -```php -send(new WelcomeEmail($name)); } } ``` -### Running the Action - +**3️⃣ Use it anywhere:** ```php -// Execute the action synchronously SendWelcomeEmail::run('user@example.com', 'John Doe'); ``` -### Creating a Dispatchable Action +**That's it!** Clean, testable, reusable. 🎉 -```php -items->sum(fn($item) => $item->price * $item->quantity); } } + +// Usage +$total = CalculateOrderTotal::run($order); ``` -### Dispatching Actions to Queue +#### Queueable Actions + +Need background processing? Just add a trait! ```php -// Dispatch to default queue -ProcessLargeDataset::dispatch($largeDataArray); +class ProcessVideoUpload +{ + use IsRunnable, IsDispatchable; -// Dispatch to specific queue -ProcessLargeDataset::dispatchOn('heavy-processing', $largeDataArray); -``` + public function handle(Video $video): void + { + // Heavy processing logic here + } +} -## Creating DTOs with Array Conversion +// Run synchronously +ProcessVideoUpload::run($video); -### Basic DTO +// Or dispatch to queue +ProcessVideoUpload::dispatch($video); -```php -validated()); -```php -// Create from array -$userData = UserData::fromArray([ - 'name' => 'John Doe', - 'email' => 'john@example.com', - 'age' => 30 -]); - -// Convert to array -$array = $userData->toArray(); +// To API response +return response()->json($product->toArray()); ``` -## Attributes Reference - -The package provides several attributes to customize array conversion behavior. All attributes can be applied to constructor parameters or class properties. +### 🏷️ Powerful Attributes -### #[FieldName] - Custom Field Mapping - -Map property names to different array keys: +#### `#[FieldName]` - API-Friendly Naming ```php - 123, - 'full_name' => 'John Doe', - 'email_address' => 'john@example.com' -]); - -$array = $response->toArray(); -// Returns: ['user_id' => 123, 'full_name' => 'John Doe', 'email_address' => 'john@example.com'] ``` -### #[DateFormat] - Date Serialization Control - -Control how DateTime objects are formatted during array conversion: +#### `#[DateFormat]` - Date Formatting Made Easy ```php -toArray(); -// Returns formatted dates according to their attributes ``` -### #[ArrayOf] - Nested Object Arrays - -Handle arrays containing objects of a specific class: +#### `#[ArrayOf]` - Handle Nested Objects ```php - 'ORD-001', - 'items' => [ - ['name' => 'Laptop', 'quantity' => 1, 'price' => 999.99], - ['name' => 'Mouse', 'quantity' => 2, 'price' => 25.00] - ], - 'bundleItems' => [ - ['name' => 'Cable', 'quantity' => 1, 'price' => 15.00] - ] -]); - -// The arrays are automatically converted to OrderItem objects -echo $order->items[0]->name; // 'Laptop' ``` -### #[Ignore] - Exclude from Conversion - -Exclude sensitive or computed properties from array conversion: +#### `#[Ignore]` - Keep Secrets Secret ```php - $this->username, 'email' => $this->email]; - } } - -// Usage -$user = new UserAccount( - username: 'johndoe', - email: 'john@example.com', - password: 'secret123', - apiKey: 'api_key_here', - createdAt: new DateTime() -); - -$array = $user->toArray(); -// Returns: ['username' => 'johndoe', 'email' => 'john@example.com', 'createdAt' => '2024-01-01 12:00:00'] -// Password and apiKey are excluded ``` -### Combining Attributes +### 🛠️ Artisan Commands -You can combine multiple attributes on the same property: +Generate boilerplate with style: -```php -toArray(); // Clean, formatted output ready for API -``` - -## Artisan Commands - -The package provides convenient Artisan commands to generate boilerplate code: - -### Generate Action Classes - -```bash -# Generate basic runnable action -php artisan make:action SendNotification - -# Generate dispatchable action (can be queued) -php artisan make:action ProcessPayment --dispatchable - -# Generate invokable action -php artisan make:action CalculateTotal --invokable -``` - -The `make:action` command supports the following options: -- `--dispatchable` - Adds the `IsDispatchable` trait for queueing support -- `--invokable` - Creates an invokable action (uses `action.invokable.stub`) - -### Generate DTO Classes - -```bash -# Generate basic DTO with array conversion capabilities -php artisan make:dto UserRegistrationData -``` - -### Custom Stubs - -You can publish and customize the stubs used by the commands by creating them in your application's `stubs/lumosolutions/actionable/` directory. The package will automatically use your custom stubs if they exist: +// The Action +class ProcessOrder +{ + use IsRunnable, IsDispatchable; -- `action.stub` - Basic action template -- `action.dispatchable.stub` - Dispatchable action template -- `action.invokable.stub` - Invokable action template -- `dto.stub` - DTO template + public function handle(OrderData $orderData): Order + { + $order = DB::transaction(function () use ($orderData) { + $order = Order::create([...]); + + // Process items + foreach ($orderData->items as $item) { + $order->items()->create([...]); + } + + // Apply discount + if ($orderData->discountCode) { + ApplyDiscount::run($order, $orderData->discountCode); + } + + return $order; + }); + + // Queue follow-up actions + SendOrderConfirmation::dispatch($order); + UpdateInventory::dispatch($order); + + return $order; + } +} -Example custom stub location: -``` -stubs/ -└── lumosolutions/ - └── actionable/ - ├── action.stub - ├── action.dispatchable.stub - ├── action.invokable.stub - └── dto.stub +// Usage - It's this simple! +$orderData = OrderData::fromArray($request->validated()); +$order = ProcessOrder::run($orderData); ``` -## Complete Example - -Here's a complete example showing how to use actions and DTOs together: +### User Registration Flow ```php - $userData->fullName, - 'email' => $userData->email, - 'birth_date' => $userData->birthDate, - 'password' => Hash::make($userData->password) - ]); - - // Send welcome email asynchronously - SendWelcomeEmail::dispatch($user->email, $user->name); - + $user = CreateUser::run($data); + + SendWelcomeEmail::dispatch($user); + NotifyAdmins::dispatch($user); + TrackRegistration::dispatch($user, $data->referralSource); + return $user; } } - -// Usage -$registrationData = UserRegistrationData::fromArray($request->all()); -$user = RegisterUser::run($registrationData); ``` -## Requirements +## 🤲 Contributing + +We love contributions! Whether it's a bug fix, new feature, or improvement to our docs - we appreciate it all. Please feel free to submit a pull request or open an issue. + +## 📄 License + +Actionable is open-sourced software licensed under the [MIT license](LICENSE). -- PHP 8.2 or higher -- Laravel 12.0 or higher +## 💬 Support & Community -## Contributing +- 🐛 **Found a bug?** [Open an issue](https://github.com/LumoSolutions/actionable/issues) +- 💡 **Have an idea?** [Start a discussion](https://github.com/LumoSolutions/actionable/discussions) +- 🔒 **Security concern?** Email me at richard@lumosolutions.org -Contributions are welcome! Please feel free to submit a Pull Request. +--- -## License +
-This package is open-sourced software licensed under the [MIT license](LICENSE). +**Built with ❤️ by [Lumo Solutions](https://lumosolutions.org)** -## Support +*Actionable: Making Laravel development more enjoyable, one action at a time.* -If you discover any security vulnerabilities or bugs, please create an issue on GitHub. +