Symfony: contenedor de servicios

En cualquier aplicación, los objetos tienden a aumentar a medida que crece la aplicación. A medida que aumentan los objetos, también aumenta la dependencia entre los objetos. La dependencia de objetos debe manejarse correctamente para una aplicación exitosa.

Como se discutió en el capítulo de Componentes, Symfony proporciona un componente fácil y eficiente, DependencyInjectionpara manejar la dependencia de objetos. Un contenedor de servicios es un contenedor de objetos con una dependencia resuelta correctamente entre ellos. Aprendamos a utilizar el componente DependencyInjection en este capítulo.

Creemos un Greeterclase. El propósito de la clase Greeter es saludar al usuario como se muestra en el siguiente ejemplo.

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

El código completo de la clase Greeter es el siguiente.

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

Ahora, agreguemos la clase Greeter al contenedor de servicios. Symfony ofreceContainerBuilderpara crear un nuevo contenedor. Una vez que se crea el contenedor, la clase Greeter puede registrarse en él usando el método de registro del contenedor.

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

Aquí, hemos utilizado un argumento estático para especificar el texto de saludo, Hola. Symfony también proporciona una configuración dinámica de parámetros. Para usar un parámetro dinámico, debemos elegir un nombre y especificarlo entre% y el parámetro se puede configurar usando el contenedorsetParameter método.

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

Hemos registrado una clase de Greeter con la configuración adecuada. Ahora, podemos pedirle al contenedor que proporcione un objeto Greeter configurado correctamente utilizando el contenedorget método.

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

Hemos registrado con éxito una clase, Greeter en el contenedor, la extrajimos del contenedor y la usamos. Ahora, creemos otra claseUser, que usa la clase Greeter y ve cómo registrarla.

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

La clase User obtiene la clase Greeter usando uno de sus métodos setter,setGreeter. Para este escenario, Symfony proporciona un método,addMethodCall y una clase, Reference para hacer referencia a otra clase como se muestra en el siguiente código.

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

Finalmente, hemos registrado dos clases, Greeter y Usertener una fuerte relación entre ellos. Ahora, podemos recuperar de forma segura el objeto Usuario con la clase Greeter configurada correctamente desde el contenedor como se muestra en el siguiente código.

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

Hemos visto cómo configurar un objeto en un contenedor usando el propio PHP. Symfony también proporciona otros mecanismos. Son archivos de configuración XML y YAML. Veamos cómo configurar un contenedor usando YAML. Para esto, instalesymfony/config y symfony/yaml componentes junto con 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

La configuración de YAML se escribirá en un archivo separado, services.yml. La configuración de YAML consta de dos secciones,parameters y services. La sección de parámetros define todos los parámetros requeridos. La sección de servicios define todos los objetos. La sección de servicios se divide en varias secciones, a saber,class, arguments, y calls. Clase especifica la clase real. Arguments especifica los argumentos del constructor. Finalmente, las llamadas especifican los métodos de establecimiento. Se puede referir a otra clase usando el símbolo @, @greeter.

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

Ahora, services.yml se puede cargar y configurar usando FileLoader y YamlFileLoader como se muestra en el siguiente código.

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();

La lista completa de códigos es la siguiente.

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']]

El marco web Symfony utiliza ampliamente el componente de inyección de dependencias. Todos los componentes están vinculados por el contenedor de servicios centralizados. El framework web Symfony expone el contenedor en todos susController mediante containerpropiedad. Podemos obtener todos los objetos registrados en él, digamos logger, mailer, etc., a través de él.

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

Para encontrar el objeto registrado en el contenedor, use el siguiente comando.

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

Hay alrededor de 200 objetos en el hello aplicación web creada en el capítulo de instalación.