Design Patterns in Laravel

Md. Sohel Amin

Sr. Software Engineer
Mailforge

Github: sohelamin, appzcoder
Twitter: sohel_amin
Email: sohelamincse at gmail dot com

What is Design Pattern?

Design patterns are solutions to recurring problems, guidelines on how to tackle certain problems.

In other words, It is a description or template for how to solve a problem.

Why should use Design Patterns?

  1. To reuse codes in multiple projects
  2. Proven & tested by expert developers
  3. To follow best practices
  4. Maintenance
  5. Documentation
  6. Readability

Types of Design Pattern

1. Creational Design Patterns

These patterns deal with object creation mechanisms.

2. Structural Design Patterns

These design patterns concern class and object composition.

3. Behavioral Design Patterns

These patterns describe interactions between objects and focus on how objects communicate with each other.

Design Patterns used in Laravel 

  1. Builder/Manager Pattern (eg. AuthManager)
  2. Factory Pattern (eg. Validator)
  3. Repository Pattern (eg. UserRepository)
  4. Strategy Pattern (eg. Config/LoaderInterface)
  5. Provider Pattern (eg. AuthServiceProvider)
  6. Facade Pattern (eg. DB, Session)

To be discussed

  1. Facade Pattern
  2. Repository Pattern
  3. Provider Pattern

Facade Pattern

Facade pattern provides a simplified interface to a complex subsystem.

When to use Facade Pattern?

  1. A simple interface is required to access a complex system.
  2. A system is very complex or difficult to understand.
  3. An entry point is needed to each level of layered software.

Facade Pattern

class Cart
{
    public function addProducts($products)
    {
        // Product adding codes goes here
    }

    public function getProducts()
    {
        // Product retrieval codes goes here
    }
}

class Order
{
    public function process($products)
    {
        // Order processing codes goes here
    }
}

Facade Pattern

class Payment
{
    public function charge($charge)
    {
        // Additional charge codes goes here
    }

    public function makePayment()
    {
        // Payment method verify & payment codes goes here
    }
}

class Shipping
{
    public function calculateCharge()
    {
        // Calculation codes goes here
    }

    public function shipProducts()
    {
        // Ship process codes goes here
    }
}

Facade Pattern

class CustomerFacade
{
    public function __construct()
    {
        $this->cart = new Cart;
        $this->order = new Order;
        $this->payment = new Payment;
        $this->shipping = new Shipping;
    }

    public function addToCart($products)
    {
        $this->cart->addProducts($products);
    }

    public function checkout()
    {
        $products = $this->cart->getProducts();

        $this->totalAmount = $this->order->process($products);
    }

    public function makePayment()
    {
        $charge = $this->shipping->calculateCharge();
        $this->payment->charge($charge);

        $isCompleted = $this->payment->makePayment();

        if ($isCompleted) {
            $this->shipping->shipProducts();
        }
    }
}

Facade Pattern

$customer = new CustomerFacade;

$products = [
    [
        'name' => 'Polo T-Shirt',
        'price' => 40,
    ],
    [
        'name' => 'Smart Watch',
        'price' => 400,
    ],
];

$customer->addToCart($products);
$customer->checkout();
$customer->makePayment();

Facade Pattern in Laravel

Example: Config, Input, URL, Route, View, DB etc

return [

    'aliases' => [
        'DB' => Illuminate\Support\Facades\DB::class,
    ]
]

Step1. Open the config file config/app.php

// become
DB::select();

Facade Pattern in Laravel

<?php

namespace Illuminate\Support\Facades;

/**
 * @see \Illuminate\Database\DatabaseManager
 * @see \Illuminate\Database\Connection
 */
class DB extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'db';
    }
}

Step2. Let's open Illuminate\Support\Facades\DB.php

Facade Pattern in Laravel

protected function registerConnectionServices()
{
    $this->app->singleton('db', function ($app) {
        return new DatabaseManager($app, $app['db.factory']);
    });
}

Step3. Let's open Illuminate/Database/DatabaseServiceProvider.php

public function __call($method, $parameters)
{
    return $this->connection()->$method(...$parameters);
}

Step4. Let's open Illuminate/Database/DatabaseManager.php

// become
$app['db']

Underlying the static method

class_alias('Illuminate\Support\Facades\DB', 'DB');
// we can call DB instead of Illuminate\Support\Facades\DB now
// vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php

abstract class Facade
{
    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        return $instance->$method(...$args);
    }
}
DB::select()

Repository Pattern

The Repository pattern is usually used to create an interface between business logic & database access layers of an application

Why should use Repository Pattern?

  1. To makes code easier to maintain by centralizing the data access logic.
  2. To separately testing business and data access logic.
  3. To reduces duplication of code.
  4. To reduce errors

Repository Pattern

// app/Repositories/PostRepositoryInterface.php

<?php

namespace App\Repositories;

interface PostRepositoryInterface
{
    public function getAll();
    public function find($id);
}

Repository Pattern

// app/Repositories/PostEloquentRepository.php

<?php

namespace App\Repositories;

use App\Post;

class PostEloquentRepository implements PostRepositoryInterface
{
    public function getAll()
    {
        return Post::all();
    }

    public function find($id)
    {
        return Post::findOrFail($id);
    }
}

Repository Pattern

// app/Providers/AppServiceProvider.php

public function register()
{
    $this->app->bind(
        'App\Repositories\PostRepositoryInterface', 
        'App\Repositories\PostEloquentRepository'
    );
}

Repository Pattern

// app/Http/Controllers/Admin/PostsController.php

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Repositories\PostRepositoryInterface;

class PostsController extends Controller
{
    protected $postRepository;

    public function __construct(PostRepositoryInterface $postRepository)
    {
        $this->postRepository = $postRepository;
    }

    public function index()
    {
        $posts = $this->postRepository->getAll();

        return view('admin.posts.index', compact('posts'));
    }
}

Provider Pattern

The Provider pattern was formulated by Microsoft. It is a mid layer between an API class and the Business Logic/Data Abstraction Layer of the application.

Provider Pattern

// vendor/laravel/framework/src/Illuminate/Redis/RedisServiceProvider.php

<?php

namespace Illuminate\Redis;

use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;

class RedisServiceProvider extends ServiceProvider
{
    protected $defer = true;


    public function register()
    {
        $this->app->singleton('redis', function ($app) {
            $config = $app->make('config')->get('database.redis');

            return new RedisManager(Arr::pull($config, 'client', 'predis'), $config);
        });

        $this->app->bind('redis.connection', function ($app) {
            return $app['redis']->connection();
        });
    }

    public function provides()
    {
        return ['redis', 'redis.connection'];
    }
}

Provider Pattern

// config/app.php

<?php

return [

    'providers' => [
        Illuminate\Redis\RedisServiceProvider::class,
    ],

    'aliases' => [
        'Redis' => Illuminate\Support\Facades\Redis::class,
    ]
    
]

References

Thank You :)

Any Questions?

Design Patterns in Laravel

By sohelamin

Design Patterns in Laravel

  • 1,551