Action Pattern in Laravel: Concept, Benefits, Best Practices

laravel
tutorial
actions
patterns
Nabil Hassen
Nabil Hassen
Jul 8, 2025
Action Pattern in Laravel: Concept, Benefits, Best Practices
Last updated on Jul 8, 2025
Table of contents:

What is the Action Pattern in Laravel?

The Action Pattern is a simple architectural pattern used in Laravel and other PHP applications to encapsulate a single unit of logic inside a dedicated class. Typically, an action class has:

  • A single public method most commonly named handle() that contains the core logic.
  • A constructor that injects any required dependencies (e.g., other actions, services, repositories).

This pattern promotes cleaner, reusable, and more testable code. Action classes are generally placed under the app/Actions directory and are used to keep controllers, commands, and jobs slim and focused.

namespace App\Actions;
 
use App\Models\User;
 
class CreateUser
{
public function handle(array $data): User
{
return User::create($data);
}
}

Benefits of the Action Pattern

1. Write Once, Call Anywhere (Context-Independent)

An action can be called from any context: controllers, jobs, console commands, tests, event listeners, etc.

// In a controller
class UserController extends Controller
{
public function store(Request $request, CreateUser $createUser)
{
$user = $createUser->handle($request->validated());
 
return response()->json($user);
}
}
// In a job/command
public function handle(CreateUser $createUser)
{
$createUser->handle($this->data);
}

2. No Irrelevant Dependency Injections

Unlike service classes with multiple unrelated methods, action classes are focused. Each class handles one task and receives only the dependencies it needs. This results in constructors that are concise and purposeful. With this approach, you avoid having a constructor filled with many injected dependencies that may only be used by a few methods.

Unlike service classes with multiple unrelated methods, action classes are focused. Each class handles one task and receives only the dependencies it needs. This results in constructors that are concise and purposeful.

3. Easier to Test in Isolation

Since an action encapsulates a single unit of work, it becomes easier to test independently of the HTTP layer or other services.

public function test_it_creates_a_user()
{
$action = new CreateUser();
 
$user = $action->handle([
'name' => 'Jane Doe',
'email' => '[email protected]',
'password' => bcrypt('password'),
]);
 
$this->assertDatabaseHas('users', [
'email' => '[email protected]',
]);
 
$this->assertEquals('Jane Doe', $user->name);
}

Best Practices

1. Structure: Place actions under app/Actions

Organize your action classes in the app/Actions directory for better discoverability.

app/
└── Actions/
├── CreateUser.php
├── UpdateUser.php

2. Naming Convention: {Action}{Resource}.php

Use a clear and consistent naming convention such as CreateUser, UpdateProfile, or DeletePost.

3. Method Name: Prefer handle()

Stick with handle() for consistency with Laravel's jobs and listeners. Alternatives include __invoke, execute(), or run() but only if your team prefers a different standard.

public function handle(array $data): User

4. Method Parameters

Pass in either the resource and data, or just the data, depending on the use case.

// For updating existing resource
public function handle(User $user, array $data): User
 
// For creating new resource
public function handle(array $data): User

5. Wrap Logic in DB::transaction()

If your action performs multiple operations or calls other actions, wrap the logic inside a transaction to ensure consistency.

public function __construct(
public SyncUserRoles $syncUserRoles,
public AssignTeam $assignTeam,
) {}
 
public function handle(array $data): User
{
return DB::transaction(function () use ($data) {
$user = User::create($data);
 
// injected actions
$this->syncUserRoles->handle($user, $data['roles']);
 
$this->assignTeam->handle($user);
 
return $user;
});
}

6. Broadcast Events When Necessary

If your action modifies a resource and you want to notify the frontend (e.g., via Reverb or Inertia), broadcast events.

use App\Events\UserCreated;
 
broadcast(new UserCreated($user))->toOthers();

7. Return the Resource

Return the modified or created resource from your action.

return $user;

8. Inject Other Actions When Needed

Action classes can depend on other actions to compose complex behavior.

public function __construct(
protected SyncUserRoles $syncUserRoles,
protected AssignTeam $assignTeam,
) {}

More Resources

Nabil Hassen
Nabil Hassen
Full Stack Web Developer

Stay Updated.

I'll you email you as soon as new, fresh content is published.

Thanks for subscribing to my blog.

Latest Posts