What are the ServiceProvider and ServiceContainer design patterns used in Laravel and Symfony?

13

In the Laravel 4 ou 5 and Symfony frameworks, I realize that there are two classes that are essential for the operation of the whole system: ServiceContainer and ServiceProvider.

It seems to be a way for you to store instances of classes with their dependencies already resolved, or else a Closure , which loads the definitions of these dependencies (I think these are the "services"). So, when calling a certain service (which is in the container), we are calling the instance of the desired class simpler.

I'm going to give you an example of what I'm trying to say, but there's no ServiceProvider (service provider), but only using the service container:

Service Container

 class UrlGenerator
 {
      public function __construct(Request $request)
      {}
 }

 class Request{
       public function __construct(Header $header){}
 }

 class Header{}

Here comes the definition of the instances in the container.

 $app->bind('header', function () { 

    return new Header;
 });

 $app->bind('request', function ($app)
 {
      return new Request($app->getService('header'));
 });

 $app->bind('url', function ($app)
 {
      return new UrlGenerator($app->getService('request'));
 });

So if we needed to use the class UrlGenerator , instead of always having to pass as an instance of Request , we could do this:

 $app->getService('url')->getRoot();

Service Provider

In the other case, we have ServiceProvider , which could do the following:

class UrlGeneratorProvider extends ServiceProvider
{
     public function register()
     {
           $this->app->bind('url', function ($app) { /** **/});
     }
}

Then in this case, it would be called by ServiceContainer

$app->setService(new UrlGeneratorProvider);

In this case, I understand that UrlGeneratorProvider , passes the definition needed to create the service, simply through the register method.

I found this pattern interesting and would like to know what their name is. For some frameworks the classes responsible for containing all services are called Application , Container or ServiceContainer . In the case of "service providers" the names are always these, most of the time.

    
asked by anonymous 02.03.2016 / 13:11

1 answer

7

Developed, at the end of last year (2015), my CBT on a comparison between the Codeigniter and Laravel Frameworks 5, and on the use of design patterns in their developments. So I studied a lot about Design Patterns used in Laravel. And one of the things I liked the most about Laravel is that part of ServiceProvider.

You have fully explained the workings of the ServiceProvider and ServiceContainer . These mechanisms conform to the CONTROL INVERSION or (IoC) standard. This pattern is used to decrease class coupling.

See below for a simple class that records a sale of a product and then it should record a log.

public class VendaDeProduto { 
    public function vendeProduto($produto) { 
        //Todo o código para a venda do produto... 
        $log = new Log("Arquivo.txt"); 
        $log->grava($produto); 
    } 
}

Note that the ProductDate classes have the responsibility of instantiating Log classes. But what happens when I need to change the log file name to "FileLog.txt"?

You will need to edit all classes that instantiate the Log class and make this change. For this reason you need to remove the ProductDate class from the control of this instantiation.

There are a few ways to Reverse Control , one of them is using Dependency Injection . See the code below:

public class VendaDeProduto { 
    private $log; 
    public function vendaDeProduto(Log $logVenda) { 
        $this->log = $logVenda; 
    } 
    public function vendeProduto($produto) { 
        //Todo o código para a venda do produto... 
        $log->grava($produto); 
    } 
}
The ProductDell class needs the Log class to create a Log, but in this code the ProductDomain class received an instance of the Log class! That is, now it no longer worries about creating the Log class and simply uses it.

Now is the best thing about Laravél (in my opnial), the ServiceProvider. Here are one of several ways to implement the Log service:

class LogProvider extends ServiceProvider
{
     public function register()
     {
           $this->app->bind('Log', function ($app) { 
                return new LogFile("ArquivoLog.txt");
           });
     }
}

Note that in fact when we use the Log service, we are actually using an instance of the LogFile class. If by chance I want to stop recording the log in text file and start writing to a database, I can simply change the implementation of my service, by instantiating the LogEmBanco class .

$this->app->bind('Log', function ($app) { 
    return new LogEmBanco("MySQL");
});

And like a magic, all my logs start to be written to the MySQL database, without changing any other class.

Summarizing your question, search for CONTROL INVERSION and DEPENDENCE INJECTION .

From this link, here is everything I've written and a little more.

link

    
19.04.2016 / 06:43