Advantages of using laravel service container

Time:2021-8-30

This article reprints my personal blog
Original addressSampsonBlog

If the core of the laravel framework is anything, it is undoubtedly a service container。 Understanding the concept of service container is very important for us to use laravel. It should be said that whether we understand the concept of service container is an important condition to distinguish whether we are starting laravel or not. Because the whole framework is built on the basis of service container.

Advantages of using laravel service container
The laravel service container is like a highly automated factory. You can customize the model and use a specific interface to manufacture what you need

Because the service container is used, most objects in laravel are instantiated in this way:

$obj1 = $container->make('class1', 'class2');
$obj2 = $container->make('class3', 'class4');

However, without using the service container, you can do the same in the following way:

$obj1 = new class1(new class2());
$obj2 = new class3(new class4());

So what are the advantages of using a service container? Let’s analyze its advantages through some specific examples:

Example 1: send mail

We encapsulate the function of sending mail into a class. When it needs to be used, instantiate and call the sending method.
The following is a common way not to use the laravel service container:

/**
 *Send mail service class
 */
class EmailService{
    public function send(){
        //Todo send mail method
    }
}

//If you want to send an email anywhere, we'll copy the following two lines of code
$emailService = new EmailService();
$emailService->send();

After using the laravel service container:

$this->app->bind('emailService', function ($app) {
    return new EmailService();
});

//If you want to send an email anywhere, we'll copy the following two lines of code
$emailService = app('emailService');
$emailService->send();

This makes our code more concise, and because of the middle tier and improved flexibility (decoupling), it becomes more convenient to test (we can forge classes to replace emailservice classes) or optimize emailservice classes.

//Just change this place
$this->app->bind('emailService', function ($app) {
    return new SupperEmailService();
});

We don’t need to move the other parts of the call at all. If we don’t have this binding operation, we have to make changes in every place where the mail service is used.

//Every place you use the eamilservice class needs to be changed
$emailService = new SupperEmailService();
$emailService->send();

Example 2: implement the singleton mode

As in the above example, for performance reasons, you need the suppleeamilservice class to implement the singleton mode. Therefore, without using the laravel service container, you change the suppleemailservice class as follows:

class SupperEamilService{
      //Create a static private variable to hold the class object
     static private $instance;
   
      //Prevent direct creation of objects
      private function __construct(){
         
     }
         //Prevent cloning objects
     private function __clone(){
 
     }
     static public function getInstance(){
                 //Determine whether $instance is a uni object
                 //Not created
         if (!self::$instance instanceof self) {
             self::$instance = new self();
         }
         return self::$instance;
         
     }
     
     //Send mail method
     public function send(){
        
     }
 }

In addition, since the constructor of suppleeamilservice class is now private and the object cannot be instantiated through the new keyword, it should be changed to this in every instance of suppleemailservice class:

$emailService=SupperEmailService::getInstance();
$emailService->send();

The laravel service container naturally supports singletons. The following is the implementation of laravel:

//Just change bind to singleton 
$this->app->singleton('emailService', function ($app) {
    return new SupperEmailService();
});

To implement a singleton, you only need to change one line of code, change the original bind method to singleton, and the singleton is taken out through the container. It’s really convenient.

Example 3: travelers travel

This example assumes that a traveler can travel to Tibet by train or leg.
Do not use the laravel service container:

<?php

interface TrafficTool
{
  public function go();
}

class Train implements TrafficTool
{

  public function go()
  {
  echo "train....";
  }
}

class Leg implements TrafficTool
{
  public function go()
  {
  echo "leg..";
  }
}

class Traveller
{
  /**
  * @var Leg|null|Train
  *Travel tools
  */
  protected $_trafficTool;

  public function __construct(TrafficTool $trafficTool)
  {
  $this->_trafficTool = $trafficTool;
  }

  public function visitTibet()
  {
  $this->_trafficTool->go();
  }
}

When travelers travel by train, we usually write:

<?php
 $train = new Train();
$tra = new Traveller($train);
$tra->visitTibet();

In fact, this way of writing is very good, because the dependence on travel tools has been transferred to the outside through the interface. However, when using new to instantiate an object, there will still be dependencies. For example, $tra = New Traveler ($traffictool), which means that we must have a $traffictool before creating a traveler, that is, the traveler depends on traffictool. When using new to instantiate the traveler, the traveler and traffictool are coupled. In this way, The two components can’t be separated.

Now let’s take a look at how it is implemented using the laravel service container:
Bind classes in the service container

<?php
namespace App\Providers;

use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;

class RepositoryServiceProvider extends ServiceProvider
{
  public function register()
  {
     //Bind classes in the service container
     $this->app->bind( 'TrafficTool', 'Train');
     $this->app->bind('Traveller', 'Traveller');
  }
}

Instantiate object

<?php
//Instantiate object
$tra = app()->make('Traveller');
$tra->visitTibet();

When we use the service container to obtain the object of the travel class, the container will automatically inject the parameters required by the object. Before that, I only need to bind specific classes, which reflects the real automation and completely decouples the travel class from the travel tool class. When we need to change the way we travel, we just need to change the binding.

summary

Several simple examples are given above. If you can fully understand and master the laravel service container, it will provide you with more convenience in actual development. Of course, it is not perfect. The next blog intends to describe its shortcomings. In short, developing strengths and avoiding weaknesses in practical use is the key.