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.