Symfony - Container de Serviço

Em qualquer aplicativo, os objetos tendem a aumentar conforme o aplicativo cresce. Conforme os objetos aumentam, a dependência entre os objetos também aumenta. A dependência do objeto precisa ser tratada adequadamente para um aplicativo bem-sucedido.

Conforme discutido no capítulo Componentes, Symfony fornece um componente fácil e eficiente, DependencyInjectionpara lidar com a dependência do objeto. Um contêiner de serviço é um contêiner de objetos com dependência resolvida adequadamente entre eles. Vamos aprender como usar o componente DependencyInjection neste capítulo.

Vamos criar um Greeterclasse. O objetivo da classe Greeter é cumprimentar o usuário conforme mostrado no exemplo a seguir.

$greeter = new Greeter('Hi'); 
$greeter->greet('Jon'); // print "Hi, Jon"

O código completo da classe Greeter é o seguinte.

class Greeter { 
   private $greetingText; 
   
   public function __construct($greetingText) { 
      $this->greetingText = $greetingText; 
   }  
   public function greet($name) { 
      echo $this->greetingText . ", " . $name . "\r\n"; 
   } 
}

Agora, vamos adicionar a classe Greeter ao contêiner de serviço. Symfony forneceContainerBuilderpara criar um novo contêiner. Depois que o contêiner é criado, a classe Greeter pode ser registrada nele usando o método de registro do contêiner.

use Symfony\Component\DependencyInjection\ContainerBuilder; 
$container = new ContainerBuilder(); 
$container 
   ->register('greeter', 'Greeter') 
   ->addArgument('Hi');

Aqui, usamos o argumento estático para especificar o texto de saudação, Hi. Symfony também oferece uma configuração dinâmica de parâmetros. Para usar um parâmetro dinâmico, precisamos escolher um nome e especificá-lo entre% e o parâmetro pode ser definido usando o contêinersetParameter método.

$container = new ContainerBuilder(); 
$container 
   ->register('greeter', 'Greeter') 
   ->addArgument('%greeter.text%');  
$container->setParameter('greeter.text', 'Hi');

Registramos uma classe Greeter com a configuração adequada. Agora, podemos pedir ao contêiner para fornecer um objeto Greeter configurado corretamente usando o contêinerget método.

$greeter = $container->get('greeter'); 
$greeter->greet('Jon'); // prints "Hi, Jon"

Registramos com sucesso uma classe, Greeter no container, buscamos no container e a utilizamos. Agora, vamos criar outra classeUser, que usa a classe Greeter e vê como registrá-la.

class User { 
   private $greeter;  
   public $name; 
   public $age;  
   
   public function setGreeter(\Greeter $greeter) { 
      $this->greeter = $greeter; 
   }  
   public function greet() { 
      $this->greeter->greet($this->name); 
   } 
}

A classe User obtém a classe Greeter usando um de seus métodos setter,setGreeter. Para este cenário, Symfony fornece um método,addMethodCall e uma aula, Reference para se referir a outra classe, conforme mostrado no código a seguir.

use Symfony\Component\DependencyInjection\Reference;  
$container 
   ->register('user', 'User') 
   ->addMethodCall('setGreeter', array(new Reference('greeter')));

Finalmente, registramos duas classes, Greeter e Usertendo uma forte relação entre eles. Agora, podemos buscar com segurança o objeto User com a classe Greeter configurada corretamente no contêiner, conforme mostrado no código a seguir.

$container->setParameter('greeter.text', 'Hi'); 
$user = $container->get('user'); 
$user->name = "Jon"; 
$user->age = 20; 
$user->greet(); // Prints "Hi, Jon"

Vimos como configurar um objeto em um contêiner usando o próprio PHP. Symfony também oferece outros mecanismos. Eles são arquivos de configuração XML e YAML. Vamos ver como configurar um contêiner usando YAML. Para isso, instalesymfony/config e symfony/yaml componentes junto com symfony/dependency-injection componentes.

cd /path/to/dir 
mkdir dependency-injection-example 
cd dependency-injection-example 
composer require symfony/dependency-injection 
composer require symfony/config 
composer require symfony/yaml

A configuração YAML será escrita em um arquivo separado, services.yml. A configuração YAML consiste em duas seções,parameters e services. A seção de parâmetros define todos os parâmetros necessários. A seção de serviços define todos os objetos. A seção de serviços é dividida em várias seções, a saber,class, arguments, e calls. Classe especifica a classe real. Argumentos especifica os argumentos do construtor. Finalmente, as chamadas especificam os métodos setter. Outra classe pode ser referenciada usando o símbolo @, @greeter.

parameters: 
   greeter.text: 'Hello' 
services: 
   greeter: 
      class: Greeter
      arguments: ['%greeter.text%'] 
   user: 
      class: User 
      calls: 
         - [setGreeter, ['@greeter']]

Agora, services.yml pode ser carregado e configurado usando FileLoader e YamlFileLoader conforme mostrado no código a seguir.

use Symfony\Component\Config\FileLocator; 
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;  

$yamlContainer = new ContainerBuilder(); 
$loader = new YamlFileLoader($yamlContainer, new FileLocator(__DIR__)); 
$loader->load('services.yml');  

$yamlUser = $yamlContainer->get('user'); 
$yamlUser->name = "Jon"; 
$yamlUser->age = 25; 
$yamlUser->greet();

A lista de códigos completa é a seguinte.

main.php

<?php  
   require __DIR__ . '/vendor/autoload.php';  
   use Symfony\Component\DependencyInjection\ContainerBuilder; 
   use Symfony\Component\Config\FileLocator; 
   use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; 
   use Symfony\Component\DependencyInjection\Reference;  
   
   class Greeter { 
      private $greetingText; 
      
      public function __construct($greetingText) {
         $this->greetingText = $greetingText; 
      }  
      public function greet($name) { 
         echo $this->greetingText . ", " . $name . "\r\n"; 
      } 
   }  
   class User { 
      private $greeter;  
      public $name; 
      public $age;  
      
      public function setGreeter(\Greeter $greeter) { 
         $this->greeter = $greeter; 
      }  
      public function greet() { 
         $this->greeter->greet($this->name); 
      } 
   }  
   $container = new ContainerBuilder(); 
   $container 
      ->register('greeter', 'Greeter') 
      ->addArgument('%greeter.text%');  
   $container 
      ->register('user', 'User') 
      ->addMethodCall('setGreeter', array(new Reference('greeter')));
   
   $container->setParameter('greeter.text', 'Hi'); 
   $greeter = $container->get('greeter'); 
   $greeter->greet('Jon'); 
   
   $user = $container->get('user'); 
   $user->name = "Jon"; 
   $user->age = 20; 
   $user->greet();  
   
   $yamlContainer = new ContainerBuilder(); 
   $loader = new YamlFileLoader($yamlContainer, new FileLocator(__DIR__)); 
   $loader->load('services.yml');  

   $yamlHello = $yamlContainer->get('greeter'); 
   $yamlHello->greet('Jon'); 
   
   $yamlUser = $yamlContainer->get('user'); 
   $yamlUser->name = "Jon"; 
   $yamlUser->age = 25; 
   $yamlUser->greet();  
?>

services.yml

parameters: 
   greeter.text: 'Hello' 
services: 
   greeter: 
      class: Greeter 
      arguments: ['%greeter.text%'] 
   user: 
      class: User 
      calls: 
         - [setGreeter, ['@greeter']]

O framework web Symfony usa o componente de injeção de dependência extensivamente. Todos os componentes são vinculados ao contêiner de serviço centralizado. O framework web Symfony expõe o contêiner em todos os seusController através containerpropriedade. Podemos obter todos os objetos registrados nele, digamos logger, mailer, etc., por meio dele.

$logger = $this->container->get('logger'); 
$logger->info('Hi');

Para encontrar o objeto registrado no container, use o seguinte comando.

cd /path/to/app 
php bin/console debug:container

Existem cerca de 200 + objetos no hello aplicativo da web criado no capítulo de instalação.