Action Pattern in Laravel: Concept, Benefits, Best Practices
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 controllerclass UserController extends Controller{ public function store(Request $request, CreateUser $createUser) { $user = $createUser->handle($request->validated()); return response()->json($user); }}
// In a job/commandpublic 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', 'password' => bcrypt('password'), ]); $this->assertDatabaseHas('users', [ ]); $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 resourcepublic function handle(User $user, array $data): User // For creating new resourcepublic 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
Stay Updated.
I'll you email you as soon as new, fresh content is published.