Normalmente, eu uso Silex quando preciso construir um Backend. É simples e direto para fazer uma API endpoint usando esse micro framework. Mas há algo que eu não gosto nele: a forma de acessar o container de injeção de dependência, via “array access”. Eu preciso lembrar que tipo de objeto é o provedor do meu provedor de serviços e também a minha IDE não ajuda com autocomplete. Ok, eu poderia usar comentários do PHPDoc ou mesmo criar uma classe que herda de Silex\Application e use Traits. Normalmente, eu sou muito preguiçoso para fazer isso. Por causa disso, eu criei este simples provedor de serviços para me ajudar a fazer o que eu preciso. Deixe-me explicar um pouco.
Imagine que eu tenha uma classe assim:
class Math { public function sum($i, $j) { return $i+$j; } }
E que quero adicionar este serviço ao meu DIC:
$app['math'] = $app->share(function () { return new Math(); });
Agora, eu quero usar meu serviço na aplicação Silex:
$app->get("/", function () use ($app) { return $app['math']->sum(1, 2); });
Mas eu quero usar meu serviço da mesma forma que estou usando meus serviços nas aplicações AngularJS. Eu quero fazer algo assim:
use Foo\Math; ... $app->get("/", function (Math $math) { return $math->sum(1, 2); });
E isso é exatamente o que o meu provedor de serviços faz. Eu só preciso anexar meu provedor à minha aplicação e falar para o provedor qual é o relacionamento entre as chaves de serviço Pimple e suas instâncias concedidas.
$app->register(new InjectorServiceProvider([ 'Foo\Math' => 'math', ]));
Este é um exemplo:
composer require gonzalo123/injector
include __DIR__ . "/../vendor/autoload.php"; use Silex\Application; use Injector\InjectorServiceProvider; use Foo\Math; $app = new Application(['debug' => true]); $app->register(new InjectorServiceProvider([ 'Foo\Math' => 'math', ])); $app['math'] = function () { return new Math(); }; $app->get("/", function (Math $math) { return $math->sum(1, 2); }); $app->run();
E este é o Provedor de Serviços:
namespace Injector; use Silex\Application; use Silex\ServiceProviderInterface; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; use Symfony\Component\HttpKernel\KernelEvents; class InjectorServiceProvider implements ServiceProviderInterface { private $injectables; public function __construct($injectables = []) { $this->injectables = $injectables; } public function appendInjectables($providedClass, $key) { $this->injectables[$providedClass] = $key; } public function register(Application $app) { $app->on(KernelEvents::CONTROLLER, function (FilterControllerEvent $event) use ($app) { $reflectionFunction = new \ReflectionFunction($event->getController()); $parameters = $reflectionFunction->getParameters(); foreach ($parameters as $param) { $class = $param->getClass(); if ($class && array_key_exists($class->name, $this->injectables)) { $event->getRequest()->attributes->set($param->name, $app[$this->injectables[$class->name]]); } } }); } public function boot(Application $app) { } }
Como você pode ver, eu estou escutando o evento CONTROLLER do event dispatcher e injetando a dependência a partir do container para requisição de atributos.
O código completo está no meu GitHub.
***
Gonzalo Ayuso faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://gonzalo123.com/2015/10/19/alternative-way-to-inject-providers-in-a-silex-application/