design patterns in laravel

 Laravel, being a modern PHP framework, encourages the use of various design patterns to enhance code organization, reusability, and maintainability. Some commonly used design patterns in Laravel are

Model-View-Controller (MVC): Laravel follows the MVC pattern, where the application logic is separated into three main components - models (representing data and business logic), views (representing the presentation layer), and controllers (handling the interaction between models and views). Repository Pattern: The repository pattern abstracts the data persistence layer, providing a consistent interface to interact with the underlying data source (e.g., a database). It helps in decoupling the application code from the specific data storage implementation and allows for easier testing and code maintenance. Service Layer Pattern: The service layer pattern is used to encapsulate complex business logic and operations. Services act as an intermediary between controllers and repositories, handling tasks like data manipulation, validation, and interaction with multiple repositories. They help in keeping controllers lean and promote code reuse. Dependency Injection (DI): Laravel's service container allows for easy implementation of dependency injection. By defining dependencies in the container, Laravel automatically resolves and injects them into classes, promoting loose coupling and modular design. Facade Pattern: Laravel's facade pattern provides a simple and expressive way to access complex subsystems or services through a simple static-like interface. Facades allow for convenient access to Laravel's features without directly referencing their underlying implementations. Observer Pattern: Laravel's event system is based on the observer pattern. It allows decoupling of event producers and consumers, enabling loose coupling and separation of concerns. Events can be used to trigger actions, perform logging, send notifications, or update related data based on certain conditions. Strategy Pattern: Laravel's "Contracts" and "Service Providers" are examples of the strategy pattern. Contracts define the expected behavior of classes, while service providers implement specific implementations of those contracts. This pattern allows for swapping out implementations easily, promoting flexibility and extensibility. These are just a few examples of design patterns commonly used in Laravel. Laravel's architecture and features are designed to promote best practices and encourage the use of appropriate design patterns to build robust and maintainable applications.


Are you looking for a way to make your Laravel (or any kinda) application more organized and maintainable? Then design patterns are just what you need! Design patterns are reusable solutions to common programming problems, and Laravel makes it easy to implement them in your application.

Let’s take a look at some of the most popular design patterns used in Laravel, and how you can use them in your own projects:

Factory Pattern:

The factory pattern is used to build objects without showing the client the creation mechanism. When using Laravel, you can utilize the factory pattern to generate new objects with random data. For instance, you might develop a class called “UserFactory” that generates brand-new user objects with arbitrary names and email addresses.

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/

class UserFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/

public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => bcrypt('password'),
'remember_token' => Str::random(10),
];
}

/**
* Indicate that the model's email address should be unverified.
*/

public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}

Singleton Pattern:

To make sure that just one instance of a class is produced and used throughout the programme, utilise the singleton approach. When managing resources like database connections that shouldn’t be duplicated, Laravel’s singleton paradigm may be used. For instance, the singleton pattern may be used to guarantee that only one instance of a “Mailer” class, which delivers emails, is produced.

namespace App\Mail;

use Illuminate\Mail\Mailable;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;

class SingletonMailer extends Mailable
{
use Queueable, SerializesModels;

private static $instance;

private $data;

private function __construct() {
}

public static function getInstance() {
if (null === static::$instance) {
static::$instance = new static();
}

return static::$instance;
}

public function setData($data) {
$this->data = $data;
return $this;
}

public function build() {
return $this->view('emails.example')
->with([
'data' => $this->data,
]);
}
}
use App\Mail\SingletonMailer;
use Illuminate\Support\Facades\Mail;

$singletonMailer = SingletonMailer::getInstance();
$singletonMailer->setData(['test' => 'data']);
Mail::to('test@example.com')->send($singletonMailer);

Repository Pattern:

Repository design: This design is used to decouple the application logic from the database layer. With Laravel, you can leverage the repository pattern to change database implementations more easily without changing the application logic. A “CustomerRepository” class, for instance, can be created to handle database activities on a “Product” model.

<?php

namespace App\Interfaces;

interface CustomerRepositoryInterface
{
public function all();

public function find($id);

public function create(array $data);

// ... Any other methods you need
}
<?php

namespace App\Repositories;

use App\Interfaces\CustomerRepositoryInterface;
use App\Customer;

class CustomerRepository implements CustomerRepositoryInterface
{
protected $model;

public function __construct(User $model)
{
$this->model = $model;
}

public function all()
{
return $this->model->all();
}

public function find($id)
{
return $this->model->find($id);
}

public function create(array $data)
{
return $this->model->create($data);
}

// ... Implement any other methods you have defined
}

Observer Pattern:

The observer pattern is used to set off events when specific activities take place. When particular model events take place, you may take extra actions using Laravel’s observer approach. For instance, you might develop a class called “CustomerObserver” that sends out events (delete customer’s invoices ) whenever a “Customer” model is deleted.

<?php

namespace App\Observers\Api\V1;

use App\Models\Customer;

class CustomerObserver
{
/**
* Handle the Customer "created" event.
*/

public function created(Customer $customer): void
{
//
}

/**
* Handle the Customer "updated" event.
*/

public function updated(Customer $customer): void
{
//
}

/**
* Handle the Customer "deleted" event.
*/

public function deleted(Customer $customer): void
{
$customer->invoices()->delete();
}

/**
* Handle the Customer "restored" event.
*/

public function restored(Customer $customer): void
{
//
}

/**
* Handle the Customer "force deleted" event.
*/

public function forceDeleted(Customer $customer): void
{
//
}
}

Your Laravel application may become more organized, manageable, and adaptable by incorporating design patterns.

Dependency Injection (DI) — possesses a powerful and at the same time simple to use technique. The main goal of this technique is making a class independent of its dependencies by creating depending objects outside of the class and pass the link to those objects in the constructor, setter method, or property of the class where those objects should be used.

The advantage of using dependency injection is — it’s an easy way to swap the class with another one and create mocks for unit testing.

For example, dependency injection in the class constructor or in other words is “constructor injection”:

class User
{
protected $userService;

public function __construct(UserService $userService)
{
$this->userService = $userService
}
}

The UML diagram of this relation is:

Dependency injection in the constructor

Dependency Injection uses with the adjacent principle — Dependency Inversion Principle.

Dependency Inversion Principle (DIP) — means use flexible abstractions like Interfaces instead of the “real” classes. It’s derived from the fifth principle of the object-oriented programming principles SOLID.

The principle states two things:

  • High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g. interfaces).
  • Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.

Above example but using DIP principle will be:

class User
{
protected $userService;

public function __construct(UserServiceInterface $userService)
{
$this->userService = $userService
}
}

And the UserServiceInterface interface definition:

interface UserServiceInterface
{
public function method();
}

The class which implements the above interface:

class UserService implements UserServiceInterface
{
public function method()
{
//
}
}

The UML diagram of this relation is:

Use Dependency Inversion Principle

Dependency Injection in Laravel

Laravel has the Service Container which helps manage the class dependencies in an application. Laravel provides automatic injection using “type hinting” in the constructors of controllers, middlewares, event listeners that are resolved by container. It uses PHP Reflection to automatically resolve dependencies.

You can get the Service Container instance by calling the global Laravel function app():

$container = app();

Further, you can use Laravel Service Container outside of Laravel just install by composer:

composer require illuminate/containeruse Illuminate\Container\Container;$container = Container::getInstance();

Binding

In Laravel registration of the class binding located in Service Providers, it’s a general place for registering Laravel stuff.

To register a new binding you can use the existing register() method in the existing service provider app/Providers/AppServiceProvider.php or create a new service provider by the artisan command php artisan make:provider SomeServiceProvider.

For example, to bind the UserService class to the UserServiceInterface interface (can be used with an abstract class too) using the existing service provider AppServiceProvider:

<?phpnamespace App\Providers;use Illuminate\Support\ServiceProvider;class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->bind('UserServiceInterface', function ($app) {
return new UserService(SOME_SERVICE_CONFIG_VARIABLES);
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
}

or just define without new if not needed to pass additional arguments into the constructor:

$this->app->bind('UserServiceInterface', UserService::class);

It’s easy to assign to it an alias:

$this->app->alias('UserServiceInterface', 'userService');

Also, it’s easy to use the class as Singleton for preventing creating new object each time when a class will be resolved:

$this->app->singleton('UserServiceInterface', UserService::class);

You can extend a class and return a different object but this class must implement the same interface otherwise it will return the exception “Call to undefined method”:

$this->app->extend(UserService::class, function ($userService) {
return new UserServiceWrapper($userService);
});

If you need grouping multiple bindings, you can use tags:

$this->app->tag(UserService::class, 'services');
$this->app->tag(PostService::class, 'services');

Resolving

To resolve a class instance in Laravel you can use the global function resolve():

$userService = resolve('UserServiceInterface');

or use the make() method from the global function app():

$userService = app()->make('UserServiceInterface');

or just use app():

$userService = app('UserServiceInterface');

To retrieve all tagged (grouped) instances use the tagged method with the tag name as a parameter:

$services = app()->tagged('services')

Conclusion

Dependency Injection and Dependency Inversion principle make weak relations between classes which in turn make extending the application and unit testing easier.

I hope after reading this article you have a better understanding of how Service Container works and how it manages DI dependencies in Laravel.

No comments:

Post a Comment

Event listening in react

 How we can listen to som eevents some envents fire like click or automatically user enters into input button , that is event on word type i...