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

Organize app logic into single-purpose “Action” classes that can run as controllers, jobs, listeners, commands, and more. Define a handle method for the core task, then add asController/asJob/etc wrappers to reuse the same logic across contexts.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require lorisleiva/laravel-actions
    

    No additional configuration is required beyond this.

  2. Generate Your First Action:

    php artisan make:action ProcessPayment
    

    This creates a new class in app/Actions/ with the AsAction trait.

  3. Define Core Logic: Implement the handle() method to define the core business logic:

    use App\Models\User;
    use App\Models\Payment;
    use Lorisleiva\Actions\Concerns\AsAction;
    
    class ProcessPayment
    {
        use AsAction;
    
        public function handle(User $user, float $amount): Payment
        {
            return $user->payments()->create([
                'amount' => $amount,
                'status' => 'pending',
            ]);
        }
    }
    
  4. First Use Case: Run the action directly as an object:

    $payment = ProcessPayment::run($user, 99.99);
    

Where to Look First

  • Documentation: laravelactions.com (official docs with examples and API reference).
  • Artisan Commands: php artisan make:action (for scaffolding) and php artisan actions:list (to list all actions).
  • Example Actions: Check the app/Actions/ directory for existing actions in your project.

Implementation Patterns

Core Workflow

  1. Single Responsibility Principle: Each action class encapsulates one specific task (e.g., ProcessPayment, SendWelcomeEmail). This replaces controllers, jobs, and listeners with focused, reusable components.

  2. Multi-Context Execution: Define how the action runs in different contexts using asX methods:

    public function asController(Request $request): JsonResponse
    {
        $payment = $this->handle($request->user(), $request->amount);
        return response()->json(['success' => true]);
    }
    
    public function asJob(): void
    {
        $this->handle(auth()->user(), 99.99);
    }
    
  3. Dependency Injection: Inject dependencies directly into handle():

    public function handle(User $user, PaymentGateway $gateway, float $amount)
    {
        $gateway->charge($user, $amount);
    }
    

Integration Tips

  • Controllers: Register actions as invokable controllers in routes:

    Route::post('/pay', ProcessPayment::class)->middleware('auth');
    
  • Jobs: Dispatch actions as jobs:

    ProcessPayment::dispatch($user, 99.99);
    
  • Listeners: Attach actions to events:

    Event::listen(OrderPlaced::class, ProcessPayment::class);
    
  • Commands: Use actions for CLI tasks:

    public function handle()
    {
        $this->info('Processing payment...');
        ProcessPayment::run($this->user, $this->amount);
    }
    
  • Testing: Mock actions in tests:

    $action = Mockery::mock(ProcessPayment::class);
    $action->shouldReceive('handle')->once();
    

Advanced Patterns

  • Conditional Execution: Use shouldRun() to gate logic:

    public function shouldRun(): bool
    {
        return auth()->user()->hasActiveSubscription();
    }
    
  • Validation: Validate inputs in asController or use validate() helper:

    public function asController(Request $request): JsonResponse
    {
        $validated = $request->validate(['amount' => 'required|numeric']);
        return $this->handle($request->user(), $validated['amount']);
    }
    
  • Transactions: Wrap handle() in a transaction:

    public function handle(): void
    {
        DB::transaction(fn() => $this->coreLogic());
    }
    

Gotchas and Tips

Pitfalls

  1. Method Naming Conflicts: Avoid naming handle() methods in your action that conflict with Laravel’s magic methods (e.g., __invoke). The AsAction trait relies on handle().

  2. Middleware in Controllers: Middleware registered on routes won’t automatically apply to asController. Manually add middleware in the route definition:

    Route::post('/pay', ProcessPayment::class)->middleware('auth');
    
  3. Job Failures: If using asJob, ensure jobFailed() is defined to handle failures:

    public function jobFailed(Throwable $e): void
    {
        Log::error("Payment failed: " . $e->getMessage());
    }
    
  4. Circular Dependencies: Actions should avoid circular dependencies. If ActionA calls ActionB and vice versa, refactor to extract shared logic into a service.

  5. IDE Autocomplete: Some IDEs may not recognize asX methods. Add PHPDoc annotations:

    /**
     * @return \Illuminate\Http\JsonResponse
     */
    public function asController(Request $request)
    

Debugging Tips

  • Log Inputs/Outputs: Use Laravel’s logging in handle() to debug:

    Log::debug('Processing payment for user:', ['user_id' => $user->id]);
    
  • Check Action Execution: Use actions:list to verify registered actions:

    php artisan actions:list
    
  • Test Isolation: Test actions in isolation by mocking dependencies:

    $action = new ProcessPayment();
    $this->mock(User::class)->shouldReceive('payments')->andReturn($mockPayments);
    

Extension Points

  1. Custom Decorators: Override default decorators (e.g., for jobs) by binding custom classes:

    $this->app->bind(
        \Lorisleiva\Actions\Jobs\JobDecorator::class,
        \App\Actions\Decorators\CustomJobDecorator::class
    );
    
  2. Dynamic Contexts: Use asDynamic() to handle arbitrary contexts:

    public function asDynamic($context): mixed
    {
        if ($context === 'api') {
            return $this->asController(new Request());
        }
    }
    
  3. Action Groups: Organize actions into namespaces or groups (e.g., App\Actions\Payments\ProcessPayment).

  4. Event Listeners: For complex listeners, use shouldQueue() to defer processing:

    public function shouldQueue(): bool
    {
        return true;
    }
    

Configuration Quirks

  • Laravel Version Compatibility: Ensure your Laravel version matches the package’s supported versions (check composer.json). As of v2.10.0, Laravel 13 is supported.

  • Octane Events: If using Octane, ensure asListener uses handle() instead of asListener for events:

    public function handle(OrderPlaced $event): void
    {
        // Logic here
    }
    
  • Windows Compatibility: The package includes Windows-specific tests. If you encounter path issues, ensure your storage/ and bootstrap/ paths are correctly configured.

Performance Tips

  • Avoid Heavy Logic in handle(): Offload complex operations to services or jobs to keep actions lightweight.

  • Reuse Actions: Instantiate actions once and reuse them (e.g., in service containers) to avoid overhead:

    $action = app(ProcessPayment::class);
    $action->handle($user, $amount);
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport