Back-End

4 nov, 2015

Formas alternativas para injetar providers em uma aplicação Silex

Publicidade

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/