Published on: 2022-10-17
Laravel remains one of the most popular PHP frameworks today, and writing maintainable code is critical as your project scales.
One of the core foundations of good software design is the SOLID principles. Letโs explore how these principles apply to a Laravel project, with examples and a class diagram to tie it together.
โ๏ธ Prerequisites
- Laravel version: 9.x (current LTS release)
- PHP version: 8.0+
๐งฉ What is SOLID?
- S: Single Responsibility Principle
- O: Open/Closed Principle
- L: Liskov Substitution Principle
- I: Interface Segregation Principle
- D: Dependency Inversion Principle
๐ Example Context: User Registration Flow
Imagine youโre building a user registration feature with email verification. Hereโs a bad design:
class UserController extends Controller
{
public function register(Request $request)
{
$validated = $request->validate([...]);
$user = User::create($validated);
// Sending email directly here violates SRP
Mail::to($user->email)->send(new WelcomeMail($user));
return response()->json($user);
}
}
๐จ Why is this bad?
The controller:
- Handles validation
- Creates the user
- Sends email
It violates the Single Responsibility Principle by doing too much.
โ Applying SOLID Principles
1. Single Responsibility Principle (SRP)
Each class or method should have only one reason to change. Refactor:
class UserService
{
public function register(array $data): User
{
$user = User::create($data);
event(new UserRegistered($user));
return $user;
}
}
Now your UserController only handles the HTTP layer, and the email logic moves to an event listener.
2. Open/Closed Principle (OCP)
Your code should be open for extension, closed for modification.
Instead of modifying UserService to handle SMS welcome messages in the future, extend functionality via events and listeners:
// EventServiceProvider.php
protected $listen = [
UserRegistered::class => [
SendWelcomeEmail::class,
SendWelcomeSMS::class,
],
];
3. Liskov Substitution Principle (LSP)
Child classes must be substitutable for parent classes. Example:
If you extend Laravelโs Notification class, your subclass should work anywhere a Notification is expected without breaking functionality.
4. Interface Segregation Principle (ISP)
Donโt force classes to implement interfaces they donโt use. Example:
Instead of a single bloated interface:
interface UserActions {
public function login();
public function register();
public function logout();
public function verifyEmail();
}
Split into smaller, focused interfaces.
5. Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions.
For example, using repositories:
interface UserRepositoryInterface {
public function create(array $data): User;
}
class EloquentUserRepository implements UserRepositoryInterface {
public function create(array $data): User {
return User::create($data);
}
}
Inject UserRepositoryInterface instead of a concrete implementation in your service.
๐ SOLID Principle Class Diagram (Laravel Context)
๐ง TL;DR
- Laravel 9 + PHP 8 support clean OOP patterns out of the box.
- Following SOLID principles improves readability, testability, and extensibility.
- Use events, services, and interfaces to keep your Laravel codebase robust.