If you want to code like a pro in Laravel, understanding design patterns is a game-changer. Laravel itself is built on various design patterns, and using them correctly can make your applications more scalable, maintainable, and efficient. Here are some essential Laravel design patterns you need to know:
- Singleton Pattern π Ensures a class has only one instance and provides a global point of access. Use case: When you need a single shared instance, like a logging service, database connection, or configuration settings.
β Example (Service Binding in Laravel)
// AppServiceProvider.php
public function register()
{
$this->app->singleton('App\Services\PaymentGateway', function ($app) {
return new PaymentGateway();
});
}
Why? This ensures that PaymentGateway is only instantiated once and reused throughout the app.
References:
Refactoring Guru β Singleton in PHP
Elevating Laravel Development with the Signleton Design Pattern
How to use Laravelβs bind and signleton methods
- Repository Pattern π Decouples database logic from business logic. Use case: Makes it easier to switch between databases or mock data for testing.
β Example (Using a Repository in Laravel)
// Interface
interface UserRepositoryInterface {
public function getAllUsers();
}
// Repository Implementation
class UserRepository implements UserRepositoryInterface {
public function getAllUsers() {
return User::all();
}
}
// Binding in AppServiceProvider
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
Why? Your controllers only depend on UserRepositoryInterface, so you can swap implementations without modifying business logic.
- Factory Pattern π Creates objects without specifying the exact class to instantiate. Use case: When you need dynamic object creation, such as generating test data or creating models with varying attributes.
β Example (Laravel Model Factories)
// Define in database/factories/UserFactory.php
use Illuminate\Database\Eloquent\Factories\Factory;
class UserFactory extends Factory {
protected $model = User::class;
public function definition() {
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'password' => bcrypt('password')
];
}
}
// Use in tests or seeders
User::factory()->count(10)->create();
Why? Automates object creation for testing and seeding without manually writing insert queries.
- Strategy Pattern π Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Use case: Payment gateways, authentication strategies, or data formatting.
β Example (Payment Strategy in Laravel)
interface PaymentStrategy {
public function pay($amount);
}
class PayPalPayment implements PaymentStrategy {
public function pay($amount) {
return "Paid $amount via PayPal";
}
}
class StripePayment implements PaymentStrategy {
public function pay($amount) {
return "Paid $amount via Stripe";
}
}
// Using the strategy
function processPayment(PaymentStrategy $paymentMethod, $amount) {
return $paymentMethod->pay($amount);
}
echo processPayment(new PayPalPayment(), 100);
Why? Allows switching payment providers without modifying core payment logic.
- Observer Pattern π Allows objects to be notified when another objectβs state changes. Use case: Email notifications, logging, updating user stats.
β Example (Laravel Event Listeners)
// Event: UserRegistered
class UserRegistered {
public $user;
public function __construct(User $user) {
$this->user = $user;
}
}
// Listener: SendWelcomeEmail
class SendWelcomeEmail {
public function handle(UserRegistered $event) {
Mail::to($event->user->email)->send(new WelcomeEmail());
}
}
// Fire Event
event(new UserRegistered($user));
Why? Decouples the event trigger from the response (e.g., sending an email, logging activity).
- Decorator Pattern π Dynamically adds behavior to objects without modifying their class. Use case: Wrapping existing functionality with extra features, like adding logging to a service.
β Example (Using Middleware in Laravel)
// LoggingMiddleware.php
class LoggingMiddleware {
public function handle($request, Closure $next) {
Log::info("Request made to: " . $request->url());
return $next($request);
}
}
Why? Keeps code modular and applies behavior dynamically without modifying the original request handling.
- Facade Pattern π Provides a static interface to a class while maintaining flexibility. Use case: Simplifies access to complex subsystems, like caching, authentication, or logging.
β Example (Laravel Facade Usage)
use Illuminate\Support\Facades\Cache;
// Store a value
Cache::put('key', 'value', 3600);
// Retrieve value
$value = Cache::get('key');
Why? Facades make complex functionality easily accessible while keeping the underlying implementation flexible.
- Command Pattern π Encapsulates a request as an object, allowing for queuing, logging, and undo mechanisms. Use case: Queue jobs, scheduled tasks, and artisan commands.
β Example (Laravel Queue Jobs)
class SendEmailJob implements ShouldQueue {
public function handle() {
Mail::to('user@example.com')->send(new WelcomeEmail());
}
}
// Dispatch Job
dispatch(new SendEmailJob());
Why? Helps structure background tasks and make them manageable.
Conclusion
Using design patterns in Laravel improves code quality, maintainability, and scalability. Mastering these patterns helps you write professional-grade applications that are modular, testable, and efficient.
π Which pattern do you find most useful in your projects?
Top comments (0)