Documentação do Symfony - versão 3.1
Renderizada do repositório symfony-docs-pt-BR no Github
URLs bonitas são uma obrigação absoluta para qualquer aplicação web séria. Isto
significa deixar para trás URLs feias como index.php?article_id=57
em favor
de algo como /read/intro-to-symfony
.
Ter flexibilidade é ainda mais importante. E se você precisasse mudar a
URL de uma página de /blog
para /news
? Quantos links você precisaria para
investigá-los e atualizar para fazer a mudança ? Se você está usando o roteador do Symfony,
a mudança é simples.
O roteador do Symfony2 deixa você definir URLs criativas que você mapeia para diferentes áreas de sua aplicação. No final deste capítulo, você será capaz de : * Criar rotas complexas que mapeiam para os controladores * Gerar URLs dentro de templates e controladores * Carregar recursos de roteamento de pacotes (ou algum lugar a mais) * Depurar suas rotas
Um rota é um mapa de um padrão URL para um controlador. Por exemplo, suponha
que você queira ajustar qualquer URL como /blog/my-post
ou /blog/all-about-symfony
e enviá-la ao controlador que pode olhar e mudar aquela entrada do blog.
A rota é simples:
1 2 3 4 | # app/config/routing.yml
blog_show:
pattern: /blog/{slug}
defaults: { _controller: AcmeBlogBundle:Blog:show }
|
1 2 3 4 5 6 7 8 9 10 | <!-- app/config/routing.xml -->
<?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">
<route id="blog_show" pattern="/blog/{slug}">
<default key="_controller">AcmeBlogBundle:Blog:show</default>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 10 | // app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('blog_show', new Route('/blog/{slug}', array(
'_controller' => 'AcmeBlogBundle:Blog:show',
)));
return $collection;
|
O padrão definido pela rota blog_show
age como /blog/*
onde
o coringa é dado pelo nome slug
. Para a URL /blog/my-blog-post
,
a variável slug
obtém um valor de my-blog-post
, que está disponível
para você usar em seu controlador (continue lendo).
O parâmetro _controller
é uma chave especial que avisa o Symfony qual controlador
deveria ser executado quando uma URL corresponde a essa rota. A string _controller
é chamada logical name. Ela segue um
padrão que aponta para uma classe e método PHP específico:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // src/Acme/BlogBundle/Controller/BlogController.php
namespace Acme\BlogBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BlogController extends Controller
{
public function showAction($slug)
{
$blog = // use the $slug varible to query the database
return $this->render('AcmeBlogBundle:Blog:show.html.twig', array(
'blog' => $blog,
));
}
}
|
Parabéns ! Você agora criou sua primeira rota conectou ela a
um controlador. Agora, quando você visitar /blog/my-post
, o controlador showAction
será executado e a variável $slug
será igual a my-post
.
Esse é o objetivo do roteador do Symfony2: mapear a URL de uma requisição para um controlador. Ao longo do caminho, você aprenderá todos os tipos de truques que tornam o mapeamento fácil, mesmo das URLS mais complexas.
Quando uma requisição é feita para sua aplicação, ela contém um endereço para
o “recurso” exato que o cliente está requisitando.Esse endereço é chamado de
URL, (ou URI), e poderia ser /contact
, /blog/read-me
, ou qualquer coisa
a mais. Considere a seguinte requisição de exemplo :
1 | GET /blog/my-blog-post
|
O objetido do sistema de roteamento do Symfony2 é analisar esta URL e determinar qual controlador deveria ser executado. O processo interior parece isso:
app.php
);Response
.Symfony carrega todas as rotas para sua aplicação de um arquivo de configuração
de roteamento. O arquivo é geralmente app/config/routing.yml
, mas pode ser configurado
para ser qualquer coisa (incluindo um arquivo XML ou PHP) via arquivo de configuração
de aplicação:
1 2 3 4 | # app/config/config.yml
framework:
# ...
router: { resource: "%kernel.root_dir%/config/routing.yml" }
|
1 2 3 4 5 | <!-- app/config/config.xml -->
<framework:config ...>
<!-- ... -->
<framework:router resource="%kernel.root_dir%/config/routing.xml" />
</framework:config>
|
1 2 3 4 5 | // app/config/config.php
$container->loadFromExtension('framework', array(
// ...
'router' => array('resource' => '%kernel.root_dir%/config/routing.php'),
));
|
Tip
Mesmo que toda as rotas sejam carregadas de um arquivo só, é uma prática comum incluir recursos de roteamento adicionais de dentro do arquivo. Veja a seção:ref:routing-include-external-resources para mais informação.
Definir uma rota é fácil, e uma aplicação típica terá um monte de rotas.
A basic route consists of just two parts: the pattern
to match and a
defaults
array:
1 2 3 | _welcome:
pattern: /
defaults: { _controller: AcmeDemoBundle:Main:homepage }
|
1 2 3 4 5 6 7 8 9 10 11 | <?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">
<route id="_welcome" pattern="/">
<default key="_controller">AcmeDemoBundle:Main:homepage</default>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 | use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('_welcome', new Route('/', array(
'_controller' => 'AcmeDemoBundle:Main:homepage',
)));
return $collection;
|
A rota combina a homepage (/
) e mapeia ele para o controlador
AcmeDemoBundle:Main:homepage
. A string _controller
é traduzida pelo Symfony2 em uma
função verdadeira do PHP e exectudada. Aquele processo irá ser explicado brevemente
na seção Padrão de nomeação do Controlador.
Claro que o sistema de roteamento suporta rotas muito mais interessantes. Muitas rotas irão conter uma ou mais chamadas de espaços reservados “coringa”:
1 2 3 | blog_show:
pattern: /blog/{slug}
defaults: { _controller: AcmeBlogBundle:Blog:show }
|
1 2 3 4 5 6 7 8 9 10 | <?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">
<route id="blog_show" pattern="/blog/{slug}">
<default key="_controller">AcmeBlogBundle:Blog:show</default>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 | use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('blog_show', new Route('/blog/{slug}', array(
'_controller' => 'AcmeBlogBundle:Blog:show',
)));
return $collection;
|
O padrão irá corresponder a qualquer coisa que pareça /blog/*
. Melhor ainda,
o valor correspondendo ao espaço reservado {slug}
estará disponível no seu controlador.
Em outras palavras, se a URL é /blog/hello-world
, uma variável
$slug
, com o valor de hello-world
, estará disponível no controlador.
Isto pode ser usado, por exemplo, para carregar o post do blog correspondendo àquela string.
Este padrão não irá, entretanto, simplesmente ajustar /blog
. Isso é porque,
por padrão, todos os espaços reservados são requeridos. Isto pode ser mudado ao adicionar um valor
de espaço reservado ao array defaults
.
Para tornar as coisas mais excitantes, adicione uma nova rota que mostre uma lista de todos os posts do blog para essa aplicação de blog imaginária:
1 2 3 | blog:
pattern: /blog
defaults: { _controller: AcmeBlogBundle:Blog:index }
|
1 2 3 4 5 6 7 8 9 10 | <?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">
<route id="blog" pattern="/blog">
<default key="_controller">AcmeBlogBundle:Blog:index</default>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 | use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('blog', new Route('/blog', array(
'_controller' => 'AcmeBlogBundle:Blog:index',
)));
return $collection;
|
Até agora, essa rota é tão simples quanto possível - contém nenhum espaço reservado
e só irá corresponder à URL exata /blog
. Mas e se você precisar dessa rota
para suportar paginação, onde /blog/2
mostre a segunda página do entradas do
blog ? Atualize a rota para ter uma nova {page}
de espaço reservado:
1 2 3 | blog:
pattern: /blog/{page}
defaults: { _controller: AcmeBlogBundle:Blog:index }
|
1 2 3 4 5 6 7 8 9 10 | <?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">
<route id="blog" pattern="/blog/{page}">
<default key="_controller">AcmeBlogBundle:Blog:index</default>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 | use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('blog', new Route('/blog/{page}', array(
'_controller' => 'AcmeBlogBundle:Blog:index',
)));
return $collection;
|
Como o espaço reservado {slug}
anterior, o valor correspondendo a {page}
estará disponível dentro do seu controlador. Este valor pode ser usado para determinar qual
conjunto de posts do blog mostrar para determinada página.
Mas espere ! Como espaços reservados são requeridos por padrão, essa rota não irá
mais corresponder simplesmente a /blog
. Ao invés disso, para ver a página 1 do blog,
você precisaria usar a URL /blog/1
! Como não há meios para uma aplicação web ricase
comportar, modifique a rota para fazer o parâmetro {page}
opcional.
Isto é feito ao incluir na coleção defaults
:
1 2 3 | blog:
pattern: /blog/{page}
defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
|
1 2 3 4 5 6 7 8 9 10 11 | <?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">
<route id="blog" pattern="/blog/{page}">
<default key="_controller">AcmeBlogBundle:Blog:index</default>
<default key="page">1</default>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 10 | use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('blog', new Route('/blog/{page}', array(
'_controller' => 'AcmeBlogBundle:Blog:index',
'page' => 1,
)));
return $collection;
|
Ao adicionar page
para a chave defaults
, o espaço reservado {page}
não é mais
requerido. A URL /blog
irá corresponder a essa rota e o valor do
parâmetro page
será fixado para 1
. A URL /blog/2
irá também
corresponder, atribuindo ao parâmetro page
o valor 2
. Perfeito.
/blog | {page} = 1 |
/blog/1 | {page} = 1 |
/blog/2 | {page} = 2 |
Dê uma rápida olhada nos roteamentos que foram criados até agora:
1 2 3 4 5 6 7 | blog:
pattern: /blog/{page}
defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
blog_show:
pattern: /blog/{slug}
defaults: { _controller: AcmeBlogBundle:Blog:show }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?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">
<route id="blog" pattern="/blog/{page}">
<default key="_controller">AcmeBlogBundle:Blog:index</default>
<default key="page">1</default>
</route>
<route id="blog_show" pattern="/blog/{slug}">
<default key="_controller">AcmeBlogBundle:Blog:show</default>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('blog', new Route('/blog/{page}', array(
'_controller' => 'AcmeBlogBundle:Blog:index',
'page' => 1,
)));
$collection->add('blog_show', new Route('/blog/{show}', array(
'_controller' => 'AcmeBlogBundle:Blog:show',
)));
return $collection;
|
Você pode apontar o problema ? Perceba que ambas as rotas tem padrão que combinam
URL’s que pareçam /blog/*
. O roteador do Symfony irá sempre escolher a
primeira rota correspondente que ele encontra. Em outras palavras, a rota blog_show
nunca será correspondida. Ao invés disso, uma URL como /blog/my-blog-post
irá corresponder
à primeira rota (blog
) e retorna um valor sem sentido de my-blog-post
ao parâmetro {page}
.
URL | route | parameters |
---|---|---|
/blog/2 | blog | {page} = 2 |
/blog/my-blog-post | blog | {page} = my-blog-post |
A resposta para o problema é adicionar mais requisitos de rota. As rotas neste
exemplo funcionariam perfeitamente se o padrão /blog/{page}
somente correspondesse
a URLs onde a porção {page}
fosse um integer. Felizmente, requisitos de expressões
regulares podem facilmente ser adicionados para cada parâmetro. Por exemplo:
1 2 3 4 5 | blog:
pattern: /blog/{page}
defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
requirements:
page: \d+
|
1 2 3 4 5 6 7 8 9 10 11 12 | <?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">
<route id="blog" pattern="/blog/{page}">
<default key="_controller">AcmeBlogBundle:Blog:index</default>
<default key="page">1</default>
<requirement key="page">\d+</requirement>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 10 11 12 | use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('blog', new Route('/blog/{page}', array(
'_controller' => 'AcmeBlogBundle:Blog:index',
'page' => 1,
), array(
'page' => '\d+',
)));
return $collection;
|
O requisito \d+
é uma expressão regular que diz o valor do
parâmetro {page}
deve ser um dígito (em outras palavras, um número). A rota``blog``
ainda irá correponder a uma URL como /blog/2
(porque 2 é um número), mas não irá mair
corresponder a URL como /blog/my-blog-post
(porque my-blog-post
não é um número).
Como resultado, uma URL como /blog/my-blog-post
não irá corresponder apropriadamente
à rota blog_show
.
URL | rota | parâmetros |
---|---|---|
/blog/2 | blog | {page} = 2 |
/blog/my-blog-post | blog_show | {slug} = my-blog-post |
Como os requisitos de parâmetros são expressões regulares, a complexidade e flexibilidade de cada requisito é inteiramente de sua responsabilidade. Suponha que a página inicial de sua aplicação está disponível em dois idiomas diferentes, baseada na URL:
1 2 3 4 5 | homepage:
pattern: /{culture}
defaults: { _controller: AcmeDemoBundle:Main:homepage, culture: en }
requirements:
culture: en|fr
|
1 2 3 4 5 6 7 8 9 10 11 12 | <?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">
<route id="homepage" pattern="/{culture}">
<default key="_controller">AcmeDemoBundle:Main:homepage</default>
<default key="culture">en</default>
<requirement key="culture">en|fr</requirement>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 10 11 12 | use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('homepage', new Route('/{culture}', array(
'_controller' => 'AcmeDemoBundle:Main:homepage',
'culture' => 'en',
), array(
'culture' => 'en|fr',
)));
return $collection;
|
Para requisições recebidas, a parte {culture}
da URL é comparada
com a expressão regular (en|fr)
.
/ | {culture} = en |
/en | {culture} = en |
/fr | {culture} = fr |
/es | won’t match this route |
Em adição à URL, você também pode ajustar o “método” da requisição recebida (em outras palavras, GET, HEAD, POST, PUT, DELETE).Suponha que você tenha um formulário de contato com dois controladores - um para exibir o formulário (em uma requisição GET) e uma para processar o formulário quando ele é enviado (em uma requisição POST). Isto pode ser acompanhando com a seguinte configuração de rota:
1 2 3 4 5 6 7 8 9 10 11 | contact:
pattern: /contact
defaults: { _controller: AcmeDemoBundle:Main:contact }
requirements:
_method: GET
contact_process:
pattern: /contact
defaults: { _controller: AcmeDemoBundle:Main:contactProcess }
requirements:
_method: POST
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?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">
<route id="contact" pattern="/contact">
<default key="_controller">AcmeDemoBundle:Main:contact</default>
<requirement key="_method">GET</requirement>
</route>
<route id="contact_process" pattern="/contact">
<default key="_controller">AcmeDemoBundle:Main:contactProcess</default>
<requirement key="_method">POST</requirement>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('contact', new Route('/contact', array(
'_controller' => 'AcmeDemoBundle:Main:contact',
), array(
'_method' => 'GET',
)));
$collection->add('contact_process', new Route('/contact', array(
'_controller' => 'AcmeDemoBundle:Main:contactProcess',
), array(
'_method' => 'POST',
)));
return $collection;
|
Apesar do fato que estas duas rotas tem padrões idênticos (/contact
),
a primeira rota irá aceitar somente requisições GET e a segunda rota irá somente
aceitar requisiçõs POST. Isso significa que você pode exibir o formulário e enviar o
formulário pela mesma URL, enquanto usa controladores distintos para as duas ações.
Note
Se nenhum valor _method
em requitement
for especificado, a rota irá aceitar todos os metodos.
Como os outros requisitos, o requisito _method
é analisado como uma expressão
regular. Para aceitar requisições GET
ou POST
, você pode usar GET|POST
.
Até esse ponto, você tem tudo que você precisa para criar uma poderosa estrutura de roteamento em Symfony. O exemplo seguinte mostra quão flexível o sistema de roteamento pode ser:
1 2 3 4 5 6 7 | article_show:
pattern: /articles/{culture}/{year}/{title}.{_format}
defaults: { _controller: AcmeDemoBundle:Article:show, _format: html }
requirements:
culture: en|fr
_format: html|rss
year: \d+
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?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">
<route id="article_show" pattern="/articles/{culture}/{year}/{title}.{_format}">
<default key="_controller">AcmeDemoBundle:Article:show</default>
<default key="_format">html</default>
<requirement key="culture">en|fr</requirement>
<requirement key="_format">html|rss</requirement>
<requirement key="year">\d+</requirement>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('homepage', new Route('/articles/{culture}/{year}/{title}.{_format}', array(
'_controller' => 'AcmeDemoBundle:Article:show',
'_format' => 'html',
), array(
'culture' => 'en|fr',
'_format' => 'html|rss',
'year' => '\d+',
)));
return $collection;
|
Como você viu, essa rota só irá funcionar se a parte {culture}
da
URL ou é en
ou fr
e se {year}
é um número. Esta rota também
mostra como você pode usar um período entre espaços reservados ao invés de
uma barra. URLs que correspondam a esta rota poderia parecer como:
/articles/en/2010/my-post
/articles/fr/2010/my-post.rss
Como você viu, cada parâmetro de roteamento ou valor padrão está eventualmente disponível como um argumento no método do controlador. Adicionalmente, existem três parâmetros que são especiais: cada um adiciona uma parte única de funcionalidade dentro da sua aplicação:
_controller
: Como você viu, este parâmetro é usado para determinar qual
controlador é executado quando a rota é correspondida;_format
: Usado para fixar o formato de requisição (read more);_locale
: Usado para fixar a localidade no pedido (read more);Tip
Se você usar o parâmetro _locale
na rota, aquele valor será também
armazenado na sessão, então, os pedidos posteriores mantêm a mesma localidade.
Cada rota deve ter um parâmetro _controller
, que ordena qual
controlador deveria ser executado quando uma rota é correspondida. Esse parâmetro
usa um padrão de string simples chamado logical controller name, que
o Symfony mapeia para uma classe e método PHP específico. O padrão tem três partes,
cada uma separada por dois pontos:
bundle:controller:action
Por exemplo, um valor _controller
de AcmeBlogBundle:Blog:show
significa:
Bundle | Classe do Controlador | Nome do Método |
---|---|---|
AcmeBlogBundle | BlogController | showAction |
O controlador poderia parecer assim:
1 2 3 4 5 6 7 8 9 10 11 12 | // src/Acme/BlogBundle/Controller/BlogController.php
namespace Acme\BlogBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BlogController extends Controller
{
public function showAction($slug)
{
// ...
}
}
|
Perceba que Symfony adiciona a string Controller
para o nome da classe (Blog
=> BlogController
) e Action
para o nome do método (show
=> showAction
).
Você também poderia referir a esse controler usando os nomes totalmente qualificados de
classe e método:Acme\BlogBundle\Controller\BlogController::showAction
.
Mas se você seguir alguma convenções simples, o nome lógico é mais conciso
e permite mais flexibilidade.
Note
Em complemento ao utilizar o nome lógico ou o nome de classe totalmente qualificado,
Symfony suporta um terceiro modo de referir a um controlador. Esse método
usa somente um separador de dois pontos (ex: service_name:indexAction
) e
referir um controlador como um serviço (veja /cookbook/controller/service).
Os parâmetros de rota (ex: {slug}
) são especialmente importantes porque
cada um é disponibilizado como um argumento para o método do controlador:
1 2 3 4 | public function showAction($slug)
{
// ...
}
|
Na realidade, a coleção inteira defaults
é mesclada com um valor de parâmetro
para formar um único array. Cada chave daquele array está disponível como um
argumento no controlador.
Em outras palavras, para cada argumento do método do seu controlador, Symfony procura
por um parâmetro de rota daquele nome e atribui o valor para aquele argumento.
No exemplo avançado acima, qualquer combinação (em qualquer ordem) das seguintes
variáveis poderia ser usada como argumentos para o método showAction()
:
$culture
$year
$title
$_format
$_controller
Como os espaços resercados e a coleção defaults
são mesclados juntos, mesmo
a variável $_controller
está disponível. Para uma discussão mais detalhada,
veja Parâmetros de Rota como Argumentos do Controlador.
Tip
Você também pode usar uma variável especial $_route
, que é fixada para
o nome da rota que foi correspondida.
Todas as rotas são carregadas por um arquivo de configuração individual - geralmente app/config/routing.yml
(veja Criando Rotas acima). É comum, entretanto, que você queria carregar recursos
de outros lugares, como um arquivo de roteamento que resida dentro de um pacote. Isso
pode ser feito mediante “importação” daquele arquivo:
1 2 3 | # app/config/routing.yml
acme_hello:
resource: "@AcmeHelloBundle/Resources/config/routing.yml"
|
1 2 3 4 5 6 7 8 9 | <!-- app/config/routing.xml -->
<?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="@AcmeHelloBundle/Resources/config/routing.xml" />
</routes>
|
1 2 3 4 5 6 7 | // app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
$collection = new RouteCollection();
$collection->addCollection($loader->import("@AcmeHelloBundle/Resources/config/routing.php"));
return $collection;
|
Note
Quando importar recursos do YAML, a chave (ex: acme_hello
) é insignificante.
Somente esteja certo que é única, então nenhuma outra linha a sobrescreverá.
A chave resource
carrega o recurso de determinado roteamento. Neste exemplo
o recurso é um atalho inteiro para o arquivo, onde a sintaxe do atalho @AcmeHelloBundle
resolve o atalho daquele pacote. O arquivo importado poderia parecer algo como isso:
1 2 3 4 | # src/Acme/HelloBundle/Resources/config/routing.yml
acme_hello:
pattern: /hello/{name}
defaults: { _controller: AcmeHelloBundle:Hello:index }
|
1 2 3 4 5 6 7 8 9 10 11 | <!-- src/Acme/HelloBundle/Resources/config/routing.xml -->
<?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">
<route id="acme_hello" pattern="/hello/{name}">
<default key="_controller">AcmeHelloBundle:Hello:index</default>
</route>
</routes>
|
1 2 3 4 5 6 7 8 9 10 | // src/Acme/HelloBundle/Resources/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('acme_hello', new Route('/hello/{name}', array(
'_controller' => 'AcmeHelloBundle:Hello:index',
)));
return $collection;
|
As rotas daquele arquivo são analisadas e carregadas da mesma forma que o arquivo principal de roteamento.
Você também pode escolher providenciar um “prefixo” para as rotas importadas. Por exemplo
suponha que você queira que a rota acme_hello
tnha um padrão final de /admin/hello/{name}
ao invés de simplesmente /hello/{name}
:
1 2 3 4 | # app/config/routing.yml
acme_hello:
resource: "@AcmeHelloBundle/Resources/config/routing.yml"
prefix: /admin
|
1 2 3 4 5 6 7 8 9 | <!-- app/config/routing.xml -->
<?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="@AcmeHelloBundle/Resources/config/routing.xml" prefix="/admin" />
</routes>
|
1 2 3 4 5 6 7 | // app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
$collection = new RouteCollection();
$collection->addCollection($loader->import("@AcmeHelloBundle/Resources/config/routing.php"), '/admin');
return $collection;
|
A string /admin
irá agora ser prefixada ao padrão de cada rota
carregada do novo recurso de roteamento.
Enquanto adiciona e personalizar rotas, é útil ser capaz de visualizar
e obter informação detalhada sobre suas rotas. Um grande modo para ver cada rota
em sua aplicação é pelo comando de console router:debug
. Execute
o seguinte comando a partir da raiz de seu projeto.
1 | php app/console router:debug
|
O comando irá imprimir uma lista útil de todas as rotas configuradas em sua aplicação:
1 2 3 4 5 6 | homepage ANY /
contact GET /contact
contact_process POST /contact
article_show ANY /articles/{culture}/{year}/{title}.{_format}
blog ANY /blog/{page}
blog_show ANY /blog/{slug}
|
Você também pode obter informação muito específica em uma rota individual ao incluir o nome da rota após o comando:
1 | php app/console router:debug article_show
|
New in version 2.1: O comando router:match
foi adicionado no Symfony 2.1
Você pode verificar, se houver, que rota corresponde à um caminho com o
comando de console router:match
:
1 2 | $ php app/console router:match /articles/en/2012/article.rss
Route "article_show" matches
|
O sistema de roteamento deveria também ser usado para gerar URLs. Na realidade, roteamento
é um sistema bi-direcional: mapeando a URL para um controlador+parâmetros e
parâmetros+rota de voltar para a URL. Os métodos
match()
e
generate()
formam esse sistema
bi-directional. Considere a rota blog_show
de um exemplo anterior:
$params = $router->match('/blog/my-blog-post');
// array('slug' => 'my-blog-post', '_controller' => 'AcmeBlogBundle:Blog:show')
$uri = $router->generate('blog_show', array('slug' => 'my-blog-post'));
// /blog/my-blog-post
Para gerar a URL, você precisa especificar o nome da rota (ex: blog_show
)
e quaisquer coringas(ex: slug = my-blog-post
) usado no padrão para
aquela rota. Com essa informação, qualquer URL pode ser facilmente gerada:
1 2 3 4 5 6 7 8 9 | class MainController extends Controller
{
public function showAction($slug)
{
// ...
$url = $this->get('router')->generate('blog_show', array('slug' => 'my-blog-post'));
}
}
|
Em uma sessão futura, você irá aprender como gerar URLs a partir de templates.
Tip
Se o frontend de sua aplicação usa requisições AJAX, você poderia querer ser capaz de ferar URLs em JavaScript baseados na sua configuração de roteamento. Ao usar `FOSJsRoutingBundle`_, você poderia fazer exatamente isso:
1 | var url = Routing.generate('blog_show', { "slug": 'my-blog-post});
|
Para mais informações, veja a documentação para aquele pacote.
Por padrão, o roteador irá gerar URLs relativas (ex: /blog
). Para gerar
uma URL absoluta, simplesmente passe true
ao terceiro argumento do
método generate()
:
1 2 | $router->generate('blog_show', array('slug' => 'my-blog-post'), true);
// http://www.example.com/blog/my-blog-post
|
Note
O host que é usado quando gera uma URL absoluta é o host
do objeto Request
atual. Isso é detectado automaticamente baseado
na informação do servidor abastecida pelo PHP. Quando gerar URLs absolutas para
rodar scripts a partir da linha de comando, você precisará fixar manualmente o
host no objeto Request
:
1 | $request->headers->set('HOST', 'www.example.com');
|
O método generate
pega um array de valores coringa para gerar a URI.
Mas se você passar valores extras, eles serão adicionados ao URI como uma string de consulta:
$router->generate('blog', array('page' => 2, 'category' => 'Symfony'));
// /blog/2?category=Symfony
O lugar mais comum para gerar uma URL é pelo template, ao fazer vinculação entre páginas na sua aplicação.Isso é feito da mesma forma que antes, mas usando uma função helper de template:
1 2 3 | <a href="{{ path('blog_show', { 'slug': 'my-blog-post' }) }}">
Read this blog post.
</a>
|
1 2 3 | <a href="<?php echo $view['router']->generate('blog_show', array('slug' => 'my-blog-post')) ?>">
Read this blog post.
</a>
|
URLs absolutas também podem ser geradas.
1 2 3 | <a href="{{ url('blog_show', { 'slug': 'my-blog-post' }) }}">
Read this blog post.
</a>
|
1 2 3 | <a href="<?php echo $view['router']->generate('blog_show', array('slug' => 'my-blog-post'), true) ?>">
Read this blog post.
</a>
|
Roteamento é um sistema para mapear a URL de requisiçõs recebidas para a função do controlador que deveria ser chamada para processar a requisição. Em ambas permite a você especificar URLs bonitas e manter a funcionalidade de sua aplicação desacoplada daquelas URLs. Roteamento é um mecanismo de duas vias, significando que também deveria ser usada para gerar URLs.
Rotamento, check! Agora, descubra o poder dos controladores.