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

Laravel Actions organizes your app around single-purpose “action” classes. Write the core logic once in handle(), then run it as a controller, job, listener, command, or plain object. Keep business logic reusable, testable, and consistent across entry points.

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.

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

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

    $article = PublishArticle::run('My Title', 'My Content');
    

Where to Look First

  • Documentation: laravelactions.com for detailed guides.
  • Artisan Commands: php artisan make:action for scaffolding.
  • AsAction Trait: Understand the 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). Example:

    class SendWelcomeEmail
    {
        use AsAction;
    
        public function handle(User $user): void
        {
            Mail::to($user->email)->send(new WelcomeEmail($user));
        }
    }
    
  2. Reusable Logic: Extract shared logic into helper methods or services. Actions can call other actions:

    public function handle(User $user, string $content): void
    {
        $article = $this->createArticle($user, $content);
        $this->notifyTeam($article);
    }
    
    protected function createArticle(User $user, string $content): Article
    {
        return Article::create(['user_id' => $user->id, 'body' => $content]);
    }
    
  3. Integration Patterns:

    • Controllers: Register actions as invokable controllers:
      Route::post('/articles', PublishArticle::class);
      
    • Jobs: Use asJob for async tasks:
      public function asJob(): void
      {
          $this->handle(...);
      }
      
      Dispatch with:
      PublishArticle::dispatch($title, $body);
      
    • Listeners: Attach to events:
      Event::listen(NewArticlePublished::class, PublishArticle::class);
      
    • Commands: Convert to CLI tools:
      public function asCommand(): int
      {
          $this->handle(...);
          return 0;
      }
      
      Register in app/Console/Kernel.php:
      protected $commands = [
          PublishArticle::class,
      ];
      
  4. Validation: Use Laravel’s validation directly in handle() or leverage asController for FormRequest integration:

    public function asController(Request $request): ArticleResource
    {
        $validated = $request->validate([
            'title' => 'required|string',
            'body' => 'required|string',
        ]);
        return new ArticleResource($this->handle(...$validated));
    }
    
  5. Testing: Mock actions easily:

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

Advanced Patterns

  1. Dependency Injection: Inject services via constructor:

    class PublishArticle
    {
        public function __construct(protected Notifier $notifier) {}
    
        public function handle(User $user, string $content): void
        {
            $article = Article::create(['user_id' => $user->id, 'body' => $content]);
            $this->notifier->notify($article);
        }
    }
    
  2. Conditional Execution: Use shouldRun() for guards:

    public function shouldRun(): bool
    {
        return Auth::check() && Auth::user()->can('publish');
    }
    
  3. Custom Decorators: Extend default decorators (e.g., JobDecorator) for custom behavior:

    class CustomJobDecorator extends JobDecorator
    {
        public function __construct(Closure $action)
        {
            parent::__construct($action);
        }
    
        public function handle(): void
        {
            Log::info('Custom job started');
            parent::handle();
        }
    }
    

    Register in config/laravel-actions.php:

    'decorators' => [
        'job' => CustomJobDecorator::class,
    ],
    
  4. API Resources: Return API resources in asController:

    public function asController(Request $request): ArticleResource
    {
        return new ArticleResource($this->handle(...));
    }
    
  5. Inertia.js: Return Inertia responses:

    public function asController(Request $request): Inertia\Response
    {
        return Inertia::render('Articles/Show', [
            'article' => $this->handle(...),
        ]);
    }
    

Gotchas and Tips

Pitfalls

  1. Method Naming Conflicts: Avoid naming custom methods handle or asX (e.g., asController) to prevent overrides. Fix: Use descriptive names like execute() or perform().

  2. Middleware in Controllers: Middleware registered on routes won’t apply to actions used as objects or jobs. Fix: Use shouldRun() or inject middleware logic into the action.

  3. Job Failures: By default, failed jobs throw exceptions. Use jobFailed() to handle failures gracefully:

    public function jobFailed(Throwable $e): void
    {
        Sentry::captureException($e);
    }
    
  4. Circular Dependencies: Actions calling other actions may create tight coupling. Prefer dependency injection over chaining:

    // Avoid:
    $this->notifyTeam($this->publishArticle($user));
    
    // Prefer:
    $this->notifier->notify($this->articleService->publish($user));
    
  5. Laravel Version Mismatches: Ensure compatibility (e.g., Laravel 11+ requires v2.8.0+). Check the changelog.


Debugging Tips

  1. Log Action Execution: Add logging in handle() or decorators:

    Log::debug('Action executed with args:', [$title, $body]);
    
  2. Backtrace Limits: Increase xdebug.max_nesting_level if stack traces are truncated:

    xdebug.max_nesting_level=256
    
  3. IDE Autocomplete: Enable PHPDoc annotations for better IDE support:

    /**
     * @param User $user
     * @param string $title
     * @return Article
     */
    public function handle(User $user, string $title): Article
    
  4. Testing Actions: Use run() in tests to bypass decorators:

    $action = new PublishArticle();
    $result = $action->run($user, $title); // Skips asJob/asController logic
    

Configuration Quirks

  1. Custom Decorators: Override defaults in config/laravel-actions.php:

    'decorators' => [
        'job' => App\Decorators\CustomJobDecorator::class,
    ],
    
  2. Job Queue Connections: Specify the queue connection in asJob:

    public function asJob(): void
    {
        $this->onQueue('high_priority');
        $this->handle(...);
    }
    
  3. Event Listeners: Ensure the asListener method signature matches the event’s payload:

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

Extension Points

  1. Custom Action Traits: Extend AsAction for shared behavior:

    trait LoggableAction
    {
        public function handle(...$args)
        {
            Log::info('Action started', $args);
            $result = $this->perform(...$args);
            Log::info('Action completed', ['result' => $result]);
            return $result;
        }
    
        abstract protected function perform(...$args);
    }
    
  2. Dynamic Method Resolution: Use method_exists() to check for asX methods dynamically:

    if (method_exists($action, 'asJob
    
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.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata