PHPackages                             bradietilley/laravel-actions - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. bradietilley/laravel-actions

ActiveLibrary[Framework](/categories/framework)

bradietilley/laravel-actions
============================

Action class framework for Laravel

v0.3.0(1y ago)36MITPHP

Since Feb 17Pushed 1y ago1 watchersCompare

[ Source](https://github.com/bradietilley/laravel-actions)[ Packagist](https://packagist.org/packages/bradietilley/laravel-actions)[ RSS](/packages/bradietilley-laravel-actions/feed)WikiDiscussions main Synced yesterday

READMEChangelog (3)Dependencies (6)Versions (5)Used By (0)

Laravel Actions
===============

[](#laravel-actions)

A simple yet flexible implementation of Actions in Laravel.

[![Static Analysis](https://github.com/bradietilley/laravel-actions/actions/workflows/static.yml/badge.svg)](https://github.com/bradietilley/laravel-actions/actions/workflows/static.yml/badge.svg)[![Tests](https://github.com/bradietilley/laravel-actions/actions/workflows/tests.yml/badge.svg)](https://github.com/bradietilley/laravel-actions/actions/workflows/tests.yml/badge.svg)[![Laravel Version](https://camo.githubusercontent.com/1a0dd3f2234cab82fce80f256db4918d50a06680a4984f5db9f1ca7f58a1daea/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c25323056657273696f6e2d31312e782d463933323243)](https://camo.githubusercontent.com/1a0dd3f2234cab82fce80f256db4918d50a06680a4984f5db9f1ca7f58a1daea/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c25323056657273696f6e2d31312e782d463933323243)[![PHP Version](https://camo.githubusercontent.com/0742096904180c78a3bf41632578254b265ae67e07f02a3832a3dc0224fd7c29/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f50485025323056657273696f6e2d382e332d344635423933)](https://camo.githubusercontent.com/0742096904180c78a3bf41632578254b265ae67e07f02a3832a3dc0224fd7c29/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f50485025323056657273696f6e2d382e332d344635423933)

Introduction
------------

[](#introduction)

Actions are compartmentalised bits of code that perform an... Action. This package provides a one-stop shop for how to define your application's actions. Many developers believe that actions "should not" be defined as methods in your `Model` (such as `$user->assignDefaultRole()`), nor should they be in standalone `Job` classess (`App\Jobs\Users\AssignDefaultRole`). This is where this package comes in to play.

In this package, Actions are built similar to synchronously dispatched Jobs. Such as there's a dispatcher that dispatches the action synchronously, just like with jobs, and there's also even a Facade to enable faking of the actions, just like with jobs.

The separation from `Bus` is crucial for sanity in larger projects where you have a huge amount of jobs and actions. Plus it just makes sense. Just like how you wouldn't want `Event::fake()` to fake the `Bus` classes (jobs), you wouldn't want `Bus::fake()` to fake your `Action` classes. Or maybe you do. Up to you. Either way...

Installation
------------

[](#installation)

```
composer require bradietilley/laravel-actions

```

Documentation
-------------

[](#documentation)

First, brush up on your `Bus` knowledge (i.e. Jobs). Because this is pretty much a standalone copy of how (sychronously) dispatched jobs operate in conjunction with the `Bus::fake()` and `Bus::assert*()` methods.

### Actions → The `Action` class

[](#actions--the-action-class)

First and foremost, the equivalent to a job is a class that implements the `BradieTilley\Actions\Contracts\Actionable` interface. An `Actionable` class in one that has a `handle` method (like a job) and one that can be dispatched (like a job). The `Actionable` interface doesn't provide any method signature to allow for full customisation and dependency injection (to workaround a limitation of PHP).

Creating an `Actionable` class is easy. The easiest way would be to extend the `BradieTilley\Actions\Action` abstract class which has the boilerplate you need. Alternatively, implement the `Actionable` interface and add in the `BradieTilley\Actions\Dispatchable` trait.

Here's a rudimentary example of an action, using both of the aforementioned approaches:

```
/**
 * Using the Action class
 */
class AssignDefaultRole extends \BradieTilley\Actions\Action
{
    public function __construct(public readonly User $user)
    {}

    public function handle(): User
    {
        if ($this->user->role) {
            return $this->user->role;
        }

        $this->user->update([
            'role' => $default => Role::DEFAULT,
        ]);

        return $default;
    }
}

/**
 * Using the Actionable interface and Dispatchable trait.
 */
class AssignDefaultRole implements \BradieTilley\Actions\Contracts\Actionable
{
    use \BradieTilley\Actions\Dispatchable;

    public function __construct(public readonly User $user)
    {}

    public function handle(): User
    {
        if ($this->user->role) {
            return $this->user->role;
        }

        $this->user->update([
            'role' => $default => Role::DEFAULT,
        ]);

        return $default;
    }
}
```

### Actions → The `Action` facade

[](#actions--the-action-facade)

A facade has been made available using the `BradieTilley\Actions\Facades\Action` class.

You can dispatch actions using the facade, such as

```
$role = Action::dispatch(new AssignDefaultRole($user));
```

However you can also avoid the facade entirely by using the dispatch method (probably more preferred.):

```
$role = AssignDefaultRole::dispatch($user);
```

### Actions → Replacing Actions

[](#actions--replacing-actions)

You can also replace actions - whether you're in a test environment or you just want some complex on-the-fly action swapping. Simply use the facade's `replace()` method to replace any actions with other actions. A replacement action should share a similar signature to the replaced action, such as having the same arguments and return type, but is not enforced.

```
Action::replace(ExampleActionA::class, ExampleActionB::class);

ExampleActionA::dispatch($user, '123');
// Runs ExampleActionB with $user and '123' constructor arguments instead of ExampleActionA
```

You can also replace multiple at once:

```
Action::replace([
    // replace an action from a package and use your own action
    DownloadAvatarFromUrl::class => CustomDownloadAvatarFromUrl::class,
    // temporarily disable an integration
    SynchroniseMemberToMailchimp::class => CustomEmptyAction::class,
]);
```

### Testing → Faking

[](#testing--faking)

The `Action` facade wraps the underlying `Dispatcher`, which can be swapped out for a `FakeDispatcher` that tracks all Actions that have been dispatched, just like the `Bus` Dispatcher does with jobs.

An example of this is:

```
use BradieTilley\Actions\Facades\Action;

// your test
Action::fake();

// your app
AssignDefaultRole::dispatch($user);

// your test
Action::assertDispatched(AssignDefaultRole::class); // pass
Action::assertNotDispatched(AssignAdminRole::class); // pass
```

The following methods are supported:

- `Action::assertDispatched()`
- `Action::assertDispatchedTimes()`
- `Action::assertNotDispatched()`
- `Action::assertNothingDispatched()`

These operate exactly like their `Bus` counterpart, so feel free to refer to Laravel's Bus Faking docs for how to use these 4 methods as there may be crossover in functionality.

### Testing → Faking specific actions

[](#testing--faking-specific-actions)

You may wish to fake only a subset of actions. This can be achieved via the `Action::fake()` facade call:

```
use BradieTilley\Actions\Facades\Action;

// your test
Action::fake([
    RecordAuditLog::class,
]);

// your app
AssignDefaultRole::dispatch($user); // still runs
RecordAuditLog::dispatch($user); // doesn't run

// your test
Action::assertDispatched(RecordAuditLog::class); // pass
```

### Testing → Faking all except specific actions

[](#testing--faking-all-except-specific-actions)

You may wish to fake all actions except a few. This can be achieved via the `except()` method:

```
use BradieTilley\Actions\Facades\Action;

// your test
Action::fake()->except([
    AssignDefaultRole::class,
]);

// your app
AssignDefaultRole::dispatch($user); // still runs
RecordAuditLog::dispatch($user); // doesn't run

// your test
Action::assertDispatched(RecordAuditLog::class); // pass
```

### Testing → Faking and un-faking actions\*\*

[](#testing--faking-and-un-faking-actions)

Often your test suite will provide a constant list of actions to fake, however for specific tests you might wish to include additional actions to fake.

```
Action::fake([
    ExampleA::class,
]);

ExampleA::dispatch(); // will skip
ExampleB::dispatch(); // will run

Action::addFake(ExampleB::class);

ExampleA::dispatch(); // will skip
ExampleB::dispatch(); // will skip

Action::removeFake(ExampleA::class);

ExampleA::dispatch(); // will run
ExampleB::dispatch(); // will skip
```

### Testing → Allowing execution of actions

[](#testing--allowing-execution-of-actions)

From experience, this is a common approach. Something that Bus doesn't offer (as far as I know) is to allow for assertions against dispatched jobs but have those jobs *still run*. With actions, just simply allow execution using the following syntax:

```
use BradieTilley\Actions\Facades\Action;

// your test
Action::fake()->allowExecution();

// your app
AssignDefaultRole::dispatch($user); // still runs
RecordAuditLog::dispatch($user); // still runs

// your test
Action::assertDispatched(RecordAuditLog::class); // pass
```

And then you can turn it off mid-test too:

```
use BradieTilley\Actions\Facades\Action;

// your test
Action::fake()->disallowExecution();

// your app
AssignDefaultRole::dispatch($user); // doesn't run
RecordAuditLog::dispatch($user); // doesn't run

// your test
Action::assertDispatched(RecordAuditLog::class); // pass
```

### Events → Listening and monitoring action usage

[](#events--listening-and-monitoring-action-usage)

Often you may want to produce reporting, logging, or other event-driven workflows. This can be achieved via Laravel's event architecture.

Immediately before an action is dispatched, it will trigger an event: `BradieTilley\Actions\Events\ActionDispatching`.

The `BradieTilley\Actions\Contracts\Actionable` class is provided in the event under the `action` property.

```
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;

Event::listen(function (ActionDispatching $event) {
    Log::channel('actions')->debug(sprintf(
        'Running action %s',
        $event->action::class,
    ));
});
```

Immediately after an action is dispatched, it will trigger an event: `BradieTilley\Actions\Events\ActionDispatched`.

The `BradieTilley\Actions\Contracts\Actionable` class is provided in the event under the `action` property.

A summary of the time it took to execute the action (`SebastianBergmann\Timer\Duration` class) is provided in the event under the `duration` property.

```
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;

Event::listen(function (ActionDispatched $event) {
    Log::channel('actions')->debug(sprintf(
        'Successfuly ran action %s in %s milliseconds',
        $event->action::class,
        $event->duration->asMilliseconds(),
    ));
});
```

When an action throws a `Throwable` error/exception, it will trigger an event: `BradieTilley\Actions\Events\ActionFailed`.

The `BradieTilley\Actions\Contracts\Actionable` class is provided in the event under the `action` property.

The exception (instance of `Throwable`) class is provided in the event under the `error` property.

```
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;

Event::listen(function (ActionFailed $event) {
    Log::channel('actions')->debug(sprintf(
        'Failed to run action %s with error %s (see sentry)',
        $event->action::class,
        $event->error->getMessage(),
    ));
});
```

Author
------

[](#author)

- [Bradie Tilley](https://github.com/bradietilley)

###  Health Score

23

—

LowBetter than 26% of packages

Maintenance38

Infrequent updates — may be unmaintained

Popularity8

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity35

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~86 days

Total

4

Last Release

609d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/44430471?v=4)[Bradie Tilley](/maintainers/bradietilley)[@bradietilley](https://github.com/bradietilley)

---

Top Contributors

[![bradietilley](https://avatars.githubusercontent.com/u/44430471?v=4)](https://github.com/bradietilley "bradietilley (26 commits)")

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/bradietilley-laravel-actions/health.svg)

```
[![Health](https://phpackages.com/badges/bradietilley-laravel-actions/health.svg)](https://phpackages.com/packages/bradietilley-laravel-actions)
```

###  Alternatives

[unopim/unopim

UnoPim Laravel PIM

10.5k2.4k](/packages/unopim-unopim)[stephenjude/filament-jetstream

A Laravel starter kit built with Filament inspired by Jetstream.

17760.2k3](/packages/stephenjude-filament-jetstream)[ecotone/laravel

Ecotone for Laravel — CQRS, Event Sourcing, Sagas, Durable Workflows, and Outbox on top of Laravel Queue, via PHP attributes.

21318.6k3](/packages/ecotone-laravel)[codewithdennis/larament

Larament is a time-saving starter kit to quickly launch Laravel 13.x projects. It includes FilamentPHP 5.x pre-installed and configured, along with additional tools and features to streamline your development workflow.

3891.8k](/packages/codewithdennis-larament)[aurorawebsoftware/arflow

ArFlow is a Laravel package that allows you to implement workflow management for your Laravel Eloquent models.

233.4k2](/packages/aurorawebsoftware-arflow)[slimani/filament-media-manager

A media manager plugin for Filament.

126.9k](/packages/slimani-filament-media-manager)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
