Documentação do Symfony - versão 3.1
Renderizada do repositório symfony-docs-pt-BR no Github
Ainda está com a gente depois das primeiras duas partes? Então você já está se tornando um viciado no Symfony! Sem mais delongas, vamos descobrir o que os controladores podem fazer por você.
Atualmente, uma aplicação web deve ser capaz de entregar mais do que apenas
páginas HTML. Desde XML para feeds RSS ou Web Services, até JSON para
requisições Ajax, existem muitos formatos diferentes para escolher. Dar suporte
para esses formatos no Symfony é simples. É só ajustar a rota, como aqui que
acrescentando um valor padrão xml
para a variável _format
:
// src/Acme/DemoBundle/Controller/DemoController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/**
* @Route("/hello/{name}", defaults={"_format"="xml"}, name="_demo_hello")
* @Template()
*/
public function helloAction($name)
{
return array('name' => $name);
}
Usando o formato de requisição (como definido pelo valor _format
), o
Symfony automaticamente seleciona o template correto, nesse caso o
hello.xml.twig
:
1 2 3 4 | <!-- src/Acme/DemoBundle/Resources/views/Demo/hello.xml.twig -->
<hello>
<name>{{ name }}</name>
</hello>
|
Isso é tudo. Para os formatos padrão, o Symfony também irá escolher
automaticamente o melhor cabeçalho Content-Type
para a resposta. Se você
quiser dar suporte para diferentes formatos numa única action, em vez disso use
o marcador {_format}
no padrão da rota:
// src/Acme/DemoBundle/Controller/DemoController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/**
* @Route("/hello/{name}.{_format}", defaults={"_format"="html"}, requirements={"_format"="html|xml|json"}, name="_demo_hello")
* @Template()
*/
public function helloAction($name)
{
return array('name' => $name);
}
O controlador agora será chamado por URLs parecidas com
/demo/hello/Fabien.xml
ou /demo/hello/Fabien.json
.
A entrada requirements
define expressões regulares que os marcadores
precisam casar. Nesse exemplo, se você tentar requisitar
/demo/hello/Fabien.js
irá receber um erro HTTP 404 pois a requisição
não casa com o requisito _format
.
Se você quiser redirecionar o usuário para outra página, use o método
redirect()
:
return $this->redirect($this->generateUrl('_demo_hello', array('name' => 'Lucas')));
O método generateUrl()
é o mesmo que a função path()
que usamos nos
templates. Ele pega o nome da rota e um array de parâmetros como argumentos e
retorna a URL amigável associada.
Você também pode facilmente encaminhar a action para uma outra com o método
forward()
. Internamente, o Symfony faz uma “sub-requisição”, e retorna
o objeto Response
daquela sub-requisição:
$response = $this->forward('AcmeDemoBundle:Hello:fancy', array('name' => $name, 'color' => 'green'));
// faça algo com a resposta ou a retorne diretamente
Além dos valores dos marcadores de rota, o controlador tem acesso ao objeto
Request
:
$request = $this->getRequest();
$request->isXmlHttpRequest(); // essa é uma requisição Ajax?
$request->getPreferredLanguage(array('en', 'fr'));
$request->query->get('page'); // pega um parâmetro $_GET
$request->request->get('page'); // pega um parâmetro $_POST
Em um template, você também pode acessar o objeto Request
via a variável
app.request
:
1 2 3 | {{ app.request.query.get('page') }}
{{ app.request.parameter('page') }}
|
Mesmo o protocolo HTTP sendo stateless (não tendo monitoração de estado), o Symfony fornece um objeto interessante que representa o cliente (seja ele uma pessoa real utilizando um navegador, um bot ou um web service). Entre duas requisições, o Symfony guarda os atributos num cookie usando sessões nativas do PHP.
É fácil guardar e recuperar a informação da sessão a partir de qualquer controlador:
$session = $this->getRequest()->getSession();
// guarda um atributo para reutilização na próxima requisição do usuário
$session->set('foo', 'bar');
// em outro controlador para outra requisição
$foo = $session->get('foo');
// usa um valor default se a chave não existe
$filters = $session->set('filters', array());
Você pode guardar pequenas mensagens que ficarão disponíveis apenas para a próxima requisição:
// guarda uma mensagem para a próxima requisição somente (em um controlador)
$session->getFlashBag()->add('notice', 'Congratulations, your action succeeded!');
// exibe quaisquer mensagens no próximo pedido (no template)
{% for flashMessage in app.session.flashbag.get('notice') %}
<div>{{ flashMessage }}</div>
{% endfor %}
Isso é útil quando você precisa definir uma mensagem de sucesso antes de redirecionar o usuário para outra página (que então mostrará a mensagem). Por favor note que quando você usa has() ao invés de get(), a mensagem flash não será apagada e, assim, permanece disponível durante os pedidos seguintes.
A versão Standard Edition do Symfony vem com uma configuração de segurança simples que atende as necessidades mais comuns:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | # app/config/security.yml
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
in_memory:
memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/demo/secured/login$
security: false
secured_area:
pattern: ^/demo/secured/
form_login:
check_path: /demo/secured/login_check
login_path: /demo/secured/login
logout:
path: /demo/secured/logout
target: /demo/
|
Essa configuração requer que os usuários se autentiquem para acessar qualquer
URL começada por /demo/secured/
e define dois usuários válidos: user
e
admin
. Além disso o usuário admin
tem uma permissão ROLE_ADMIN
,
que também inclui a permissão ROLE_USER
(veja a configuração
role_hierarchy
).
Tip
Para melhorar a legibilidade, nessa nossa configuração simplificada as
senhas são guardadas em texto puro, mas você pode usar algum algoritmo
de hash ajustando a seção encoders
.
Indo para a URL http://localhost/app_dev.php/demo/secured/hello
você será automaticamente redirecionado para o formulário de login pois o
recurso é protegido por um firewall
.
Você também pode forçar a action para requisitar uma permissão especial usando
a annotation @Secure
no controlador:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\SecurityExtraBundle\Annotation\Secure;
/**
* @Route("/hello/admin/{name}", name="_demo_secured_hello_admin")
* @Secure(roles="ROLE_ADMIN")
* @Template()
*/
public function helloAdminAction($name)
{
return array('name' => $name);
}
Agora, se autentique como user
(que não tem a permissão ROLE_ADMIN
)
e, a partir da página protegida hello, clique no link “Hello resource secured”.
O Symfony deve retornar um erro HTTP 403, indicando que o usuário está
“proibido” de acessar o recurso.
Note
A camada de segurança do Symfony é bem flexível e vem com muitos serviços de usuários (como no Doctrine ORM) e autenticação (como HTTP básico, HTTP digest ou certificados X509). Leia o capítulo “/book/security” do livro para mais informação de como usá-los e configurá-los.
A medida que seu site começa a ter mais tráfego, você vai querer evitar fazer a
geração dos mesmos recursos várias e várias vezes. O Symfony usa cabeçalhos de
cache HTTP para gerenciar o cache dos recursos. Para estratégias simples de
cache, use a annotation conveniente @Cache()
:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
/**
* @Route("/hello/{name}", name="_demo_hello")
* @Template()
* @Cache(maxage="86400")
*/
public function helloAction($name)
{
return array('name' => $name);
}
Nesse exemplo, o recurso ficará em cache por um dia. Mas você também pode usar validações em vez de expiração, ou uma combinação de ambos, se isso se encaixar melhor nas suas necessidades.
O cache de recursos é gerenciado pelo proxy reverso embutido no Symfony. Mas como o cache é gerenciado usando cabeçalhos de cache HTTP normais, você pode substituir o proxy reverso com o Varnish ou o Squid e estender a sua aplicação de forma fácil.
Note
Mas como se virar se você não puder fazer cache de páginas inteiras? O Symfony continua tendo a solução, via Edge Side Includes (ESI), que são suportados nativamente. Aprenda mais sobre isso lendo o capítulo “/book/http_cache” do livro.
Isso foi tudo, e acho que não gastamos nem 10 minutos. Fizemos uma breve introdução aos bundles na primeira parte e todas as funcionalidades sobre as quais aprendemos até agora são parte do bundle núcleo do framework. Graças aos bundles, tudo no Symfony pode ser estendido ou substituído. Esse é o tema da próxima parte do tutorial.