Installation β’ Quick Start β’ Features β’ Documentation β’ Examples
Transform your Laravel code into clean, testable, and reusable actions. Say goodbye to bloated controllers and hello to elegantly organized business logic!
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!
// β 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);
Execute business logic with a single, expressive call. No more hunting through service classes!
Seamlessly queue your actions for background processing. It's as easy as changing run()
to dispatch()
!
Full IntelliSense support with auto-completion for runnable and dispatchable actions across all major IDEs.
Convert between arrays and objects effortlessly with our powerful attribute system. Perfect for APIs!
Scaffold Actions and DTOs in seconds with our intuitive Artisan commands.
Fine-tune serialization behavior with elegant attributes like #[FieldName]
, #[DateFormat]
, and more!
composer require lumosolutions/actionable
That's it! No configuration needed. Start writing better code immediately.
1οΈβ£ Generate an action:
php artisan make:action SendWelcomeEmail
2οΈβ£ Define your logic:
class SendWelcomeEmail
{
use IsRunnable;
public function handle(string $email, string $name): void
{
Mail::to($email)->send(new WelcomeEmail($name));
}
}
3οΈβ£ Use it anywhere:
SendWelcomeEmail::run('[email protected]', 'John Doe');
That's it! Clean, testable, reusable. π
Actions are the heart of your application's business logic. They're single-purpose classes that do one thing and do it well.
class CalculateOrderTotal
{
use IsRunnable;
public function handle(Order $order): float
{
return $order->items->sum(fn($item) => $item->price * $item->quantity);
}
}
// Usage
$total = CalculateOrderTotal::run($order);
Need background processing? Just add a trait!
class ProcessVideoUpload
{
use IsRunnable, IsDispatchable;
public function handle(Video $video): void
{
// Heavy processing logic here
}
}
// Run synchronously
ProcessVideoUpload::run($video);
// Or dispatch to queue
ProcessVideoUpload::dispatch($video);
// Use a specific queue
ProcessVideoUpload::dispatchOn('video-processing', $video);
DTOs with superpowers! Convert between arrays and objects seamlessly.
class ProductData
{
use ArrayConvertible;
public function __construct(
public string $name,
public float $price,
public int $stock
) {}
}
// From request data
$product = ProductData::fromArray($request->validated());
// To API response
return response()->json($product->toArray());
class UserResponse
{
use ArrayConvertible;
public function __construct(
#[FieldName('user_id')]
public int $userId,
#[FieldName('full_name')]
public string $fullName
) {}
}
class EventData
{
use ArrayConvertible;
public function __construct(
#[DateFormat('Y-m-d')]
public DateTime $date,
#[DateFormat('H:i')]
public DateTime $startTime
) {}
}
class ShoppingCart
{
use ArrayConvertible;
public function __construct(
#[ArrayOf(CartItem::class)]
public array $items
) {}
}
class UserAccount
{
use ArrayConvertible;
public function __construct(
public string $email,
#[Ignore]
public string $password,
#[Ignore]
public string $apiSecret
) {}
}
Generate boilerplate with style:
# Basic action
php artisan make:action ProcessOrder
# Queueable action
php artisan make:action SendNewsletter --dispatchable
# Invokable action
php artisan make:action CalculateShipping --invokable
# DTO with array conversion
php artisan make:dto OrderData
# Enable Smart Code Completion
php artisan ide-helper:actions
// The DTO
class OrderData
{
use ArrayConvertible;
public function __construct(
#[FieldName('customer_email')]
public string $customerEmail,
#[ArrayOf(OrderItemData::class)]
public array $items,
#[FieldName('discount_code')]
public ?string $discountCode = null
) {}
}
// The Action
class ProcessOrder
{
use IsRunnable, IsDispatchable;
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;
}
}
// Usage - It's this simple!
$orderData = OrderData::fromArray($request->validated());
$order = ProcessOrder::run($orderData);
class RegisterUser
{
use IsRunnable;
public function handle(RegistrationData $data): User
{
$user = CreateUser::run($data);
SendWelcomeEmail::dispatch($user);
NotifyAdmins::dispatch($user);
TrackRegistration::dispatch($user, $data->referralSource);
return $user;
}
}
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.
Actionable is open-sourced software licensed under the MIT license.
- π Found a bug? Open an issue
- π‘ Have an idea? Start a discussion
- π Security concern? Email me at [email protected]
Built with β€οΈ by Lumo Solutions
Actionable: Making Laravel development more enjoyable, one action at a time.