Bookmark

Design Patterns in Laravel

Design Patterns in Laravel

Design pattern is a general, reusable solution to a commonly occurring problem within a given context in software design. It serves as a blueprint that can be implemented in your code to solve design challenges, promoting best practices and improving code maintainability, scalability, and readability.

1. MVC (Model-View-Controller)

The MVC pattern divides an application into three interconnected components:

  • Model: Represents the data and business logic of the application. In Laravel, Eloquent ORM is used to interact with the database.
  • View: Handles the presentation layer. Laravel's Blade templating engine simplifies the creation of dynamic views.
  • Controller:Manages the user input and updates the model or view accordingly. Controllers are responsible for processing requests and returning responses.

Example

Model

The Product model represents products in the database and interacts with the products table.

// app/Models/Product.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $fillable = ['name', 'price', 'description'];
}

View

The Blade view displays product information.

<!-- resources/views/products/index.blade.php -->
<!DOCTYPE html>
<html>
<head>
    <title>Products</title>
</head>
<body>
    <h1>Product List</h1>
    @foreach ($products as $product)
        <div>
            <h2>{{ $product->name }}</h2>
            <p>${{ $product->price }}</p>
            <p>{{ $product->description }}</p>
        </div>
    @endforeach
</body>
</html>

Controller

The ProductController fetches products from the model and passes them to the view.

// app/Http/Controllers/ProductController.php
namespace App\Http\Controllers;

use App\Models\Product;

class ProductController extends Controller
{
    public function index()
    {
        $products = Product::all();
        return view('products.index', compact('products'));
    }
}

Routes

Define a route for the controller action.

// routes/web.php
use App\Http\Controllers\ProductController;

Route::get('/products', [ProductController::class, 'index']);

2. Repository Pattern

The Repository Pattern abstracts data access logic, providing a clean API for interacting with data sources. This pattern decouples data access from business logic.

Repository Interface

Define methods for accessing user data.

// app/Repositories/UserRepositoryInterface.php
namespace App\Repositories;

interface UserRepositoryInterface
{
    public function all();
    public function find($id);
}

Repository Implementation

Implement the interface and interact with the User model.

// app/Repositories/UserRepository.php
namespace App\Repositories;

use App\Models\User;

class UserRepository implements UserRepositoryInterface
{
    public function all()
    {
        return User::all();
    }
    
    public function find($id)
    {
        return User::find($id);
    }
}

Binding Repository

Bind the repository to the interface in a service provider.

// app/Providers/AppServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Repositories\UserRepositoryInterface;
use App\Repositories\UserRepository;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->bind(UserRepositoryInterface::class, UserRepository::class);
    }
}

Using the Repository

Inject the repository into a controller.

// app/Http/Controllers/UserController.php
namespace App\Http\Controllers;

use App\Repositories\UserRepositoryInterface;

class UserController extends Controller
{
    protected $userRepository;

    public function __construct(UserRepositoryInterface $userRepository)
    {
        $this->userRepository = $userRepository;
    }
    
    public function index()
    {
        $users = $this->userRepository->all();
        return view('users.index', compact('users'));
    }
}

Routes

Define a route for the controller action.

// routes/web.php
use App\Http\Controllers\UserController;

Route::get('/users', [UserController::class, 'index']);

3. Singleton Pattern

The Singleton Pattern ensures that a class has only one instance and provides a global point of access to it. Laravel’s service container often uses this pattern.

Service Class

Define a service class that handles configuration settings.

// app/Services/ConfigService.php
namespace App\Services;

class ConfigService
{
    protected $settings;

    public function __construct()
    {
        $this->settings = config('app.settings');
    }

    public function get($key)
    {
        return $this->settings[$key] ?? null;
    }
}

Binding Singleton

Register the service as a singleton in a service provider.

// app/Providers/AppServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\ConfigService;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(ConfigService::class, function ($app) {
            return new ConfigService();
        });
    }
}

Using Singleton

Retrieve the singleton instance in a controller.

// app/Http/Controllers/SettingsController.php
namespace App\Http\Controllers;

use App\Services\ConfigService;

class SettingsController extends Controller
{
    protected $configService;

    public function __construct(ConfigService $configService)
    {
        $this->configService = $configService;
    }

    public function showSettings()
    {
        $setting = $this->configService->get('site_name');
        return view('settings', compact('setting'));
    }
}

Routes

Define a route for the controller action.

// routes/web.php
use App\Http\Controllers\SettingsController;

Route::get('/settings', [SettingsController::class, 'showSettings']);

4. Observer Pattern

The Observer Pattern defines a one-to-many dependency between objects. When one object changes state, all dependent objects are notified and updated automatically.

Observer

Create an observer to handle user creation events.

// app/Observers/UserObserver.php
namespace App\Observers;

use App\Models\User;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeEmail;

class UserObserver
{
    public function created(User $user)
    {
        Mail::to($user->email)->send(new WelcomeEmail($user));
    }
}

Register Observer

Register the observer in a service provider.

// app/Providers/AppServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Models\User;
use App\Observers\UserObserver;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        User::observe(UserObserver::class);
    }
}

5. Factory Pattern

The Factory Pattern provides a way to create objects without specifying the exact class of the object that will be created. Laravel’s model factories are a good example of this pattern.

Factory Definition

Define a factory for creating user instances.

// database/factories/UserFactory.php
namespace Database\Factories;

use App\Models\User;
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'),
        ];
    }
}

Using Factory

Generate users in tests or seeders.

// database/seeders/UserSeeder.php
namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\User;

class UserSeeder extends Seeder
{
    public function run()
    {
        User::factory()->count(50)->create();
    }
}

6. Decorator Pattern

The Decorator Pattern allows behavior to be added to individual objects, either statically or dynamically, without affecting the behavior of other objects from the same class.

Base Class

Create a base class for notifications.

// app/Notifications/Notification.php
namespace App\Notifications;

class Notification
{
    public function send($message)
    {
        // Send the notification
    }
}

Decorator

Extend the base class to add functionality.

// app/Notifications/EmailNotification.php
namespace App\Notifications;

class EmailNotification extends Notification
{
    public function send($message)
    {
        // Add email-specific logic
        parent::send($message);
    }
}

Usage

Use the decorator to send notifications.

// app/Http/Controllers/NotificationController.php
namespace App\Http\Controllers;

use App\Notifications\EmailNotification;

class NotificationController extends Controller
{
    public function send()
    {
        $notification = new EmailNotification();
        $notification->send('Hello World!');
    }
}
Post a Comment

Post a Comment