Documentação do Symfony - versão 3.1
Renderizada do repositório symfony-docs-pt-BR no Github

Como criar um Loader de Rota personalizado

Um loader de rota personalizado permite que você adicione rotas a uma aplicação sem incluí-las, por exemplo, num arquivo yaml. Isto é útil quando você tem um bundle, mas não quer adicionar manualmente as rotas ao bundle em app/config/routing.yml. Isso pode ser especialmente importante quando você quer tornar o bundle reutilizável, ou quando o disponibilizar como código aberto, porque iria retardar o processo de instalação e torná-lo suscetível a erros.

Alternativamente, você também pode usar um loader de rota personalizado quando quiser que as suas rotas sejam geradas ou localizadas automaticamente com base em alguma convenção ou padrão. Um exemplo é o FOSRestBundle onde o roteamento é gerado com base nos nomes dos métodos de ação em um controlador.

Note

Há muitos bundles que usam seus próprios loaders de rotas para realizar casos como os descritos acima, por exemplo FOSRestBundle, KnpRadBundle e SonataAdminBundle.

Carregando Rotas

As rotas em uma aplicação Symfony são carregadas pelo DelegatingLoader. Este loader usa vários outros loaders (delegados) para carregar recursos de diferentes tipos, por exemplo, arquivos YAML ou anotações @Route e @Method em arquivos de controlador. Os loaders especializados implementam a LoaderInterface e, portanto, tem dois métodos importantes: supports() e load().

Considere essas linhas do routing.yml:

1
2
3
4
_demo:
    resource: "@AcmeDemoBundle/Controller/DemoController.php"
    type:     annotation
    prefix:   /demo

Quando o loader principal realiza o parse, ele tenta todos os loaders delegados e chama seu método supports() com o determinado recurso (@AcmeDemoBundle/Controller/DemoController.php) e tipo (annotation) como argumentos. Quando um dos loaders retorna true, seu método load() será chamado, que deve retornar um RouteCollection contendo objetos Route.

Criando um Loader Personalizado

Para carregar rotas de alguma fonte personalizada (ou seja, de algo diferente de anotações, arquivos YAML ou XML), você precisa criar um loader de rota personalizado. Este loader deve implementar a LoaderInterface.

O loader de exemplo abaixo, suporta o carregamento de recursos de roteamento com um tipo extra. O tipo extra não é importante - você pode simplesmente inventar qualquer tipo de recurso que desejar. O próprio nome do recurso não é realmente usado no exemplo:

namespace Acme\DemoBundle\Routing;

use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

class ExtraLoader implements LoaderInterface
{
    private $loaded = false;

    public function load($resource, $type = null)
    {
        if (true === $this->loaded) {
            throw new \RuntimeException('Do not add the "extra" loader twice');
        }

        $routes = new RouteCollection();

        // prepare a new route
        $pattern = '/extra/{parameter}';
        $defaults = array(
            '_controller' => 'AcmeDemoBundle:Demo:extra',
        );
        $requirements = array(
            'parameter' => '\d+',
        );
        $route = new Route($pattern, $defaults, $requirements);

        // add the new route to the route collection:
        $routeName = 'extraRoute';
        $routes->add($routeName, $route);

        return $routes;
    }

    public function supports($resource, $type = null)
    {
        return 'extra' === $type;
    }

    public function getResolver()
    {
        // needed, but can be blank, unless you want to load other resources
        // and if you do, using the Loader base class is easier (see below)
    }

    public function setResolver(LoaderResolverInterface $resolver)
    {
        // same as above
    }
}

Note

Certifique-se que o controlador que você especificou realmente existe.

Agora defina um serviço para o ExtraLoader:

  • YAML
    1
    2
    3
    4
    5
    services:
        acme_demo.routing_loader:
            class: Acme\DemoBundle\Routing\ExtraLoader
            tags:
                - { name: routing.loader }
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    <?xml version="1.0" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <service id="acme_demo.routing_loader" class="Acme\DemoBundle\Routing\ExtraLoader">
                <tag name="routing.loader" />
            </service>
        </services>
    </container>
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    8
    9
    use Symfony\Component\DependencyInjection\Definition;
    
    $container
        ->setDefinition(
            'acme_demo.routing_loader',
            new Definition('Acme\DemoBundle\Routing\ExtraLoader')
        )
        ->addTag('routing.loader')
    ;
    

Observe a tag routing.loader. Todos os serviços com esta tag serão marcados como potenciais loaders de rota e adicionados como roteadores especializados para o DelegatingLoader.

Usando o Loader Personalizado

Se você não fez nada mais, seu loader de roteamento personalizado não será chamado. Em vez disso, você só precisa adicionar algumas linhas extras para a configuração de roteamento:

  • YAML
    1
    2
    3
    4
    # app/config/routing.yml
    AcmeDemoBundle_Extra:
        resource: .
        type: extra
    
  • XML
    1
    2
    3
    4
    5
    6
    7
    <?xml version="1.0" encoding="UTF-8" ?>
    <routes xmlns="http://symfony.com/schema/routing"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
    
        <import resource="." type="extra" />
    </routes>
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    // app/config/routing.php
    use Symfony\Component\Routing\RouteCollection;
    
    $collection = new RouteCollection();
    $collection->addCollection($loader->import('.', 'extra'));
    
    return $collection;
    

A parte importante aqui é a chave type. Seu valor deve ser “extra”. Este é o tipo que nosso ExtraLoader suporta e que irá certificar-se que seu método load() é chamado. A chave resource é insignificante para o ExtraLoader, então defina ela como ”.”.

Note

O cache das rotas definidas usando loaders personalizados será feita automaticamente pelo framework. Assim, sempre que você mudar alguma coisa na classe do loader, não se esqueça de limpar o cache.

Loaders mais Avançados

Na maioria dos casos é melhor não implementar a LoaderInterface você mesmo, mas estender do Loader. Esta classe sabe como usar um LoaderResolver para carregar os recursos de roteamento secundários.

Claro que você ainda precisa implementar supports() e load(). Sempre que quiser carregar outro recurso - por exemplo, um arquivo de configuração Yaml - você pode chamar o import() method:

namespace Acme\DemoBundle\Routing;

use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\RouteCollection;

class AdvancedLoader extends Loader
{
    public function load($resource, $type = null)
    {
        $collection = new RouteCollection();

        $resource = '@AcmeDemoBundle/Resources/config/import_routing.yml';
        $type = 'yaml';

        $importedRoutes = $this->import($resource, $type);

        $collection->addCollection($importedRoutes);

        return $collection;
    }

    public function supports($resource, $type = null)
    {
        return $type === 'advanced_extra';
    }
}

Note

O nome e o tipo do recurso da configuração de roteamento importada pode ser qualquer coisa que é normalmente suportada pelo loader de configuração de roteamento (YAML, XML, PHP, anotação, etc.)