Install Laravel Actions
mainInstall the package via Composer to start using actions in your Laravel application.
composer require lorisleiva/laravel-actionsrepository·main·Indexed 25 days ago
https://github.com/lorisleiva/laravel-actionsA package for organizing application logic into single-purpose PHP classes using the AsAction trait. These classes can be executed as objects, controllers, listeners, jobs, or Artisan commands, reducing boilerplate code across Laravel components. Features include built-in support for authorization, validation, response adapters, and advanced queue orchestration via JobDecorator.
Install the package via Composer to start using actions in your Laravel application.
composer require lorisleiva/laravel-actionsTo ensure your actions are reusable and testable, follow these patterns:
handle() method.handle() method should contain only domain logic. Do not include transport concerns (like HTTP requests, CLI commands, or specific queue configurations) inside handle().Action::run(...) for better readability in your application code.handle() method.// Recommended pattern for a simple action
final class PublishArticle
{
use AsAction;
public function handle(int $articleId): bool
{
// Domain logic goes here...
return true;
}
}
// Usage
$published = PublishArticle::run(42);Generate an action class using the Artisan command php artisan make:action <Name>. To enable action functionality, use the AsAction trait within your class. You can define specialized methods like asController, asListener, asJob, or asCommand to allow the action to behave differently depending on how it is invoked. The core logic should typically reside in a handle method.
class PublishANewArticle
{
use AsAction;
public function handle(User $author, string $title, string $body): Article
{
return $author->articles()->create([
'title' => $title,
'body' => $body,
]);
}
public function asController(Request $request): ArticleResource
{
$article = $this->handle(
$request->user(),
$request->get('title'),
$request->get('body'),
);
return new ArticleResource($article);
}
public function asListener(NewProductReleased $event): void
{
$this->handle(
$event->product->manager,
$event->product->name . ' Released!',
$event->product->description,
);
}
}To create an action, define a class that uses the Lorisleiva\Actions\Concerns\AsAction trait. Implement the core business logic within the handle(...) method. Keep transport-specific logic (like HTTP responses or CLI output) in adapter methods instead of handle().
<?php
namespace App\Actions;
use Lorisleiva\Actions\Concerns\AsAction;
class PublishArticle
{
use AsAction;
public function handle(int $articleId): bool
{
return true;
}
}Use the WithAttributes trait when an action needs to store and validate input via internal attributes instead of passing them directly as method arguments to handle(). This allows you to manage state within the action instance using methods like fill(), set(), and validateAttributes().
class CreateArticle
{
use AsAction;
use WithAttributes;
public function rules(): array
{
return [
'title' => ['required', 'string', 'min:8'],
'body' => ['required', 'string'],
];
}
public function handle(array $attributes): Article
{
return Article::create($attributes);
}
}
$action = CreateArticle::make()->fill([
'title' => 'My first post',
'body' => 'Hello world',
]);
$validated = $action->validateAttributes();
$article = $action->handle($validated);When testing orchestration and business logic:
handle() directly: Test the core business rules inside the action itself.Laravel Actions do not automatically register themselves as listeners for all events. You must explicitly map the event to the action in your EventServiceProvider (or your application's equivalent event registration mechanism).
// app/Providers/EventServiceProvider.php
protected $listen = [
TaxiRequested::class => [
SendOfferToNearbyDrivers::class,
],
];To expose an action as an Artisan command, use the asCommand(Command $command) method. This method acts as the entrypoint when the action is executed via the CLI.
Recommended Pattern:
$commandSignature and $commandDescription.asCommand(Command $command) to handle console-specific I/O (e.g., reading arguments from the $command object and printing output).handle(...) method to ensure the action remains decoupled from the console environment.use Illuminate\Console\Command;
class UpdateUserRole
{
use AsAction;
public string $commandSignature = 'users:update-role {user_id} {role}';
public function handle(User $user, string $newRole): void
{
$user->update(['role' => $newRole]);
}
public function asCommand(Command $command): void
{
$this->handle(
User::findOrFail($command->argument('user_id')),
$command->argument('role')
);
$command->info('Done!');
}
}To use an action as an event listener, implement the asListener method within your action class. This method acts as an adapter that extracts the necessary data from the event payload and passes it to the core handle method. This pattern keeps your business logic decoupled from the event structure.
Recommended Pattern:
handle(...) method.asListener(Event $event) to map event properties to handle arguments.EventServiceProvider.class SendOfferToNearbyDrivers
{
use AsAction;
public function handle(Address $source, Address $destination): void
{
// Core business logic goes here
}
public function asListener(TaxiRequested $event): void
{
// Adapt the event payload to the handle method
$this->handle($event->source, $event->destination);
}
}To make your action available to Artisan, you must register it in your application's console kernel.
// app/Console/Kernel.php
protected $commands = [
UpdateUserRole::class,
];To expose an action through HTTP routes, use the AsController trait. The recommended pattern is to keep domain logic in handle(...) and use asController(...) to manage HTTP adaptation (like extracting request data and returning responses).
If you need to implement your own __invoke method, you must alias the trait's implementation to avoid breaking Laravel's invokable controller registration.
class MyAction
{
use AsAction {
__invoke as protected invokeFromLaravelActions;
}
public function __invoke()
{
// Custom behavior...
}
}You can publish the default action stub file to your application's stubs directory. This allows you to customize the boilerplate code generated when using the make:action command.
Run the following Artisan command to publish the stub:
php artisan vendor:publish --tag=stubs