Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Actions Laravel Package

lorisleiva/laravel-actions

Unify your Laravel app logic into single-purpose “Action” classes that can run as controllers, jobs, listeners, or commands. Keep business logic in one place, reduce duplication, and generate actions via artisan with flexible asX entrypoints.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require lorisleiva/laravel-actions
    

    No additional configuration is required.

  2. Create Your First Action:

    php artisan make:action PublishArticle
    

    This generates a class with the AsAction trait pre-loaded.

  3. Define Core Logic: Implement the handle() method to encapsulate your business logic:

    use Lorisleiva\Actions\Concerns\AsAction;
    
    class PublishArticle
    {
        use AsAction;
    
        public function handle(string $title, string $body): void
        {
            // Your core logic here
            Article::create(['title' => $title, 'body' => $body]);
        }
    }
    
  4. First Use Case: Run the action directly as an object:

    PublishArticle::run('My Title', 'My Content');
    

Where to Look First

  • Official Documentation – Comprehensive guide with examples.
  • app/Actions/ – Default directory for organizing actions (create this folder if it doesn’t exist).
  • AsAction Trait – Understand the handle() method and asX() methods (e.g., asController, asJob).

Implementation Patterns

Core Workflow

  1. Single Responsibility Principle (SRP): Each action class handles one specific task (e.g., PublishArticle, SendWelcomeEmail). Avoid mixing concerns like validation, persistence, and notifications in a single action.

  2. Modular Registration: Register actions in routes, events, or commands without boilerplate:

    // Routes
    Route::post('/articles', PublishArticle::class);
    
    // Events
    Event::listen(NewArticleCreated::class, PublishArticle::class);
    
    // Commands
    Artisan::register(PublishArticle::class);
    
  3. Dependency Injection: Inject dependencies directly into handle() or use Laravel’s container:

    public function handle(ArticleRepository $repository, string $title)
    {
        $repository->create($title);
    }
    

Integration Patterns

1. Controllers

Replace traditional controllers with actions:

class PublishArticle
{
    use AsAction;

    public function handle(Request $request): ArticleResource
    {
        $validated = $request->validate([...]);
        return new ArticleResource(Article::create($validated));
    }

    public function asController(Request $request): ArticleResource
    {
        return $this->handle($request);
    }
}

Route:

Route::post('/articles', PublishArticle::class);

2. Jobs

Run actions asynchronously:

class PublishArticle
{
    use AsAction;

    public function handle(string $title): void
    {
        // Async logic
    }

    public function asJob(): void
    {
        $this->handle('Async Title');
    }
}

Dispatch:

PublishArticle::dispatch('Async Title');

3. Listeners

Attach actions to events:

class PublishArticle
{
    use AsAction;

    public function handle(User $user): void
    {
        $user->articles()->create([...]);
    }

    public function asListener(NewUserRegistered $event): void
    {
        $this->handle($event->user);
    }
}

Event Listener:

Event::listen(NewUserRegistered::class, PublishArticle::class);

4. Commands

Convert actions into Artisan commands:

class PublishArticle
{
    use AsAction;

    public function handle(string $title): void
    {
        // Command logic
    }

    public function asCommand(): int
    {
        $this->handle('Command Title');
        return self::SUCCESS;
    }
}

Registration:

Artisan::register(PublishArticle::class);

Usage:

php artisan publish-article

5. Validation

Use Laravel’s validation directly in handle() or leverage asFormRequest:

use Lorisleiva\Actions\Concerns\ValidatesRequests;

class PublishArticle
{
    use AsAction, ValidatesRequests;

    protected $rules = [
        'title' => 'required|string|max:255',
        'body' => 'required|string',
    ];

    public function handle(array $data): Article
    {
        return Article::create($this->validated());
    }
}

6. Testing

Mock actions easily:

public function test_publish_article()
{
    $action = PublishArticle::fake();
    $action->shouldRun();
    $this->assertDatabaseHas('articles', [...]);
}

Advanced Patterns

Conditional Execution

Use shouldRun() to conditionally execute actions:

class PublishArticle
{
    use AsAction;

    public function shouldRun(): bool
    {
        return Auth::check() && Auth::user()->can('publish');
    }

    public function handle(): void
    {
        // Logic
    }
}

Custom Decorators

Extend built-in decorators (e.g., for jobs or commands):

use Lorisleiva\Actions\Decorators\JobDecorator;

class CustomJobDecorator extends JobDecorator
{
    public function __construct($action)
    {
        parent::__construct($action);
        $this->onFailure(fn ($e) => Log::error($e));
    }
}

Configure in AppServiceProvider:

Actions::macro('job', fn ($action) => new CustomJobDecorator($action));

API Resources

Return API resources directly from asController:

public function asController(Request $request): ArticleResource
{
    $article = $this->handle($request->user(), $request->input());
    return new ArticleResource($article);
}

Gotchas and Tips

Pitfalls

  1. Method Naming Conflicts: Avoid naming methods handle() or asX() in your action class if they conflict with the trait’s methods. Use namespaces or prefixes if needed.

  2. Middleware in Controllers: If using asController, ensure middleware is applied at the route level (not in the action). The action itself doesn’t inherit middleware from the trait.

  3. Job Failures: By default, job failures are logged but not re-queued. Use onFailure() in custom decorators to handle retries:

    $action->asJob()->onFailure(fn ($e) => $this->release($e));
    
  4. Circular Dependencies: Avoid circular references between actions (e.g., ActionA calling ActionB, which calls ActionA). Use dependency injection carefully.

  5. Laravel Version Compatibility:

    • Laravel 11+: Fully supported.
    • Laravel 10: Supported but some features may require manual adjustments.
    • Laravel 9: Use v2.7.x or earlier.
    • Laravel 8: Dropped in v2.6.0.

Debugging Tips

  1. Action Not Running?: Verify the handle() method is called by adding a dd() or Log::debug() statement. Check if the asX() method is properly defined (e.g., asController, asJob).

  2. Middleware Issues: If a controller action fails silently, ensure middleware is registered in RouteServiceProvider or the route definition:

    Route::post('/articles', PublishArticle::class)->middleware('auth');
    
  3. Job Not Dispatching: Confirm the action is registered as a job:

    PublishArticle::dispatch()->asJob();
    

    Or use the asJob() method:

    PublishArticle::run()->asJob();
    
  4. Validation Errors: If validation fails in handle(), ensure ValidatesRequests is used or manually validate inputs:

    $validated = $request->validate([...]);
    $this->handle($validated);
    
  5. IDE Autocomplete: Enable IDE helpers for better autocompletion:

    composer require --dev barryvdh/laravel-ide-helper
    php artisan ide-helper:generate
    

Configuration Quirks

  1. Custom Directories: Change the default action directory by publishing the config:

    php artisan vendor:publish --provider="Lorisleiva\Actions\ActionsServiceProvider"
    

    Update config/actions.php:

    'path' => app_path('Actions/Custom'),
    
  2. Macros: Extend the Actions facade to add custom macros:

    Actions::macro('retry', fn ($action, $times = 3) => {
        for ($i = 0; $i < $times; $i++) {
            try {
                $action->run();
                break;
            } catch (Exception $e) {
                if ($i === $times - 1) throw $e;
                sleep(1);
            }
        }
    });
    

    Usage:

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai