Brief Overview Of Design Pattern Used in Laravel
In Object-oriented languages, we the programmers use the objects to do whatever we want to have the result we desire. We have many types of objects, situations, and problems in an application.
That means we need more than just one approach to solve different kinds of problems. To enhance the quality of our web applications implementing design patterns is very necessary.
Every programming language has a specific design pattern. In this tutorial, we’re going to learn about Laravel design patterns.
Prerequisite :
To complete this tutorial all you need to have is a configured computer, a text editor, a web server installed on your computers like Xampp or WampServer, composer(package manager), and a basic understanding of PHP and Laravel.
Laravel Design patterns
- Builder pattern
- Factory pattern
- Strategy pattern
- Provider pattern
- Repository pattern
- Facade pattern
Builder pattern:
The Builder pattern is one of the main design patterns of Laravel. It separates the construction of a complex object from its representation so that the same construction process can create different representations. A builder is good for creating complex products.
All the creational patterns focus on producing products. Some products are just naturally complex, though. So in that case, you can represent the building process to a director and builder.
A real-life example of the Builder pattern is the construction of a car. Let’s think we have a builder class called CarBuilder.
class CarBuilder
{
public function make(CarBuilderInterface $car):Car
{
$car->start();
$car->break();
return $car;
}
}
It returns an object which implements the Car interface. Those start()
and break()
methods are defined into CarBuilderInterface
initially.
interface CarBuilderInterface
{
public function start();
public function break();
}
Now, if we want to make a new car, at first we have to create an object of the CarBuilder class and then create a new car using builder which returns the Car instance. The code of creating an object of the CarBuilder is given below-
class CarBuilder{
public function make(CarBuilderInterface $car):Car
{
$car->start();
$car->break();
return $car;
}
}
//Create a object of CarBuilder class
$carBuilder= new CarBuilder();
//Create car using builder which returns Car instance
$car= $carBuilder->make(new CaroneBuilder());
Now we can add much functionality to CaroneBuilder
as our needs.
class CaroneBuilder implements CarBuilderInterface
{
protected $car;
public function start():Car
{
$this->car = new Car();
return $this->car;
}
public function break():Car
{
$this->car = new Car();
return $this->car;
}
}
We can also create many new model car
by using the CarBuilder
class.
The Builder pattern in Laravel is also called the manager pattern. It builds complex objects step by step and returns them. It can decide whether to return something or not.
The above example was to understand the core concept of a Builder class. Now let’s see, how Laravel does it for the default builder class-
Illuminate\Mail\TransportManager
class TransportManager extends Manager
{
protected function createSmtpDriver()
{
//Code for building a SmtpTransport class
}
protected function createMailgunDriver()
{
//Code for building a MailgunTransport class
}
protected function createSparkpostDriver()
{
//Code for building a SparkpostTransport class
}
protected function createLogDriver()
{
//Code for building a LogTransport class
}
}
For example, here we’re using TransportManager
, which is for sending emails. Let’s think we have to create a SmtpDriver
which is responsible for creating the SmtpTransport
class and this class is also responsible for sending emails. But in the end, it extends the Transport class. So, we always have to have the same interface get back from the TransportManager
. Let’s look at the default driver method-
class TransportManager extends Manager
{
public function getDefaultDriver()
{
return $this->app['config']['mail.driver'];
}
}
The default driver method in Laravel uses config, so. it’s easy to switch around. Suppose we’re using the Smtp and want to switch to MailGun or another email service, in this default driver we can do that easily. This is because of the manager(builder) pattern. The manager pattern exactly knows how to create these objects and return them to us. In the config file, we have to use the Smtp driver.
//config/mail.php
'driver' => env('MAIL_DRIVER', 'smtp'),
When the TransportManager
calls the method getDefaultDriver()
and finds the Smtp is set then it creates the SmtpDriver. Then we can send an email.
Laravel manager pattern examples:
Illuminate\Auth\AuthManager
Illuminate\Broadcasting\BroadcastManager
Illuminate\Cache\CacheManager
Illuminate\Filesystem\FilesystemManager
Illuminate\Mail\TransportManager
Illuminate\Notifications\ChannelManager
Illuminate\Queue\QueueManager
Illuminate\Session\SessionManager
Factory pattern:
The Factory pattern is a creational pattern that uses factory methods to deal with the problem of creating objects by specifying their concrete classes. The factory is responsible for creating objects, not the clients. Multiple clients can call the same factory.
Let’s go with the example of a car. If we want to make a car, we need a lot of parts like battery, radiator, brake, fuel tank, etc. To create those car parts, we need a factory.
Now, we can create those parts of our own. But it’s time-consuming and let’s think we don’t want to wait that long instead of we want a quick process like we just want to assemble a car.
We can contact different companies who sell those parts and buy from them. So, in this case, those companies are the creator or factories.
We don’t care how they make them as long as we get those parts. With this factory pattern, it’s easy to test, mock and isolate.
interface CarFactoryContract
{
public function make(array $carbattery = []): Car;
}
class CarFactory implements CarFactoryContract
{
public function make(array $carbattery = [])
{
return new Car($carbattery);
}
}
$car= (new CarFactory)->make('lithium', 'nickel', 'cobalt');
So, if we look at this simple car factory example, we have CarFactory which is responsible to make a car, and in the end, we get an instance of a car.
Laravel uses this same pattern for the views. Let’s see an example:
class PostsController
{
public function index()
{
$posts= Post::all();
return view('posts.index', ['posts' => $posts]);
}
}
In this case, we’re using the view helper and calling the view method. Here, the view method is the helper method, which is calling the factory in the end. We give the view some data which the view needs to build.
//Illuminate\Support\helpers.php
/**
* @return \Illuminate\View\View\Illuminate\Contracts\View\Factory
* /
function view($view = null, $data = [], $mergeData = [])
{
$factory = app(ViewFactory::class);
if(func_num_args() === 0){
return $factory;
}
return $factory->make($view, $data, $mergeData);
}
When we look at the helper method, we see a factory is created which is the view factory. This factory calls the make() method and this method is a factory method that always creates and returns the view object for us. This factory is responsible for creating the views we use in our controller.
Strategy pattern:
The strategy pattern is a behavioral pattern. Defines a family of interchangeable algorithms. It always uses an interface. This means the implementation details remain in separate classes. Its programs are to an interface, not an implementation.
Let’s go with the example of the car. Suppose, we have a new car ready and we’re going to deliver it. To deliver the car, we need a strategy. There are many ways to deliver a car. At first, we need to define the interface.
interface DeliveryStrategy
{
public function deliver(Address $address):DeliveryTime;
}
Here, our interface name is DeliveryStrategy. In this interface, we have a method called deliver
. We’re going to provide an address and it’ll return something for us. So, each strategy needs a deliver
method and an address object.
class ShipDelivery implements DeliveryStrategy
{
public function deliver(Address $addrress):
{
$route= new ShipRoute($address);
echo $route->calculateCosts();
echo $route->calculateDeliveryTime();
}
}
For example, we’re going to deliver the car by ship. The shipping delivery uses the shipping route. It calculates the delivery costs and time for us. We can now create a CarDelivery class that expects one of the delivery strategies and deliver the car to us.
class CarDelivery
{
public function deliverCar(DeliveryStrategy $strategy, Address $address)
{
return $strategy->deliver($address);
}
}
$address= new Address('example address 12345');
$delivery= new CarDelivery();
$delivery=>deliver(new ShipDelivery(), $address);
Because the DeliveryStrategy is an interface we give it to the CarDelivery
class to add new strategies. We already have ShipDelivery
and now we can create another delivery strategy like TruckDelivery
if we want to deliver the car by truck.
It can have different calculations but uses the same interface. Each implementation will be interchangeable means we can switch around but still, it’s going to do the same work which is delivery.
We can include our XML loader to translate the XML file and insert it. We can create our own loader folder and place it in our service provider.
class XMLLoader implements Illuminate\Translation\LoaderInterface
{
//Load the messages for the given locale.
public function load($locale, $group, $namespace= null);
//Add a new namespace to the loader.
public function addNamespace($namespace, $hint);
//Get an array of all the registered namespaces.
public function namespace();
}
In this example, class XMLloader expects the LoaderInterface which is the strategy just like our delivery strategy. There is only one load implemented in Laravel, but as we’ve seen, we can create our own load. The addNamespace()
method passes the namespaces to the load() function as grouped.
Provider pattern:
The provider pattern is the core of the Laravel framework and the packages we use. It’s a set of patterns for some essential services. It’s like a plug-in or packages into our service.
It provides classes that we can use in our namespace. Suppose, we’re going to sell Toyota’s car and want to create our franchise. Toyota will give us the way to build cars. How we set up our warehouse, it’s totally up to us.
But, the ways of making all the cars, necessary car parts and all the paperwork they’ll provide to us. So, in that case, they are the provider. In this case, our CarBuilder or CarManager is provided by Toyota.
class ToyotaServiceProvider extends ServiceProvider
{
public function register(){
//Register your services here
}
}
So, when we look at the provider pattern, we have a register method here like a normal service provider. So, basically, we need to tell Laravel that we have an implementation available for us in the code.
use App\Toyotas\CarManager;
class CarServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('car-manager', function($app) {
return new CarManager();
});
}
}
When want to access that manager class for our application, we have to bind it to our ServiceProvider and it becomes available in our code. Basically, we ask laravel to create the class for us, when we ask for that string.
The ServiceProvider is used to register services to the container. Knowingly or unknowingly we use it a lot because every functionality in Laravel is a service like the Eloquent, Translation, Cache, Config provider, etc that are included in our app.php file. They are the provider pattern and they are interchangeable. We can also add our own implementation there.
//Using instantiation
$carManager = new \App\Toyotas\CarManager();
//Using the provider pattern and the container
$carManager = app('car-manager');
$carone= $carManager->driver('carone');
So, when we call the CarManager, we can instantiate in a normal way or we can use the App\ helper
. In the end, we can call those methods on carManager
and call the driver method on it and the new car ‘carone’ is back. In the same way, we can create many new cars like this-
$cartwo= $carManager->driver('cartwo');
$carthree= $carManager->driver('carthree');
The provider pattern extends the usability of those classes in our application.
Repository pattern:
In simple words, the Repository pattern in a laravel application is a bond between models and controllers. In this process, the model should not be responsible for connecting with or retrieving data from the database. Therefore, to keep our code clean and safe, it is necessary to use the repository. It reduces the duplication and errors of our code.
Let’s think of an example, we have a customer table in our database, where we can add, store, edit and delete customer data. We also have a Customer model. The idea is, we’re going to create an interface and define some methods. Like this-
TestInterface.php:
<?php
//(Here the App\Repositories is the folder name)
namespace App\Repositories;
interface TestInterface
{
public function all();
public function get($id);
public function store(array $data);
public function update($id, array $data);
public function delete($id);
}
?>
For this tutorial, our interface name is TestInterface and we have five methods called all(), get(), store(), update() and delete(). Now our interface is created, we need to create a repository. In the repository, we’re going to call these methods from the interface and implement them. Let’s create a repository-
TestRepositoryOne.php
<?php
//(Here the App\Repositories is the folder name)
namespace App\Repositories;
use App\Models\Customer;
class TestRepositoryOne implemets TestInterface
{
//To view all the data
public function all()
{
return Customer::get();
}
//Get an individual record
public function get($id)
{
return Customer::find($id);
}
//Store the data
public function store(array $data)
{
return Customer::create($data);
}
//Update the data
public function update($id, array $data)
{
return Customer::find($id)->update($data);
}
//Delete the data
public function delete($id)
{
return Customer::destroy($id);
}
}
?>
Here our repository name is TestRepositoryOne which implements the TestInterface. Now, all those methods inside of the interface are accessible in this repository, hence we can use them. For example, the all() function returns all the data from the Customer model through the get() method. The same way is applicable to the other functions.
Now, we have an interface and a repository. But, how does it work? Right? How we’re going to access them to a controller. To use that, we need a bridge that actually binds the interface with the repository.
We can use a ServiceProvider class as a bridge. Let’s create a ServiceProvider.
RepositoriesServiceProvider:
<?php
//(Here the App\Repositories is the folder name)
namespace App\Repositories;
use Illuminate\Support\ServiceProvider;
class RepositoriesServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(
'App\Repositories\TestInterface',
'App\Repositories\TestRepositoryOne'
);
}
}
?>
Our ServiceProvider name is RepositoriesServiceProvider. You can name it as your wish. In this ServiceProvider class, we bind the interface with the repository with a register function. Now we have to add them to the Config.php file.
Config.php:
App\Repositories\RepositoriesServiceProvider::class,
Now, in the UserController, we can actually use the TestInterface class with a construct function.
UserController:
use App\Repositories\TestInterface;
class UserController extends Controller
{
protected $test;
public function __construct(TestInterface $test){
$this->test = $test;
}
//Get the data
public function index(){
$data= $this->test->all();
return view('index', compact(data));
}
//Store the data
public function store(Request $request){
$this->test->store($request->all());
return redirect()->route('customer.index');
}
//Edit the data
public function edit($id){
$data= $this->test->get($id);
return view('edit', compact(data));
}
//Update the data
public function update($id, Request $request){
$this->test->update($id, $request->all());
return redirect()->route('customer.index');
}
//Delete the data
public function delete($id){
$this->test->delete($id);
return redirect()->route('customer.index');
}
}
Now, in a normal way of CRUD, we could just implement those functions in the controller, but let’s think we’ve another controller for our Product table that uses the same CRUD system.
So, we’ve to write the same code again and again and that is not a good practice. If we have another controller, it can also implement the same interface TestInterface. So, we don’t have to declare those same functions again.
Also, it’s a very simple CRUD, but what if we have a very complex problem in our application, then it would be very easy to maintain through the different repositories. Our controller will be clean and testing the application will be easy. Another example is, let’s say we are using MySQL and want to change to MongoDB. Since the Repository Pattern uses interfaces, our application logic will remain the same and all we have to do is change out the repository.
Facade pattern:
The facade pattern provides a combined interface to a set of interfaces in a subsystem. A facade defines a higher-level interface that makes the subsystem easier to use.
The facade pattern isn’t all about covering up bad code, though. It can be used to create a simple public interface that bonds multiple classes working together in some way. Suppose you’re dealing with a large subsystem with hundreds of public methods but you only need a few of them.
You want the facade to correctly construct the various classes in the subsystem and provide those interfaces to you such that you can use them with ease. All of Laravel’s facades are defined in the Illuminate\Support\Facades namespace. Let’s see an example-
In this App\Repositories folder, let’s create a PHP file called Addition.php.
Addition.php:
<?php
namespace App\Repositories;
class calculation
{
public function sum(){
$a = 10;
$b = 20;
$result = $a + $b;
}
}
?>
We have a class called calculation and it has a function called sum. It’s a simple function that returns the sum of two numbers. Now, we can access this function in our application from anywhere. For that, at first, we have to bind it to our service provider.
Let’s create a provider class, where we’re going to bind this class. You can create a service provider with the following PHP artisan command-
php artisan make:provider CustomServiceProvider
In this example, we’re going to use the name CustomServiceProvider.
public function register()
{
app()->bind('calculation', function(){
return new \App\Repositories\Addition;
});
}
In the CustomServiceProvider, there is a register function, where we actually bind our class. The first parameter of our bind function takes the class name which is the calculation and as a second parameter, it passes a function that returns the object of that class.
Now, let’s define a custom class, where we can define the getFacadeAccessor()
method. It defines what to resolve from the container class. The Facade base class makes use of the __callStatic() magic-method to defer calls from our facade to the resolved object.
<?php
namespace App\Repositories;
use Illuminate\Support\Facades\Facade;
class Test extends Facade{
protected static function getFacadeAccessor() {
return 'calculation';
}
}
?>
Inside of the App\Repositories, we created a file called TestFacades.php, and we have a Test class that extends Facade. The static method getFacadeAccessor()
returns the calculation class that is defined in our Addition.php. Now, we have to add this CustomServiceProvider
inside of our config.php file.
‘providers’ => []:
App\Providers\CustomServiceProvider::class,
‘aliases’ => []:
Inside of aliases, we also have to define our TestFacades class name like this-
'Test' => App\Repositories\TestFacades::class,
Now, all the processes are set, we can access the sum() method from anywhere in our applicaiton.
Route::get('/index', function(){
Test::sum();
});
//Result: 30
This ‘Test’ is the name we gave in our aliases array. Now if we have multiple methods in the calculation class, we can access them too.
<?php
namespace App\Repositories;
class calculation
{
public function sum(){
$a = 10;
$b = 20;
$result = $a + $b;
}
public function another_sum(){
$a = 50;
$b = 50;
$result = $a + $b;
}
}
?>
In the calculation class, suppose we have another method called another_sum()
. We can access it too in the same way.
Route::get('/index', function(){
Test::another_sum();
});
//Result: 100
Conclusion:
In this tutorial, we learned about six design patterns of laravel with examples. With design patterns, we can make our code more reusable and solve very complex problems. Design patterns are generally divided into three fundamental groups. Which are Creational patterns, Structural patterns, and Behavioral patterns. As so far, we have discussed six patterns, which are the parts of these main three. But to gain a better knowledge of them, you have to dive deeper and practice more. I hope, this tutorial was helpful for you.