DEV Community

Cover image for Enhancing Laravel Applications with Action-Based Architecture 💡
Peter B. Youssef
Peter B. Youssef

Posted on

Enhancing Laravel Applications with Action-Based Architecture 💡

Laravel is a popular PHP framework that provides a structured and elegant way to build web applications. As your application grows in complexity, maintaining clean and organized code becomes crucial.
One approach to enhance code organization is by using the concept of "Actions".
In this article, we will delve into what Laravel Actions are, why they are beneficial, and provide practical examples of how to implement them in your Laravel applications.

What Are Laravel Actions?

Laravel Actions are a design pattern that helps organize your application's logic into reusable and encapsulated classes.
They promote the Single Responsibility Principle (SRP) by separating different functionalities into distinct classes, resulting in cleaner and more maintainable code. Actions allow you to encapsulate the steps required to perform a specific action, making your codebase more modular and easier to test.

Benefits of Using Actions:

  1. Modularity: Actions encourage breaking down your application's functionalities into small, focused classes. This promotes code reuse and ensures that changes to one part of the application have minimal impact on other parts.

  2. Readability: With Actions, your code becomes self-documenting. Each Action class represents a specific action or use case, making it easier for developers to understand the purpose of the code.

  3. Testability: Actions are highly testable because they encapsulate a specific functionality. You can write unit tests for each Action to ensure that it behaves as expected.

  4. Separation of Concerns: Actions separate different concerns, such as validation, business logic, and database interactions. This leads to cleaner code where each class focuses on a single task.

Implementing Actions:

Let's illustrate the concept of Laravel Actions with a practical example.

Example 1: Creating a User
Suppose you want to create a new user in your application. You can encapsulate this logic into an Action class.

a) Create the Action class:

php artisan make:action CreateUserAction
Enter fullscreen mode Exit fullscreen mode

b) Open CreateUserAction.php and define the logic:

namespace App\Actions;

use App\Models\User;

class CreateUserAction
{
    public function execute(array $data)
    {
        return User::create($data);
    }
}
Enter fullscreen mode Exit fullscreen mode

c) Use the Action in a controller:

namespace App\Http\Controllers;

use App\Actions\CreateUserAction;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function store(Request $request, CreateUserAction $createUserAction)
    {
        $data = $request->all();
        $user = $createUserAction->execute($data);

        return response()->json($user, 201);
    }
}
Enter fullscreen mode Exit fullscreen mode

Example 2: Sending Email Notifications
Let's say you want to send email notifications to users. Create an Action to handle this functionality.

a) Create the Action class:

php artisan make:action SendEmailNotificationAction
Enter fullscreen mode Exit fullscreen mode

b) Open SendEmailNotificationAction.php and define the logic:

namespace App\Actions;

use Illuminate\Support\Facades\Mail;
use App\Mail\NotificationEmail;

class SendEmailNotificationAction
{
    public function execute($user, $message)
    {
        Mail::to($user->email)->send(new NotificationEmail($message));
    }
}
Enter fullscreen mode Exit fullscreen mode

c) Use the Action in a controller or service:

namespace App\Http\Controllers;

use App\Actions\SendEmailNotificationAction;
use App\Models\User;
use Illuminate\Http\Request;

class NotificationController extends Controller
{
    public function sendNotification(Request $request, SendEmailNotificationAction $sendEmailNotificationAction)
    {
        $user = User::find($request->user_id);
        $message = $request->message;

        $sendEmailNotificationAction->execute($user, $message);

        return response()->json(['message' => 'Notification sent']);
    }
}
Enter fullscreen mode Exit fullscreen mode

Example 3: Creating a Blog Post
Assume you want to create a blog post in your application using an Action.

a) Create the Action class:

Create an Action class named CreateBlogPostAction using the artisan command:

php artisan make:action CreateBlogPostAction
Enter fullscreen mode Exit fullscreen mode

b) Open CreateBlogPostAction.php and define the Action Logic:

Open the CreateBlogPostAction.php file and define the logic for creating a new blog post. This may involve validating input, interacting with the database, and any other necessary steps.

use App\Models\BlogPost;

class CreateBlogPostAction
{
    public function execute(array $data)
    {
        // Validate input data
        $validatedData = validator($data, [
            'title' => 'required|string',
            'content' => 'required|string',
        ])->validate();

        // Create a new blog post
        return BlogPost::create($validatedData);
    }
}

Enter fullscreen mode Exit fullscreen mode

c) Use the Action in a controller:

In your controller, you can use the Action to handle the creation of a new blog post:

use App\Actions\CreateBlogPostAction;

class BlogPostController extends Controller
{
    public function store(Request $request, CreateBlogPostAction $createBlogPostAction)
    {
        $data = $request->only(['title', 'content']);
        $blogPost = $createBlogPostAction->execute($data);

        // Redirect or return response
    }
}
Enter fullscreen mode Exit fullscreen mode

These examples demonstrate how to use Actions in various scenarios within a Laravel application.
By encapsulating specific logic into Action classes, you can achieve better code organization, modularity, and testability.

Additional Considerations and Best Practices:

  1. Dependency Injection: In the example above, we used dependency injection to inject the CreateBlogPostAction into the controller. This approach ensures that your controller remains focused on handling HTTP-related concerns while delegating the actual business logic to the Action class.

  2. Error Handling: Consider adding error handling to your Actions. You can throw custom exceptions or return error messages based on the outcome of the Action's execution.

  3. Authorization and Validation: Actions can encapsulate not only business logic but also authorization and validation rules. This keeps your controller methods concise and easy to understand.

  4. Reusable Actions: As your application grows, you can reuse Actions across different parts of your application. For example, the same CreateBlogPostAction could be used in an API endpoint and a web form.

  5. Unit Testing: Write comprehensive unit tests for your Action classes to ensure they function as expected. Laravel's testing tools make it straightforward to test individual components.

  6. Documentation: Document your Action classes with clear descriptions of their purpose, input data, and expected outcomes. This helps other developers understand how to use them effectively.

Conclusion:

Laravel Actions provide a structured and elegant approach to organizing your application's logic. By encapsulating specific actions or use cases into separate classes, you can achieve greater modularity, readability, and testability in your codebase. Actions encourage adhering to best practices, such as the Single Responsibility Principle and the separation of concerns. As you continue developing your Laravel applications, consider adopting the Action design pattern to create cleaner, more maintainable, and more efficient code.

Keep innovating and coding with a smile! 😉👌

Top comments (0)