Você está na página 1de 454

Symfony Docs pt-BR Documentation

Verso 2.4

symfony

16/09/2014

Sumrio

Guia de Incio Rpido


1.1 Guia de Incio Rpido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1
1

Livro
2.1 Livro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

25
25

Cookbook
239
3.1 Cookbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239

Componentes
433
4.1 Os Componentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433

Documentos de Referncia
441
5.1 Documentos de Referncia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441

Bundles
445
6.1 Bundles da Edio Standard do Symfony . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445

Contribuindo
447
7.1 Contribuindo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447

ii

CAPTULO 1

Guia de Incio Rpido

Inicie rapidamente no Symfony2 com o Guia de Incio Rpido:

1.1 Guia de Incio Rpido


1.1.1 Panorama Geral
Comece a usar o Symfony2 em 10 minutos! Este captulo ir orient-lo atravs de alguns dos conceitos mais importantes por trs do Symfony2 e explicar como voc poder iniciar rapidamente, mostrando-lhe um projeto simples em
ao.
Se voc j usou um framework web antes, voc deve se sentir em casa com o Symfony2. Se no, bem-vindo uma
nova forma de desenvolver aplicaes web!
Dica: Quer saber por que e quando voc precisa usar um framework? Leia o documento Symfony em 5 minutos .

Baixando o Symfony2
Primeiro, verifique se voc tem um servidor web instalado e configurado (como o Apache) com a verso mais recente
possvel do PHP ( recomendado o PHP 5.3.8 ou mais recente).
Pronto? Comece fazendo o download da Edio Standard do Symfony2, uma distribuio do Symfony que prconfigurada para os casos de uso mais comuns e contm tambm um cdigo que demonstra como usar Symfony2
(obtenha o arquivo com os vendors includos para comear ainda mais rpido).
Aps descompactar o arquivo no seu diretrio raiz do servidor web, voc deve ter um diretrio Symfony/ parecido
com o seguinte:
www/ <- your web root directory
Symfony/ <- the unpacked archive
app/
cache/
config/
logs/
Resources/
bin/
src/
Acme/
DemoBundle/
Controller/

Symfony Docs pt-BR Documentation, Verso 2.4

Resources/
...
vendor/
symfony/
doctrine/
...
web/
app.php
...

Nota: Se voc est familiarizado com o Composer, poder executar o seguinte comando em vez de baixar o arquivo:
$ composer.phar create-project symfony/framework-standard-edition path/to/install 2.1.x-dev
# remove the Git history
$ rm -rf .git
Para uma verso exata, substitua 2.1.x-dev com a verso mais recente do Symfony
(Ex. 2.1.1). Para detalhes, veja a Pgina de Instalao do Symfony_

Dica: Se voc tem o PHP 5.4, possvel usar o servidor web integrado:
# check your PHP CLI configuration
$ php ./app/check.php
# run the built-in web server
$ php ./app/console server:run

Ento, a URL para a sua aplicao ser http://localhost:8000/app_dev.php


O servidor integrado deve ser usado apenas para fins de desenvolvimento, mas pode ajud-lo a iniciar o seu projeto de
forma rpida e fcil.

Verificando a configurao
O Symfony2 vem com uma interface visual para teste da configurao do servidor que ajuda a evitar algumas dores
de cabea que originam da m configurao do servidor Web ou do PHP. Use a seguinte URL para ver o diagnstico
para a sua mquina:
http://localhost/config.php

Nota: Todos as URLs de exemplo assumem que voc baixou e descompactou o Symfony diretamente no diretrio
raiz web do seu servidor web. Se voc seguiu as instrues acima e descompactou o diretrio Symfony em seu raiz
web, ento, adicione /Symfony/web aps localhost em todas as URLs:
http://localhost/Symfony/web/config.php

Se houver quaisquer questes pendentes informadas, corrija-as. Voc tambm pode ajustar a sua configurao, seguindo todas as recomendaes. Quando tudo estiver certo, clique em Bypass configuration and go to the Welcome
page para solicitar a sua primeira pgina real do Symfony2:
http://localhost/app_dev.php/

O Symfony2 lhe d as boas vindas e parabeniza-o por seu trabalho at agora!

Captulo 1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

Compreendendo os Fundamentos
Um dos objetivos principais de um framework garantir a Separao de Responsabilidades. Isso mantm o seu cdigo
organizado e permite que a sua aplicao evolua facilmente ao longo do tempo, evitando a mistura de chamadas ao
banco de dados, de tags HTML e de lgica de negcios no mesmo script. Para atingir este objetivo com o Symfony,
primeiro voc precisa aprender alguns conceitos e termos fundamentais.
Dica: Quer uma prova de que o uso de um framework melhor do que misturar tudo no mesmo script? Leia o
captulo Symfony2 versus o PHP puro do livro.
A distribuio vem com um cdigo de exemplo que voc pode usar para aprender mais sobre os principais conceitos
do Symfony2. V para a seguinte URL para ser cumprimentado pelo Symfony2 (substitua Fabien pelo seu primeiro
nome):
http://localhost/app_dev.php/demo/hello/Fabien

1.1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

O que est acontecendo aqui? Vamos dissecar a URL:


app_dev.php: Este o front controller. o nico ponto de entrada da aplicao e responde todas as
solicitaes dos usurios;
/demo/hello/Fabien: Este o caminho virtual para o recurso que o usurio quer acessar.
Sua responsabilidade como desenvolvedor escrever o cdigo que mapeia a solicitao do usurio
(/demo/hello/Fabien) para o recurso associado ela (a pgina HTML Hello Fabien!).
Roteamento

O Symfony2 encaminha a solicitao para o cdigo que lida com ela, tentando corresponder a URL solicitada contra alguns padres configurados. Por predefinio, esses padres (chamados de rotas) so definidos no arquivo de
configurao app/config/routing.yml. Quando voc est no ambiente dev - indicado pelo front controller
app_**dev**.php - o arquivo de configurao app/config/routing_dev.yml tambm carregado. Na Edio
Standard, as rotas para estas pginas demo so colocadas no arquivo:
# app/config/routing_dev.yml
_welcome:
pattern: /
defaults: { _controller: AcmeDemoBundle:Welcome:index }
_demo:
resource: "@AcmeDemoBundle/Controller/DemoController.php"
type:
annotation
prefix:
/demo
# ...

As trs primeiras linhas (aps o comentrio) definem o cdigo que executado quando o usurio solicita o recurso / (ou seja, a pgina de boas-vindas que voc viu anterioremente). Quando solicitado, o controlador
AcmeDemoBundle:Welcome:index ser executado. Na prxima seo, voc vai aprender exatamente o que
isso significa.

Captulo 1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

Dica: A Edio Standard do Symfony2 usa YAML para seus arquivos de configurao, mas o Symfony2 tambm
suporta XML, PHP e anotaes nativamente. Os diferentes formatos so compatveis e podem ser utilizados alternadamente dentro de uma aplicao. Alm disso, o desempenho de sua aplicao no depende do formato de configurao
que voc escolher, pois tudo armazenado em cache na primeira solicitao.

Controladores

Controlador um nome fantasia para uma funo ou mtodo PHP que manipula as solicitaes de entrada e retorna
respostas (cdigo HTML, na maioria das vezes). Em vez de usar as variveis globais e funes do PHP (como
$_GET ou header()) para gerenciar essas mensagens HTTP, o Symfony usa objetos: Request e Response. O
controlador mais simples possvel pode criar a resposta manualmente, com base na solicitao:
use Symfony\Component\HttpFoundation\Response;
$name = $request->query->get(name);
return new Response(Hello .$name, 200, array(Content-Type => text/plain));

Nota: O Symfony2 engloba a Especificao HTTP, que so as regras que regem toda a comunicao na Web. Leia o
captulo Fundamentos de Symfony e HTTP do livro para aprender mais sobre ela e o poder que isso acrescenta.
O Symfony2 escolhe o controlador com base no valor _controller da configurao de roteamento:
AcmeDemoBundle:Welcome:index. Esta string o nome lgico do controlador, e ela referencia o mtodo
indexAction da classe Acme\DemoBundle\Controller\WelcomeController:
// src/Acme/DemoBundle/Controller/WelcomeController.php
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class WelcomeController extends Controller
{
public function indexAction()
{
return $this->render(AcmeDemoBundle:Welcome:index.html.twig);
}
}

Dica:
Voc poderia ter usado o nome completo da classe e do mtodo Acme\DemoBundle\Controller\WelcomeController::indexAction - para o valor _controller.
Mas, se voc seguir algumas convenes simples, o nome lgico menor e permite mais flexibilidade.
A classe WelcomeController estende a classe nativa Controller, que fornece mtodos de atalho teis, tal como o mtodo render() que carrega e renderiza um template
(AcmeDemoBundle:Welcome:index.html.twig). O valor retornado um objeto Response populado
com o contedo processado. Assim, se as necessidades surgirem, o Response pode ser ajustado antes de ser enviado
ao navegador:
public function indexAction()
{
$response = $this->render(AcmeDemoBundle:Welcome:index.txt.twig);
$response->headers->set(Content-Type, text/plain);

1.1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

return $response;
}

No importa como voc faz isso, o objetivo final do seu controlador sempre ser retornar o objeto Response que
deve ser devolvido ao usurio. Este objeto Response pode ser populado com cdigo HTML, representar um redirecionamento do cliente, ou mesmo retornar o contedo de uma imagem JPG com um cabealho Content-Type de
image/jpg.
Dica: Estender a classe base Controller opcional. De fato um controlador pode ser uma funo PHP simples
ou at mesmo uma closure PHP. O captulo O Controlador do livro lhe ensina tudo sobre os controladores do
Symfony2.
O nome do template, AcmeDemoBundle:Welcome:index.html.twig, o nome lgico do template e faz
referncia ao arquivo Resources/views/Welcome/index.html.twig dentro do AcmeDemoBundle (localizado em src/Acme/DemoBundle). A seo bundles abaixo ir explicar porque isso til.
Agora, d uma olhada novamente na configurao de roteamento e encontre a chave _demo.
# app/config/routing_dev.yml
_demo:
resource: "@AcmeDemoBundle/Controller/DemoController.php"
type:
annotation
prefix:
/demo

O Symfony2 pode ler/importar as informaes de roteamento de diferentes arquivos escritos em


YAML, XML, PHP ou at mesmo incorporado em anotaes PHP. Aqui, o nome lgico do arquivo

@AcmeDemoBundle/Controller/DemoController.php
e
refere-se
ao
arquivo
src/Acme/DemoBundle/Controller/DemoController.php . Neste arquivo, as rotas so definidas
como anotaes nos mtodos da ao:
// src/Acme/DemoBundle/Controller/DemoController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class DemoController extends Controller
{
/**
* @Route("/hello/{name}", name="_demo_hello")
* @Template()
*/
public function helloAction($name)
{
return array(name => $name);
}
// ...
}

A anotao @Route() define uma nova rota com um padro /hello/{name} que executa o mtodo
helloAction quando corresponder. A string entre chaves como {name} chamada de placeholder. Como voc
pode ver, o seu valor pode ser obtido atravs do argumento do mtodo $name.
Nota: Mesmo as anotaes no sendo suportadas nativamente pelo PHP, voc as usar extensivamente no Symfony2
como uma forma conveniente de configurar o comportamento do framework e manter a configurao prxima ao
cdigo.
Se voc verificar o cdigo do controlador, poder ver que em vez de renderizar um template e retornar um objeto

Captulo 1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

Response como antes, ele apenas retorna um array de parmetros. A anotao @Template() diz ao Symfony para
renderizar o template para voc, passando cada varivel do array ao template. O nome do template que renderizado
segue o nome do controlador. Assim, neste exemplo, o template AcmeDemoBundle:Demo:hello.html.twig
renderizado (localizado em src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig).
Dica: As anotaes @Route() e @Template() so mais poderosas do que os exemplos simples mostrados neste
tutorial. Saiba mais sobre anotaes em controladores_ na documentao oficial.

Templates

O controlador renderiza o template src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig


(ou AcmeDemoBundle:Demo:hello.html.twig se voc usar o nome lgico):
{# src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig #}
{% extends "AcmeDemoBundle::layout.html.twig" %}
{% block title "Hello " ~ name %}
{% block content %}
<h1>Hello {{ name }}!</h1>
{% endblock %}

Por padro, o Symfony2 usa o Twig como seu template engine, mas voc tambm pode usar templates tradicionais
PHP se voc escolher. No prximo captulo apresentaremos como os templates funcionam no Symfony2.
Bundles

Voc pode ter se perguntado por que a palavra bundle usada em muitos nomes que vimos at agora. Todo o cdigo
que voc escreve para a sua aplicao est organizado em bundles. Na forma de falar do Symfony2, um bundle um
conjunto estruturado de arquivos (arquivos PHP, folhas de estilo, JavaScripts, imagens, ...) que implementam uma funcionalidade nica (um blog, um frum, ...) e que podem ser facilmente compartilhados com outros desenvolvedores.
At agora, manipulamos um bundle, AcmeDemoBundle. Voc vai aprender mais sobre bundles no ltimo captulo
deste tutorial.
Trabalhando com Ambientes
Agora que voc tem uma compreenso melhor de como funciona o Symfony2, verifique a parte inferior de qualquer
pgina renderizada com o Symfony2. Voc deve observar uma pequena barra com o logotipo do Symfony2. Isso
chamado de Barra de ferramentas para Debug Web e a melhor amiga do desenvolvedor.

1.1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

Mas, o que voc v inicialmente apenas a ponta do iceberg; clique sobre o estranho nmero hexadecimal para revelar
mais uma ferramenta de depurao muito til do Symfony2: o profiler.

claro, voc no vai querer mostrar essas ferramentas quando implantar a sua aplicao em produo. por isso
que voc vai encontrar um outro front controller no diretrio web/ (app.php), que otimizado para o ambiente de
produo:
http://localhost/app.php/demo/hello/Fabien

E, se voc usar o Apache com o mod_rewrite habilitado, poder at omitir a parte app.php da URL:
http://localhost/demo/hello/Fabien

Por ltimo, mas no menos importante, nos servidores de produo, voc deve apontar seu diretrio raiz web para o
diretrio web/ para proteger sua instalao e ter uma URL ainda melhor:

Captulo 1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

http://localhost/demo/hello/Fabien

Nota: Note que as trs URLs acima so fornecidas aqui apenas como exemplos de como uma URL parece quando
o front controller de produo usado (com ou sem mod_rewrite). Se voc realmente experiment-los em uma
instalao do Symfony Standard Edition voc receber um erro 404 pois o AcmeDemoBundle est habilitado somente
no ambiente dev e suas rotas importam o app/config/routing_dev.yml.
Para fazer a sua aplicao responder mais rpido, o Symfony2 mantm um cache sob o diretrio app/cache/. No
ambiente de desenvolvimento (app_dev.php), esse cache liberado automaticamente sempre que fizer alteraes
em qualquer cdigo ou configurao. Mas esse no o caso do ambiente de produo (app.php) onde o desempenho
fundamental. por isso que voc deve sempre usar o ambiente de desenvolvimento ao desenvolver a sua aplicao.
Diferentes ambientes de uma dada aplicao diferem apenas na sua configurao. Na verdade, uma configurao pode
herdar de outra:
# app/config/config_dev.yml
imports:
- { resource: config.yml }
web_profiler:
toolbar: true
intercept_redirects: false

O ambiente dev (que carrega o arquivo de configurao config_dev.yml) importa o arquivo global
config.yml e, em seguida, modifica-o, neste exemplo, habilitando a barra de ferramentas para debug web.
Consideraes Finais
Parabns! Voc j teve a sua primeira amostra de cdigo do Symfony2. Isso no foi to difcil, foi? H muito mais para
explorar, mas, voc j deve ter notado como o Symfony2 torna muito fcil implementar web sites de forma melhor e
mais rpida. Se voc est ansioso para aprender mais sobre o Symfony2, mergulhe na prxima seo: A Viso.

1.1.2 A View
Depois de ler a primeira parte desse tutorial, voc decidiu que o Symfony2 vale pelo menos mais 10 minutos? Boa
escolha! Nessa segunda parte, voc vai aprender sobre o sistema de template do Symfony2, o Twig. Ele um sistema
de templates para PHP flexvel, rpido e seguro. Ele faz com que seus templates sejam mais legveis e concisos e
tambm os torna mais amigveis para os web designers.
Nota: Em vez do Twig, voc tambm pode usar PHP para os seus templates. Ambos so suportados pelo Symfony2.

Familiarizando-se com o Twig


Dica: Se quiser aprender a usar o Twig, ns recomendamos fortemente que leia a documentao oficial dele. Essa
seo apenas uma viso geral sobre os principais conceitos.
Um template Twig um arquivo de texto que pode gerar qualquer tipo de contedo (HTML, XML, CSV, LaTex, ...).
O Twig define dois tipos de delimitadores:
{{ ...

}}: Imprime uma varivel ou o resultado de uma expresso;

{% ...

%}: Controla a lgica do template; usado para executar loops for e instrues if, por exemplo.

1.1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

Abaixo temos um template mnimo que ilustra alguns comandos bsicos usando as duas vriaveis, page_title e
navigation, que so passadas para o template:
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<h1>{{ page_title }}</h1>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
</body>
</html>

Dica: Podem ser includos comentrios nos templates usando o delimitador {# ...

#}.

Para renderizar um template no Symfony, use o mtodo render a partir do controller, e passe para ele todas as
varivels necessrias ao template:
$this->render(AcmeDemoBundle:Demo:hello.html.twig, array(
name => $name,
));

As variveis passadas para o template podem ser strings, arrays ou at objetos. O Twig abstrai a diferena entre eles e
deixa acessar os atributos de uma varivel usando dot notation (.):
{# array(name => Fabien) #}
{{ name }}
{# array(user => array(name => Fabien)) #}
{{ user.name }}
{# force array lookup #}
{{ user[name] }}
{# array(user => new User(Fabien)) #}
{{ user.name }}
{{ user.getName }}
{# force method name lookup #}
{{ user.name() }}
{{ user.getName() }}
{# pass arguments to a method #}
{{ user.date(Y-m-d) }}

Nota: importante saber que as chaves no fazem parte da varivel mas sim do comando de impresso. Se voc
acessar variveis em tags no coloque as chaves em volta delas.

10

Captulo 1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

Decorando os Templates
frequente em um projeto que os templates compartilhem elementos comuns, como os bem-conhecidos cabealho e
rodap. No Symfony2, gostamos de enxergar essa situao de uma forma diferente: um template pode ser decorado
por outro. Funciona exatamente do mesmo jeito que nas classes PHP: a herana de templates permite que se construa
o template base layout, que contm todos os elementos comuns do seu site, e define blocos que os templates filhos
podem sobrescrever.
O template hello.html.twig herda do layout.html.twig, graas a tag extends:
{# src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig #}
{% extends "AcmeDemoBundle::layout.html.twig" %}
{% block title "Hello " ~ name %}
{% block content %}
<h1>Hello {{ name }}!</h1>
{% endblock %}

A notao AcmeDemoBundle::layout.html.twig parece familiar, no mesmo? Ela a mesma notao


usada para referenciar um template normal. A parte :: significa simplesmente que o elemento controller est vazio,
ento o arquivo correspondente guardado diretamente no diretrio Resources/views/.
Agora, vamos dar uma olhada em um layout.html.twig simplificado:
{# src/Acme/DemoBundle/Resources/views/layout.html.twig #}
<div class="symfony-content">
{% block content %}
{% endblock %}
</div>

As tags {% block %} definem blocos que os templates filhos podem preencher. Tudo o que essas tags fazem dizer
ao sistema de template que um filho pode sobrescrever aquelas partes de seu template pai.
Nesse exemplo, o template hello.html.twig sobrescreve o bloco content, que significa que o
texto Hello Fabien renderizado dentro do elemento div.symfony-content
Usando Tags, Filtros e Funes
Uma das melhores funcionalidades do Twig sua extensibilidade por meio de tags, filtros e funes. O Symfony2 j
vem com muitos desses embutidos facilitando o trabalho do designer de templates.
Incluindo outros Templates

A melhor forma de compartilhar um trecho de cdigo entre vrios templates distintos criar um novo desses que possa
ser includo nos outros.
Crie um template embedded.html.twig:
{# src/Acme/DemoBundle/Resources/views/Demo/embedded.html.twig #}
Hello {{ name }}

E altere o template index.html.twig para inclu-lo:


{# src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig #}
{% extends "AcmeDemoBundle::layout.html.twig" %}
{# override the body block from embedded.html.twig #}

1.1. Guia de Incio Rpido

11

Symfony Docs pt-BR Documentation, Verso 2.4

{% block content %}
{% include "AcmeDemoBundle:Demo:embedded.html.twig" %}
{% endblock %}

Incorporando outros Controllers

E o que fazer se voc quiser incorporar o resultado de um outro controller em um template? Isso muito til quando
estiver trabalhado com Ajax, ou quando o template incorporado precisa de alguma varivel que no est disponvel no
template principal.
Suponha que voc tenha criado uma action fancy, e quer inclu-la dentro do template index. Para fazer isso, use a
tag render:
{# src/Acme/DemoBundle/Resources/views/Demo/index.html.twig #}
{% render "AcmeDemoBundle:Demo:fancy" with { name: name, color: green } %}

Aqui, a string AcmeDemoBundle:Demo:fancy se refere a action fancy do controller Demo. Os argumentos


(namee color) agem como variveis de requisies simuladas (como se fancyAction estivesse manipulando uma requisio totalmente nova) e ficam disponveis para o controller:
// src/Acme/DemoBundle/Controller/DemoController.php
class DemoController extends Controller
{
public function fancyAction($name, $color)
{
// create some object, based on the $color variable
$object = ...;

return $this->render(AcmeDemoBundle:Demo:fancy.html.twig, array(name => $name, object =


}
// ...
}

Criando Links entre Pginas

Quando estamos falando de aplicaes web, a criao de links entre pginas uma obrigao. Em vez de fazer
hardcode das URLS nos templates, usamos a funo path que sabe como gerar URLs baseando-se na configurao
das rotas. Dessa forma, todas as URLs podem ser atualizadas facilmente apenas mudando essa configurao:
<a href="{{ path(_demo_hello, { name: Thomas }) }}">Greet Thomas!</a>

A funo path pega o nome da rota e um array de parmetros como argumentos. O nome da rota a chave principal
sob a qual as rotas so referenciadas e os parmetros so os valores dos marcadores definidos no padro da rota:
// src/Acme/DemoBundle/Controller/DemoController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/**
* @Route("/hello/{name}", name="_demo_hello")
* @Template()
*/
public function helloAction($name)
{

12

Captulo 1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

return array(name => $name);


}

Dica: A funo url cria URLs absolutas: {{ url(_demo_hello, { name:

Thomas }) }}.

Incluindo Assets: imagens, JavaScripts e folhas de estilo

O que seria da Internet sem as imagens, os JavaScripts e as folhas de estilo? O Symfony2 fornece a funo asset
para lidar com eles de forma fcil:
<link href="{{ asset(css/blog.css) }}" rel="stylesheet" type="text/css" />
<img src="{{ asset(images/logo.png) }}" />

O objetivo principal da funo asset deixar sua aplicao mais porttil. Graas a ela, voc pode mover o diretrio
raiz da aplicao para qualquer lugar no diretrio web root sem mudar nem uma linha no cdigo de seus templates.
Escapando Variveis
O Twig configurado por padro para escapar automaticamente toda a sada de dados. Leia a documentao do Twig
para aprender mais sobre como escapar a sada de dados e sobre a extenso Escaper.
Consideraes Finais
O Twig simples mas poderoso. Graas a incluso de layouts, blocos, templates e actions, muito fcil organizar seus
templates de uma maneira lgica e extensvel. No entanto se voc no estiver confortvel com o Twig sempre poder
usar templates PHP no Symfony sem problemas.
Voc est trabalhando com o Symfony2 h apenas 20 minutos, mas j pode fazer coisas incrveis com ele. Esse o
poder do Symfony2. Aprender a base fcil, e logo voc aprender que essa simplicidade est escondida debaixo de
uma arquitetura muito flexvel.
Mas eu j estou me adiantando. Primeiro, voc precisa aprender mais sobre o controller e esse exatamente o assunto
da prxima parte do tutorial. Pronto para mais 10 minutos de Symfony2?

1.1.3 O Controller
Ainda est com a gente depois das primeiras duas partes? Ento voc j est se tornando um viciado no Symfony2!
Sem mais delongas, vamos descobrir o que os controllers podem fazer por voc.
Usando Formatos
Atualmente, uma aplicao web deve ser capaz de entregar mais do que apenas pginas HTML. Desde XML para
feeds RSS ou Web Services, at JSON para requisies Ajax, existem muitos formatos diferentes para escolher. Dar
suporte para esses formatos no Symfony2 simples. s ajustar a rota, como aqui que acrescentando um valor padro
xml para a varivel _format:
// src/Acme/DemoBundle/Controller/DemoController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

1.1. Guia de Incio Rpido

13

Symfony Docs pt-BR Documentation, Verso 2.4

/**
* @Route("/hello/{name}", defaults={"_format"="xml"}, name="_demo_hello")
* @Template()
*/
public function helloAction($name)
{
return array(name => $name);
}

Usando o formato de requisio (como definido pelo valor _format), o Symfony2 automaticamente seleciona o
template correto, nesse caso o hello.xml.twig:
<!-- src/Acme/DemoBundle/Resources/views/Demo/hello.xml.twig -->
<hello>
<name>{{ name }}</name>
</hello>

Isso tudo. Para os formatos padro, o Symfony2 tambm ir escolher automaticamente o melhor cabealho
Content-Type para a resposta. Se voc quiser dar suporte para diferentes formatos numa nica action, em vez
disso use o marcador {_format} no padro 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|j
* @Template()
*/
public function helloAction($name)
{
return array(name => $name);
}

O controller agora ser chamado


/demo/hello/Fabien.json.

por

URLs

parecidas

com

/demo/hello/Fabien.xml

ou

A entrada requirements define expresses regulares que os marcadores precisam casar. Nesse exemplo, se voc
tentar requisitar /demo/hello/Fabien.js ir receber um erro HTTP 404 pois a requisio no casa com o
requisito _format.
Redirecionamento e Encaminhamento
Se voc quiser redirecionar o usurio para outra pgina, use o mtodo redirect():
return $this->redirect($this->generateUrl(_demo_hello, array(name => Lucas)));

O mtodo generateUrl() o mesmo que a funo path() que usamos nos templates. Ele pega o nome da rota
e um array de parmetros como argumentos e retorna a URL amigvel associada.
Voc tambm pode facilmente encaminhar a action para uma outra com o mtodo forward(). Internamente, o
Symfony faz uma sub-requisio, e retorna o objeto Response daquela sub-requisio:

$response = $this->forward(AcmeDemoBundle:Hello:fancy, array(name => $name, color => green));


// faa algo com a resposta ou a retorne diretamente

14

Captulo 1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

Pegando informao da Requisio


Alm dos valores dos marcadores de rota, o controller tem acesso ao objeto Request:
$request = $this->getRequest();
$request->isXmlHttpRequest(); // essa uma requisio Ajax?
$request->getPreferredLanguage(array(en, fr));
$request->query->get(page); // pega um parmetro $_GET
$request->request->get(page); // pega um parmetro $_POST

Em um template, voc tambm pode acessar o objeto Request via a varivel app.request:
{{ app.request.query.get(page) }}
{{ app.request.parameter(page) }}

Persistindo os Dados na Sesso


Mesmo o protocolo HTTP sendo stateless (no tendo monitorao 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 requisies, o Symfony2 guarda os atributos num cookie usando sesses nativas do PHP.
fcil guardar e recuperar a informao da sesso a partir de qualquer controller:
$session = $this->getRequest()->getSession();
// guarda um atributo para reutilizao na prxima requisio do usurio
$session->set(foo, bar);
// em outro controller para outra requisio
$foo = $session->get(foo);
// usa um valor default se a chave no existe
$filters = $session->set(filters, array());

Voc pode guardar pequenas mensagens que ficaro disponveis apenas para a prxima requisio:
// guarda uma mensagem para a prxima requisio somente (em um controller)
$session->getFlashBag()->add(notice, Congratulations, your action succeeded!);
// exibe quaisquer mensagens no prximo 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 usurio para outra pgina
(que ento mostrar a mensagem). Por favor note que quando voc usa has() ao invs de get(), a mensagem flash no
ser apagada e, assim, permanece disponvel durante os pedidos seguintes.

1.1. Guia de Incio Rpido

15

Symfony Docs pt-BR Documentation, Verso 2.4

Protegendo Recursos
A verso Standard Edition do Symfony vem com uma configurao de segurana simples que atende as necessidades
mais comuns:
# 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 configurao requer que os usurios se autentiquem para acessar qualquer URL comeada por
/demo/secured/ e define dois usurios vlidos: user e admin. Alm disso o usurio admin tem uma permisso ROLE_ADMIN, que tambm inclui a permisso ROLE_USER (veja a configurao role_hierarchy).
Dica: Para melhorar a legibilidade, nessa nossa configurao simplificada as senhas so guardadas em texto puro,
mas voc pode usar algum algoritmo de hash ajustando a seo encoders.
Indo para a URL http://localhost/app_dev.php/demo/secured/hello voc ser automaticamente
redirecionado para o formulrio de login pois o recurso protegido por um firewall.
Voc tambm pode forar a action para requisitar uma permisso especial usando a annotation @Secure no controller:
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")

16

Captulo 1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

* @Template()
*/
public function helloAdminAction($name)
{
return array(name => $name);
}

Agora, se autentique como user (que no tem a permisso ROLE_ADMIN) e, a partir da pgina protegida hello,
clique no link Hello resource secured. O Symfony2 deve retornar um erro HTTP 403, indicando que o usurio est
proibido de acessar o recurso.
Nota: A camada de segurana do Symfony2 bem flexvel e vem com muitos servios de usurios (como no Doctrine
ORM) e autenticao (como HTTP bsico, HTTP digest ou certificados X509). Leia o captulo Segurana do livro
para mais informao de como us-los e configur-los.

Fazendo Cache dos Recursos


A medida que seu site comea a ter mais trfego, voc vai querer evitar fazer a gerao dos mesmos recursos vrias
e vrias vezes. O Symfony2 usa cabealhos de cache HTTP para gerenciar o cache dos recursos. Para estratgias
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 tambm pode usar validaes em vez de expirao,
ou uma combinao de ambos, se isso se encaixar melhor nas suas necessidades.
O cache de recursos gerenciado pelo proxy reverso embutido no Symfony2. Mas como o cache gerenciado usando
cabealhos de cache HTTP normais, voc pode substituir o proxy reverso com o Varnish ou o Squid e estender a sua
aplicao de forma fcil.
Nota: Mas como se virar se voc no puder fazer cache de pginas inteiras? O Symfony2 continua tendo a soluo,
via Edge Side Includes (ESI), que so suportados nativamente. Aprenda mais sobre isso lendo o captulo HTTP
Cache do livro.

Consideraes Finais
Isso foi tudo, e acho que no gastamos nem 10 minutos. Fizemos uma breve introduo aos bundles na primeira parte
e todas as funcionalidades sobre as quais aprendemos at agora so parte do bundle ncleo do framework. Graas aos
bundles, tudo no Symfony2 pode ser estendido ou substitudo. Esse o tema da prxima parte do tutorial.

1.1. Guia de Incio Rpido

17

Symfony Docs pt-BR Documentation, Verso 2.4

1.1.4 A Arquitetura
Voc meu heri! Quem imaginaria que voc ainda estaria aqui aps as trs primeiras partes? Seus esforos sero
bem recompensados em breve. As trs primeiras partes no contemplaram profundamente a arquitetura do framework.
Porque ela faz o Symfony2 destacar-se na multido de frameworks, vamos mergulhar na arquitetura agora.
Compreendendo a estrutura de diretrio
A estrutura de diretrio de uma aplicao do Symfony2 bastante flexvel, mas a estrutura do diretrio da distribuio
Standard Edition reflete a estrutura tpica e recomendada de uma aplicao Symfony2:
app/: A configurao da aplicao;
src/: O cdigo PHP do projeto;
vendor/: As dependncias de terceiros;
web/: O diretrio raiz web.
O Diretrio web/

O diretrio raiz web o local de todos os arquivos pblicos e estticos, como imagens, folhas de estilo e arquivos
JavaScript. tambm o local onde cada front controller reside:
// web/app.php
require_once __DIR__./../app/bootstrap.php.cache;
require_once __DIR__./../app/AppKernel.php;
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel(prod, false);
$kernel->loadClassCache();
$kernel->handle(Request::createFromGlobals())->send();

O kernel primeiro solicita o arquivo bootstrap.php.cache, que inicializa a estrutura e regista o autoloader (veja
abaixo).
Como qualquer front controller, o app.php usa uma classe Kernel, AppKernel, para a inicializao da aplicao.
O Diretrio app/

A classe AppKernel o principal ponto de entrada da configurao da aplicao e, como tal, ele armazenado no
diretrio app/.
Essa classe deve implementar dois mtodos:
registerBundles() que deve retornar um array de todos os bundles necessrios para executar a aplicao.
registerContainerConfiguration() que carrega a configurao da aplicao (veremos mais sobre
isso depois).
O autoloading do PHP pode ser configurado via app/autoload.php:
// app/autoload.php
use Symfony\Component\ClassLoader\UniversalClassLoader;
$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(

18

Captulo 1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

Symfony
Sensio
JMS
Doctrine\\Common
Doctrine\\DBAL
Doctrine
Monolog
Assetic
Metadata

=>
=>
=>
=>
=>
=>
=>
=>
=>

array(__DIR__./../vendor/symfony/symfony/src, __DIR__./../vendor/bundles
__DIR__./../vendor/bundles,
__DIR__./../vendor/jms/,
__DIR__./../vendor/doctrine/common/lib,
__DIR__./../vendor/doctrine/dbal/lib,
__DIR__./../vendor/doctrine/orm/lib,
__DIR__./../vendor/monolog/monolog/src,
__DIR__./../vendor/kriswallsmith/assetic/src,
__DIR__./../vendor/jms/metadata/src,

));
$loader->registerPrefixes(array(
Twig_Extensions_ => __DIR__./../vendor/twig/extensions/lib,
Twig_
=> __DIR__./../vendor/twig/twig/lib,
));
// ...
$loader->registerNamespaceFallbacks(array(
__DIR__./../src,
));
$loader->register();

O UniversalClassLoader usado para fazer o autoload dos arquivos que respeitam as normas tcnicas de
interoperabilidade para namespaces do PHP 5.3 ou a conveno de nomenclatura para classes do PEAR. Como voc
pode ver aqui, todas as dependncias so armazenadas sob o diretrio vendor/, mas isso apenas uma conveno.
Voc pode armazen-las onde quiser, globalmente em seu servidor ou localmente em seus projetos.
Nota: Se voc quiser saber mais sobre a flexibilidade do autoloader do Symfony2, leia o captulo O Componente
ClassLoader.

Compreendendo o Sistema dos Bundles


Esta seo apresenta um dos maiores e mais poderosos recursos do Symfony2, o sistema de bundle.
Um bundle como um plugin em outro software. Ento por que ele chamado de bundle de no de plugin? Porque
tudo um bundle no Symfony2, desde as funcionalidades do ncleo do framework at o cdigo que voc escreve para
a sua aplicao. Os bundles so cidados de primeira classe no Symfony2. Isso lhe fornece a flexibilidade de usar
funcionalidades pr-construdas que vm em bundles de terceiros ou distribuir os seus prprios bundles. Isso torna
mais fcil a tarefa de escolher quais recursos que sero habilitados na sua aplicao e otimiz-los da maneira que
desejar. E, no final do dia, o cdigo da sua aplicao to importante quanto o prprio framework.
Registrando um Bundle

Uma aplicao composta de bundles, que foram definidos no mtodo registerBundles() da classe
AppKernel. Cada bundle um diretrio que contm uma nica classe Bundle que descreve ele:
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Symfony\Bundle\MonologBundle\MonologBundle(),
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),

1.1. Guia de Incio Rpido

19

Symfony Docs pt-BR Documentation, Verso 2.4

new
new
new
new

Symfony\Bundle\DoctrineBundle\DoctrineBundle(),
Symfony\Bundle\AsseticBundle\AsseticBundle(),
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),

);
if (in_array($this->getEnvironment(), array(dev, test))) {
$bundles[] = new Acme\DemoBundle\AcmeDemoBundle();
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
$bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
}
return $bundles;
}

Alm do AcmeDemoBundle que ns j falamos, observe que o kernel tambm habilita outros bundles, como o
FrameworkBundle, DoctrineBundle, SwiftmailerBundle e o AsseticBundle. Todos eles fazem
parte do framework.
Configurando um Bundle

Cada bundle pode ser personalizado atravs dos arquivos de configurao escritos em YAML, XML ou PHP. Esta a
configurao padro:
# app/config/config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
framework:
#esi:
#translator:
secret:
router:
form:
csrf_protection:
validation:
templating:
default_locale:
session:
auto_start:

~
{ fallback: "%locale%" }
"%secret%"
{ resource: "%kernel.root_dir%/config/routing.yml" }
true
true
{ enable_annotations: true }
{ engines: [twig] } #assets_version: SomeVersionScheme
"%locale%"
true

# Twig Configuration
twig:
debug:
"%kernel.debug%"
strict_variables: "%kernel.debug%"
# Assetic Configuration
assetic:
debug:
"%kernel.debug%"
use_controller: false
bundles:
[ ]
# java: /usr/bin/java
filters:
cssrewrite: ~
# closure:

20

Captulo 1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

#
jar: "%kernel.root_dir%/java/compiler.jar"
# yui_css:
#
jar: "%kernel.root_dir%/java/yuicompressor-2.4.2.jar"
# Doctrine Configuration
doctrine:
dbal:
driver:
"%database_driver%"
host:
"%database_host%"
port:
"%database_port%"
dbname:
"%database_name%"
user:
"%database_user%"
password: "%database_password%"
charset: UTF8
orm:
auto_generate_proxy_classes: "%kernel.debug%"
auto_mapping: true
# Swiftmailer Configuration
swiftmailer:
transport: "%mailer_transport%"
host:
"%mailer_host%"
username: "%mailer_user%"
password: "%mailer_password%"
jms_security_extra:
secure_controllers: true
secure_all_services: false

Cada entrada como framework define a configurao para um bundle especfico. Por exemplo, framework configura o FrameworkBundle enquanto swiftmailer configura o SwiftmailerBundle.
Cada ambiente pode substituir a configurao padro, ao fornecer um arquivo de configurao especfico. Por exemplo,
o ambiente dev carrega o arquivo config_dev.yml, que carrega a configurao principal (ou seja, config.yml)
e, ento, modifica ela para adicionar algumas ferramentas de depurao:
# app/config/config_dev.yml
imports:
- { resource: config.yml }
framework:
router:
{ resource: "%kernel.root_dir%/config/routing_dev.yml" }
profiler: { only_exceptions: false }
web_profiler:
toolbar: true
intercept_redirects: false
monolog:
handlers:
main:
type:
path:
level:
firephp:
type:
level:

stream
"%kernel.logs_dir%/%kernel.environment%.log"
debug
firephp
info

1.1. Guia de Incio Rpido

21

Symfony Docs pt-BR Documentation, Verso 2.4

assetic:
use_controller: true

Estendendo um Bundle

Alm de ser uma boa forma de organizar e configurar seu cdigo, um bundle pode estender um outro bundle. A herana do bundle permite substituir qualquer bundle existente a fim de personalizar seus controladores, templates ou qualquer um de seus arquivos. Aqui o onde os nomes lgicos (por exemplo,
@AcmeDemoBundle/Controller/SecuredController.php) so teis: eles abstraem onde o recurso
realmente armazenado.
Nomes Lgicos de Arquivos Quando voc quer fazer referncia um arquivo de um bundle, use esta notao:
@BUNDLE_NAME/path/to/file; o Symfony2 ir resolver @BUNDLE_NAME para o caminho real do bundle. Por
exemplo, o caminho lgico @AcmeDemoBundle/Controller/DemoController.php seria convertido para
src/Acme/DemoBundle/Controller/DemoController.php, pois o Symfony conhece a localizao do
AcmeDemoBundle.
Nomes Lgicos de Controladores Para os controladores, voc precisa referenciar os
de mtodos usando o formato BUNDLE_NAME:CONTROLLER_NAME:ACTION_NAME. Por
plo,
AcmeDemoBundle:Welcome:index mapeia para o mtodo indexAction da
Acme\DemoBundle\Controller\WelcomeController.

nomes
exemclasse

Nomes Lgicos de Templates Para os templates, o nome lgico AcmeDemoBundle:Welcome:index.html.twig


convertido para o caminho de arquivo src/Acme/DemoBundle/Resources/views/Welcome/index.html.twig.
Os templates tornam-se ainda mais interessantes quando voc percebe que eles no precisam ser armazenados no
sistema de arquivos. Voc pode facilmente armazen-los em uma tabela do banco de dados, por exemplo.
Estendendo Bundles Se voc seguir estas convenes, ento voc pode usar bundle inheritance para sobrescrever os arquivos, controladores ou templates. Por exemplo, voc pode criar um bundle - AcmeNewBundle
- e especificar que ele sobrescreve o AcmeDemoBundle.
Quando o Symfony carregar o controlador AcmeDemoBundle:Welcome:index, ele ir primeiro verificar a classe WelcomeController em
AcmeNewBundle e, se ela no existir, ento ir verificar o AcmeDemoBundle. Isto significa que um bundle
pode sobrescrever quase qualquer parte de outro bundle!
Voc entende agora porque o Symfony2 to flexvel? Compartilhe os seus bundles entre aplicaes, armazene-os
localmente ou globalmente, a escolha sua.
Usando os Vendors
So grandes as probabilidades de que a sua aplicao depender de bibliotecas de terceiros. Estas devem ser armazenadas no diretrio vendor/. Este diretrio j contm as bibliotecas do Symfony2, a biblioteca do SwiftMailer, o
ORM Doctrine, o sistema de template Twig e algumas outras bibliotecas e bundles de terceiros.
Entendendo o Cache e Logs
O Symfony2 provavelmente um dos mais rpidos frameworks full-stack atualmente. Mas como pode ser to rpido
se ele analisa e interpreta dezenas de arquivos YAML e XML para cada pedido? A velocidade , em parte, devido ao
seu sistema de cache. A configurao da aplicao analisada somente no primeiro pedido e depois compilada em
cdigo PHP comum, que armazenado no diretrio app/cache/. No ambiente de desenvolvimento, o Symfony2
22

Captulo 1. Guia de Incio Rpido

Symfony Docs pt-BR Documentation, Verso 2.4

inteligente o suficiente para liberar o cache quando voc altera um arquivo. Mas, no ambiente de produo, sua a
responsabilidade de limpar o cache quando voc atualizar o seu cdigo ou alterar sua configurao.
Ao desenvolver uma aplicao web, as coisas podem dar errado em muitos aspectos. Os arquivos de log no diretrio
app/logs/ dizem tudo sobre os pedidos e ajudam a resolver os problemas rapidamente.
Utilizando a Interface da Linha de Comando
Cada aplicao vem com uma ferramenta de interface de linha de comando (app/console) que ajuda na manuteno da sua aplicao. Ela fornece comandos que aumentam a sua produtividade ao automatizar tarefas tediosas e
repetitivas.
Execute-a sem argumentos para saber mais sobre suas capacidades:
$ php app/console

A opo --help ajuda a descobrir o uso de um comando:


$ php app/console router:debug --help

Consideraes finais
Me chame de louco, mas, depois de ler esta parte, voc deve estar confortvel em mover as coisas e fazer o Symfony2
trabalhar para voc. Tudo no Symfony2 projetado para sair do seu caminho. Portanto, sinta-se livre para renomear e
mover os diretrios como voc desejar.
E isso tudo para o incio rpido. Desde testes at o envio de e-mails, voc ainda precisa aprender muito para se tornar
um mestre no Symfony2. Pronto para aprofundar nestes tpicos agora? No procure mais - v para o Livro oficial e
escolha qualquer tema que voc desejar.
Panorama Geral >
A View >
O Controller >
A Arquitetura

1.1. Guia de Incio Rpido

23

Symfony Docs pt-BR Documentation, Verso 2.4

24

Captulo 1. Guia de Incio Rpido

CAPTULO 2

Livro

Mergulhe no Symfony2 com os tpicos guia:

2.1 Livro
2.1.1 Fundamentos de Symfony e HTTP
Parabns! Aprendendo sobre o Symfony2, voc est no caminho certo para ser um desenvolvedor web mais produtivo,
bem preparado e popular (na verdade, este ltimo por sua prpria conta). O Symfony2 foi criado para voltar ao
bsico: desenvolver ferramentas para ajuda-lo a criar aplicaes mais robustas de uma maneira mais rpida sem ficar
no seu caminho. Ele foi construdo baseando-se nas melhores ideias de diversas tecnologias: as ferramentas e conceitos
que voc est prestes a aprender representam o esforo de milhares de pessoas, realizado durante muitos anos. Em
outras palavras, voc no est apenas aprendendo o Symfony, voc est aprendendo os fundamentos da web, boas
prticas de desenvolvimento e como usar diversas biblotecas PHP impressionantes, dentro e fora do Symfony2. Ento,
prepare-se.
Seguindo a filosofia do Symfony2, este captulo comea explicando o conceito fundamental para o desenvolvimento
web: o HTTP. Independente do seu conhecimento anterior ou linguagem de programao preferida, esse captulo
uma leitura obrigatria para todos.
HTTP simples
HTTP (Hypertext Transfer Protocol, para os geeks) uma linguagem textual que permite que duas mquinas se
comuniquem entre si. s isso! Por exemplo, quando voc vai ler a ltima tirinha do xkcd, acontece mais ou menos
a seguinte conversa:

25

Symfony Docs pt-BR Documentation, Verso 2.4

Apesar da linguagem real ser um pouco mais formal, ainda assim ela bastante simples. HTTP o termo usado para
descrever essa linguagem simples baseada em texto. No importa como voc desenvolva para a web, o objetivo do seu
servidor sempre ser entender simples requisies de texto e enviar simples respostas de texto.
O Symfony2 foi criado fundamentado nessa realidade. Voc pode at no perceber, mas o HTTP algo que voc
utilizada todos os dias. Com o Symfony2 voc ir aprender a domina-lo.
Primeiro Passo: O Cliente envia uma Requisio

Toda comunicao na web comea com uma requisio. Ela uma mensagem de texto criada por um cliente (por
exemplo, um navegador, um app para iPhone etc) em um formato especial conhecido como HTTP. O cliente envia
essa requisio para um servidor e, ento, espera pela resposta.
Veja a primeira parte da interao (a requisio) entre um navegador e o servidor web do xkcd:

No linguajar do HTTP, essa requisio se parece com isso:


GET / HTTP/1.1
Host: xkcd.com
Accept: text/html
User-Agent: Mozilla/5.0 (Macintosh)

26

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Essa simples mensagem comunica tudo o que necessrio sobre o recurso exato que o cliente est requisitando. A
primeira linha de uma requisio HTTP a mais importante e contm duas coisas: a URI e o mtodo HTTP.
A URI (por exemplo, /, /contact etc) um endereo nico ou localizao que identifica o recurso que o cliente
quer. O mtodo HTTP (por exemplo, GET) define o que voc quer fazer com o recurso. Os mtodos HTTP so os
verbos da requisio e definem algumas maneiras comuns de agir em relao ao recurso:
GET
POST
PUT
DELETE

Recupera o recurso do servidor


Cria um recurso no servidor
Atualiza um recurso no servidor
Exclui um recurso do servidor

Tendo isso em mente, voc pode imaginar como seria uma requisio HTTP para excluir uma postagem especfica de
um blog, por exemplo:
DELETE /blog/15 HTTP/1.1

Nota: Existem na verdade nove mtodos definidos pela especificao HTTP, mas a maioria deles no so muito
utilizados ou suportados. Na realidade, muitos dos navegadores modernos no suportam os mtodos PUT e DELETE.
Alm da primeira linha, uma requisio HTTP invariavelmente contm outras linhas de informao chamadas de
cabealhos da requisio. Os cabealhos podem fornecer uma vasta quantidade de informaes, tais como o Host
que foi requisitado, os formatos de resposta que o cliente aceita (Accept) e a aplicao que o cliente est utilizando
para enviar a requisio (User-Agent). Muitos outros cabealhos existem e podem ser encontrados na Wikipedia,
no artigo List of HTTP header fields
Segundo Passo: O Servidor envia uma resposta

Uma vez que o servidor recebeu uma requisio, ele sabe exatamente qual recurso o cliente precisa (atravs do URI)
e o que o cliente quer fazer com ele (atravs do mtodo). Por exemplo, no caso de uma requisio GET, o servidor
prepara o o recurso e o retorna em uma resposta HTTP. Considere a resposta do servidor web do xkcd:

Traduzindo para HTTP, a resposta enviada para o navegador ser algo como:
HTTP/1.1 200 OK
Date: Sat, 02 Apr 2011 21:05:05 GMT
Server: lighttpd/1.4.19
Content-Type: text/html

2.1. Livro

27

Symfony Docs pt-BR Documentation, Verso 2.4

<html>
<!-- HTML for the xkcd comic -->
</html>

A resposta HTTP contm o recurso requisitado (nesse caso, o contedo HTML), bem como outras informaes. A
primeira linha especialmente importante e contm o cdigo de status da resposta HTTP (nesse caso, 200). Esse
cdigo de status uma representao geral da resposta enviada requisio do cliente. A requisio foi bem sucedida? Ocorreu algum erro? Existem diferentes cdigos de status para indentificar sucesso, um erro, ou que o cliente
precisa fazer alguma coisa (por exemplo, redirecionar para outra pgina). Uma lista completa pode ser encontrada na
Wikipedia, no artigo List of HTTP status codes.
Assim como uma requisio, uma resposta HTTP tambm contm informaes adicionais conhecidas como cabealhos HTTP. Por exemplo, um cabealho importante nas respostas HTTP o Content-Type. O contedo de um
mesmo recurso pode ser retornado em vrios formatos diferentes, incluindo HTML, XML ou JSON, s para citar
alguns. O cabealho Content-Type diz ao cliente qual o formato que est sendo retornado.
Existem diversos outros cabealhos, alguns deles bastante poderosos. Certos cabealhos, por exemplo, podem ser
utilizados para criar um poderoso sistema de cache.
Requisies, Respostas e o Desenvolvimento Web

Essa conversao de requisio-resposta o processo fundamental que dirige toda a comunicao na web. Apesar de
to importante e poderoso esse processo, ainda assim, inevitavelmente simples.
O fato mais importante : independente da linguagem que voc utiliza, o tipo de aplicao que voc desenvolva (web,
mobile, API em JSON) ou a filosofia de desenvolvimento que voc segue, o objetivo final da aplicao sempre ser
entender cada requisio e criar e enviar uma resposta apropriada.
O Symfony foi arquitetado para atender essa realidade.
Dica: Para aprender mais sobre a especificao HTTP, leia o original HTTP 1.1 RFC ou HTTP Bis, que trata-se de
um esforo para facilitar o entendimento da especificao original. Para verificar as requisies e respostas enviadas
enquanto navega em um site, voc pode utilizar a extenso do Firefox chamada Live HTTP Headers.

Requisies e Respostas no PHP


Como interagir com a requisio e criar uma resposta utilizando o PHP? Na verdade, o PHP abstrai um pouco
desse processo:
<?php
$uri = $_SERVER[REQUEST_URI];
$foo = $_GET[foo];
header(Content-type: text/html);
echo The URI requested is: .$uri;
echo The value of the "foo" parameter is: .$foo;

Por mais estranho que possa parecer, essa pequena aplicao, de fato, l informaes da requisio HTTP e a est
utilizando para criar um resposta HTTP. Em vez de interpretar a requisio pura, o PHP prepara algumas variveis
superglobais , tais como $_SERVER e $_GET, que contm toda a informao da requisio. Da mesma forma, em vez
de retornar o texto da resposta no formato do HTTP, voc pode utilizar a funo header() para criar os cabealhos e
simplesmente imprimir o que ser o contedo da mensagem da reposta. O PHP ir criar uma reposta HTTP verdadeira
que ser retornada para o cliente.

28

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

HTTP/1.1 200 OK
Date: Sat, 03 Apr 2011 02:14:33 GMT
Server: Apache/2.2.17 (Unix)
Content-Type: text/html
The URI requested is: /testing?foo=symfony
The value of the "foo" parameter is: symfony

Requisies e Respostas no Symfony


O Symfony fornece uma alternativa abordagem feita com o PHP puro, utilizando duas classes que permitem a interao com as requisies e respostas HTTP de uma maneira mais fcil. A classe Request uma simples representao
orientada a objetos de uma requisio HTTP. Com ela, voc tem todas as informaes da requisio nas pontas dos
dedos:
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
// the URI being requested (e.g. /about) minus any query parameters
$request->getPathInfo();
// retrieve GET and POST variables respectively
$request->query->get(foo);
$request->request->get(bar);
// retrieves an instance of UploadedFile identified by foo
$request->files->get(foo);
$request->getMethod();
$request->getLanguages();

// GET, POST, PUT, DELETE, HEAD


// an array of languages the client accepts

Como um bnus, a classe Request faz um monte de trabalho com o qual voc nunca precisar se preocupar. Por
exemplo, o mtodo isSecure() verifica os trs valores diferentes que o PHP utiliza para indicar ser o usurio est
utilizando uma conexo segura (https, por exemplo).
O Symfony tambm fornece a classe Response: uma simples representao em PHP de uma resposta HTTP. Assim
possvel que sua aplicao utilize uma interface orientada a objetos para construir a resposta que precisa ser enviada
ao cliente:
use Symfony\Component\HttpFoundation\Response;
$response = new Response();
$response->setContent(<html><body><h1>Hello world!</h1></body></html>);
$response->setStatusCode(200);
$response->headers->set(Content-Type, text/html);
// prints the HTTP headers followed by the content
$response->send();

Com tudo isso, mesmo que o Symfony no oferecesse mais nada, voc j teria um kit de ferramentas para facilmente
acessar informaes sobre a requisio e uma interface orientada a objetos para criar a resposta. Mesmo depois de
aprender muitos dos poderosos recursos do Symfony, tenha em mente que o objetivo da sua aplicao sempre ser
interpretar uma requisio e criar a resposta apropriada baseada na lgica da sua aplicao.
Dica:

2.1. Livro

As classes Request e Response fazem parte de um componente do Symfony chamado

29

Symfony Docs pt-BR Documentation, Verso 2.4

HttpFoundation. Esse componente pode ser utilizado de forma independente ao framework e tambm possui
classes para tratar sesses e upload de arquivos.

A Jornada da Requisio at a Resposta


Como o prprio HTTP, os objetos Request e Response so bastante simples. A parte difcil de se construir uma
aplicao escrever o que acontece entre eles. Em outras palavras, o trabalho de verdade escrever o cdigo que
interpreta a a requisio e cria a resposta.
A sua aplicao provavelmente faz muitas coisas como enviar emails, tratar do envio de formulrios, salvar coisas no
banco de dados, renderizar pginas HTML e proteger o contedo com segurana. Como cuidar de tudo isso e ainda
ter um cdigo organizado e de fcil manuteno?
O Symfony foi criado para que ele resolva esses problemas, no voc.
O Front Controller

Tradicionalmente, aplicaes so construdas para que cada pgina do site seja um arquivo fsico:
index.php
contact.php
blog.php

Existem diversos problemas para essa abordagem, incluindo a falta de flexibilidade das URLs (e se voc quiser mudar
o arquivo blog.php para news.php sem quebrar todos os seus links?) e o fato de que cada arquivo deve ser
alterado manualmente para incluir um certo conjunto de arquivos essenciais de forma que a segurana, conexes com
banco de dados e a aparncia do site continue consistente.
Uma soluo muito melhor utilizar um front controller: um nico arquivo PHP que trata todas as requisies enviadas
para a sua aplicao. Por exemplo:
/index.php
/index.php/contact
/index.php/blog

executa index.php
executa index.php
executa index.php

Dica: Utilizando o mod_rewrite do Apache (ou o equivalente em outros servidores web), as URLs podem ser
simplificadas facilmente para ser somente /, /contact e /blog.
Agora, cada requisio tratada exatamente do mesmo jeito. Em vez de arquivos PHP individuais para executar cada
URL, o front controller sempre ser executado, e o roteamento de cada URL para diferentes partes da sua aplicao
feito internamente. Assim resolve-se os dois problemas da abordagem original. Quase todas as aplicaes modernas
fazem isso - incluindo apps como o Wordpress.
Mantenha-se Organizado

Dentro do front controller, como voc sabe qual pgina deve ser renderizada e como renderiza-las de uma maneira
sensata? De um jeito ou de outro, voc precisar verificar a URI requisitada e executar partes diferentes do seu cdigo
dependendo do seu valor. Isso pode acabar ficando feio bem rpido:
// index.php
$request = Request::createFromGlobals();
$path = $request->getPathInfo(); // the URL being requested

30

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

if (in_array($path, array(, /)) {


$response = new Response(Welcome to the homepage.);
} elseif ($path == /contact) {
$response = new Response(Contact us);
} else {
$response = new Response(Page not found., 404);
}
$response->send();

Resolver esse problema pode ser difcil. Felizmente exatamente o que o Symfony foi projetado para fazer.
O Fluxo de uma Aplicao Symfony

Quando voc deixa que o Symfony cuide de cada requisio, sua vida fica muito mais fcil. O framework segue um
simples padro para toda requisio:

Figura 2.1: As requisies recebidas so interpretadas pelo roteamento e passadas para as funes controller que
retornam objetos do tipo Response.
Cada pgina do seu site definida no arquivo de configurao de roteamento que mapeia diferentes URLs para
diferentes funes PHP. O trabalho de cada funo, chamadas de controller, usar a informao da requisio - junto
com diversas outras ferramentas disponveis no Symfony - para criar e retornar um objeto Response. Em outras
palavras, o seu cdigo deve estar nas funes controller: l onde voc interpreta a requisio e cria uma resposta.
fcil! Vamos fazer uma reviso:
Cada requisio executa um arquivo front controller;
O sistema de roteamento determina qual funo PHP deve ser executada, baseado na informao da requisio
e na configurao de roteamento que voc criou;
A funo PHP correta executada, onde o seu cdigo cria e retorna o objeto Response apropriado.
Uma Requisio Symfony em Ao

Sem entrar em muitos detalhes, vamos ver esse processo em ao. Suponha que voc quer adicionar a pgina
/contact na sua aplicao Symfony. Primeiro, adicione uma entrada para /contact no seu arquivo de configurao de roteamento:
2.1. Livro

31

Symfony Docs pt-BR Documentation, Verso 2.4

contact:
pattern: /contact
defaults: { _controller: AcmeDemoBundle:Main:contact }

Nota: Esse exemplo utiliza YAML para definir a configurao de roteamento. Essa configurao tambm pode ser
escrita em outros formatos, tais como XML ou PHP.
Quando algum visitar a pgina /contact, essa rota ser encontrada e o controller especfico ser executado. Como
voc ir aprender no captulo sobre roteamento, a string AcmeDemoBundle:Main:contact uma sintaxe encurtada para apontar para o mtodo contactAction dentro de uma classe chamada MainController:
class MainController
{
public function contactAction()
{
return new Response(<h1>Contact us!</h1>);
}
}

Nesse exemplo extremamente simples, o controller simplesmente cria um objeto Response com o HTML
<h1>Contact us!</h1>. No captulo sobre controller, voc ir aprender como um controller pode renderizar templates, fazendo com que o seu cdigo de apresentao (por exemplo, qualquer coisa que gere HTML) fique em um
arquivo de template separado. Assim deixamos o controller livre para se preocupar apenas com a parte complicada:
interagir com o banco de dados, tratar os dados enviados ou enviar emails.
Symfony2: Construa sua aplicao, no suas Ferramentas
Agora voc sabe que o objetivo de qualquer aplicao interpretar cada requisio recebida e criar uma resposta
apropriada. Conforme uma aplicao cresce, torna-se mais difcil de manter o seu cdigo organizado e de fcil
manuteno. Invariavelmente, as mesmas tarefas complexas continuam a aparecer: persistir dados no banco, renderizar
e reutilizar templates, tratar envios de formulrios, enviar emails, validar entradas dos usurios e cuidar da segurana.
A boa notcia que nenhum desses problemas nico. O Symfony um framework cheio de ferramentas para voc
construir a sua aplicao e no as suas ferramentas. Com o Symfony2, nada imposto: voc livre para utilizar o
framework completo ou apenas uma parte dele.
Ferramentas Independentes: Os Componentes do Symfony2

Ento, o que o Symfony2? Primeiramente, trata-se de uma coleo de vinte bibliotecas independentes que podem
ser utilizadas dentro de qualquer projeto PHP. Essas bibliotecas, chamadas de Components do Symfony2, contm
coisas teis para praticamente qualquer situao, independente de como o seu projeto desenvolvido. Alguns desses
componentes so:
HttpFoundation - Contm as classes Request e Response, bem como outras classes para tratar de sesses e
upload de arquivos;
Routing - Um poderoso e rpido sistema de roteamento que permite mapear uma URI especfica (por exemplo,
/contact) para uma informao sobre como a requisio deve ser tratada (por exemplo, executar o mtodo
contactAction());
Form - Um framework completo e flexvel para criar formulrios e tratar os dados enviados por eles;
Validator Um sistema para criar regras sobre dados e validar se os dados enviados pelos usurios seguem ou no
essas regras;

32

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

ClassLoader Uma biblioteca de autoloading que faz com que classes PHP possam ser utilizadas sem precisar
adicionar manualmente um require para cada arquivo que as contm;
Templating Um conjunto de ferramentas para renderizar templates, tratar da herana de templates (por exemplo,
um template decorado com um layout) e executar outras tarefas comuns relacionadas a templates;
Security - Uma biblioteca poderosa para tratar qualquer tipo de segurana dentro de sua aplicao;
Translation Um framework para traduzir strings na sua aplicao.
Cada um desses componentes funcionam de forma independente e podem ser utilizados em qualquer projeto PHP, no
importa se voc utiliza o Symfony2 ou no. Cada parte foi feita para ser utilizada e substituda quando for necessrio.
A soluo completa: O framework Symfony2

Ento, o que o framework Symfony2? Ele uma biblioteca PHP que realiza duas tarefas distintas:
1. Fornecer uma seleo de componentes (os componentes do Symfony2, por exemplo) e bibliotecas de terceiros
(por exemplo, a Swiftmailer, utilizada para enviar emails);
2. Fornecer as configuraes necessrias e uma cola para manter todas as peas juntas.
O objetivo do framework integrar vrias ferramentas independentes para criar uma experincia consistente para
o desenvolvedor. At prprio prprio framework um pacote Symfony2 (um plugin, por exemplo) que pode ser
configurado ou completamente substitudo.
O Symfony2 fornece um poderoso conjunto de ferramentas para desenvolver aplicaes web rapidamente sem impor
nada. Usurios normais podem iniciar o desenvolvimento rapidamente utilizando uma distribuio do Symfony2, que
contm o esqueleto de um projeto com as princpais itens padro. Para os usurios mais avanados, o cu o limite.

2.1.2 Symfony2 versus o PHP puro


Por que usar o Symfony2 melhor do que abrir um arquivo e sair escrevendo PHP puro?
Se voc nunca utilizou um framework PHP, no est familiarizado com a filosofia MVC ou est apenas interessado em
entender todo esse hype sobre o Symfony2, este captulo para voc. Em vez de dizer que o Symfony2 permite que
voc desenvolva mais rpido e melhor do que com PHP puro, voc vai ver por si mesmo.
Nesse captulo, voc ir escrever uma simples aplicao em PHP puro, e, ento, refatora-la para deixa-la mais organizada. Voc vai viajar no tempo, vendo as decises sobre o porqu o desenvolvimento web evoluiu com o passar dos
tempos para onde ele est agora.
Ao final, voc ver como o Symfony2 pode resgata-lo das tarefas simples e coloca-lo de volta no controle do seu
cdigo.
Um simples Blog em PHP puro
Nesse captulo, voc vai construir uma aplicao para um blog utilizando apenas o PHP puro. Para comear, crie uma
nica pgina que exibe as postagens armazenadas no banco de dados. Escrever isso em PHP puro rpido e simples:
<?php
// index.php
$link = mysql_connect(localhost, myuser, mypassword);
mysql_select_db(blog_db, $link);
$result = mysql_query(SELECT id, title FROM post, $link);
?>

2.1. Livro

33

Symfony Docs pt-BR Documentation, Verso 2.4

<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>
<ul>
<?php while ($row = mysql_fetch_assoc($result)): ?>
<li>
<a href="/show.php?id=<?php echo $row[id] ?>">
<?php echo $row[title] ?>
</a>
</li>
<?php endwhile; ?>
</ul>
</body>
</html>
<?php
mysql_close($link);

Simples de escrever, rpido de executar e, conforme sua aplicao crescer, impossvel de manter. Existem diversos
problemas que precisam ser tratados:
Sem verificaes de erros: E se a conexo com o banco de dados falhar?
Organizao pobre: Se a aplicao crescer, esse arquivo tambm ir crescer e ficar impossvel de dar manuteno. Onde voc deve colocar o cdigo que cuida de tratar os envios de formulrios? Como voc valida os
dados? Onde voc deve colocar o cdigo que envia emails?
Dificuldade para reutilizar cdigo: Uma vez que tudo est em um nico arquivo, no h como reutilizar
qualquer parte dele em outras pginas do blog.
Nota: Um outro problema no mencionado aqui o fato do banco de dados estar amarrado ao MySQL. Apesar de
no ser tratado aqui, o Symfony2 integra-se totalmente com o Doctrine, uma biblioteca dedicada a abstrao de banco
de dados e mapeamento.
Vamos ao trabalho de resolver esses problemas e mais ainda.
Isolando a Apresentao

O cdigo pode ter ganhos imediatos ao separar a lgica da aplicao do cdigo que prepara o HTML para apresentao:
<?php
// index.php
$link = mysql_connect(localhost, myuser, mypassword);
mysql_select_db(blog_db, $link);
$result = mysql_query(SELECT id, title FROM post, $link);
$posts = array();
while ($row = mysql_fetch_assoc($result)) {
$posts[] = $row;
}

34

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

mysql_close($link);
// include the HTML presentation code
require templates/list.php;

Agora o cdigo HTML est armazenado em um arquivo separado (templates/list.php), que um arquivo
HTML que utiliza um sintaxe PHP parecida com a de templates:
<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="/read?id=<?php echo $post[id] ?>">
<?php echo $post[title] ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</body>
</html>

Por conveno, o arquivo que contm toda a lgica da aplicao - index.php - conhecido como controller. O
termo controller uma palavra que voc vai escutar bastante, independente da linguagem ou framework voc utilize.
Ela refere-se a rea do seu cdigo que processa as entradas do usurio e prepara uma resposta.
Nesse caso, nosso controller prepara os dados do banco de dados e ento inclui um template para apresenta-los. Com
o controller isolado, voc pode facilmente mudar apenas o arquivo de template caso precise renderizar os posts de
blog em algum outro formato (por exemplo, list.json.php para o formato JSON).
Isolando a Lgica (Domnio) da Aplicaco

Por enquanto a aplicao tem apenas uma pgina. Mas e se uma segunda pgina precisar utilizar a mesma conexo
com o banco de dados, ou at o mesmo array de posts do blog? Refatore o cdigo de forma que o comportamento
principal e as funes de acesso aos dados da aplicao fiquem isolados em um novo arquivo chamado model.php:
<?php
// model.php
function open_database_connection()
{
$link = mysql_connect(localhost, myuser, mypassword);
mysql_select_db(blog_db, $link);
return $link;
}
function close_database_connection($link)
{
mysql_close($link);
}
function get_all_posts()

2.1. Livro

35

Symfony Docs pt-BR Documentation, Verso 2.4

{
$link = open_database_connection();
$result = mysql_query(SELECT id, title FROM post, $link);
$posts = array();
while ($row = mysql_fetch_assoc($result)) {
$posts[] = $row;
}
close_database_connection($link);
return $posts;
}

Dica: O nome model.php foi utilizado porque a lgica e o acesso aos dados de uma aplicao so tradicionalmente
conhecidos como a camada de modelo. Em uma aplicao bem organizada, a maioria do cdigo representando
as suas regras de negcio devem estar apenas no model (em vez de estar em um controller). Ao contrrio desse
exemplo, somente uma parte do model (ou nenhuma) est realmente relacionada ao banco de dados.
Agora o controller (index.php) ficou bem simples:
<?php
require_once model.php;
$posts = get_all_posts();
require templates/list.php;

Agora, a nica tarefa do controller recuperar os dados da camada de modelo da sua aplicao (o model) e chamar o
template para renderiza-los. Esse um exemplo bem simples do padro model-view-controller.
Isolando o Layout

At esse ponto a aplicao foi refatorada em trs partes distintas, oferecendo vrias vantagens e a oportunidade de
reutilizar quase qualquer coisa em outras pginas.
A nica parte do cdigo que no pode ser reutilizada o layout da pgina. Conserte isso criando um novo arquivo
chamado layout.php:
<!-- templates/layout.php -->
<html>
<head>
<title><?php echo $title ?></title>
</head>
<body>
<?php echo $content ?>
</body>
</html>

Assim o template (templates/list.php) pode ficar mais simples extendendo o layout:


<?php $title = List of Posts ?>
<?php ob_start() ?>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>

36

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

<a href="/read?id=<?php echo $post[id] ?>">


<?php echo $post[title] ?>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php $content = ob_get_clean() ?>
<?php include layout.php ?>

Agora voc foi apresentado a uma metodologia que permite a reutilizao do layout. Infelizmente, para fazer isso, voc
forado a utilizar no template algumas funes feias do PHP (ob_start(), ob_get_clean()). O Symfony2
utiliza o componente Templating que permite realizar isso de uma maneira limpa e fcil. Logo voc ver esse
componente em ao.
Adicionando a pgina show ao Blog
A pgina list foi refatorada para que o cdigo fique mais organizado e reutilizvel. Para provar isso, adicione ao
blog uma pgina chamada show, que exibe um nico post identificado pelo parmetro id.
Para comear, crie uma nova funo no arquivo model.php que recupera o post com base no id informado:
// model.php
function get_post_by_id($id)
{
$link = open_database_connection();
$id = mysql_real_escape_string($id);
$query = SELECT date, title, body FROM post WHERE id = .$id;
$result = mysql_query($query);
$row = mysql_fetch_assoc($result);
close_database_connection($link);
return $row;
}

Em seguida, crie um novo arquivo chamado show.php - o controller para essa nova pgina:
<?php
require_once model.php;
$post = get_post_by_id($_GET[id]);
require templates/show.php;

Por fim, crie um novo arquivo de template - templates/show.php - para renderizar individualmente o post do
blog:
<?php $title = $post[title] ?>
<?php ob_start() ?>
<h1><?php echo $post[title] ?></h1>
<div class="date"><?php echo $post[date] ?></div>
<div class="body">
<?php echo $post[body] ?>
</div>

2.1. Livro

37

Symfony Docs pt-BR Documentation, Verso 2.4

<?php $content = ob_get_clean() ?>


<?php include layout.php ?>

Criar a segunda pgina foi bastante fcil e nenhum cdigo foi duplicado. Ainda assim, essa pgina criou mais alguns problemas persistentes que um framework pode resolver para voc. Por exemplo, se o parmetro id no for
informado, ou for invlido, a pgina ir quebrar. Seria mais interessante exibir uma pgina de erro 404, mas isso
ainda no pode ser feito de uma maneira fcil. Pior ainda, caso voc esquea de tratar o id utilizando a funo
mysql_real_escape_string(), todo o seu banco de dados estar correndo o risco de sofrer ataques de SQL
injection.
Um problema ainda maior que cada controller deve incluir o arquivo model.php individualmente. O que acontece
se cada controller, de repente, precisar incluir um arquivo adicional para executar alguma outra tarefa global (impor segurana, por exemplo)? Da maneira como est agora, esse cdigo teria que ser adicionado em cada arquivo controller.
Se voc esquecer de incluir algo em algum arquivo espero que no seja algo relacionado a segurana...
Um Front Controller para a salvao
A soluo utilizar um front controller: um nico arquivo PHP que ir processar todas as requisies. Com um front
controller, as URIs vo mudar um pouco, mas comeam a ficar mais flexveis:
Without a front controller
/index.php
=> Blog post list page (index.php executed)
/show.php
=> Blog post show page (show.php executed)
With index.php as the front controller
/index.php
=> Blog post list page (index.php executed)
/index.php/show
=> Blog post show page (index.php executed)

Dica: O index.php pode ser removido da URI se voc estiver utilizando regras de rewrite no Apache (ou algo
equivalente). Nesse caso, a URI resultante para a pgina show ser simplesmente /show.
Ao utilizar um front controller, um nico arquivo PHP (nesse caso o index.php) ir renderizar todas as requisies.
Para a pgina show do blog, o endereo /index.php/show ir, na verdade, executar o arquivo index.php, que
agora responsvel por redirecionar as requisies internamente baseado na URI completa. Como voc pode ver, um
front controller uma ferramente bastante poderosa.
Criando o Front Controller

Voc est prestes a dar um grande passo com a sua aplicao. Com um arquivo para gerenciar todas as suas requisies, voc pode centralizar coisas como segurana, configuraes e roteamento. Nessa aplicao, o arquivo
index.php deve ser esperto o suficiente para renderizar a pgina com a lista de posts ou a pgina com um nico
post baseado na URI da requisio:
<?php
// index.php
// load and initialize any global libraries
require_once model.php;
require_once controllers.php;
// route the request internally
$uri = $_SERVER[REQUEST_URI];
if ($uri == /index.php) {

38

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

list_action();
} elseif ($uri == /index.php/show && isset($_GET[id])) {
show_action($_GET[id]);
} else {
header(Status: 404 Not Found);
echo <html><body><h1>Page Not Found</h1></body></html>;
}

Por questo de organizao, ambos os controllers (os antigos arquivos index.php e show.php) agora so funes
e cada uma foi movida para um arquivo separado, chamado controllers.php:
function list_action()
{
$posts = get_all_posts();
require templates/list.php;
}
function show_action($id)
{
$post = get_post_by_id($id);
require templates/show.php;
}

Sendo um front controller, index.php agora tem um papel inteiramente novo, que inclui carregar as bibliotecas
principais e rotear a aplicao de forma que um dos controllers (as funes list_action() e show_action())
seja chamado. Na verdade, o front controller est comeando a ficar bastante parecido com o mecanismo do Symfony2
utilizado para tratar e redirecionar as requisies:
Dica: Uma outra vantagem do front controller ter URLs flexveis. Note que a URL para a pgina que exibe um post
no blog pode mudar de /show para /read alterando o cdigo apenas em um nico lugar. Antes, um arquivo teria
que ser renomeado. No Symfony2 as URLs podem ser ainda mais flexveis.
At agora, a aplicao evoluiu de um nico arquivo PHP para para uma estrutura organizada que permite a reutilizao
de cdigo. Voc deve estar mais feliz, mas longe de estar satisfeito. Por exemplo, o sistema de roteamento ainda
no consistente e no reconhece que a pgina de listagem (index.php) tambm pode ser acessada via / (se as
regras de rewrite foram adicionadas no Apache). Alm disso, em vez de desenvolver o blog, boa parte do tempo foi
gasto trabalhando na arquitetura do cdigo (por exemplo, roteamento, execuo de controllers, templates etc). Mais
tempo ainda ser necessrio para tratar o envio de formulrios, validao das entradas, logs e segurana. Por que voc
tem que reinventar solues para todos esses problemas?
Adicione um toque de Symfony2

Symfony2 para a salvao. Antes de realmente utilizar o Symfony2, voc precisa ter certeza que o PHP sabe onde
encontrar as classes do framework. Isso pode ser feito com o autoloader fornecido pelo Symfony. Um autoloader
uma ferramenta que permite a utilizao de classes PHP sem a necessidade de incluir os seus arquivos explicitamente.
Primeiro, faa o download do symfony e o coloque no diretrio vendor/symfony/symfony/. A seguir, crie um
o arquivo app/bootstrap.php. Utilize-o para dar require dos dois arquivos da aplicao e para configurar o
autoloader:
<?php
// bootstrap.php
require_once model.php;
require_once controllers.php;
require_once vendor/symfony/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.php;

2.1. Livro

39

Symfony Docs pt-BR Documentation, Verso 2.4

$loader = new Symfony\Component\ClassLoader\UniversalClassLoader();


$loader->registerNamespaces(array(
Symfony => __DIR__./../vendor/symfony/symfony/src,
));
$loader->register();

Esse cdigo diz ao autoloader onde esto as classes do Symfony. Com isso, voc pode comear a utilizar as classes
sem precisar de um require para os arquivos que as contm.
Dentro da filosofia do Symfony est a idia de que a principal tarefa de uma aplicao interpretar cada requisio
e retornar uma resposta. Para essa finalidade, o Symfony2 fornece as classes Request e Response. Elas so
representaes orientadas a objetos da requisio HTTP pura sendo processada e da resposta HTTP sendo retornada.
Utilize-as para melhorar o blog:
<?php
// index.php
require_once app/bootstrap.php;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$uri = $request->getPathInfo();
if ($uri == /) {
$response = list_action();
} elseif ($uri == /show && $request->query->has(id)) {
$response = show_action($request->query->get(id));
} else {
$html = <html><body><h1>Page Not Found</h1></body></html>;
$response = new Response($html, 404);
}
// echo the headers and send the response
$response->send();

Agora os controller so responsveis por retornar um objeto Response. Para tornar isso mais fcil, voc pode
adicionar uma nova funo chamada render_template(), que, a propsito, funciona de forma um pouco parecida
com o mecanismo de template do Symfony2:
// controllers.php
use Symfony\Component\HttpFoundation\Response;
function list_action()
{
$posts = get_all_posts();
$html = render_template(templates/list.php, array(posts => $posts));
return new Response($html);
}
function show_action($id)
{
$post = get_post_by_id($id);
$html = render_template(templates/show.php, array(post => $post));
return new Response($html);
}

40

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

// helper function to render templates


function render_template($path, array $args)
{
extract($args);
ob_start();
require $path;
$html = ob_get_clean();
return $html;
}

Ao adicionar uma pequena parte do Symfony2, a aplicao ficou mais flexvel e confivel. A classe Request
fornece uma maneira segura para acessar informaes sobre a requisio HTTP. Especificamente, o mtodo
getPathInfo() retorna a URI limpa (sempre retornando /show e nunca /index.php/show). Assim, mesmo
que o usurio utilize /index.php/show, a aplicao inteligente o suficiente para direcionar a requisio para
show_action().
O objeto Response d flexibilidade ao construir a resposta HTTP, permitindo a adio de cabealhos HTTP e contedo atravs de um interface orientada a objetos. Apesar das respostas nessa aplicao ainda serem simples, essa
flexibilidade ser til conforme a aplicao crescer.
A aplicao de exemplo no Symfony2

O blog j passou por um longo caminho, mas ele ainda tem muito cdigo para uma aplicao to simples. Por
esse caminho, ns tambm inventamos um simples sistema de roteamento e um mtodo utilizando ob_start() e
ob_get_clean() para renderiar templates. Se, por alguma razo, voc precisasse continuar a construir esse framework do zero, voc poderia pelo menos utilizar isoladamente os components Routing e Templating do Symfony,
que j resolveriam esses problemas.
Em vez de re-resolver problemas comuns, voc pode deixar que o Symfony2 cuide deles pra voc. Aqui est um
exemplo da mesma aplicao, agora feito com o Symfony2:
<?php
// src/Acme/BlogBundle/Controller/BlogController.php
namespace Acme\BlogBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BlogController extends Controller
{
public function listAction()
{
$posts = $this->get(doctrine)->getEntityManager()
->createQuery(SELECT p FROM AcmeBlogBundle:Post p)
->execute();
return $this->render(AcmeBlogBundle:Blog:list.html.php, array(posts => $posts));
}
public function showAction($id)
{
$post = $this->get(doctrine)
->getEntityManager()
->getRepository(AcmeBlogBundle:Post)
->find($id);

2.1. Livro

41

Symfony Docs pt-BR Documentation, Verso 2.4

if (!$post) {
// cause the 404 page not found to be displayed
throw $this->createNotFoundException();
}
return $this->render(AcmeBlogBundle:Blog:show.html.php, array(post => $post));
}
}

Os dois controller ainda esto bastante leves. Cada um utiliza a biblioteca de ORM Doctrine para recuperar objetos do
banco de dados e o componente Templating para renderizar e retornar um objeto Response. O template list ficou
um pouco mais simples:
<!-- src/Acme/BlogBundle/Resources/views/Blog/list.html.php -->
<?php $view->extend(::layout.html.php) ?>
<?php $view[slots]->set(title, List of Posts) ?>

<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="<?php echo $view[router]->generate(blog_show, array(id => $post->getId())) ?>"
<?php echo $post->getTitle() ?>
</a>
</li>
<?php endforeach; ?>
</ul>

O layout est praticamente idntico:


<!-- app/Resources/views/layout.html.php -->
<html>
<head>
<title><?php echo $view[slots]->output(title, Default title) ?></title>
</head>
<body>
<?php echo $view[slots]->output(_content) ?>
</body>
</html>

Nota: Vamos deixar o template da pgina show como um exerccio para voc, uma vez que trivial cria-lo com base
no template da pgina list
Quando o mecanismo do Symfony2 (chamado de Kernel) iniciado, ele precisa de um mapa que indique quais
controllers devem ser executados de acordo com a requisio. A configurao de roteamento contm essa informao
em um formato legvel:
# app/config/routing.yml
blog_list:
pattern: /blog
defaults: { _controller: AcmeBlogBundle:Blog:list }
blog_show:
pattern: /blog/show/{id}
defaults: { _controller: AcmeBlogBundle:Blog:show }

Agora que o Symfony2 est cuidando dessas tarefas simples, o front controller ficou extremamente simples. Uma

42

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

vez que ele faz to pouco, voc nunca mais ter que mexer nele depois de criado (e se voc estiver utilizando uma
distribuio do Symfony2, voc nem mesmo precisar cria-lo!):
<?php
// web/app.php
require_once __DIR__./../app/bootstrap.php;
require_once __DIR__./../app/AppKernel.php;
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel(prod, false);
$kernel->handle(Request::createFromGlobals())->send();

A nica tarefa do front controller iniciar o mecanismo (Kernel) do Symfony2 e passar para ele o objeto Request
que deve ser manuseado. Ento o Symfony utiliza o mapa de rotas para determinar qual controller chamar. Assim
como antes, o mtodo controller responsvel por retornar o objeto Response. No h muito mais que ele precise
fazer.
Para uma representao visual de como o Symfony2 trata cada requisio, veja o diagrama de fluxo da requisio.
Onde vantagem utilizar o Symfony2

Nos prximos captulos voc ir aprender mais sobre cada parte do Symfony funciona e a organizao recomendada
para um projeto. Por enquanto, vamos ver como a migrao do PHP puro para o Symfony2 facilitou a sua vida:
A sua aplicao agora tem um cdigo limpo e organizado de forma consistente apesar do Symfony no
te forar a isso). Isso aumenta a usabilidade e permite que novos desenvolvedores sejam produtivos no seu
projeto de uma maneira mais rpida.
100% do cdigo que voc escreveu para a sua aplicao. Voc no precisa desenvolver ou manter ferramentas de baixo nvel como autoloading, roteamento, ou renderizao nos controllers.
O Symfony2 te d acesso a ferramentas open source como Doctrine e os componentes Templating, Security,
Form, Validation e Translation (s para citar alguns).
A aplicao agora faz uso de URLs totalmente flexveis graas ao componente Routing.
A arquitetura do Symfony2 centrada no HTTP te d acesso a poderosas ferramentas como HTTP caching feito
pelo cache interno de HTTP do Symfony2 ou por ferramentas ainda mais poderosas como o Varnish_. Esse
assunto ser tratado em um prximo captulo sobre caching.
E talvez o melhor de tudo, ao utilizar o Symfony2, voc tem acesso a todo um conjunto de ferramentas open
source de alta qualidade desenvolvidas pela comunidade do Symfony2! Para mais informaes, visite o site Symfony2Bundles.org
Melhores templates
Se voc optar por utiliza-lo, o Symfony2 vem com um sistema de template padro chamado Twig que torna mais fcil
a tarefa de escrever templates e os deixa mais fcil de ler. Isso significa que a aplicao de exemplo pode ter ainda
menos cdigo! Pegue como exemplo o template list escrito com o Twig:
{# src/Acme/BlogBundle/Resources/views/Blog/list.html.twig #}
{% extends "::layout.html.twig" %}
{% block title %}List of Posts{% endblock %}
{% block body %}
<h1>List of Posts</h1>

2.1. Livro

43

Symfony Docs pt-BR Documentation, Verso 2.4

<ul>
{% for post in posts %}
<li>
<a href="{{ path(blog_show, { id: post.id }) }}">
{{ post.title }}
</a>
</li>
{% endfor %}
</ul>
{% endblock %}

O template layout.html.twig correspondente tambm fica mais fcil de escrever:


{# app/Resources/views/layout.html.twig #}
<html>
<head>
<title>{% block title %}Default title{% endblock %}</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>

O Twig bem suportado no Symfony2. E, mesmo que os templates em PHP sempre sero suportados pelo framework,
continuaremos a discutir sobre as muitas vantagens do Twig. Para mais informaes, veja o captulo sobre templates.
Aprenda mais no Cookbook
/cookbook/templating/PHP
Como definir Controladores como Servios

2.1.3 Instalando e Configurando o Symfony


O objetivo deste captulo te deixar com uma aplicao pronta e funcionando, feita com o Symfony. Felizmente, o
Symfony oferece o que chamamos de distribuies, que so projetos bsicos e funcionais que voc pode baixar e
utilizar como base para comear a desenvolver imediatamente.
Dica: Se o que voc procura so instrues sobre a melhor maneira de criar um projeto e armazena-lo por meio de
um sistema de controle de verso, veja Utilizando Controle de Verso.

Instalando uma Distribuio do Symfony2


Dica: Primeiro, certifique-se de que voc tem um servidor web (Apache, por exemplo) com a verso mais recente
possvel do PHP ( recomendado o PHP 5.3.8 ou superior). Para mais informaes sobre os requisitos do Symfony2,
veja a referncia sobre requisitos. Para informaes sobre a configurao de seu especfico root do servidor web,
verifique a seguinte documentao: Apache | Nginx .
O Symfony2 tem pacotes chamados de distribuies, que so aplicaes totalmente funcionais que j vem com
as bibliotecas bsicas do framework, uma seleo de alguns pacotes teis, uma estrutura de diretrios com tudo o
necessrio e algumas configuraes padro. Ao baixar uma distribuio, voc est baixando o esqueleto de uma
aplicao funcional que pode ser utilizado imediatamente para comear a desenvolver.

44

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Comece acessando a pgina de download do Symfony2 em http://symfony.com/download. Nessa pgina, voc ver
Symfony Standard Edition, que a principal distribuio do Symfony2. Existem duas formas de iniciar o seu projeto:
Opo 1) Composer

Composer uma biblioteca de gerenciamento de dependncias para PHP, que voc pode usar para baixar a Edio
Standard do Symfony2.
Comece fazendo o download do Composer em qualquer lugar em seu computador local. Se voc tem o curl instalado,
to fcil como:
curl -s https://getcomposer.org/installer | php

Nota: Se o seu computador no est pronto para usar o Composer, voc ver algumas recomendaes ao executar
este comando. Siga as recomendaes para que o Composer funcione corretamente.
O Composer um arquivo executvel PHAR, que voc pode usar para baixar a Distribuio Standard:

php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony 2.1.x-de

Dica: Para uma verso exata, substitua 2.1.x-dev com a verso mais recente do Symfony (por exemplo, 2.1.1). Para
mais detalhes, consulte a Pgina de Instalao do Symfony_
Este comando pode demorar alguns minutos para ser executado pois o Composer baixa a Distribuio Padro, juntamente com todas as bibliotecas vendor de que ela precisa. Quando terminar, voc deve ter um diretrio parecido com
o seguinte:
path/to/webroot/ <- your web root directory
Symfony/ <- the new directory
app/
cache/
config/
logs/
src/
...
vendor/
...
web/
app.php
...

Opo 2) Fazer download de um arquivo

Voc tambm pode fazer download de um arquivo da Edio Standard. Aqui, voc vai precisar fazer duas escolhas:
Faa o download do arquivo tgz ou zip - ambos so equivalentes, faa o download daquele que voc est mais
confortvel em usar;
Faa o download da distribuio com ou sem vendors. Se voc est pensando em usar mais bibliotecas de
terceiros ou bundles e gerenci-los atravs do Composer, voc provavelmente deve baixar sem vendors.
Baixe um dos arquivos em algum lugar sob o diretrio raiz do seu servidor web local e descompacte-o. A partir de
uma linha de comando UNIX, isto pode ser feito com um dos seguintes comandos (substituindo ### com o seu nome
real do arquivo):

2.1. Livro

45

Symfony Docs pt-BR Documentation, Verso 2.4

# for .tgz file


$ tar zxvf Symfony_Standard_Vendors_2.1.###.tgz
# for a .zip file
$ unzip Symfony_Standard_Vendors_2.1.###.zip

Se voc baixou o arquivo sem vendors, voc definitivamente precisa ler a prxima seo.
Voc pode facilmente substituir a estrutura de diretrios padro. Veja
Padro do Symfony para mais
informaes.

Como Substituir a Estrutura de Diretrio

Atualizando os Vendors

Neste ponto, voc baixou um projeto Symfony totalmente funcional em que voc vai comear a desenvolver a sua
prpria aplicao. Um projeto Symfony depende de um nmero de bibliotecas externas. Estas so baixadas no
diretrio vendor/ do seu projeto atravs de uma biblioteca chamada Composer_.
Dependendo de como voc baixou o Symfony, voc pode ou no precisar fazer a atualizao de seus vendors agora.
Mas, a atualizao de seus vendors sempre segura, e garante que voc tem todas as bibliotecas vendor que voc
precisa.
Passo 1: Obter o Composer _ (O excelente novo sistema de pacotes do PHP)
curl -s http://getcomposer.org/installer | php

Certifique-se de que voc baixou o composer.phar no mesmo diretrio onde o arquivo composer.json
encontra-se (por padro, no raiz de seu projeto Symfony).
Passo 2: Instalar os vendors
$ php composer.phar install

Este comando faz o download de todas as bibliotecas vendor necessrias - incluindo o Symfony em si - dentro do
diretrio vendor/.
Nota: Se voc no tem o curl instalado, voc tambm pode apenas baixar o arquivo installer manualmente em
http://getcomposer.org/installer. Coloque este arquivo em seu projeto e execute:
php installer
php composer.phar install

Dica: Ao executar php composer.phar install ou php composer.phar update, o composer vai
executar comandos de ps instalao/atualizao para limpar o cache e instalar os assets. Por padro, os assets sero
copiados para o seu diretrio web. Para criar links simblicos em vez de copiar os assets, voc pode adicionar
uma entrada no n extra do seu arquivo composer.json com a chave symfony-assets-install e o valor
symlink:
"extra": {
"symfony-app-dir": "app",
"symfony-web-dir": "web",
"symfony-assets-install": "symlink"
}

Ao passar relative ao invs de symlink para o symfony-assets-install, o comando ir gerar links


simblicos relativos.

46

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Configurao e Instalao

Nesse ponto, todas as bibliotecas de terceiros necessrios encontram-se no diretrio vendor/. Voc tambm tem um
instalao padro da aplicao em app/ e alguns cdigos de exemplo no diretrio src/.
O Symfony2 tem um script para testar as configurao do servidor de forma visual, que ajuda garantir que o servidor
web e o PHP esto configurados para o framework. Utilize a seguinte URL para verificar a sua configurao:
http://localhost/config.php

Se algum problema foi encontrado, ele deve ser corrigido agora, antes de prosseguir.
Configurando as Permisses
Um problema comum que os diretrios app/cache e app/logs devem ter permisso de escrita para o
servidor web e para o usurio da linha de comando. Em um sistema UNIX, se o usurio do seu servidor web
for diferente do seu usurio da linha de comando, voc pode executar os seguintes comandos para garantir que
as permisses estejam configuradas corretamente. Mude o www-data para o usurio do servidor web e o
yourname para o usurio da linha de comando:
1. Utilizando ACL em um sistema que suporta chmod +a
Muitos sistemas permitem que voc utilize o comando chmod +a. Tente ele primeiro e se der erro tente o
prximo mtodo:
rm -rf app/cache/*
rm -rf app/logs/*

sudo chmod +a "www-data allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs


sudo chmod +a "yourname allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs

2. Utilizando ACL em um sistema que no suporta chmod +a


Alguns sistemas no suportam o comando chmod +a, mas suportam um outro chamado setfacl. Pode ser
necessrio que voc habilite o suporte a ACL na sua partio e instale o setfacl antes de utiliza-lo (esse o caso
no Ubuntu, por exemplo) da seguinte maneira:
sudo setfacl -R -m u:www-data:rwx -m u:yourname:rwx app/cache app/logs
sudo setfacl -dR -m u:www-data:rwx -m u:yourname:rwx app/cache app/logs

3. Sem utilizar ACL


Se voc no tem acesso para alterar a ACL de diretrios, ser necessrio alterar a umask para que os diretrios
de cache e log tenham permisso de escrita para o grupo ou para todos (vai depender se o usurio do servidor
web e o usurio da linha de comando esto no mesmo grupo). Para isso, coloque a seguinte linha no comeo dos
arquivos app/console, web/app.php e web/app_dev.php:
umask(0002); // This will let the permissions be 0775
// or
umask(0000); // This will let the permissions be 0777

Note que se voc tem acesso a ACL no seu servidor, esse ser o mtodo recomendado, uma vez que alterar a
umask no uma operao thread-safe.
Quando tudo estiver feito, clique em Go to the Welcome page para acessar a sua primeira webpage Symfony2 real:
http://localhost/app_dev.php/

O Symfony2 dever lhe dar as boas vindas e parabeniza-lo pelo trabalho duro at agora!

2.1. Livro

47

Symfony Docs pt-BR Documentation, Verso 2.4

Iniciando o Desenvolvimento
Agora que voc tem uma aplicao Symfony2 totalmente funcional, voc pode comear o desenvolvimento! A sua
distribuio deve conter alguns cdigos de exemplo - verifique o arquivo README.rst includo na distribuio (voc
pode abri-lo como um arquivo de texto) para aprender sobre os exemplos includos e como voc pode remov-los mais
tarde.
Se voc novo no Symfony, junte-se a ns em page_creation, onde voc aprender como criar pginas, mudar
configuraes e tudo mais que precisar para a sua nova aplicao.
Certifique-se tambm verificar o Cookbook, que contm uma grande variedade de artigos sobre a resoluo de problemas especficos com Symfony.
Utilizando Controle de Verso
Se voc est utilizando um sistema de controle de verso como Git ou Subversion, voc pode instala-lo e comear
a realizar os commits do seu projeto normalmente. A edio padro do Symfony o ponto inicial para o seu novo
projeto.
Para instrues especficas sobre a melhor maneira de configurar o seu projeto para ser armazenado no git, veja Como
Criar e Armazenar um Projeto Symfony2 no git.
Ignorando o diretrio vendor/

Se voc baixou o arquivo sem itens de terceiros (without vendors), voc pode ignorar todo o diretrio vendor/
com segurana e no envi-lo para o controle de verso. No Git, isso feito criando e o arquivo .gitignore e
adicionando a seguinte linha:

48

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

/vendor/

Agora, o diretrio vendor no ser enviado para o controle de verso. Isso bom (na verdade, timo!) porque quando
algum clonar ou fizer check out do projeto, ele/ela poder simplesmente executar o script php composer.phar
install para instalar todas as bibliotecas vendor necessrias.

2.1.4 Controlador
Um controlador uma funo PHP que voc cria e que pega informaes da requisio HTTP para criar e retornar uma
resposta HTTP (como um objeto Response do Symfony2). A resposta pode ser uma pgina HTML, um documento
XML, um array JSON serializado, uma imagem, um redirecionamento, um erro 404 ou qualquer coisa que voc
imaginar. O controlador contm toda e qualquer lgica arbritria que sua aplicao precisa para renderizar o contedo
de uma pgina.
Para ver quo simples isso, vamos ver um controlador do Symfony2 em ao. O seguinte controlador deve renderizar
uma pgina que mostra apenas Hello world!:
use SymfonyComponentHttpFoundationResponse;
public function helloAction() {
return new Response(Hello world!);
}
O objetivo de um controlador sempre o mesmo: criar e retornar um objeto Response. Ao longo do caminho, ele
pode ler informaes da requisio, carregar um recurso do banco de dados, mandar um e-mail ou gravar informaes
na sesso do usurio. Mas em todos os casos, o controlador acabar retornando o objeto Response que ser mandado
de volta para o cliente.
No h nenhuma mgica e nenhum outro requisito para se preocupar! Aqui temos alguns exemplos comuns:
O Controlador A prepara um objeto Response representando o contedo da pgina inicial do site.
O Controlador B l o parmetro slug da requisio para carregar uma entrada do blog no banco de dados e
cria um um objeto Response mostrando o blog. Se o slug no for encontrado no banco de dados, ele cria e
retorna um objeto Response com um cdigo de status 404.
O Controlador C trata a o envio de um formulrio de contato. Ele l a informao do formulrio a partir da
requisio, salva a informao de contato no banco de dados e envia por e-mail a informao de contato para
o webmaster. Finalmente, ele cria um objeto Response que redireciona o navegador do cliente para a pgina
thank you do formulrio de contato.
O Ciclo de Vida da Requisio, Controlador e Resposta
Toda requisio tratada por um projeto com Symfony 2 passa pelo mesmo ciclo de vida simples. O framework cuida
das tarefas repetitivas e por fim executa um controlador onde reside o cdigo personalizado da sua aplicao:
1. Toda requisio tratada por um nico arquivo front controlador (por exemplo, app.php ou app_dev.php)
que inicializa a aplicao;
2. O Router l a informao da requisio (por exemplo, a URI), encontra uma rota que casa com aquela informao e l o parmetro _controller da rota;
3. O controlador que casou com a rota executado e o cdigo dentro do controlador cria e retorna um objeto
Response;
4. Os cabealhos HTTP e o contedo do objeto Response so enviados de volta para o cliente.

2.1. Livro

49

Symfony Docs pt-BR Documentation, Verso 2.4

Criar uma pgina to fcil quanto criar um controlador (#3) e fazer uma rota que mapeie uma URL para aquele
controlador (#2).
Nota: Embora tenha um nome similar, um front controller diferente dos controladores dos quais vamos falar
nesse captulo. Um front controller um pequeno arquivo PHP que fica no seu diretrio web e atravs do qual todas as
requisies so direcionadas. Uma aplicao tpica ter um front controller de produo (por exemplo, app.php) e
um front controller de desenvolvimento (por exemplo, app_dev.php). Provavelmente voc nunca precisar editar,
visualizar ou se preocupar com os front controllers da sua aplicao.

Um Controlador Simples
Embora um controlador possa ser qualquer cdigo PHP que possa ser chamado (uma funo, um mtodo em um objeto
ou uma Closure), no Symfony2 um controlador geralmente um nico mtodo dentro de um objeto controlador.
Os controladores tambm so chamados de aes:
1

// src/Acme/HelloBundle/Controller/HelloController.php

2
3
4

namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;

5
6
7
8
9
10
11
12

class HelloController
{
public function indexAction($name)
{
return new Response(<html><body>Hello .$name.!</body></html>);
}
}

Dica:
Note que o controlador o mtodo indexAction, que fica dentro de uma classe controladora
(HelloController). No se confunda com a nomenclatura: uma classe controladora apenas um forma conveniente de agrupar vrios controladores/aes juntos. Geralmente a classe controladora ir agrupar vrios controladores/aes (por exemplo, updateAction, deleteAction etc).
Esse controlador bem simples, mas vamos explic-lo:
linha 3: O Symfony2 se beneficia da funcionalidade de namespace do PHP 5.3 colocando a classe controladora
inteira dentro de um namespace. A palavra chave use importa a classe Response que nosso controlador tem
que retornar.
linha 6: O nome da classe a concatenao de um nome para a classe controlador (ou seja, Hello) com a
palavra Controller. Essa uma conveno que fornece consistncia aos controladores e permite que eles
sejam referenciados usando apenas a primeira parte do nome (ou seja, Hello) na configurao de roteamento.
linha 8: Toda ao em uma classe controladora sufixada com Action e referenciada na configurao de
roteamento pelo nome da ao (index). Na prxima seo, voc criar uma rota que mapeia uma URI para
essa action. Voc aprender como os marcadores de posio das rotas ({name}) tornam-se argumentos no
mtodo da action ($name).
linha 10: O controlador cria e retorna um objeto Response.
Mapeando uma URL para um Controlador
O novo controlador retorna uma pgina HTML simples. Para ver realmente essa pgina no seu navegador voc precisa
criar uma rota que mapeia um padro especfico de URL para o controlador:

50

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

YAML
# app/config/routing.yml
hello:
pattern:
/hello/{name}
defaults:
{ _controller: AcmeHelloBundle:Hello:index }

XML
<!-- app/config/routing.xml -->
<route id="hello" pattern="/hello/{name}">
<default key="_controller">AcmeHelloBundle:Hello:index</default>
</route>

PHP
// app/config/routing.php
$collection->add(hello, new Route(/hello/{name}, array(
_controller => AcmeHelloBundle:Hello:index,
)));

Agora, acessar /hello/ryan executa o controlador HelloController::indexAction() e passa ryan


para a varivel $name. A criao de uma pgina significa simplesmente criar um mtodo controlador e associar
uma rota.
Note a sintaxe usada para referenciar o controlador: AcmeHelloBundle:Hello:index. O Symfony2 usa
uma notao flexvel de string para referenciar diferentes controladores. Essa a sintaxe mais comum e diz ao
Symfony2 para buscar por uma classe controladora chamada helloController dentro de um bundle chamado
AcmeHelloBundle. Ento o mtodo indexAction() executado.
Para mais detalhes sobre o formato de string usado para referenciar diferentes controladores, veja Padro de nomeao
do Controlador.
Nota: Esse exemplo coloca a configurao de roteamento diretamente no diretrio app/config/. Uma forma
melhor de organizar suas rotas colocar cada uma das rotas no bundle a qual elas pertencem. Para mais informaes,
veja Incluindo Recursos Externos de Roteamento.
Dica: Voc pode aprender muito mais sobre o sistema de roteamento no captulo Roteamento.

Parmetros de Rota como Argumentos do Controlador

Voc j sabe que o parmetro _controller em AcmeHelloBundle:Hello:index se refere ao mtodo


HelloController::indexAction() que est dentro do bundle AcmeHelloBundle. O que mais interessante so os argumentos que so passados para o mtodo:
<?php
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class HelloController extends Controller
{
public function indexAction($name)
{
// ...

2.1. Livro

51

Symfony Docs pt-BR Documentation, Verso 2.4

}
}

O controlador tem um nico argumento, $name, que corresponde ao parmetro {name} da rota casada (ryan
no nosso exemplo). Na verdade quando executa seu controlador, o Symfony2 casa cada um dos argumentos do
controlador com um parmetro da rota casada. Veja o seguinte exemplo:
YAML
# app/config/routing.yml
hello:
pattern:
/hello/{first_name}/{last_name}
defaults:
{ _controller: AcmeHelloBundle:Hello:index, color: green }

XML
<!-- app/config/routing.xml -->
<route id="hello" pattern="/hello/{first_name}/{last_name}">
<default key="_controller">AcmeHelloBundle:Hello:index</default>
<default key="color">green</default>
</route>

PHP
// app/config/routing.php
$collection->add(hello, new Route(/hello/{first_name}/{last_name}, array(
_controller => AcmeHelloBundle:Hello:index,
color
=> green,
)));

O controlador dessa rota pode receber vrios argumentos:


public function indexAction($first_name, $last_name, $color) {
// ...
}
Observe que tanto as variveis de marcadores de posio ({first_name}, {last_name}) quanto a vriavel
padro color esto disponveis como argumentos no controlador. Quando uma rota casada, as variveis marcadoras
de posio so mescladas com as variveis default criando um array que fica disponvel para o seu controlador.
O mapeamento de parmetros de rota com argumentos do controlador fcil e flexvel. Tenha em mente as seguintes
orientaes enquanto estiver desenvolvendo.
A ordem dos argumentos do controlador no importa
O Symfony capaz de casar os nomes dos parmetros da rota com os nomes das variveis na assinatura do mtodo do controlador. Em outras palavras, ele sabe que o parmetro {last_name} casa
com o argumento $last_name. Os argumentos do controlador podem ser totalmente reordenados
e continuam funcionando perfeitamente:
public function indexAction($last_name, $color, $first_name) {
// ..
}
Todo argumento obrigatrio do controlador tem que corresponder a um parmetro de roteamento
O seguinte deveria lanar uma RuntimeException porque no existe nenhum parmetro foo
definido na rota:
public function indexAction($first_name, $last_name, $color, $foo) {
52

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

// ..
}
Deixando o argumento opcional, no entanto, tudo corre bem. O seguinte exemplo no lana
uma exceo:
public function indexAction($first_name, $last_name, $color, $foo = bar) {
// ..
}
Nem todos os parmetros de roteamento precisam ser argumentos no seu controlador
Se, por exemplo, last_name no for importante para o seu controlador, voc pode omitir inteiramente ele:
public function indexAction($first_name, $color) {
// ..
}
Dica: Cada uma das rotas tem um parmetro _route especial, que igual ao nome da rota que foi casada (por
exemplo, hello). Embora no seja til geralmente, ele tambm fica disponvel como um argumento do controlador.

O Request como um Argumento do Controlador

Por convenincia, voc tambm pode fazer com que o Symfony passe o objeto Request como um argumento para
seu controlador. Isso conveniente especialmente quando voc estiver trabalhando com formulrios, por exemplo:
use SymfonyComponentHttpFoundationRequest;
public function updateAction(Request $request) {
$form = $this->createForm(...);
$form->bind($request); // ...
}
A Classe Base do Controlador
Por convenincia, o Symfony2 vem com uma classe Controller base que ajuda com algumas das tarefas mais
comuns dos controladores e fornece s suas classes controladoras acesso qualquer recurso que elas possam precisar.
Estendendo essa classe Controller, voc se beneficia com vrios mtodos helper.
Adicione a instruo use no topo da sua classe Controller e ento modifique o HelloController para
estend-lo:
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController extends Controller
{
public function indexAction($name)
{

2.1. Livro

53

Symfony Docs pt-BR Documentation, Verso 2.4

return new Response(<html><body>Hello .$name.!</body></html>);


}
}

Isso no muda realmente nada o jeito que seu controlador trabalha. Na prxima seo voc aprender sobre os mtodos
helper que a classe controladora base disponibiliza. Esses mtodos so apenas atalhos para usar funcionalidades do
ncleo do Symfony2 que esto disponveis para voc usando ou no a classe base Controller. Uma boa maneira
de ver a funcionalidade do ncleo em ao olhar a prpria classe Controller.
Dica: Estender a classe base opcional no Symfony; ela contm atalhos teis mas nada que seja mandatrio.
Voc tambm pode estender Symfony\Component\DependencyInjection\ContainerAware. O objeto
continer de servios ento ser acessvel por meio da propriedade container.
Nota: Voc tambm pode definir seus Controllers como Servios.

Tarefas Comuns dos Controladores


Embora virtualmente um controlador possa fazer qualquer coisa, a maioria dos controladores iro realizar as mesmas
tarefas bsicas repetidas vezes. Essas tarefas, como redirecionamentos, direcionamentos, renderizao de templates e
acesso a servios nucleares so muitos fceis de gerenciar no Symfony2.
Redirecionando

Se voc quiser redirecionar o usurio para outra pgina, use o mtodo redirect():
public function indexAction()
{
return $this->redirect($this->generateUrl(homepage));
}

O mtodo generateUrl() apenas uma funo helper que gera a URL de uma determinada rota. Para mais
informaes, veja o captulo Roteamento.
Por padro, o mtodo redirect() efetua um redirecionamento 302 (temporrio). Para realizar um redirecionamento 301 (permanente), modifique o segundo argumento:
public function indexAction()
{
return $this->redirect($this->generateUrl(homepage), 301);
}

Dica: O mtodo redirect() simplesmente um atalho que cria um objeto Response especializado em redirecionar o usurio. Ele equivalente a:
use Symfony\Component\HttpFoundation\RedirectResponse;
return new RedirectResponse($this->generateUrl(homepage));

Direcionando

Voc tambm pode facilmente direcionar internamente para outro controlador com o mtodo forward(). Em vez
de redirecionar o navegador do usurio, ele faz uma sub-requisio interna e chama o controlador especificado. O
54

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

mtodo forward() retorna o objeto Response que retornado pelo controlador:


public function
{
$response =
name
color
));

indexAction($name)
$this->forward(AcmeHelloBundle:Hello:fancy, array(
=> $name,
=> green

// pode modificar a resposta ou retorn-la diretamente


return $response;
}

Note que o mtodo forward() usa a mesma representao em string do controlador que foi usada na configurao
de roteamento. Nesse caso, a classe controlador alvo ser HelloController dentro de AcmeHelloBundle. O
array passado para o mtodo se torna os argumentos no controlador resultante. Essa mesma interface usada quando se
embutem controladores em templates (veja Incorporao de Controllers). O mtodo controlador alvo deve se parecer
com o seguinte:
public function fancyAction($name, $color)
{
// ... cria e retorna um objeto Response
}

E da mesma forma, quando criamos um controlador para uma rota, a ordem dos argumentos para fancyAction no
importa. O Symfony2 combina os nomes das chaves dos ndices (por exemplo, name) com os nomes dos argumentos
do mtodo (por exemplo, $name). Se voc mudar a ordem dos argumentos, o Symfony2 continuar passando os
valores corretos para cada varivel.
Dica: Assim como em outros mtodos do Controller base, o mtodo forward apenas um atalho para
uma funcionalidade nuclear do Symfony2. Um direcionamento pode ser realizado diretamente por meio do servio
http_kernel. Um direcionamento retorna um objeto Response:
$httpKernel
$response =
name
color
));

= $this->container->get(http_kernel);
$httpKernel->forward(AcmeHelloBundle:Hello:fancy, array(
=> $name,
=> green,

Renderizando Templates

Apesar de no ser um requisito, a maioria dos controladores ir, no fim das contas, renderizar um template que
responsvel por gerar o HTML (ou outro formato) para o controlador. O mtodo renderView() renderiza um
template e retorna seu contedo. O contedo do template pode ser usado para criar um objeto Response:
$content = $this->renderView(AcmeHelloBundle:Hello:index.html.twig, array(name => $name));
return new Response($content);

Isso pode ser feito at em um nico passo usando o mtodo render(), que retorna um objeto Response com o
contedo do template:
return $this->render(AcmeHelloBundle:Hello:index.html.twig, array(name => $name));

Em ambos os casos, o template


AcmeHelloBundle ser renderizado.
2.1. Livro

Resources/views/Hello/index.html.twig

dentro

do

55

Symfony Docs pt-BR Documentation, Verso 2.4

O sistema de template do Symfony explicado com mais detalhes no captulo Templating.


Dica: O mtodo renderView um atalho para usar diretamente o servio templating. O servio templating
tambm pode ser usado diretamente:
$templating = $this->get(templating);
$content = $templating->render(AcmeHelloBundle:Hello:index.html.twig, array(name => $name));

Acessando outros Servios

Quando se estende a classe controladora base, voc pode acessar qualquer um dos servios Symfony2 atravs do
mtodo get(). Aqui esto alguns dos servios mais comuns que voc pode precisar:
$request = $this->getRequest();
$templating = $this->get(templating);
$router = $this->get(router);
$mailer = $this->get(mailer);

Existem outros inmeros servios disponveis e voc encorajado a definir os seus prprios. Para listar todos os
servios disponveis, use o comando do console container:debug:
php app/console container:debug

Para mais informaes, veja o captulo Container de Servio.


Gerenciando Erros e Pginas 404
Quando algo no for encontrado, voc deve usar de forma correta o protocolo HTTP e retornar uma resposta 404. Para
isso, voc lanar um tipo especial de exceo. Se estiver estendendo a classe controlador base, faa o seguinte:
public function indexAction()
{
$product = // retrieve the object from database
if (!$product) {
throw $this->createNotFoundException(The product does not exist);
}
return $this->render(...);
}

O mtodo createNotFoundException() cria um objeto especial NotFoundHttpException, que no fim


dispara uma resposta HTTP 404 de dentro do Symfony.
lgico que voc livre para lanar qualquer classe Exception no seu controlador - o Symfony ir retornar
automaticamente uma resposta HTTP cdigo 500.
throw new \Exception(Something went wrong!);

Em todo caso, uma pgina de erro estilizada mostrada para o usurio final e uma pgina de erro com informaes de
debug completa mostrada para o desenvolvedor (no caso de visualizar a pgina no modo debug). Ambas as pginas
podem ser personalizadas. Para detalhes, leia a receita Como personalizar as pginas de erro no cookbook.

56

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Gerenciando a Sesso
O Symfony2 fornece um objeto de sesso muito bom que voc pode usar para guardar informaes sobre o usurio (seja ele uma pessoa real usando um navegador, um rob ou um web service) entre requisies. Por padro, o
Symfony2 guarda os atributos em um cookie usando as sesses nativas do PHP.
O armazenamento e a recuperao de informaes da sesso so feitos facilmente de qualquer controlador:
$session = $this->getRequest()->getSession();
// store an attribute for reuse during a later user request
$session->set(foo, bar);
// in another controller for another request
$foo = $session->get(foo);
// use a default value if the key doesnt exist
$filters = $session->get(filters, array());

Esses atributos permanecero no usurio at o fim da sesso.


Mensagens Flash

Voc tambm guardar pequenas mensagens que sero armazenadas na sesso do usurio apenas por uma requisio.
Isso til no processamento de formulrios: voc pode redirecionar o usurio e mostrar uma mensagem especial na
requisio seguinte. Esses tipos de mensagens so chamadas de mensagens flash.
Por exemplo, imagine que voc esteja processando a submisso de um formulrio:
public function updateAction()
{
$form = $this->createForm(...);
$form->bind($this->getRequest());
if ($form->isValid()) {
// do some sort of processing
$this->get(session)->getFlashBag()->add(notice, Your changes were saved!);
return $this->redirect($this->generateUrl(...));
}
return $this->render(...);
}

Depois do processamento da requisio, o controlador define uma mensagem flash notice e ento faz o redirecionamento. O nome (notice) no importante - apenas o que voc usa para identificar o tipo da mensagem.
No template da prxima action, o cdigo a seguir poderia ser usado para renderizar a mensagem notice:
Twig
{% for flashMessage in app.session.flashbag.get(notice) %}
<div class="flash-notice">
{{ flashMessage }}
</div>
{% endfor %}

PHP

2.1. Livro

57

Symfony Docs pt-BR Documentation, Verso 2.4

<?php foreach ($view[session]->getFlashBag()->get(notice) as $message): ?>


<div class="flash-notice">
<?php echo "<div class=flash-error>$message</div>" ?>
</div>
<?php endforeach; ?>

Por definio, as mensagens flash so feitas para existirem por exatamente uma requisio (elas se vo num instante
- gone in a flash). Elas foram projetadas para serem usadas entre redirecionamentos exatamente como voc fez nesse
exemplo.
O Objeto Response
O nico requisito de um controlador retornar um objeto Response. A classe Response uma abstrao PHP em
volta da resposta HTTP - a mensagem em texto cheia de cabealhos HTTP e contedo que mandado de volta para o
cliente:
// create a simple Response with a 200 status code (the default)
$response = new Response(Hello .$name, 200);
// create a JSON-response with a 200 status code
$response = new Response(json_encode(array(name => $name)));
$response->headers->set(Content-Type, application/json);

Dica: A propriedade headers a classe HeaderBag com vrios mtodos teis para ler e modificar os cabealhos
do Response. Os nomes dos cabealhos so normalizados de forma que usar Content-Type seja equivalente a
content-type ou mesmo content_type.

O Objeto Request
Alm dos valores nos marcadores de roteamento, o controlador tambm tem acesso ao objeto Request quando est
estendendo a classe Controller base:
$request = $this->getRequest();
$request->isXmlHttpRequest(); // is it an Ajax request?
$request->getPreferredLanguage(array(en, fr));
$request->query->get(page); // get a $_GET parameter
$request->request->get(page); // get a $_POST parameter

Assim como com o objeto Response, os cabealhos da requisio so guardados em um objeto HeaderBag e so
facilmente acessados.
Consideraes Finais
Sempre que criar uma pgina, no final voc precisar escrever algum cdigo que contenha a lgica dessa pgina. No
Symfony, isso chamado de controlador, e ele uma funo PHP que faz tudo que for necessrio para no fim retornar
o objeto Response final que ser retornado ao usurio.
Para facilitar a vida, voc pode escolher estender uma classe Controller base, que contm mtodos que so atalhos
para muitas tarefas comuns dos controladores. Por exemplo, uma vez que voc no queira colocar cdigo HTML no
seu controlador, voc pode usar o mtodo render() para renderizar e retornar o contedo de um template.
58

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Em outros captulos, voc ver como o controlador pode ser usado para persistir e buscar objetos em um banco de
dados, processar submisses de formulrios, gerenciar cache e muito mais.
Saiba mais no Cookbook
Como personalizar as pginas de erro
Como definir Controladores como Servios

2.1.5 Roteamento
URLs bonitas so uma obrigao absoluta para qualquer aplicao web sria. Isto significa deixar para trs 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 pgina de /blog para /news?
Quantos links voc precisaria para investig-los e atualizar para fazer a mudana ? Se voc est usando o roteador do
Symfony, a mudana simples.
O roteador do Symfony2 deixa voc definir URLs criativas que voc mapeia para diferentes reas de sua aplicao. No
final deste captulo, 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
Roteamento em Ao
Um rota um mapa de um padro 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:
YAML
# app/config/routing.yml
blog_show:
pattern:
/blog/{slug}
defaults: { _controller: AcmeBlogBundle:Blog:show }

XML

<!-- 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/rout
<route id="blog_show" pattern="/blog/{slug}">
<default key="_controller">AcmeBlogBundle:Blog:show</default>
</route>
</routes>

PHP
// 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(

2.1. Livro

59

Symfony Docs pt-BR Documentation, Verso 2.4

_controller => AcmeBlogBundle:Blog:show,


)));
return $collection;

O padro definido pela rota blog_show age como /blog/* onde o coringa dado pelo nome slug. Para a URL
/blog/my-blog-post, a varivel slug obtm um valor de my-blog-post, que est disponvel para voc usar
em seu controlador (continue lendo).
O parmetro _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 padro
que aponta para uma classe e mtodo PHP especfico:
// 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,
));
}
}

Parabns ! 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 varivel $slug ser igual a my-post.
Esse o objetivo do roteador do Symfony2: mapear a URL de uma requisio para um controlador. Ao longo do
caminho, voc aprender todos os tipos de truques que tornam o mapeamento fcil, mesmo das URLS mais complexas.
Roteamento: Por debaixo do capuz
Quando uma requisio feita para sua aplicao, ela contm um endereo para o recurso exato que o cliente est
requisitando.Esse endereo chamado de URL, (ou URI), e poderia ser /contact, /blog/read-me, ou qualquer
coisa a mais. Considere a seguinte requisio de exemplo :
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:
1. A requisao controlada pelo front controller do Symfony2 front controller (ex: app.php);
2. O ncleo do Symfony2 (ex: Kernel) pergunta ao roteador para inspecionar a requisio;
3. O roteador ajusta a URL recebida para uma rota especfica e retorna informao sobre a rota, incluindo o
controlador que deveria ser executado;
4. O kernel do Symfony2 executa o controlador, que retorna por ltimo um objeto Response.

60

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Figura 2.2: A camada de roteamento uma ferramenta que traduza a URL recebida em um controlador especfico para
executar.
Criando rotas
Symfony carrega todas as rotas para sua aplicao de um arquivo de configurao 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 configurao de aplicao:
YAML
# app/config/config.yml
framework:
# ...
router:
{ resource: "%kernel.root_dir%/config/routing.yml" }

XML
<!-- app/config/config.xml -->
<framework:config ...>
<!-- ... -->
<framework:router resource="%kernel.root_dir%/config/routing.xml" />
</framework:config>

PHP
// app/config/config.php
$container->loadFromExtension(framework, array(
// ...
router
=> array(resource => %kernel.root_dir%/config/routing.php),
));

Dica: Mesmo que toda as rotas sejam carregadas de um arquivo s, uma prtica comum incluir recursos de
roteamento adicionais de dentro do arquivo. Veja a seo:ref:routing-include-external-resources para mais informao.

2.1. Livro

61

Symfony Docs pt-BR Documentation, Verso 2.4

Configurao de rota bsica

Definir uma rota fcil, e uma aplicao tpica ter um monte de rotas. A basic route consists of just two parts: the
pattern to match and a defaults array:
YAML
_welcome:
pattern:
defaults:

/
{ _controller: AcmeDemoBundle:Main:homepage }

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/rout
<route id="_welcome" pattern="/">
<default key="_controller">AcmeDemoBundle:Main:homepage</default>
</route>
</routes>

PHP
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 funo verdadeira do PHP e exectudada. Aquele processo ir ser
explicado brevemente na seo Padro de nomeao do Controlador.
Roteando com Espaos reservados

Claro que o sistema de roteamento suporta rotas muito mais interessantes. Muitas rotas iro conter uma ou mais
chamadas de espaos reservados coringa:
YAML
blog_show:
pattern:
defaults:

/blog/{slug}
{ _controller: AcmeBlogBundle:Blog:show }

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/rout

62

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

<route id="blog_show" pattern="/blog/{slug}">


<default key="_controller">AcmeBlogBundle:Blog:show</default>
</route>
</routes>

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 padro ir corresponder a qualquer coisa que parea /blog/*. Melhor ainda, o valor correspondendo ao espao
reservado {slug} estar disponvel no seu controlador. Em outras palavras, se a URL /blog/hello-world,
uma varivel $slug, com o valor de hello-world, estar disponvel no controlador. Isto pode ser usado, por
exemplo, para carregar o post do blog correspondendo quela string.
Este padro no ir, entretanto, simplesmente ajustar /blog. Isso porque, por padro, todos os espaos reservados
so requeridos. Isto pode ser mudado ao adicionar um valor de espao reservado ao array defaults.
Espaos reservados Requeridos e Opcionais

Para tornar as coisas mais excitantes, adicione uma nova rota que mostre uma lista de todos os posts do blog para essa
aplicao de blog imaginria:
YAML
blog:
pattern:
defaults:

/blog
{ _controller: AcmeBlogBundle:Blog:index }

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/rout
<route id="blog" pattern="/blog">
<default key="_controller">AcmeBlogBundle:Blog:index</default>
</route>
</routes>

PHP
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(blog, new Route(/blog, array(
_controller => AcmeBlogBundle:Blog:index,
)));

2.1. Livro

63

Symfony Docs pt-BR Documentation, Verso 2.4

return $collection;

At agora, essa rota to simples quanto possvel - contm nenhum espao reservado e s ir corresponder URL
exata /blog. Mas e se voc precisar dessa rota para suportar paginao, onde /blog/2 mostre a segunda pgina do
entradas do blog ? Atualize a rota para ter uma nova {page} de espao reservado:
YAML
blog:
pattern:
defaults:

/blog/{page}
{ _controller: AcmeBlogBundle:Blog:index }

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/rout
<route id="blog" pattern="/blog/{page}">
<default key="_controller">AcmeBlogBundle:Blog:index</default>
</route>
</routes>

PHP
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 espao reservado {slug} anterior, o valor correspondendo a {page} estar disponvel dentro do seu controlador. Este valor pode ser usado para determinar qual conjunto de posts do blog mostrar para determinada pgina.
Mas espere ! Como espaos reservados so requeridos por padro, essa rota no ir mais corresponder simplesmente
a /blog. Ao invs disso, para ver a pgina 1 do blog, voc precisaria usar a URL /blog/1! Como no h meios
para uma aplicao web ricase comportar, modifique a rota para fazer o parmetro {page} opcional. Isto feito ao
incluir na coleo defaults:
YAML
blog:
pattern:
defaults:

/blog/{page}
{ _controller: AcmeBlogBundle:Blog:index, page: 1 }

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/rout
<route id="blog" pattern="/blog/{page}">

64

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

<default key="_controller">AcmeBlogBundle:Blog:index</default>
<default key="page">1</default>
</route>
</routes>

PHP
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 espao reservado {page} no mais requerido. A URL /blog ir
corresponder a essa rota e o valor do parmetro page ser fixado para 1. A URL /blog/2 ir tambm corresponder,
atribuindo ao parmetro page o valor 2. Perfeito.
/blog
/blog/1
/blog/2

{page} = 1
{page} = 1
{page} = 2

Adicionando Requisitos

D uma rpida olhada nos roteamentos que foram criados at agora:


YAML
blog:
pattern:
defaults:

/blog/{page}
{ _controller: AcmeBlogBundle:Blog:index, page: 1 }

blog_show:
pattern:
defaults:

/blog/{slug}
{ _controller: AcmeBlogBundle:Blog:show }

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/rout
<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>

2.1. Livro

65

Symfony Docs pt-BR Documentation, Verso 2.4

PHP
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 padro que combinam URLs que paream /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 invs disso, uma URL como /blog/my-blog-post ir corresponder primeira rota (blog) e retorna um valor sem sentido de my-blog-post ao parmetro {page}.
URL
/blog/2
/blog/my-blog-post

route
blog
blog

parameters
{page} = 2
{page} = my-blog-post

A resposta para o problema adicionar mais requisitos de rota. As rotas neste exemplo funcionariam perfeitamente
se o padro /blog/{page} somente correspondesse a URLs onde a poro {page} fosse um integer. Felizmente,
requisitos de expresses regulares podem facilmente ser adicionados para cada parmetro. Por exemplo:
YAML
blog:
pattern:
/blog/{page}
defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
requirements:
page: \d+

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/rout
<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>

PHP
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(blog, new Route(/blog/{page}, array(

66

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

_controller => AcmeBlogBundle:Blog:index,


page => 1,
), array(
page => \d+,
)));
return $collection;

O requisito \d+ uma expresso regular que diz o valor do parmetro {page} deve ser um dgito (em outras palavras,
um nmero). A rotablog ainda ir correponder a uma URL como /blog/2 (porque 2 um nmero), mas no ir
mair corresponder a URL como /blog/my-blog-post (porque my-blog-post no um nmero).
Como resultado, uma URL como /blog/my-blog-post no ir corresponder apropriadamente rota
blog_show.
URL
/blog/2
/blog/my-blog-post

rota
blog
blog_show

parmetros
{page} = 2
{slug} = my-blog-post

Rotas prematuras sempre Vencem


Isso tudo significa que a ordem das rotas muito importante. Se a rota blog_show foi colocada acima da
rota blog, a URL /blog/2corresponderia a blog_show ao invs de blog j que o parmetro{slug} de blog_show no tem requisitos. By using proper ordering and clever requirements, you can
accomplish just about anything.
Como os requisitos de parmetros so expresses regulares, a complexidade e flexibilidade de cada requisito inteiramente de sua responsabilidade. Suponha que a pgina inicial de sua aplicao est disponvel em dois idiomas
diferentes, baseada na URL:
YAML
homepage:
pattern:
/{culture}
defaults: { _controller: AcmeDemoBundle:Main:homepage, culture: en }
requirements:
culture: en|fr

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/rout
<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>

PHP
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();

2.1. Livro

67

Symfony Docs pt-BR Documentation, Verso 2.4

$collection->add(homepage, new Route(/{culture}, array(


_controller => AcmeDemoBundle:Main:homepage,
culture => en,
), array(
culture => en|fr,
)));
return $collection;

Para requisies recebidas, a parte {culture} da URL comparada com a expresso regular (en|fr).
/
/en
/fr
/es

{culture} = en
{culture} = en
{culture} = fr
wont match this route

Adicionando Requisio de Mtodo HTTP

Em adio URL, voc tambm pode ajustar o mtodo da requisio recebida (em outras palavras, GET, HEAD,
POST, PUT, DELETE).Suponha que voc tenha um formulrio de contato com dois controladores - um para exibir
o formulrio (em uma requisio GET) e uma para processar o formulrio quando ele enviado (em uma requisio
POST). Isto pode ser acompanhando com a seguinte configurao de rota:
YAML
contact:
pattern: /contact
defaults: { _controller: AcmeDemoBundle:Main:contact }
requirements:
_method: GET
contact_process:
pattern: /contact
defaults: { _controller: AcmeDemoBundle:Main:contactProcess }
requirements:
_method: POST

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/rout
<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>

PHP

68

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

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 padres idnticos (/contact), a primeira rota ir aceitar somente requisies
GET e a segunda rota ir somente aceitar requisis POST. Isso significa que voc pode exibir o formulrio e enviar o
formulrio pela mesma URL, enquanto usa controladores distintos para as duas aes.
Nota: 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 expresso regular. Para aceitar requisies
GET ou POST, voc pode usar GET|POST.
Exemplo avanado de roteamento

At esse ponto, voc tem tudo que voc precisa para criar uma poderosa estrutura de roteamento em Symfony. O
exemplo seguinte mostra quo flexvel o sistema de roteamento pode ser:
YAML
article_show:
pattern: /articles/{culture}/{year}/{title}.{_format}
defaults: { _controller: AcmeDemoBundle:Article:show, _format: html }
requirements:
culture: en|fr
_format: html|rss
year:
\d+

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/rout
<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>

2.1. Livro

69

Symfony Docs pt-BR Documentation, Verso 2.4

</route>
</routes>

PHP
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 nmero.
Esta rota tambm mostra como voc pode usar um perodo entre espaos reservados ao invs de uma barra. URLs que
correspondam a esta rota poderia parecer como:
/articles/en/2010/my-post
/articles/fr/2010/my-post.rss
O Parmetro de Roteamento Especial _format
Esse exemplo tambm resslta o parmetro de roteamento especial _format. Quando usa esse parmetro, o
valor correspondido se torna o formato requisitado do ojeto Request. Ultimamente, o formato requisitado
usado para certas coisas como as configurar o Content-Type da resposta (ex: um formato de requisio
json traduz em um Content-Type de application/json). Ele tambm pode ser usado no controlador
para alterar um template diferente para cada valor de _format. O parmetro _format um modo muito
poderoso para alterar o mesmo contedo em formatos diferentes.

Parmetros de Roteamento Especiais

Como voc viu, cada parmetro de roteamento ou valor padro est eventualmente disponvel como um argumento no
mtodo do controlador. Adicionalmente, existem trs parmetros que so especiais: cada um adiciona uma parte nica
de funcionalidade dentro da sua aplicao:
_controller: Como voc viu, este parmetro usado para determinar qual controlador executado quando
a rota correspondida;
_format: Usado para fixar o formato de requisio (read more);
_locale: Usado para fixar a localidade no pedido (read more);
Dica: Se voc usar o parmetro _locale na rota, aquele valor ser tambm armazenado na sesso, ento, os pedidos
posteriores mantm a mesma localidade.

70

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Padro de nomeao do Controlador


Cada rota deve ter um parmetro _controller, que ordena qual controlador deveria ser executado quando uma rota
correspondida. Esse parmetro usa um padro de string simples chamado logical controller name, que o Symfony
mapeia para uma classe e mtodo PHP especfico. O padro tem trs partes, cada uma separada por dois pontos:
bundle:controller:action
Por exemplo, um valor _controller de AcmeBlogBundle:Blog:show significa:
Bundle
AcmeBlogBundle

Classe do Controlador
BlogController

Nome do Mtodo
showAction

O controlador poderia parecer assim:


// 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 mtodo (show => showAction).
Voc tambm poderia referir a esse controler usando os nomes totalmente qualificados de classe e mtodo:Acme\BlogBundle\Controller\BlogController::showAction. Mas se voc seguir alguma convenes simples, o nome lgico mais conciso e permite mais flexibilidade.
Nota: Em complemento ao utilizar o nome lgico ou o nome de classe totalmente qualificado, Symfony suporta um terceiro modo de referir a um controlador. Esse mtodo usa somente um separador de dois pontos (ex:
service_name:indexAction) e referir um controlador como um servio (veja Como definir Controladores
como Servios).

Parmetros de Rota e Argumentos de Controlador


Os parmetros de rota (ex: {slug}) so especialmente importantes porque cada um disponibilizado como um
argumento para o mtodo do controlador:
public function showAction($slug)
{
// ...
}

Na realidade, a coleo inteira defaults mesclada com um valor de parmetro para formar um nico array. Cada
chave daquele array est disponvel como um argumento no controlador.
Em outras palavras, para cada argumento do mtodo do seu controlador, Symfony procura por um parmetro de
rota daquele nome e atribui o valor para aquele argumento. No exemplo avanado acima, qualquer combinao (em
qualquer ordem) das seguintes variveis poderia ser usada como argumentos para o mtodo showAction():
$culture

2.1. Livro

71

Symfony Docs pt-BR Documentation, Verso 2.4

$year
$title
$_format
$_controller
Como os espaos resercados e a coleo defaults so mesclados juntos, mesmo a varivel $_controller est
disponvel. Para uma discusso mais detalhada, veja Parmetros de Rota como Argumentos do Controlador.
Dica: Voc tambm pode usar uma varivel especial $_route, que fixada para o nome da rota que foi correspondida.

Incluindo Recursos Externos de Roteamento


Todas as rotas so carregadas por um arquivo de configurao 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 importao daquele arquivo:
YAML
# app/config/routing.yml
acme_hello:
resource: "@AcmeHelloBundle/Resources/config/routing.yml"

XML
<!-- 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/rout
<import resource="@AcmeHelloBundle/Resources/config/routing.xml" />
</routes>

PHP
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
$collection = new RouteCollection();
$collection->addCollection($loader->import("@AcmeHelloBundle/Resources/config/routing.php"));
return $collection;

Nota: Quando importar recursos do YAML, a chave (ex: acme_hello) insignificante. Somente esteja certo que
nica, ento 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:
YAML

72

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

# src/Acme/HelloBundle/Resources/config/routing.yml
acme_hello:
pattern: /hello/{name}
defaults: { _controller: AcmeHelloBundle:Hello:index }

XML
<!-- 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/rout
<route id="acme_hello" pattern="/hello/{name}">
<default key="_controller">AcmeHelloBundle:Hello:index</default>
</route>
</routes>

PHP
// 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 so analisadas e carregadas da mesma forma que o arquivo principal de roteamento.
Prefixando Rotas Importadas

Voc tambm pode escolher providenciar um prefixo para as rotas importadas. Por exemplo suponha que voc
queira que a rota acme_hello tnha um padro final de /admin/hello/{name} ao invs de simplesmente
/hello/{name}:
YAML
# app/config/routing.yml
acme_hello:
resource: "@AcmeHelloBundle/Resources/config/routing.yml"
prefix:
/admin

XML
<!-- 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/rout
<import resource="@AcmeHelloBundle/Resources/config/routing.xml" prefix="/admin" />
</routes>

2.1. Livro

73

Symfony Docs pt-BR Documentation, Verso 2.4

PHP
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;

$collection = new RouteCollection();


$collection->addCollection($loader->import("@AcmeHelloBundle/Resources/config/routing.php"), /a
return $collection;

A string /admin ir agora ser prefixada ao padro de cada rota carregada do novo recurso de roteamento.
Visualizando e Depurando Rotas
Enquanto adiciona e personalizar rotas, til ser capaz de visualizar e obter informao detalhada sobre suas rotas. Um
grande modo para ver cada rota em sua aplicao pelo comando de console router:debug. Execute o seguinte
comando a partir da raiz de seu projeto.
php app/console router:debug

O comando ir imprimir uma lista til de todas as rotas configuradas em sua aplicao:
homepage
contact
contact_process
article_show
blog
blog_show

ANY
GET
POST
ANY
ANY
ANY

/
/contact
/contact
/articles/{culture}/{year}/{title}.{_format}
/blog/{page}
/blog/{slug}

Voc tambm pode obter informao muito especfica em uma rota individual ao incluir o nome da rota aps o comando:
php app/console router:debug article_show

Novo na verso 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:
$ php app/console router:match /articles/en/2012/article.rss
Route "article_show" matches

Gerando URLs
O sistema de roteamento deveria tambm ser usado para gerar URLs. Na realidade, roteamento um sistema bidirecional: mapeando a URL para um controlador+parmetros e parmetros+rota de voltar para a URL. Os mtodos
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 padro para aquela rota. Com essa informao, qualquer URL pode ser facilmente gerada:

74

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

class MainController extends Controller


{
public function showAction($slug)
{
// ...
$url = $this->get(router)->generate(blog_show, array(slug => my-blog-post));
}
}

Em uma sesso futura, voc ir aprender como gerar URLs a partir de templates.
Dica: Se o frontend de sua aplicao usa requisies AJAX, voc poderia querer ser capaz de ferar URLs em
JavaScript baseados na sua configurao de roteamento. Ao usar FOSJsRoutingBundle, voc poderia fazer exatamente
isso:
var url = Routing.generate(blog_show, { "slug": my-blog-post});

Para mais informaes, veja a documentao para aquele pacote.

Gerando URLs Absolutas

Por padro, o roteador ir gerar URLs relativas (ex: /blog). Para gerar uma URL absoluta, simplesmente passe
true ao terceiro argumento do mtodo generate():
$router->generate(blog_show, array(slug => my-blog-post), true);
// http://www.example.com/blog/my-blog-post

Nota: O host que usado quando gera uma URL absoluta o host do objeto Request atual. Isso detectado
automaticamente baseado na informao 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:
$request->headers->set(HOST, www.example.com);

Gerando URLs com Strings de Consulta

O mtodo generate pega um array de valores coringa para gerar a URI. Mas se voc passar valores extras, eles
sero adicionados ao URI como uma string de consulta:
$router->generate(blog, array(page => 2, category => Symfony));
// /blog/2?category=Symfony

Gerando URLs de um template

O lugar mais comum para gerar uma URL pelo template, ao fazer vinculao entre pginas na sua aplicao.Isso
feito da mesma forma que antes, mas usando uma funo helper de template:
Twig
<a href="{{ path(blog_show, { slug: my-blog-post }) }}">
Read this blog post.
</a>

2.1. Livro

75

Symfony Docs pt-BR Documentation, Verso 2.4

PHP

<a href="<?php echo $view[router]->generate(blog_show, array(slug => my-blog-post)) ?>">


Read this blog post.
</a>

URLs absolutas tambm podem ser geradas.


Twig
<a href="{{ url(blog_show, { slug: my-blog-post }) }}">
Read this blog post.
</a>

PHP

<a href="<?php echo $view[router]->generate(blog_show, array(slug => my-blog-post), true


Read this blog post.
</a>

Sumrio
Roteamento um sistema para mapear a URL de requisis recebidas para a funo do controlador que deveria ser
chamada para processar a requisio. Em ambas permite a voc especificar URLs bonitas e manter a funcionalidade
de sua aplicao desacoplada daquelas URLs. Roteamento um mecanismo de duas vias, significando que tambm
deveria ser usada para gerar URLs.
Aprenda mais do Cookbook
Como forar as rotas a usar sempre HTTPS ou HTTP

2.1.6 Criando e usando Templates


Como voc sabe o controller responsvel por controlar cada requisio que venha de uma aplicao Symfony2. Na
realidade, o controller delega muito do trabalho pesado para outros lugares ento aquele cdigo pode ser testado e
reusado. Quando um controller precisa gerar HTML, CSS e qualquer outro contedo, ele entrega o trabalho para o
engine de template. Nesse captulo, voc ir aprender como escrever templates poderosas que podem ser usada para
retornar contedo para o usurio, preencher corpo de e-mail, e mais. Voc ir aprender atalhos, maneiras espertas de
extender templates e como reusar cdigo de template.
Templates
Um template simplesmente um arquivo de texto que pode gerar qualquer formato baseado em texto (HTML, XML,
CSV, LaTeX ...). O tipo mais familiar de template um template em PHP - um arquivo de texto analisado pelo PHP
que contm uma mistura de texto e cdigo PHP:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to Symfony!</title>
</head>
<body>
<h1><?php echo $page_title ?></h1>

76

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

<ul id="navigation">
<?php foreach ($navigation as $item): ?>
<li>
<a href="<?php echo $item->getHref() ?>">
<?php echo $item->getCaption() ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</body>
</html>

Mas Symfony2 empacota at mesmo uma linguagem muito poderosa de template chamada Twig. Twig permite a voc
escrever templates consisos e legveis que so mais amigveis para web designers e, de certa forma, mais poderosos
que templates de PHP:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to Symfony!</title>
</head>
<body>
<h1>{{ page_title }}</h1>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
</body>
</html>

Twig define dois tipos de sintaxe especiais:


{{ ...

}}: Diga algo: exibe uma varivel ou o resultado de uma expresso para o template;

{% ... %}: Faa algo: uma tag que controla a lgica do template; ela usada para executar certas
sentenas como for-loops por exemplo.
Nota: H uma terceira sintaxe usada para criar comentrios {# this is a comment #}. Essa sintaxe pode ser
usada atravs de mltiplas linhas, parecidas com a sintaxe equivalente em PHP /* comment */.
Twig tambm contm filtros, que modificam contedo antes de serem interpretados. O seguinte filtro transforma a
varivel title toda em letra maiscula antes de interpret-la:
{{ title | upper }}

Twig vem com uma longa lista de tags e filtros_ que esto disponveis por padro. Voc pode at mesmo adicionar
suas prprias extenses_ para o Twig quando precisar.
Dica:
Registrar uma extenso Twig to fcil quanto criar um novo servio e atribuir tag nele com
twig.extension tag.
Como voc ver atravs da documentao, Twig tambm suporta funes e nova funes podem ser facilmente adicionadas. Por exemplo, a seguinte funo usa uma tag padro for e a funo cycle para ento imprimir dez tags div,
alternando entre classes odd e even:

2.1. Livro

77

Symfony Docs pt-BR Documentation, Verso 2.4

{% for i in 0..10 %}
<div class="{{ cycle([odd, even], i) }}">
<!-- some HTML here -->
</div>
{% endfor %}

Durante este captulo, exemplos de template sero mostrados tanto em Twig como PHP.
Por que Twig?
Templates Twig so feitas para serem simples e no iro processar tags PHP. Isto pelo design: o sistema de
template do Twig feito para expressar apresentao, no lgica de programa. Quanto mais voc usa Twig, mais
voc ir apreciar e beneficiar desta distino. E claro, voc ser amado por web designers de todos os lugares.
Twig pode tambm fazer coisas que PHP no pode, como por exemplo herana verdadeira de template (Templates do Twig compilam classes PHP que herdam uma da outra), controle de espao em branco, caixa de areia, e a
incluso de funes personalizadas e filtros que somente afetam templates. Twig contm pequenos recursos que
fazem escrita de templates mais fcil e mais concisa. Considere o seguinte exemplo, que combina um loop com
uma sentena lgia if:
<ul>
{% for user in users %}
<li>{{ user.username }}</li>
{% else %}
<li>No users found</li>
{% endfor %}
</ul>

Cache do Template Twig

Twig rpido. Cada template Twig compilado para uma classe nativa PHP que processada na execuo. As
classes compiladas so localizadas no diretrio app/cache/{environment}/twig (onde {environment}
o ambiente, como dev ou prod), e em alguns casos pode ser til durante a depurao. Veja environments-summary
para mais informaes de ambientes.
Quando o modo debug habilitado (comum no ambiente dev), um template Twig ser automaticamente recompilado
quando mudanas so feitas nele. Isso significa que durante o desenvolvimento voc pode alegremente fazer mudanas
para um template Twig e imediatamente ver as mudanas sem precisar se preocupar sobre limpar qualquer cache.
Quando o modo debug desabilitado (comum no ambiente prod), entretanto, voc deve limpar o cache do diretrio
Twig para que ento os templates Twig se regenerem. Lembre de fazer isso quando distribuir sua aplicao.
Herana e Layouts de Template
Mais frequentemente que no, templates compartilham elementos comuns em um projeto, como o header, footer,
sidebar ou outros. Em Symfony2, ns gostamos de pensar sobre esse problema de forma diferente: um template pode
ser decorado por outro. Isso funciona exatemente da mesma forma como classes PHP: herana de template permite
voc construir um layout de template base que contenha todos os elementos comuns de seu site definidos como
blocos (pense em classe PHP com mtodos base). Um template filho pode extender o layout base e sobrepor os
blocos (pense subclasse PHP que sobreponha certos mtodos de sua classe pai).
Primeiro, construa um arquivo de layout de base:
Twig

78

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

{# app/Resources/views/base.html.twig #}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>{% block title %}Test Application{% endblock %}</title>
</head>
<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog">Blog</a></li>
</ul>
{% endblock %}
</div>
<div id="content">
{% block body %}{% endblock %}
</div>
</body>
</html>

PHP
<!-- app/Resources/views/base.html.php -->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title><?php $view[slots]->output(title, Test Application) ?></title>
</head>
<body>
<div id="sidebar">
<?php if ($view[slots]->has(sidebar): ?>
<?php $view[slots]->output(sidebar) ?>
<?php else: ?>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog">Blog</a></li>
</ul>
<?php endif; ?>
</div>
<div id="content">
<?php $view[slots]->output(body) ?>
</div>
</body>
</html>

Nota: Apesar da discusso sobre herana de template ser em termos do Twig, a filosofia a mesma entre templates
Twig e PHP.
Este template define o esqueleto do documento base HTML de um pgina simples de duas colunas. Neste exemplo, trs
reas {% block %} so definidas (title, sidebar e body). Cada bloco pode ser sobreposto por um template
filho ou largado com sua implementao padro. Esse template poderia tambm ser processado diretamente. Neste
caso os blocos title, sidebar e body blocks deveriam simplesmente reter os valores padro neste template.

2.1. Livro

79

Symfony Docs pt-BR Documentation, Verso 2.4

Um template filho poderia ser como este:


Twig
{# src/Acme/BlogBundle/Resources/views/Blog/index.html.twig #}
{% extends ::base.html.twig %}
{% block title %}My cool blog posts{% endblock %}
{% block body %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

PHP
<!-- src/Acme/BlogBundle/Resources/views/Blog/index.html.php -->
<?php $view->extend(::base.html.php) ?>
<?php $view[slots]->set(title, My cool blog posts) ?>
<?php $view[slots]->start(body) ?>
<?php foreach ($blog_entries as $entry): ?>
<h2><?php echo $entry->getTitle() ?></h2>
<p><?php echo $entry->getBody() ?></p>
<?php endforeach; ?>
<?php $view[slots]->stop() ?>

Nota: O template pai idenficado por uma sintaxe especial de string (::base.html.twig) que indica que o
template reside no diretrio app/Resources/views do projeto. Essa conveno de nomeamento explicada
inteiramente em Nomeao de Template e Localizaes.
A chave para herana template a tag {% extends %}. Ela avisa o engine de template para primeiro avaliar o template base, que configura o layout e define vrios blocos. O template filho ento processado, ao ponto que os blocos
title e body do template pai sejam substitudos por aqueles do filho. Dependendo do valor de blog_entries,
a sada poderia parecer com isso:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>My cool blog posts</title>
</head>
<body>
<div id="sidebar">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog">Blog</a></li>
</ul>
</div>
<div id="content">
<h2>My first post</h2>
<p>The body of the first post.</p>
<h2>Another post</h2>
<p>The body of the second post.</p>

80

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

</div>
</body>
</html>

Perceba que como o template filho no definiu um bloco sidebar, o valor do template pai usado no lugar. Contedo
dentro de uma tag {% block %} em um template pai sempre usado por padro.
Voc pode usar muitos nveis de herana quanto quiser. Na prxima sesso, um modelo comum de herana de trs
nveis ser explicado assim como os templates so organizados dentro de um projeto Symfony2.
Quando trabalhar com herana de template, aqui esto algumas dicas para guardar na cabea:
Se voc usa {% extends %} em um template, ele deve ser a primeira tag naquele template.
Quanto mais tags {% block %} voc tiver no template base, melhor. Lembre, templates filhos no precisam
definir todos os blocos do pai, ento criar tantos blocos em seus templates base quanto voc quiser e dar a cada
um padro sensato. Quanto mais blocos seus templates base tiverem, mais flexvel seu layout ser.
Se voc achar voc mesmo duplicando contedo em um determinado nmero de templates, isto provavelmente
significa que voc deveria mover aquele contedo para um {% block %} no template pai. Em alguns casos,
uma soluo melhor pode ser mover o contedo para um novo template e incluir ele (veja Incluir outras
Templates).
Se voc precisa obter o contedo de um bloco do template pai, voc pode usar a funo {{ parent() }}.
Isso til se voc quiser adicionar ao contedo de um bloco pai ao invs de sobrepor ele:
.. code-block:: html+jinja
{% block sidebar %}
<h3>Table of Contents</h3>
...
{{ parent() }}
{% endblock %}

Nomeao de Template e Localizaes


Por padro, templates podem residir em duas localizaes diferentes:
app/Resources/views/: O diretrio de aplicao de views pode abrigar templates bases para toda a
aplicao(ex: os layout de sua aplicao) assim como os tempates que sobrepem templates de pacote (veja
Sobrepondo Templates de Pacote);
application-wide base templates (i.e. your applications layouts) as well as templates that override bundle templates (see Sobrepondo Templates de Pacote);
path/to/bundle/Resources/views/:
Cada pacote abriga as templates dele no diretrio
Resources/views (e sub-diretrios). A maioria dos templates ir residir dentro de um pacote.
Symfony2 usa a sintaxe de string bundle:controller:template para templates. Isso permite vrios tipos diferente de
template, cada um residindo em uma localizao especifica:
AcmeBlogBundle:Blog:index.html.twig: Esta sintaxe usada para especificar um template para
uma pgina especfica. As trs partes do string, cada uma separada por dois pontos, (:), signitca o seguinte:
AcmeBlogBundle:
(bundle)
src/Acme/BlogBundle);

template

reside

entro

de

Blog: (controller) indica que o template reside dentro do sub-diretrio


Resources/views;

AcmeBlogBundle
Blog

(e.g.
de

index.html.twig: (template) o verdadeiro nome do arquivo index.html.twig.


2.1. Livro

81

Symfony Docs pt-BR Documentation, Verso 2.4

Assumindo que o AcmeBlogBundle reside em src/Acme/BlogBundle, o atalho final para o layout seria
src/Acme/BlogBundle/Resources/views/Blog/index.html.twig.
AcmeBlogBundle::layout.html.twig: Essa sintaxe refere ao template base que especfica para
AcmeBlogBundle. Since the middle, controller, portion is missing (e.g. Blog), the template lives at
Resources/views/layout.html.twig inside AcmeBlogBundle.
::base.html.twig: Esta sintaxe refere a uma template base para toda a aplicao ou layout. Perceba
que a string comea com dois sinais de dois pontos (::), significando que ambas as partes bundle controller
esto faltando. Isto significa que o template no localizado em qualquer pacote, mas sim na raiz do diretrio
app/Resources/views/.
Na seo Sobrepondo Templates de Pacote, voc ir descobrir como cada template reside dentro do
AcmeBlogBundle, por exemplo, pode ser sobreposto ao colocar um template de mesmo nome no diretrio
app/Resources/AcmeBlogBundle/views/. Isso d o poder de sobrepor templates de qualquer pacote pago.
Dica: Esperanosamente, a sintaxe de nomeao de template parece familiar - a mesma conveno para nomeao
usada para referir para Padro de nomeao do Controlador.

Sufixo de Template

O formato bundle:controller:template de cada template especifica onde o arquivo de template est localizado. Cada
nome de template tambm tem duas expresses que especificam o formato e engine para aquela template.
AcmeBlogBundle:Blog:index.html.twig - formato HTML, engine Twig
AcmeBlogBundle:Blog:index.html.php - formato HTML, engine PHP
AcmeBlogBundle:Blog:index.css.twig - formato CSS, engine Twig
Por padro, qualquer template Symfony2 ou pode ser escrito em Twig ou em PHP, e a ltima parte da extenso (ex:
.twig ou .php) especifica qual dessas duas engines deveria ser usada. A primeira parte da extenso, (ex: .html,
.css, etc) o formato final que o template ir gerar. Ao contrrio de engine, que determina como Symfony2 analisa
o template, isso simplesmente uma ttica organizacional em caso do mesmo recurso precisar ser transformado como
HTML(index.html.twig), XML (index.xml.twig), ou qualquer outro formato. Para mais informaess,
leia a seo Debugging.
Nota: As engines disponveis podem ser configurados e at mesmo ter novas engines adicionadas. Veja Configurao de Template para mais detalhes.

Tags e Helpers
Voc j entende as bases do templates, como eles so chamados e como usar herana de template. As partes mais
difceis esto realmente atrs de voc. Nesta seo, voc ir aprender sobre um grande grande grupo de ferramentas
disponveis para ajudar a desempenhar as tarefas de template mais comuns como incluir outras templates, vincular
pginas e incluir imagens.
Symfony2 vem acompanhado com vrias tags Twig especializadas e funes que facilitam o trabalho do designer de
template. Em PHP, o sistema de template providencia um sistema extenso de helper que providencia funcionalidades
teis no contexto de template.
Ns realmente vimos umas poucas tags Twig construdas ({% block %} e {% extends %}) como exemplo de
um helper PHP ($view[slots]). Vamos aprender um pouco mais.

82

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Incluir outras Templates

Voc ir frequntemente querer incluir a mesma template ou fragmento de cdigo em vrias pginas diferentes. Por
exemplo, em uma aplicao com artigos de notcias, a exibio do artigo no cdigo do template poderia ser usada
numa pgina de detalhes do artigo, num a pgina exibindo os artigos mais populares, ou em uma lista dos ltimos
artigos.
Quando voc precisa reutilizar um pedao de um cdigo PHP, voc tipicamente move o cdigo para uma nova classe
ou funo PHP. O mesmo verdade para template. Ao mover o cdigo do template reutilizado em um template
prprio, ele pode ser includo em qualquer outro template. Primeiro, crie o template que voc precisar reutilizar.
Twig
{# src/Acme/ArticleBundle/Resources/views/Article/articleDetails.html.twig #}
<h2>{{ article.title }}</h2>
<h3 class="byline">by {{ article.authorName }}</h3>
<p>
{{ article.body }}
</p>

PHP
<!-- src/Acme/ArticleBundle/Resources/views/Article/articleDetails.html.php -->
<h2><?php echo $article->getTitle() ?></h2>
<h3 class="byline">by <?php echo $article->getAuthorName() ?></h3>
<p>
<?php echo $article->getBody() ?>
</p>

Incluir este template de qualquer outro template fcil:


Twig
{# src/Acme/ArticleBundle/Resources/Article/list.html.twig #}
{% extends AcmeArticleBundle::layout.html.twig %}
{% block body %}
<h1>Recent Articles<h1>

{% for article in articles %}


{% include AcmeArticleBundle:Article:articleDetails.html.twig with {article: article
{% endfor %}
{% endblock %}

PHP
<!-- src/Acme/ArticleBundle/Resources/Article/list.html.php -->
<?php $view->extend(AcmeArticleBundle::layout.html.php) ?>
<?php $view[slots]->start(body) ?>
<h1>Recent Articles</h1>

<?php foreach ($articles as $article): ?>


<?php echo $view->render(AcmeArticleBundle:Article:articleDetails.html.php, array(art
<?php endforeach; ?>
<?php $view[slots]->stop() ?>

O template includo usando a tag {% include %}. Perceba que o nome do template segue a mesma conveno

2.1. Livro

83

Symfony Docs pt-BR Documentation, Verso 2.4

tpica. O template articleDetails.html.twig usa uma varivel article. Isso passado por um template
list.html.twig usando o comando with.
Dica: A sintaxe {article: article} a sintaxe Twig padro para hash maps (ex: um array com chaves
nomeadas). Se ns precisarmos pass-lo em elementos mltiplos, ele poderia ser algo como: {foo: foo,
bar: bar}.

Incorporao de Controllers

Em alguns casos, voc precisa fazer mais do que incluir um template simples. Suponha que voc tenha uma barra
lateral no seu layout que contenha os trs artigos mais recentes. Recuperar os trs artigos podem incluir consultar a
base de dados ou desempenhar outra lgica pesada que no pode ser a partir de um template.
A soluo simplesmnte incorporar o resultado de um controller inteiro para seu template. Primeiro, crie o controller
que retorne um certo nmero de artigos recentes :
// src/Acme/ArticleBundle/Controller/ArticleController.php
class ArticleController extends Controller
{
public function recentArticlesAction($max = 3)
{
// make a database call or other logic to get the "$max" most recent articles
$articles = ...;

return $this->render(AcmeArticleBundle:Article:recentList.html.twig, array(articles => $a


}
}

A template recentList perfeitamente straightforward:


Twig
{# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #}
{% for article in articles %}
<a href="/article/{{ article.slug }}">
{{ article.title }}
</a>
{% endfor %}

PHP
<!-- src/Acme/ArticleBundle/Resources/views/Article/recentList.html.php -->
<?php foreach ($articles as $article): ?>
<a href="/article/<?php echo $article->getSlug() ?>">
<?php echo $article->getTitle() ?>
</a>
<?php endforeach; ?>

Nota:
Perceba que ns fizemos uma gambiarra e fizemos um hardcode no artigo URL desse exemplo (ex:
/article/*slug*). Isso uma prtica ruim. Na prxima seo, voc ir aprender como fazer isso corretamente.
Para incluir um controller, voc ir precisar referir a ela usando a sintaxe de string padro para controllers (isto ,
bundle:controller:action):
Twig

84

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

{# app/Resources/views/base.html.twig #}
...
<div id="sidebar">
{% render "AcmeArticleBundle:Article:recentArticles" with {max: 3} %}
</div>

PHP
<!-- app/Resources/views/base.html.php -->
...
<div id="sidebar">
<?php echo $view[actions]->render(AcmeArticleBundle:Article:recentArticles, array(max
</div>

Sempre quando voc pensar que voc precisa de uma varivel ou uma pea de informao que voc no tenha acesso
em um template, considere transformar o controller. Controllers so rpidos para executar e promovem uma boa
organizao e utilizao do cdigo.
Contedo Assncrono com hinclude.js

Novo na verso 2.1: suporte ao hinclude.js foi adicionado no Symfony 2.1


Os controladores podem ser incorporados assncronamente usando a biblioteca javascript hinclude.js_. Como o contedo incorporado vm de outra pgina (ou controlador, neste assunto), o Symfony2 usa o helper padro render para
configurar tags hinclude:
Twig
{% render ...:news with {}, {standalone: js} %}

PHP
<?php echo $view[actions]->render(...:news, array(), array(standalone => js)) ?>

Nota: hinclude.js_ nprecisa ser includo em sua pgina para funcionar.


Contedo padro (enquanto carregar ou se o javascript est desabilitado) pode ser definido globalmente na configurao da sua aplicao:
YAML
# app/config/config.yml
framework:
# ...
templating:
hinclude_default_template: AcmeDemoBundle::hinclude.html.twig

XML
<!-- app/config/config.xml -->
<framework:config>
<framework:templating hinclude-default-template="AcmeDemoBundle::hinclude.html.twig" />
</framework:config>

PHP

2.1. Livro

85

Symfony Docs pt-BR Documentation, Verso 2.4

// app/config/config.php
$container->loadFromExtension(framework, array(
// ...
templating
=> array(
hinclude_default_template => array(AcmeDemoBundle::hinclude.html.twig),
),
));

Vinculao s Pginas

Criar links para outras pgina em sua aplicao uma das tarefas mais comuns para um template. Ao invs de fazer
um hardcode nas URLs nos templates, use a funo do Twig path (ou o helper router no PHP) para gerar URLs
baseadas na configurao de roteamento. Mais tarde, se voc quiser modificar a URL de uma pgina particular, tudo
que voc precisar fazer mudar as configuraes de roteamento; os templates iro automatricamente gerar a nova
URL.
Primeiro, vincule a pgina _welcome , que acessvel pela seguinte configurao de roteamento:
YAML
_welcome:
pattern: /
defaults: { _controller: AcmeDemoBundle:Welcome:index }

XML
<route id="_welcome" pattern="/">
<default key="_controller">AcmeDemoBundle:Welcome:index</default>
</route>

PHP
$collection = new RouteCollection();
$collection->add(_welcome, new Route(/, array(
_controller => AcmeDemoBundle:Welcome:index,
)));
return $collection;

Para vincular pgina, apenas use a funo Twig path e refira para a rota:
Twig
<a href="{{ path(_welcome) }}">Home</a>

PHP
<a href="<?php echo $view[router]->generate(_welcome) ?>">Home</a>

Como esperado, isso ir gerar a URL /. Vamos ver como isso ir funcionar com uma rota mais complicada:
YAML
article_show:
pattern: /article/{slug}
defaults: { _controller: AcmeArticleBundle:Article:show }

XML

86

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

<route id="article_show" pattern="/article/{slug}">


<default key="_controller">AcmeArticleBundle:Article:show</default>
</route>

PHP
$collection = new RouteCollection();
$collection->add(article_show, new Route(/article/{slug}, array(
_controller => AcmeArticleBundle:Article:show,
)));
return $collection;

Neste caso, voc precisa especificar tanto o nome da rota (article_show) como um valor para o parmetro
{slug}. Usando esta rota, vamos revisitar o template recentList da sesso anterior e vincular aos artigos corretamente:
Twig
{# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #}
{% for article in articles %}
<a href="{{ path(article_show, { slug: article.slug }) }}">
{{ article.title }}
</a>
{% endfor %}

PHP

<!-- src/Acme/ArticleBundle/Resources/views/Article/recentList.html.php -->


<?php foreach ($articles in $article): ?>
<a href="<?php echo $view[router]->generate(article_show, array(slug => $article->getS
<?php echo $article->getTitle() ?>
</a>
<?php endforeach; ?>

Dica: Voc tambm pode gerar uma URL absoluta ao usar a funo url do Twig:
<a href="{{ url(_welcome) }}">Home</a>

O mesmo pode ser feito em templates PHP ao passar um terceiro argumento ao mtodo generate():
<a href="<?php echo $view[router]->generate(_welcome, array(), true) ?>">Home</a>

Vinculando os Assets

Templates podem frequentemente referir a imagens, Javascript, folhas de estilo e outros recursos. Claro voc poderia
fazer um hardcode do atalho desses assets (ex: /images/logo.png), mas Symfony2 providencia uma opo mais
dinmica via funo assets do Twig:
Twig
<img src="{{ asset(images/logo.png) }}" alt="Symfony!" />
<link href="{{ asset(css/blog.css) }}" rel="stylesheet" type="text/css" />

PHP

2.1. Livro

87

Symfony Docs pt-BR Documentation, Verso 2.4

<img src="<?php echo $view[assets]->getUrl(images/logo.png) ?>" alt="Symfony!" />

<link href="<?php echo $view[assets]->getUrl(css/blog.css) ?>" rel="stylesheet" type="text/c

O principal propsito da funo asset tornar sua aplicao mais porttil. Se sua aplicao reside na raiz do
seu host (ex: http://example.com), ento os atalhos interpretados deveriam ser /images/logo.png. Mas se sua
aplicao reside em um sub-diretrio (ex: http://example.com/my_app), cada caminho do asset deveria interpretar
com o diretrio (e.g. /my_app/images/logo.png). A funo asset toma conta disto ao determinar como sua
aplicao est sendo usada e gerando os atalhos de acordo com o correto.
Adicionalmente, se voc usar funo asset, Symfony pode automaticamente anexar uma string de consulta para asset,
em detrimento de garantir que assets estticos atualizados no sero armazenados quando distribudos. Por exemplo,
/images/logo.png poderia parecer como /images/logo.png?v2. Para mais informaes, veja a opo de
configurao ref-framework-assets-version.
Incluindo Folhas de Estilo e Javascript no Twig
Nenhum site seria completo sem incluir arquivos Javascript e folhas de estilo. Em Symfony, a incluso desses assets
elegantemente manipulada ao tirar vantagem das heranas de template do Symfony.
Dica: Esta seo ir ensinar voc a filosofia por trs disto, incluindo folha de estilo e asset Javascript em Symfony.
Symfony tambm engloba outra biblioteca, chamada Assetic, que segue essa filosofia mas tambm permite voc fazer
mais coisas muito interessantes com esses assets. Para mais informaes sobre usar Assetic veja Como usar o Assetic
para o Gerenciamento de Assets.
Comece adicionando dois blocos a seu template base que ir abrigar seus assets: uma chamada stylesheets dentro
da tag head e outra chamada javascripts justamente acima do fechamento da tag body. Esses blocos iro conter
todas as folhas de estilo e Javascripts que voc ir precisar atravs do seu site:
{# app/Resources/views/base.html.twig #}
<html>
<head>
{# ... #}
{% block stylesheets %}
<link href="{{ asset(/css/main.css) }}" type="text/css" rel="stylesheet" />
{% endblock %}
</head>
<body>
{# ... #}
{% block javascripts %}
<script src="{{ asset(/js/main.js) }}" type="text/javascript"></script>
{% endblock %}
</body>
</html>

Isso fcil o bastante ! Mas e se voc precisar incluir uma folha de estilo ou Javascript de um template filho ? Por
exemplo, suponha que voc tenha uma pgina de contatos e voc precise incluir uma folha de estilo contact.css
bem naquela pgina. Dentro do template da pgina de contatos, faa o seguinte:
{# src/Acme/DemoBundle/Resources/views/Contact/contact.html.twig #}
{# extends ::base.html.twig #}
{% block stylesheets %}
{{ parent() }}

88

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

<link href="{{ asset(/css/contact.css) }}" type="text/css" rel="stylesheet" />


{% endblock %}
{# ... #}

No template filho, voc simplesmente sobrepe o bloco stylesheets e coloca sua nova tag de folha de estilo dentro
daquele bloco. Claro, desde que voc queira adicionar ao contedo do bloco pai (e realmente no ir *substitu-lo),
voc deveria usar a funo parent() do Twig function para incluir tudo do bloco stylesheets do template base.
Voc pode tambm incluir assets localizados em seus arquivos de pacotes Resources/public. Voc precisar
executar o comandophp app/console assets:install target [symlink] , que move (ou symlinks) arquivos dentro da
localizao correta. (target sempre por padro web).
<link href="{{ asset(bundles/acmedemo/css/contact.css) }}" type="text/css" rel="stylesheet" />

O resultado final uma pgina que inclui ambas as folhas de estilo main.css e contact.css.
Configurando e usando o Servio templating
O corao do sistema de template em Symfony2 o template Engine. Este objeto especial responsvel por manipular templates e retornar o contedo deles. Quando voc manipula um template em um controller, por exemplo, voc
est na verdade usando o servio do template engine. Por exemplo:
return $this->render(AcmeArticleBundle:Article:index.html.twig);

equivalente a:
$engine = $this->container->get(templating);
$content = $engine->render(AcmeArticleBundle:Article:index.html.twig);
return $response = new Response($content);

O engine de template (ou servio) pr-configurada para trabalhar automaticamente dentro de Symfony2. Ele pode,
claro, ser configurado mais adiante no arquivo de configurao da aplicao:
YAML
# app/config/config.yml
framework:
# ...
templating: { engines: [twig] }

XML
<!-- app/config/config.xml -->
<framework:templating>
<framework:engine id="twig" />
</framework:templating>

PHP
// app/config/config.php
$container->loadFromExtension(framework, array(
// ...
templating
=> array(
engines => array(twig),
),
));

2.1. Livro

89

Symfony Docs pt-BR Documentation, Verso 2.4

Vrias opes de configurao esto disponveis e esto cobertos em Configuration Appendix.


Nota: O engine twig obrigatrio para usar o webprofiler (bem como outros pacotes de terceiros).

Sobrepondo Templates de Pacote


A comunidade Symfony2 orgulha-se de si prpria em criar e manter pacotes de alta qualidade (veja Symfony2Bundles.org) para um grande nmero de funcionalidades diferentes. Uma vez que voc use um pacote de
terceiros, voc ir certamente precisar sobrepor e personalizar um ou mais de seus templates.
Suponha que voc incluiu o imaginrio open-source AcmeBlogBundle em seu projeto (ex: no diretrio
src/Acme/BlogBundle). E enquanto voc estiver realmente feliz com tudo, voc quer sobrepor a pgina de
lista do blog para personalizar a marcao especificamente para sua aplicao. Ao se aprofundar no controller
Blog eo AcmeBlogBundle, voc encontrar o seguinte:
public function indexAction()
{
$blogs = // some logic to retrieve the blogs
$this->render(AcmeBlogBundle:Blog:index.html.twig, array(blogs => $blogs));
}

Quando AcmeBlogBundle:Blog:index.html.twig manipulado, Symfony2 realmente observa duas diferentes localizaes para o template:
1. app/Resources/AcmeBlogBundle/views/Blog/index.html.twig
2. src/Acme/BlogBundle/Resources/views/Blog/index.html.twig
Para sobrepor o template de pacote, s copie o template index.html.twig do pacote
para
app/Resources/AcmeBlogBundle/views/Blog/index.html.twig
(o
diretrio
app/Resources/AcmeBlogBundle no existiro, ento voc precisar cri-lo). Voc est livre agora
para personalizar o template.
Esta lgica tambm se aplica a templates de pacote base. Suponha tambm que cada template em AcmeBlogBundle
herda de um template base chamado AcmeBlogBundle::layout.html.twig. Justo como antes, Symfony2 ir
observar os seguintes dois lugares para o template:
1. app/Resources/AcmeBlogBundle/views/layout.html.twig
2. src/Acme/BlogBundle/Resources/views/layout.html.twig
Uma
vez
novamente,
para
sobrepor
o
template,
app/Resources/AcmeBlogBundle/views/layout.html.twig.
nalizar esta cpia como voc quiser.

apenas
copie
ele
para
Voc agora est livre para perso-

Se voc voltar um passo atrs, ver que Symfony2 sempre comea a observar no diretrio
app/Resources/{BUNDLE_NAME}/views/ por um template. Se o template no existe aqui, ele continua checando dentro do diretrio Resources/views do prprio pacote. Isso significa que todos os templates do
pacote podem ser sobrepostos ao coloc-los no sub-diretrio correto app/Resources.
Sobrepondo Templates Centrais

Como o framework Symfony um pacote por si s, templates centrais podem ser sobrepostos da mesma forma.
Por exemplo, o ncleo TwigBundle contm um nmeto de diferentes templates exception e error que podem
ser sobrepostas ao copiar cada uma do diretrio Resources/views/Exception do TwigBundle para, voc
adivinhou, o diretrio app/Resources/TwigBundle/views/Exception .

90

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Herana de Trs Nveis


Um modo comum de usar herana usar uma aproximao em trs nveis. Este mtodo trabalha perfeitamente com
trs tipos diferentes de templates que ns certamente cobrimos:
Criar um arquivo app/Resources/views/base.html.twig que contm o layout principal para sua
aplicao (como nos exemplos anteriores). Internamente, este template chamado ::base.html.twig;
Cria um template para cada seo do seu site. Por exemplo, um AcmeBlogBundle, teria um template chamado AcmeBlogBundle::layout.html.twig que contm somente elementos especficos para a sesso
no blog:
{# src/Acme/BlogBundle/Resources/views/layout.html.twig #}
{% extends ::base.html.twig %}
{% block body %}
<h1>Blog Application</h1>
{% block content %}{% endblock %}
{% endblock %}

Criar templates individuais para cada pgina e fazer cada um estender a template de sesso apropriada.
Por exemplo, a pgina index deveria ser chamada de algo prximo a
AcmeBlogBundle:Blog:index.html.twig e listar os blogs de posts reais.
{# src/Acme/BlogBundle/Resources/views/Blog/index.html.twig #}
{% extends AcmeBlogBundle::layout.html.twig %}
{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

Perceba que este template estende a template de sesso - (AcmeBlogBundle::layout.html.twig) que por
sua vez estende o layout de aplicao base (::base.html.twig). Isso o modelo comum de herana de trs
nveis.
Quando construir sua aplicao, voc pode escolher seguir esse mtodo ou simplesmente tornar cada template de
pgina estender a template de aplicao base diretamente (ex: {% extends ::base.html.twig %}). O
modelo de trs templates o mtodo de melhor prtica usado por vendor bundles ento aquele template base para um
pacote pode ser facilmente sobreposto para propriamente estender seu layout base de aplicao.
Sada para escape
Quando gerar HTML de um template, sempre h um risco que uma varivel de template pode gerar HTML involutrio
ou codigo do lado cliente perigoso. O resultado que o contedo dinmico poderia quebrar o HTML de uma pgina
de resultados ou permitir um usurio maldoso realizar um ataque Cross Site Scripting (XSS). Considere esse exemplo
clssico:
Twig
Hello {{ name }}

PHP

2.1. Livro

91

Symfony Docs pt-BR Documentation, Verso 2.4

Hello <?php echo $name ?>

Imagine que o usurio entre o seguinte cdigo como o nome dele/dela:


<script>alert(hello!)</script>

Sem qualquer outra sada de escape, o resultado da template ir causar uma caixa de alerta em JavaScript para saltar
na tela:
Hello <script>alert(hello!)</script>

E enquanto isso parece inofensivo, se um usurio pode chegar to longe, o mesmo usurio deveria tambm ser capaz
de escrever Javascript que realiza aes maliciosas dentro de uma rea segura de um usurio legtimo e desconhecido.
A resposta para o problema sada para escape. Sem a sada para escape ativa, o mesmo template ir manipular
inofensivamente, e literalmente imprimir a tag script na tela:
Hello &lt;script&gt;alert(&#39;helloe&#39;)&lt;/script&gt;

Os sistemas de templating Twig e PHP aproximam-se do problema de formas diferentes. Se voc est usando Twig,
sada para escape ativado por padro e voc est protegido. Em PHP, sada para escape no automtico, significando
que voc precisar manualmente fazer o escape quando necessrio.
Sada para escape em Twig

Se voc est usando templates Twig, ento sada para escape ativado por padro. Isto significa que voc est protegido
externamente de consequencias acidentais por cdigo submetido por usurio. Por padro, a sada para escape assume
que o contedo est sendo escapado pela sada HTML.
Em alguns casos, voc precisar desabilitar sada para escape quando voc est manipulando uma varivel que
confivel e contm marcao que no poderia ter escape. Suponha que usurios administrativos so capazes de escrever
artigos que contenham cdigo HTML. Por padro, Twig ir escapar o corpo do artigo. Para faz-lo normalamente,
adicione o filtro raw: {{ article.body | raw }}.
Voc pode tambm desabilitar sada para escape dentro de uma rea {% block %} ou para um template inteiro.
Para mais informaes, veja Output Escaping na documentao do Twig.
Sada para escape em PHP

Sada para escape no automtica quando usamos templates PHP. Isso significa que a menos que voc escolha escapar
uma varivel explicitamente, voc no est protegido. Para usar sada para escape use o mtodo de view escape():
Hello <?php echo $view->escape($name) ?>

Por padro, o mtodo escape() assume que a varivel est sendo manipulada dentro de um contexto HTML (e assim
a varivel escapa e est segura para o HTML). O segundo argumento deixa voc mudar o contexto. Por exemplo, para
gerar algo em uma string Javascript, use o contexto js :
var myMsg = Hello <?php echo $view->escape($name, js) ?>;

Debugging
Novo na verso 2.0.9: Esta funcionalidade est disponvel no Twig 1.5.x, e foi adicionada primeiramente no Symfony 2.0.9.

92

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Ao utilizar o PHP, voc pode usar o var_dump() se precisa encontrar rapidamente o valor de uma varivel passada.
Isso til, por exemplo, dentro de seu controlador. O mesmo pode ser conseguido ao usar o Twig com a extenso de
depurao. Esta extenso precisa ser ativada na configurao:
YAML
# app/config/config.yml
services:
acme_hello.twig.extension.debug:
class:
Twig_Extension_Debug
tags:
- { name: twig.extension }

XML
<!-- app/config/config.xml -->
<services>
<service id="acme_hello.twig.extension.debug" class="Twig_Extension_Debug">
<tag name="twig.extension" />
</service>
</services>

PHP
// app/config/config.php
use Symfony\Component\DependencyInjection\Definition;
$definition = new Definition(Twig_Extension_Debug);
$definition->addTag(twig.extension);
$container->setDefinition(acme_hello.twig.extension.debug, $definition);

O dump dos parmetros do template pode ser feito usando a funo dump:
{# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #}
{{ dump(articles) }}
{% for article in articles %}
<a href="/article/{{ article.slug }}">
{{ article.title }}
</a>
{% endfor %}

O dump das variveis somente ser realizado se a definio debug do Twig (no config.yml) for true. Por
padro, isto signifca que ser feito o dump das variveis no ambiente dev mas no no prod.
Verificao de Sintaxe
Novo na verso 2.1: O comando twig:lint foi adicionado no Symfony 2.1
Voc pode verificar erros de sintaxe nos templates do Twig usando o comando de console twig:lint:
# You can check by filename:
$ php app/console twig:lint src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig
# or by directory:
$ php app/console twig:lint src/Acme/ArticleBundle/Resources/views
# or using the bundle name:
$ php app/console twig:lint @AcmeArticleBundle

2.1. Livro

93

Symfony Docs pt-BR Documentation, Verso 2.4

Formatos de Template
Templates so uma forma genrica de modificar contedo em qualquer formato. E enquanto em muitos casos voc ir
usar templates para modificar contedo HTML, um template pode to fcil como certo gerar JavaScript, CSS, XML
ou qualquer outro formato que voc possa sonhar.
Por exemplo, o mesmo recurso sempre modificado em diversos formatos diferentes. Para modificar uma pgina
inicial de um artigo XML, simplesmente inclua o formato no nome do template:
nome do template XML: AcmeArticleBundle:Article:index.xml.twig
nome do arquivo do template XML: index.xml.twig
Na realidade, isso nada mais que uma conveno de nomeao e o template no realmente modificado de forma
diferente ao baseado no formato dele.
Em muitos casos, voc pode querer permitir um controller unitrio para modificar mltiplos formatos diferentes baseado no formato de requisio. Por aquela razo, um padro comum fazer o seguinte:
public function indexAction()
{
$format = $this->getRequest()->getRequestFormat();
return $this->render(AcmeBlogBundle:Blog:index..$format..twig);
}

O getRequestFormat no objeto Request padroniza para html, mas pode retornar qualquer outro formato baseado no formato solicitado pelo usurio. O formato solicitado frequentemente mais gerenciado pelo roteamento, onde
uma rota pode ser configurada para que /contact configure o formato requisitado html enquanto /contact.xml
configure o formato para xml. Para mais informaes, veja Advanced Example in the Routing chapter.
Para criar links que incluam o parmetro de formato, inclua uma chave _format no detalhe do parmetro:
Twig
<a href="{{ path(article_show, {id: 123, _format: pdf}) }}">
PDF Version
</a>

PHP

<a href="<?php echo $view[router]->generate(article_show, array(id => 123, _format => p


PDF Version
</a>

Consideraes finais
O engine de template em Symfony uma ferramenta poderosa que pode ser usada cada momento que voc precisa para
gerar contedo de apresentao em HTML, XML ou qualquer outro formato. E apesar de tempaltes serem um jeito
comum de gerar contedo em um controller, o uso deles no so obrigatrios. O objeto Response object retornado
por um controller pode ser criado com ou sem o uso de um template:
// creates a Response object whose content is the rendered template
$response = $this->render(AcmeArticleBundle:Article:index.html.twig);
// creates a Response object whose content is simple text
$response = new Response(response content);

94

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Engine de template do Symfony muito flexvel e dois editores de template diferentes esto disponveis por padro:
os tradicionais templates do PHP e os polidos e poderosos templates do Twig . Ambos suportam uma hierarquia de
template e vm empacotados com um conjunto rico de funes helper capazes de realizar as tarefas mais comuns.
No geral, o tpico de template poderia ser pensado como uma ferramenta poderosa que est sua disposio. Em
alguns casos, voc pode no precisar modificar um template, e em Symfony2, isso absolutamente legal.
Aprenda mais do Cookbook
/cookbook/templating/PHP
Como personalizar as pginas de erro

2.1.7 Bancos de Dados e Doctrine


Temos que dizer, uma das tarefas mais comuns e desafiadoras em qualquer aplicao envolve persistir e ler informaes
de um banco de dados. Felizmente o Symfony vem integrado com o Doctrine, uma biblioteca cujo nico objetivo
fornecer poderosas ferramentas que tornem isso fcil. Nesse captulo voc aprender a filosofia bsica por trs do
Doctrine e ver quo fcil pode ser trabalhar com um banco de dados.
Nota: O Doctrine totalmente desacoplado do Symfony, e seu uso opcional. Esse captulo totalmente sobre o
Doctrine ORM, que visa permitir fazer mapeamento de objetos para um banco de dados relacional (como o MySQL,
PostgreSQL ou o Microsoft SQL). fcil usar consultas SQL puras se voc preferir, isso explicado na entrada do
cookbook Como usar a Camada DBAL do Doctrine.
Voc tambm pode persistir dados no MongoDB usando a biblioteca Doctrine ODM. Para mais informaes, leia a
documentao /bundles/DoctrineMongoDBBundle/index.

Um Exemplo Simples: Um Produto


O jeito mais fcil de entender como o Doctrine trabalha vendo-o em ao. Nessa seo, voc configurar seu banco
de dados, criar um objeto Product, far sua persistncia no banco e depois ir retorn-lo.
Codifique seguindo o exemplo
Se quiser seguir o exemplo deste captulo, crie um AcmeStoreBundle via:
php app/console generate:bundle --namespace=Acme/StoreBundle

Configurando o Banco de Dados

Antes de comear realmente, voc precisa configurar a informao de conexo do seu banco. Por conveno, essa
informao geralmente configurada no arquivo app/config/parameters.yml:
# app/config/parameters.yml
parameters:
database_driver:
pdo_mysql
database_host:
localhost
database_name:
test_project
database_user:
root
database_password: password

2.1. Livro

95

Symfony Docs pt-BR Documentation, Verso 2.4

Nota: Definir a configurao pelo parameters.yml apenas uma conveno. Os parmetros definidos naquele
arquivo so referenciados pelo arquivo de configurao principal na configurao do Doctrine:
doctrine:
dbal:
driver:
host:
dbname:
user:
password:

%database_driver%
%database_host%
%database_name%
%database_user%
%database_password%

Colocando a informao do banco de dados em um arquivo separado, voc pode manter de forma fcil diferentes
verses em cada um dos servidores. Voc pode tambm guardar facilmente a configurao de banco (ou qualquer
outra informao delicada) fora do seu projeto, por exemplo dentro do seu diretrio de configurao do Apache. Para
mais informaes, de uma olhada em Como definir Parmetros Externos no Container de Servios.
Agora que o Doctrine sabe sobre seu banco, pode deixar que ele faa a criao dele para voc:
php app/console doctrine:database:create

Criando uma Classe Entidade

Suponha que voc esteja criando uma aplicao onde os produtos precisam ser mostrados. Antes mesmo de pensar
sobre o Doctrine ou banco de dados, voc j sabe que ir precisar de um objeto Product para representar esses
produtos. Crie essa classe dentro do diretrio Entity no seu bundle AcmeStoreBundle:
// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
class Product
{
protected $name;
protected $price;
protected $description;
}

A classe - frequentemente chamada de entidade, que significa uma classe bsica para guardar dados - simples
e ajuda a cumprir o requisito de negcio referente aos produtos na sua aplicao. Essa classe ainda no pode ser
persistida no banco de dados - ela apenas uma classe PHP simples.
Dica: Depois que voc aprender os conceitos por trs do Doctrine, voc pode deix-lo criar essa classe entidade para
voc:

php app/console doctrine:generate:entity --entity="AcmeStoreBundle:Product" --fields="name:string(255

Adicionando Informaes de Mapeamento

O Doctrine permite que voc trabalhe de uma forma muito mais interessante com banco de dados do que apenas buscar
registros de uma tabela baseada em colunas para um array. Em vez disso, o Doctrine permite que voc persista objetos
inteiros no banco e recupere objetos inteiros do banco de dados. Isso funciona mapeando uma classe PHP com uma
tabela do banco, e as propriedades dessa classe com as colunas da tabela:

96

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Para o Doctrine ser capaz disso, voc tem apenas que criar metadados, em outras palavras a configurao que diz ao
Doctrine exatamente como a classe Product e suas propriedades devem ser mapeadas com o banco de dados. Esses
metadados podem ser especificados em vrios diferentes formatos incluindo YAML, XML ou diretamente dentro da
classe Product por meio de annotations:
Nota: Um bundle s pode aceitar um formato para definio de metadados. Por exemplo, no possvel misturar
definies em YAML com definies por annotations nas classes entidade.
Annotations
// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="product")
*/
class Product
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="string", length=100)
*/
protected $name;
/**
* @ORM\Column(type="decimal", scale=2)
*/
protected $price;
/**
* @ORM\Column(type="text")
*/
protected $description;
}

2.1. Livro

97

Symfony Docs pt-BR Documentation, Verso 2.4

YAML
# src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml
Acme\StoreBundle\Entity\Product:
type: entity
table: product
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
name:
type: string
length: 100
price:
type: decimal
scale: 2
description:
type: text

XML
<!-- src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.xml -->
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Acme\StoreBundle\Entity\Product" table="product">
<id name="id" type="integer" column="id">
<generator strategy="AUTO" />
</id>
<field name="name" column="name" type="string" length="100" />
<field name="price" column="price" type="decimal" scale="2" />
<field name="description" column="description" type="text" />
</entity>
</doctrine-mapping>

Dica: O nome da tabela opcional e, se omitido, ser determinado automaticamente baseado no nome da classe
entidade.
O Doctrine permite que voc escolha entre uma grande variedade de diferentes tipos de campo, cada um com suas
opes especficas. Para informaes sobre os tipos de campos disponveis, d uma olhada na seo Referncia dos
Tipos de Campos do Doctrine.
Veja tambm:
Voc tambm pode conferir a Documentao Bsica sobre Mapeamento do Doctrine para todos os detalhes sobre o
tema. Se voc usar annotations, ir precisar prefixar todas elas com ORM\ (i.e. ORM\Column(..)), o que no
citado na documentao do Doctrine. Voc tambm ir precisar incluir o comando use Doctrine\ORM\Mapping
as ORM;, que importa o prefixo ORM das annotations.

98

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Cuidado: Tenha cuidado para que o nome da sua classe e suas propriedades no esto mapeadas com o nome
de um comando SQL protegido (como groupou user). Por exemplo, se o nome da sua classe entidade
Group ento, por padro, o nome da sua tabela ser group, que causar um erro de SQL em alguns dos bancos
de dados. D uma olhada na Documentao sobre os nomes de comandos SQL reservados para ver como escapar
adequadamente esses nomes. Alternativamente, se voc pode escolher livremente seu esquema de banco de dados,
simplesmente mapeie para um nome de tabela ou nome de coluna diferente. Veja a documentao do Doctrine
sobre Classes persistentes e Mapeamento de propriedades

Nota: Quando usar outra biblioteca ou programa (i.e. Doxygen) que usa annotations voc dever colocar a annotation
@IgnoreAnnotation na classe para indicar que annotations o Symfony deve ignorar.
Por exemplo, para prevenir que a annotation @fn gere uma exceo, inclua o seguinte:
/**
@IgnoreAnnotation(fn)
*/
class Product

Gerando os Getters e Setters

Apesar do Doctrine agora saber como persistir um objeto Product num banco de dados, a classe ainda no realmente til. Como Product apenas uma classe PHP usual, voc precisa criar os mtodos getters e setters (i.e.
getName(), setName() para acessar sua suas propriedades (at as propriedades protected). Felizmente o
Doctrine pode fazer isso por voc executando:
php app/console doctrine:generate:entities Acme/StoreBundle/Entity/Product

Esse comando garante que todos os getters e setters esto criados na classe Product. Ele um comando seguro voc pode execut-lo muitas e muitas vezes: ele apenas gera getters e setters que ainda no existem (i.e. ele no altera
os models j existentes).
Cuidado: O comando doctrine:generate:entities gera um backup do Product.php original chamado de Product.php~. Em alguns casos, a presena desse arquivo pode causar um erro Cannot redeclare
class. seguro remov-lo.
Voc pode gerar todas as entidades que so conhecidas por um bundle (i.e. cada classe PHP com a informao de
mapeamento do Doctrine) ou de um namespace inteiro.
php app/console doctrine:generate:entities AcmeStoreBundle
php app/console doctrine:generate:entities Acme

Nota: O Doctrine no se importa se as suas propriedades so protected ou private, ou se voc no tem um


mtodo getter ou setter. Os getters e setters so gerados aqui apenas porque voc ir precisar deles para interagir com
o seu objeto PHP.

Criando as Tabelas/Esquema do Banco de Dados

Agora voc tem uma classe utilizvel Product com informao de mapeamento assim o Doctrine sabe exatamente
como fazer a persistncia dela. claro, voc ainda no tem a tabela correspondente product no seu banco de dados.

2.1. Livro

99

Symfony Docs pt-BR Documentation, Verso 2.4

Felizmente, o Doctrine pode criar automaticamente todas as tabelas necessrias no banco para cada uma das entidades
conhecidas da sua aplicao. Para isso, execute:
php app/console doctrine:schema:update --force

Dica: Na verdade, esse comando extremamente poderoso. Ele compara o que o banco de dados deveria se
parecer (baseado na informao de mapeamento das suas entidades) com o que ele realmente se parece, e gera os
comandos SQL necessrios para atualizar o banco para o que ele deveria ser. Em outras palavras, se voc adicionar
uma nova propriedade com metadados de mapeamento na classe Producte executar esse comando
novamente, ele ir criar a instruo alter table para adicionar as novas
colunas na tabela product existente.
Uma maneira ainda melhor de se aproveitar dessa funcionalidade por meio das migrations, que lhe permitem
criar essas instrues SQL e guard-las em classes migration que podem ser rodadas de forma sistemtica no seu
servidor de produo para que se possa acompanhar e migrar o schema do seu banco de dados de uma forma mais
segura e confivel.
Seu banco de dados agora tem uma tabela product totalmente funcional com as colunas correspondendo com os
metadados que foram especificados.
Persistindo Objetos no Banco de Dados

Agora que voc tem uma entidade Product mapeada e a tabela correspondente product, j est pronto para
persistir os dados no banco. De dentro de um controller, isso bem simples. Inclua o seguinte mtodo no
DefaultController do bundle:
1
2
3
4

// src/Acme/StoreBundle/Controller/DefaultController.php
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...

5
6
7
8
9
10
11

public function createAction()


{
$product = new Product();
$product->setName(A Foo Bar);
$product->setPrice(19.99);
$product->setDescription(Lorem ipsum dolor);

12

$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();

13
14
15
16

return new Response(Created product id .$product->getId());

17
18

Nota: Se voc estiver seguindo o exemplo na prtica, precisar criar a rota que aponta para essa action se quiser v-la
funcionando.
Vamos caminhar pelo exemplo:
linhas 8-11 Nessa parte voc instancia o objeto $product como qualquer outro objeto PHP normal;
linha 13 Essa linha recuperar o objeto entity manager do Doctrine, que o responsvel por lidar com o processo
de persistir e retornar objetos do e para o banco de dados;
linha 14 O mtodo persist() diz ao Doctrine para gerenciar o objeto $product. Isso no gera (ainda)
um comando real no banco de dados.
100

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

linha 15 Quando o mtodo flush() chamado, o Doctrine verifica em todos os objetos que ele gerencia para
ver se eles necessitam ser persistidos no banco. Nesse exemplo, o objeto $product ainda no foi persistido,
por isso o entity manager executa um comando INSERT e um registro criado na tabela product.
Nota: Na verdade, como o Doctrine conhece todas as entidades gerenciadas, quando voc chama o mtodo flush(),
ele calcula um changeset geral e executa o comando ou os comandos mais eficientes possveis. Por exemplo, se voc
vai persistir um total de 100 objetos Product e em seguida chamar o mtodo flush(), o Doctrine ir criar um
nico prepared statment e reutiliz-lo para cada uma das inseres. Esse padro chamado de Unit of Work, e
utilizado porque rpido e eficiente.
Na hora de criar ou atualizar objetos, o fluxo de trabalho quase o mesmo. Na prxima seo, voc ver como o
Doctrine inteligente o suficiente para rodar uma instruo UPDATE de forma automtica se o registro j existir no
banco.
Dica: O Doctrine fornece uma biblioteca que permite a voc carregar programaticamente dados de teste no seu
projeto (i.e. fixture data). Para mais informaes, veja /bundles/DoctrineFixturesBundle/index.

Trazendo Objetos do Banco de Dados

Trazer um objeto a partir do banco ainda mais fcil. Por exemplo, suponha que voc tenha configurado uma rota
para mostrar um Product especfico baseado no seu valor id:
public function showAction($id)
{
$product = $this->getDoctrine()
->getRepository(AcmeStoreBundle:Product)
->find($id);
if (!$product) {
throw $this->createNotFoundException(No product found for id .$id);
}
// faz algo, como passar o objeto $product para um template
}

Quando voc busca um tipo de objeto em particular, voc sempre usa o que chamamos de repositrio. Voc pode
pensar num repositrio como uma classe PHP cuja nica funo auxiliar a trazer entidades de uma determinada
classe. Voc pode acessar o objeto repositrio por uma classe entidade dessa forma:
$repository = $this->getDoctrine()
->getRepository(AcmeStoreBundle:Product);

Nota: A string AcmeStoreBundle:Product um atalho que voc pode usar em qualquer lugar no Doctrine em
vez do nome completo da classe entidade (i.e Acme\StoreBundle\Entity\Product). Desde que sua entidade
esteja sob o namespace Entity do seu bundle, isso vai funcionar.
Uma vez que voc tiver seu repositrio, ter acesso a todos os tipos de mtodos teis:
// Busca pela chave primria (geralmente "id")
$product = $repository->find($id);
// nomes de mtodos dinmicos para busca baseados no valor de uma coluna
$product = $repository->findOneById($id);
$product = $repository->findOneByName(foo);

2.1. Livro

101

Symfony Docs pt-BR Documentation, Verso 2.4

// busca *todos* os produtos


$products = $repository->findAll();
// busca um grupo de produtos baseada numa valor arbitrrio de coluna
$products = $repository->findByPrice(19.99);

Nota: Naturalmente, voc pode tambm pode rodar consultas complexas, vamos aprender mais sobre isso na seo
Consultando Objetos.
Voc tambm pode se aproveitar dos mtodos bem teis findBy e findOneBy para retornar facilmente objetos
baseando-se em mltiplas condies:
// busca por um produto que corresponda a um nome e um preo
$product = $repository->findOneBy(array(name => foo, price => 19.99));
// busca por todos os produtos correspondentes a um nome, ordenados por
// preo
$product = $repository->findBy(
array(name => foo),
array(price => ASC)
);

Dica: Quando voc renderiza uma pgina, voc pode ver quantas buscas foram feitas no canto inferior direito da web
debug toolbar.

Se voc clicar no cone, ir abrir o profiler, mostrando a voc as consultas exatas que foram feitas.

Atualizando um Objeto

Depois que voc trouxe um objeto do Doctrine, a atualizao fcil. Suponha que voc tenha uma rota que mapeia o
id de um produto para uma action de atualizao em um controller:
public function updateAction($id)
{
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository(AcmeStoreBundle:Product)->find($id);
if (!$product) {
throw $this->createNotFoundException(No product found for id .$id);
}
$product->setName(New product name!);

102

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

$em->flush();
return $this->redirect($this->generateUrl(homepage));
}

Atualizar um objeto envolve apenas trs passos:


1. retornar um objeto do Doctrine;
2. modificar o objeto;
3. chamar flush() no entity manager
Observe que no necessrio chamar $em->persist($product). Chamar novamente esse mtodo apenas diz
ao Doctrine para gerenciar ou ficar de olho no objeto $product. Nesse caso, como o objeto $product foi trazido
do Doctrine, ele j est sendo gerenciado.
Excluindo um Objeto

Apagar um objeto muito semelhante, mas requer um chamada ao mtodo remove() do entity manager:
$em->remove($product);
$em->flush();

Como voc podia esperar, o mtodo remove() notifica o Doctrine que voc quer remover uma determinada entidade
do banco. A consulta real DELETE, no entanto, no executada de verdade at que o mtodo flush() seja chamado.
Consultando Objetos
Voc j viu como o repositrio objeto permite que voc execute consultas bsicas sem nenhum esforo:
$repository->find($id);
$repository->findOneByName(Foo);

claro, o Doctrine tambm permite que se escreva consulta mais complexas usando o Doctrine Query Language
(DQL). O DQL similar ao SQL exceto que voc deve imaginar que voc est consultando um ou mais objetos de
uma classe entidade (i.e. Product) em vez de consultar linhas em uma tabela (i.e. product).
Quando estiver consultando no Doctrine, voc tem duas opes: escrever consultas Doctrine puras ou usar o Doctrines
Query Builder.
Consultando Objetos com DQL

Imagine que voc queira buscar por produtos, mas retornar apenas produtos que custem menos que 19,99, ordenados
do mais barato para o mais caro. De um controller, faa o seguinte:
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
SELECT p FROM AcmeStoreBundle:Product p WHERE p.price > :price ORDER BY p.price ASC
)->setParameter(price, 19.99);
$products = $query->getResult();

Se voc se sentir confortvel com o SQL, ento o DQL deve ser bem natural. A grande diferena que voc precisa
pensar em termos de objetos em vez de linhas no banco de dados. Por esse motivo, voc faz um select from
AcmeStoreBundle:Product e d para ele o alias p.
2.1. Livro

103

Symfony Docs pt-BR Documentation, Verso 2.4

O mtodo getResult() retorna um array de resultados. Se voc estiver buscando por apenas um objeto, voc pode
usar em vez disso o mtodo getSingleResult():
$product = $query->getSingleResult();

Cuidado: O mtodo getSingleResult() gera uma exceo Doctrine\ORM\NoResultException


se nenhum resultado for retornado e uma Doctrine\ORM\NonUniqueResultException se mais de um
resultado for retornado. Se voc usar esse mtodo, voc vai precisar envolv-lo em um bloco try-catch e garantir
que apenas um resultado retornado (se estiver buscando algo que possa de alguma forma retornar mais de um
resultado):
$query = $em->createQuery(SELECT ....)
->setMaxResults(1);
try {
$product = $query->getSingleResult();
} catch (\Doctrine\Orm\NoResultException $e) {
$product = null;
}
// ...

A sintaxe DQL incrivelmente poderosa, permitindo que voc faa junes entre entidades facilmente (o tpico de
relacionamentos ser coberto posteriormente), grupos etc. Para mais informaes, veja a documentao oficial do
Doctrine Query Language.
Configurando parmetros
Tome nota do mtodo setParameter(). Quando trabalhar com o Doctrine, sempre uma boa ideia configurar os valores externos como placeholders, o que foi feito na consulta acima:
... WHERE p.price > :price ...

Voc pode definir o valor do placeholder pricechamando o mtodo setParameter():


->setParameter(price, 19.99)

Usar parmetros em vez de colocar os valores diretamente no texto da consulta feito para prevenir ataques
de SQL injection e deve ser feito sempre. Se voc estiver usando mltiplos parmetros, voc pode definir seus
valores de uma vez s usando o mtodo setParameters():
->setParameters(array(
price => 19.99,
name => Foo,
))

Usando o Doctrines Query Builder

Em vez de escrever diretamente suas consultas, voc pode alternativamente usar o QueryBuilder do Doctrine para
fazer o mesmo servio usando uma bela interface orientada a objetos. Se voc utilizar uma IDE, pode tambm se
beneficiar do auto-complete medida que voc digita o nome dos mtodos. A partir de um controller:
$repository = $this->getDoctrine()
->getRepository(AcmeStoreBundle:Product);
$query = $repository->createQueryBuilder(p)
->where(p.price > :price)

104

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

->setParameter(price, 19.99)
->orderBy(p.price, ASC)
->getQuery();
$products = $query->getResult();

O objeto QueryBuilder contm todos os mtodos necessrios para criar sua consulta. Ao chamar o mtodo
getQuery(), o query builder retorna um objeto Query normal, que o mesmo objeto que
voc criou diretamente na seo anterior.
Para mais informaes, consulte a documentao do Query Builder do Doctrine.
Classes Repositrio Personalizadas

Nas sees anteriores, voc comeou a construir e usar consultas mais complexas de dentro de um controller. De modo
a isolar, testar e reutilizar essas consultas, uma boa ideia criar uma classe repositrio personalizada para sua entidade
e adicionar mtodos com sua lgica de consultas l dentro.
Para fazer isso, adicione o nome da classe repositrio na sua definio de mapeamento.
Annotations
// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="Acme\StoreBundle\Repository\ProductRepository")
*/
class Product
{
//...
}

YAML
# src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml
Acme\StoreBundle\Entity\Product:
type: entity
repositoryClass: Acme\StoreBundle\Repository\ProductRepository
# ...

XML
<!-- src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.xml -->
<!-- ... -->
<doctrine-mapping>
<entity name="Acme\StoreBundle\Entity\Product"
repository-class="Acme\StoreBundle\Repository\ProductRepository">
<!-- ... -->
</entity>
</doctrine-mapping>

O Doctrine pode gerar para voc a classe repositrio usando o mesmo comando utilizado anteriormente para criar os
mtodos getters e setters que estavam faltando:

2.1. Livro

105

Symfony Docs pt-BR Documentation, Verso 2.4

php app/console doctrine:generate:entities Acme

Em seguida, adicione um novo mtodo - findAllOrderedByName() - para sua recm-gerada classe repositrio.
Esse mtodo ir buscar por todas as entidades Product, ordenadas alfabeticamente.
// src/Acme/StoreBundle/Repository/ProductRepository.php
namespace Acme\StoreBundle\Repository;
use Doctrine\ORM\EntityRepository;
class ProductRepository extends EntityRepository
{
public function findAllOrderedByName()
{
return $this->getEntityManager()
->createQuery(SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC)
->getResult();
}
}

Dica: O entity manager pode ser acessado via $this->getEntityManager() de dentro do repositrio.
Voc pode usar esse novo mtodo da mesma forma que os mtodos padres find do repositrio:
$em = $this->getDoctrine()->getManager();
$products = $em->getRepository(AcmeStoreBundle:Product)
->findAllOrderedByName();

Nota: Quando estiver usando uma classe repositrio personalizada, voc continua tendo acesso aos mtodos padres
finder com find() e findAll().

Relacionamentos/Associaes de Entidades
Suponha que todos os produtos na sua aplicao pertenam exatamente a uma categoria. Nesse caso, voc precisa
de um objeto Category e de uma forma de relacionar um objeto Produto com um objeto Category. Comece
criando uma entidade Category. Como voc sabe que ir eventualmente precisar de fazer a persistncia da classe
atravs do Doctrine, voc pode deix-lo criar a classe por voc.

php app/console doctrine:generate:entity --entity="AcmeStoreBundle:Category" --fields="name:string(25

Esse comando gera a entidade Category para voc, com um campo id, um campo name e as funes getters e
setters relacionadas.
Metadado para Mapeamento de Relacionamentos

Para relacionar as entidades Category e Product, comece criando a propriedade products na classe
Category:
// src/Acme/StoreBundle/Entity/Category.php
// ...
use Doctrine\Common\Collections\ArrayCollection;
class Category
{

106

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

// ...
/**
* @ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
protected $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
}

Primeiro, como o objeto Category ir se relacionar a vrios objetos Product, uma propriedade array
products adicionada para guardar esses objetos Product. Novamente, isso no feito porque o Doctrine
precisa dele, mas na verdade porque faz sentido dentro da aplicao guardar um array de objetos Product.
Nota:
O cdigo no mtodo __construct() importante porque o Doctrine requer que a propriedade
$productsseja um objeto ArrayCollection. Esse objeto se parece e age quase exatamente como
um array, mas tem mais um pouco de flexibilidade embutida. Se isso te deixa desconfortvel, no se preocupe. Apenas
imagine que ele um array e voc estar em boas mos.
Em seguida, como cada classe Product pode se relacionar exatamente com um objeto Category, voc ir querer
adicionar uma propriedade $category na classe Product:
// src/Acme/StoreBundle/Entity/Product.php
// ...
class Product
{
// ...
/**
* @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* @ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
}

Finalmente, agora que voc adicionou um nova propriedade tanto na classe Category quanto na Product, diga ao
Doctrine para gerar os mtodos getters e setters que esto faltando para voc:
php app/console doctrine:generate:entities Acme

Ignore o metadado do Doctrine por um instante. Agora voc tem duas classes - Category e Product com um relacionamento natural um-para-muitos. A classe categoria contm um array de objetos Product e o objeto Product
pode conter um objeto Category. Em outras palavras - voc construiu suas classes de um jeito que faz sentido para
as suas necessidades. O fato de que os dados precisam ser persistidos no banco sempre secundrio.
Agora, olhe o metadado acima da propriedade $category na classe Product. A informao aqui diz para o
Doctrine que a classe relacionada a Category e que ela deve guardar o id do registro categoria em um campo
category_id que fica na tabela product. Em outras palavras, o objeto Category ser guardado na propriedade
$category, mas nos bastidores, o Doctrine ir persistir esse relacionamento guardando o valor do id da categoria
na coluna category_id da tabela product.

2.1. Livro

107

Symfony Docs pt-BR Documentation, Verso 2.4

O metadado acima da propriedade $products do objeto Category menos importante, e simplesmente diz ao
Doctrine para olhar a propriedade Product.category para descobrir como o relacionamento mapeado.
Antes de continuar, tenha certeza de dizer ao Doctrine para adicionar uma nova tabela category, alm de uma
coluna product.category_id e uma nova chave estrangeira:
php app/console doctrine:schema:update --force

Nota: Esse comando deve ser usado apenas durante o desenvolvimento. Para um mtodo mais robusto de atualizao
sistemtica em um banco de dados de produo, leia sobre as Doctrine migrations.

Salvando as Entidades Relacionadas

Agora o momento de ver o cdigo em ao. Imagine que voc est dentro de um controller:

108

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

// ...
use Acme\StoreBundle\Entity\Category;
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...
class DefaultController extends Controller
{
public function createProductAction()
{
$category = new Category();
$category->setName(Main Products);
$product = new Product();
$product->setName(Foo);
$product->setPrice(19.99);
// relaciona a categoria com esse produto
$product->setCategory($category);
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->persist($product);
$em->flush();
return new Response(
Created product id: .$product->getId(). and category id: .$category->getId()
);
}
}

Agora, um registro nico adicionado para ambas tabelas category e product.


A coluna
product.category_id para o novo produto definida como o que for definido como id na nova categoria.
O Doctrine gerencia a persistncia desse relacionamento para voc.
Retornando Objetos Relacionados

Quando voc precisa pegar objetos associados, seu fluxo de trabalho parecido com o que foi feito anteriormente.
Primeiro, consulte um objeto $product e ento acesse seu o objeto Category relacionado:
public function showAction($id)
{
$product = $this->getDoctrine()
->getRepository(AcmeStoreBundle:Product)
->find($id);
$categoryName = $product->getCategory()->getName();
// ...
}

Nesse exemplo, voc primeiro busca por um objeto Product baseado no id do produto. Isso gera uma consulta
apenas para os dados do produto e faz um hydrate do objeto $product com esses dados. Em seguida, quando voc
chamar $product->getCategory()->getName(), o Doctrine silenciosamente faz uma segunda consulta para
buscar a Category que est relacionada com esse Product. Ele prepara o objeto $category e o retorna para
voc.

2.1. Livro

109

Symfony Docs pt-BR Documentation, Verso 2.4

O que importante o fato de que voc tem acesso fcil as categorias relacionadas com os produtos, mas os dados da
categoria no so realmente retornados at que voc pea pela categoria (i.e. sofre lazy load).
Voc tambm pode buscar na outra direo:
public function showProductAction($id)
{
$category = $this->getDoctrine()
->getRepository(AcmeStoreBundle:Category)
->find($id);
$products = $category->getProducts();
// ...
}

Nesse caso, ocorre a mesma coisa: primeiro voc busca por um nico objeto Category, e ento o Doctrine faz
uma segunda busca para retornar os objetos Product relacionados, mas apenas se voc pedir por eles (i.e. quando
voc chama ->getProducts()). A varivel $products uma array de todos os objetos Product que esto
relacionados com um dado objeto Category por meio do valor de seu campo category_id.

110

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Relacionamentos e Classes Proxy


O lazy loading possvel porque, quando necessrio, o Doctrine retorna um objeto proxy no lugar do objeto
real. Olhe novamente o exemplo acima:
$product = $this->getDoctrine()
->getRepository(AcmeStoreBundle:Product)
->find($id);
$category = $product->getCategory();
// prints "Proxies\AcmeStoreBundleEntityCategoryProxy"
echo get_class($category);

Esse objeto proxy estende o verdadeiro objeto Category, e se parece e age exatamente como ele.
A diferena que, por usar um objeto proxy, o Doctrine pode retardar a busca pelos dados reais da Categoryat que voc realmente precise daqueles dados (e.g. at que
voc chame $category->getName()).
As classes proxy so criadas pelo Doctrine e armazenadas no diretrio cache. E apesar de que voc provavelmente nunca ir notar que o seu objeto $category na verdade um objeto proxy, importante manter isso em
mente.
Na prxima seo, quando voc retorna os dados do produto e categoria todos de uma vez (via um join), o
Doctrine ir retornar o verdadeiro objeto Category, uma vez que nada precisa ser carregado de modo lazy
load.

Juntando Registros Relacionados

Nos exemplos acima, duas consultas foram feitas - uma para o objeto original (e.g uma Category) e uma para os
objetos relacionados (e.g. os objetos Product).
Dica: Lembre que voc pode visualizar todas as consultas feitas durante uma requisio pela web debug toolbar.
claro, se voc souber antecipadamente que vai precisar acessar ambos os objetos, voc pode evitar a segunda consulta
atravs da emisso de um join na consulta original. Inclua o mtodo seguinte na classe ProductRepository:
// src/Acme/StoreBundle/Repository/ProductRepository.php
public function findOneByIdJoinedToCategory($id)
{
$query = $this->getEntityManager()
->createQuery(
SELECT p, c FROM AcmeStoreBundle:Product p
JOIN p.category c
WHERE p.id = :id
)->setParameter(id, $id);
try {
return $query->getSingleResult();
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}

Agora, voc pode usar esse mtodo no seu controller para buscar um objeto Product e sua Category relacionada
com apenas um consulta:

2.1. Livro

111

Symfony Docs pt-BR Documentation, Verso 2.4

public function showAction($id)


{
$product = $this->getDoctrine()
->getRepository(AcmeStoreBundle:Product)
->findOneByIdJoinedToCategory($id);
$category = $product->getCategory();
// ...
}

Mais Informaes sobre Associaes

Essa seo foi uma introduo para um tipo comum de relacionamento de entidades, o um-para-muitos. Para
detalhes mais avanados e exemplos de como usar outros tipos de relacionamentos (i.e. um-para-um,
muitos-para-muitos), verifique a Documentao sobre Mapeamento e Associaes do Doctrine.
Nota: Se voc estiver usando annotations, ir precisar prefixar todas elas com ORM\ (e.g ORM\OneToMany),
o que no est descrito na documentao do Doctrine. Voc tambm precisar incluir a instruo use
Doctrine\ORM\Mapping as ORM;, que faz a importao do prefixo ORM das annotations.

Configurao
O Doctrine altamente configurvel, embora voc provavelmente no vai precisar se preocupar com a maioria de suas
opes. Para saber mais sobre a configurao do Doctrine, veja a seo Doctrine do reference manual.
Lifecycle Callbacks
s vezes, voc precisa executar uma ao justamente antes ou depois de uma entidade ser inserida, atualizada ou
apagada. Esses tipos de aes so conhecidas como lifecycle callbacks, pois elas so mtodos callbacks que voc
precisa executar durante diferentes estgios do ciclo de vida de uma entidade (i.e. a entidade foi inserida, atualizada,
apagada, etc.).
Se voc estiver usando annotations para seus metadados, comece habilitando esses callbacks. Isso no necessrio se
estiver utilizando YAML ou XML para seus mapeamentos:
/**
* @ORM\Entity()
* @ORM\HasLifecycleCallbacks()
*/
class Product
{
// ...
}

Agora, voc pode dizer ao Doctrine para executar um mtodo em cada um dos eventos de ciclo de vida disponveis.
Por exemplo, suponha que voc queira definir uma coluna created do tipo data para a data atual, apenas quando for
a primeira persistncia da entidade (i.e. insero):
Annotations
/**
* @ORM\prePersist
*/

112

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

public function setCreatedValue()


{
$this->created = new \DateTime();
}

YAML
# src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml
Acme\StoreBundle\Entity\Product:
type: entity
# ...
lifecycleCallbacks:
prePersist: [ setCreatedValue ]

XML
<!-- src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.xml -->
<!-- ... -->
<doctrine-mapping>
<entity name="Acme\StoreBundle\Entity\Product">
<!-- ... -->
<lifecycle-callbacks>
<lifecycle-callback type="prePersist" method="setCreatedValue" />
</lifecycle-callbacks>
</entity>
</doctrine-mapping>

Nota: O exemplo acima presume que voc tenha criado e mapeado uma propriedade created (que no foi mostrada
aqui).
Agora, logo no momento anterior a entidade ser persistida pela primeira vez, o Doctrine ir automaticamente chamar
esse mtodo e o campo created ser preenchido com a data atual.
Isso pode ser repetido para qualquer um dos outros eventos de ciclo de vida, que incluem:
preRemove
postRemove
prePersist
postPersist
preUpdate
postUpdate
postLoad
loadClassMetadata
Para mais informaes sobre o que esses eventos significam e sobre os lifecycle callbacks em geral, veja a Documentao sobre Lifecycle Events do Doctrine.

2.1. Livro

113

Symfony Docs pt-BR Documentation, Verso 2.4

Lifecycle Callbacks e Event Listeners


Observe que o mtodo setCreatedValue() no recebe nenhum argumento. Esse o comportamento usual
dos lifecycle callbacks e intencional: eles devem ser mtodos simples que esto preocupados com as transformaes internas dos dados na entidade (e.g. preencher um campo created/updated ou gerar um valor slug).
Se voc precisar fazer algo mais pesado - como rotinas de log ou mandar um e-mail - voc deve registrar uma
classe externa como um event listener ou subscriber e dar para ele acesso aos recursos que precisar. Para mais
informaes, veja Como Registrar Ouvintes e Assinantes de Eventos.

Extenses do Doctrine: Timestampable, Sluggable, etc.


O Doctrine bastante flexvel, e um grande nmero de extenses de terceiros est disponvel o que permirte que voc
execute facilmente tarefas repetitivas e comuns nas suas entidades. Isso inclui coisas como Sluggable, Timestampable,
Loggable, Translatable e Tree.
Para mais informaes sobre como encontrar e usar essas extenses, veja o artigo no cookbook sobre using common
Doctrine extensions.
Referncia dos Tipos de Campos do Doctrine
O Doctrine j vem com um grande nmero de tipos de campo disponvel. Cada um deles mapeia um tipo de dados do
PHP para um tipo de coluna especfico em qualquer banco de dados que voc estiver utilizando. Os seguintes tipos
so suportados no Doctrine:
Strings
string (usado para strings curtas)
text (usado para strings longas)
Nmeros
integer
smallint
bigint
decimal
float
Datas e Horrios (usa um objeto DateTime para esses campos no PHP)
date
time
datetime
Outros Tipos
boolean
object (serializado e armazenado em um campo CLOB)
array (serializado e guardado em um campo CLOB)
Para mais informaes, veja a Documentao sobre Tipos de Mapeamento do Doctrine.

114

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Opes de Campo

Cada campo pode ter um conjunto de opes aplicado sobre ele. As opes disponveis incluem type (o padro
string), name, lenght, unique e nullable. Olhe alguns exemplos de annotations:
/**
* Um campo string com tamanho 255 que no pode ser nulo
* (segue os valores padres para "type", "length" e *nullable* options)
*
* @ORM\Column()
*/
protected $name;
/**
* Um campo string com tamanho 150 persistido na coluna "email_adress"
* e com um ndice nico
*
* @ORM\Column(name="email_address", unique="true", length="150")
*/
protected $email;

Nota: Existem mais algumas opes que no esto listadas aqui. Para mais detalhes, veja a Documentao sobre
Mapeamento de Propriedades do Doctrine.

Comandos de Console
A integrao com o Doctrine2 ORM fornece vrios comandos de console no namespace doctrine. Para ver a lista
de comandos, voc pode executar o console sem nenhum argumento:
php app/console

A lista dos comandos disponveis ser mostrada, muitos dos quais comeam com o prefixo doctrine. Voc pode encontrar mais informaes sobre qualquer um desses comandos (e qualquer comando do Symfony) rodando o comando
help. Por exemplo, para pegar detalhes sobre o comando doctrine:database:create, execute:
php app/console help doctrine:database:create

Alguns comandos interessantes e notveis incluem:


doctrine:ensure-production-settings - verifica se o ambiente atual est configurado de forma
eficiente para produo. Deve ser sempre executado no ambiente prod:
php app/console doctrine:ensure-production-settings --env=prod

doctrine:mapping:import - permite ao Doctrine fazer introspeco de um banco de dados existente e


criar a informao de mapeamento. Para mais informaes veja Como gerar Entidades de uma base de dados
existente.
doctrine:mapping:info - diz para voc todas as entidades que o Doctrine tem conhecimento e se existe
ou no algum erro bsico com o mapeamento.
doctrine:query:dql and doctrine:query:sql - permite que voc execute consultas DQL ou SQL
diretamente na linha de comando.
Nota:
Para poder carregar data fixtures para seu banco de dados, voc precisa ter o bundle DoctrineFixturesBundle instalado.
Para aprender como fazer isso, leia a entrada
/bundles/DoctrineFixturesBundle/index da documentao.

2.1. Livro

115

Symfony Docs pt-BR Documentation, Verso 2.4

Sumrio
Com o Doctrine, voc pode se focar nos seus objetos e como eles podem ser teis na sua aplicao, deixando a
preocupao com a persistncia de banco de dados em segundo plano. Isso porque o Doctrine permite que voc use
qualquer objeto PHP para guardar seus dados e se baseia nos metadados de mapeamento para mapear os dados de um
objetos para um tabela especfica no banco.
E apesar do Doctrine girar em torno de um conceito simples, ele incrivelmente poderoso, permitindo que voc crie
consultas complexas e faa subscrio em eventos que permitem a voc executar aes diferentes medida que os
objetos vo passando pelo seu ciclo de vida de persistncia.
Para mais informaes sobre o Doctrine, veja a seo Doctrine do cookbook, que inclui os seguintes artigos:
/bundles/DoctrineFixturesBundle/index
Como usar as extens?es do Doctrine: Timestampable, Sluggable, Translatable, etc.

2.1.8 Testes
Sempre que voc escrever uma nova linha de cdigo, voc tambm adiciona potenciais novos bugs. Para construir
aplicaes melhores e mais confiveis, voc deve testar seu cdigo usando testes funcionais e unitrios.
O Framework de testes PHPUnit
O Symfony2 se integra com uma biblioteca independente - chamada PHPUnit - para dar a voc um rico framework
de testes. Esse capitulo no vai abranger o PHPUnit propriamente dito, mas ele tem a sua excelente documentao
documentation.
Nota: O Symfony2 funciona com o PHPUnit 3.5.11 ou posterior, embora a verso 3.6.4 necessria para testar o
cdigo do ncleo do Symfony.
Cada teste - quer seja teste unitrio ou teste funcional - uma classe PHP que deve residir no sub-diretrio Tests/ de
seus bundles. Se voc seguir essa regra, voc pode executar todos os testes da sua aplicao com o seguinte comando:
# espefifique o diretrio de configurao na linha de comando
$ phpunit -c app/

A opo -c diz para o PHPUnit procurar no diretrio app/ por um arquivo de configurao. Se voc est curioso
sobre as opes do PHPUnit, d uma olhada no arquivo app/phpunit.xml.dist.
Dica: O Code coverage pode ser gerado com a opo --coverage-html.

Testes Unitrios
Um teste unitrio geralmente um teste de uma classe PHP especifica. Se voc quer testar o comportamento global
da sua aplicao, veja a seo sobre Testes Funcionais.
Escrever testes unitrios no Symfony2 no nada diferente do que escrever um teste unitrio padro do PHPUnit.
Vamos supor que, por exemplo, voc tem uma classe incrivelmente simples chamada Calculator no diretrio
Utility/ do seu bundle:

116

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

// src/Acme/DemoBundle/Utility/Calculator.php
namespace Acme\DemoBundle\Utility;
class Calculator
{
public function add($a, $b)
{
return $a + $b;
}
}

Para testar isso, crie um arquivo chamado CalculatorTest no diretrio Tests/Utility do seu bundle:
// src/Acme/DemoBundle/Tests/Utility/CalculatorTest.php
namespace Acme\DemoBundle\Tests\Utility;
use Acme\DemoBundle\Utility\Calculator;
class CalculatorTest extends \PHPUnit_Framework_TestCase
{
public function testAdd()
{
$calc = new Calculator();
$result = $calc->add(30, 12);
// assert that our calculator added the numbers correctly!
$this->assertEquals(42, $result);
}
}

Nota: Por conveno, o sub-diretrio Tests/ deve replicar o diretrio do seu bundle. Ento, se voc estiver testando
uma classe no diretrio Utility/ do seu bundle, coloque o teste no diretrio Tests/Utility/.
Assim como na rua aplicao verdadeira - o autoloading automaticamente habilitado via o arquivo
bootstrap.php.cache (como configurado por padro no arquivo phpunit.xml.dist).
Executar os testes para um determinado arquivo ou diretrio tambm muito fcil:
# executa todos os testes no diretrio Utility
$ phpunit -c app src/Acme/DemoBundle/Tests/Utility/
# executa os testes para a classe Article
$ phpunit -c app src/Acme/DemoBundle/Tests/Utility/CalculatorTest.php
# executa todos os testes para todo o Bundle
$ phpunit -c app src/Acme/DemoBundle/

Testes Funcionais
Testes funcionais verificam a integrao das diferentes camadas de uma aplicao (do roteamento as views). Eles no
so diferentes dos testes unitrios levando em considerao o PHPUnit, mas eles tem um fluxo bem especifico:
Fazer uma requisio;
Testar a resposta;
Clicar em um link ou submeter um formulrio;

2.1. Livro

117

Symfony Docs pt-BR Documentation, Verso 2.4

Testar a resposta;
Repetir a operao.
Seu Primeiro Teste Funcional

Testes funcionais so arquivos PHP simples que esto tipicamente no diretrio Tests/Controller do seu bundle. Se voc quer testar as pginas controladas pela sua classe DemoController, inicie criando um novo arquivo
DemoControllerTest.php que extende a classe especial WebTestCase.
Por exemplo, o Symfony2 Standard Edition fornece um teste funcional simples para o DemoController (DemoControllerTest) descrito assim:
// src/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php
namespace Acme\DemoBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DemoControllerTest extends WebTestCase
{
public function testIndex()
{
$client = static::createClient();
$crawler = $client->request(GET, /demo/hello/Fabien);
$this->assertTrue($crawler->filter(html:contains("Hello Fabien"))->count() > 0);
}
}

Dica: Para executar seus testes funcionais, a classe WebTestCase class inicializa o kernel da sua aplicao. Na maioria dos casos, isso acontece automaticamente. Entretando, se o seu kernel est em um diretrio diferente do padro,
voc vai precisar modificar seu arquivo phpunit.xml.dist para alterar a varivel de ambiente KERNEL_DIR
para o diretrio do seu kernel:
<phpunit
<!-- ... -->
<php>
<server name="KERNEL_DIR" value="/path/to/your/app/" />
</php>
<!-- ... -->
</phpunit>

O mtodo createClient() retorna um cliente, que como um navegador que voc vai usar para navegar no seu
site:
$crawler = $client->request(GET, /demo/hello/Fabien);

O mtodo request() (veja mais sobre o mtodo request) retorna um objeto Crawler que pode ser usado para
selecionar um elemento na Response, clicar em links, e submeter formulrios.
Dica: O Crawler s funciona se a resposta um documento XML ou HTML. Para pegar a resposta bruta, use
$client->getResponse()->getContent().
Clique em um link primeiramente selecionando-o com o Crawler usando uma expresso XPath ou um seletor CSS,
ento use o Client para clicar nele. Por exemplo, o segunte cdigo acha todos os links com o texto Greet, ento
seleciona o segundo, e ento clica nele:
118

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

$link = $crawler->filter(a:contains("Greet"))->eq(1)->link();
$crawler = $client->click($link);

Submeter um formulrio muito parecido, selecione um boto do formulrio, opcionalmente sobrescreva alguns
valores do formulrio, ento submeta-o:
$form = $crawler->selectButton(submit)->form();
// pega alguns valores
$form[name] = Lucas;
$form[form_name[subject]] = Hey there!;
// submete o formulrio
$crawler = $client->submit($form);

Dica: O formulrio tambm pode manipular uploads e tem mtodos para preencher diferentes tipos de campos (ex.
select() e tick()). Para mais detalhers, veja a seo Forms_ abaixo.
Agora que voc pode facilmente navegar pela sua aplicao, use as afirmaes para testar que ela realmente faz o que
voc espera que ela faa. Use o Crawler para fazer afirmaes no DOM:
// Afirma que a resposta casa com um seletor informado
$this->assertTrue($crawler->filter(h1)->count() > 0);

Ou, teste contra o contedo do Response diretamente se voc s quer afirmar que o conteudo contm algum texto ou
se o Response no um documento XML/HTML:
$this->assertRegExp(/Hello Fabien/, $client->getResponse()->getContent());

Mais sobre o mtodo request():


A assinatura completa do mtodo request() :
request(
$method,
$uri,
array $parameters = array(),
array $files = array(),
array $server = array(),
$content = null,
$changeHistory = true
)

O array server so valores brutos que voc espera encontrar normalmente na varivel superglobal do PHP
$_SERVER. Por exemplo, para setar os cabealhos HTTP Content-Type e Referer, voc passar o seguinte:
$client->request(
GET,
/demo/hello/Fabien,
array(),
array(),
array(
CONTENT_TYPE => application/json,
HTTP_REFERER => /foo/bar,
)
);

2.1. Livro

119

Symfony Docs pt-BR Documentation, Verso 2.4

Trabalhando com o Teste Client


O teste Client simula um cliente HTTP como um navegador e faz requisies na sua aplicao Symfony2:
$crawler = $client->request(GET, /hello/Fabien);

O mtodo request() pega o mtodo HTTP e a URL como argumentos e retorna uma instancia de Crawler.
Utilize o Crawler para encontrar elementos DOM no Response. Esses elementos podem ento ser usados para clicar
em links e submeter formulrios:
$link = $crawler->selectLink(Go elsewhere...)->link();
$crawler = $client->click($link);
$form = $crawler->selectButton(validate)->form();
$crawler = $client->submit($form, array(name => Fabien));

Os mtodos click() e submit() retornam um objeto Crawler. Esses mtodos so a melhor maneira de navegar
na sua aplicao por tomarem conta de vrias coisas para voc, como detectar o mtodo HTTP de um formulrio e dar
para voc uma tima API para upload de arquivos.
Dica: Voc vai aprende rmais sobre os objetos Link e Form na seo Crawler abaixo.
O mtodo request pode tambm ser usado para simular submisses de formulrios diretamente ou fazer requisies
mais complexas:
// Submeter diretamente um formurio (mas utilizando o Crawler mais fcil!)
$client->request(POST, /submit, array(name => Fabien));
// Submisso de formulrio com um upload de arquivo
use Symfony\Component\HttpFoundation\File\UploadedFile;
$photo = new UploadedFile(
/path/to/photo.jpg,
photo.jpg,
image/jpeg,
123
);
// ou
$photo = array(
tmp_name => /path/to/photo.jpg,
name => photo.jpg,
type => image/jpeg,
size => 123,
error => UPLOAD_ERR_OK
);
$client->request(
POST,
/submit,
array(name => Fabien),
array(photo => $photo)
);
// Executa uma requisio de DELETE e passa os cabealhos HTTP
$client->request(
DELETE,
/post/12,
array(),
array(),

120

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

array(PHP_AUTH_USER => username, PHP_AUTH_PW => pa$$word)


);

Por ltimo mas no menos importante, voc pode forar cara requisio para ser executada em seu proprio processo
PHP para evitar qualquer efeito colateral quando estiver trabalhando com vrios clientes no mesmo script:
$client->insulate();

Navegando

O Cliente suporta muitas operao que podem ser realizadas em um navegador real:
$client->back();
$client->forward();
$client->reload();
// Limpa todos os cookies e histrico
$client->restart();

Acessando Objetos Internos

Se voc usa o cliente para testar sua aplicao, voc pode querer acessar os objetos internos do cliente:
$history
= $client->getHistory();
$cookieJar = $client->getCookieJar();

Voc tambm pode pegar os objetos relacionados a requisio mais recente:


$request = $client->getRequest();
$response = $client->getResponse();
$crawler = $client->getCrawler();

Se as suas requisio no so isoladas, voc pode tambm acessar o Container e o Kernel:


$container = $client->getContainer();
$kernel
= $client->getKernel();

Acessando o Container

altamente recomendado que um teste funcional teste somente o Response. Mas em circunstancias extremamente
raras, voc pode querer acessar algum objeto interno para escrever afirmaes. Nestes casos, voc pode acessar o
dependency injection container:
$container = $client->getContainer();

Esteja ciente que isso no funciona se voc isolar o cliente ou se voc usar uma camada HTTP. Para ver a lista de
servios disponves na sua aplicao, utilize a task container:debug.
Dica: Se a informao que voc precisa verificar est disponvel no profiler, uso-o ento

2.1. Livro

121

Symfony Docs pt-BR Documentation, Verso 2.4

Acessando dados do Profiler

En cada requisio, o profiler do Symfony coleta e guarda uma grande quantidade de dados sobre a manipulao
interna de cada request. Por exemplo, o profiler pode ser usado para verificar se uma determinada pgina executa
menos consultas no banco quando estiver carregando.
Para acessar o Profiler da ltima requisio, fao o seguinte:
$profile = $client->getProfile();

Para detalhes especificos de como usar o


/cookbook/testing/profiling do cookbook.

profiler

dentro

de

um

teste,

seja

artigo

Redirecionamento

Quando uma requisio retornar uma redirecionamento como resposta, o cliente automaticamente segue o redirecionamento. Se voc quer examinar o Response antes do redirecionamento use o mtodo followRedirects():
$client->followRedirects(false);

Quando o cliente no segue os redirecionamentos, voc pode forar o redirecionamento com o mtodo
followRedirect():
$crawler = $client->followRedirect();

O Crawler
Uma instancia do Crawler retornada cada vez que voc faz uma requisio com o Client. Ele permite que voc
examinar documentos HTML, selecionar ns, encontrar links e formulrios.
Examinando

Como o jQuery, o Crawler tem metodos para examinar o DOM de um documento HTML/XML. Por exemplo, isso
encontra todos os elementos input[type=submit], seleciona o ltimo da pgina, e ento seleciona o elemento
imediatamente acima dele:
$newCrawler = $crawler->filter(input[type=submit])
->last()
->parents()
->first()
;

Muitos outros mtodos tambm esto disponveis:

122

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Metodos
filter(h1.title)
filterXpath(h1)
eq(1)
first()
last()
siblings()
nextAll()
previousAll()
parents()
children()
reduce($lambda)

Descrio
Ns que casam com o seletor CSS
Ns que casam com a expresso XPath
N para a posio especifica
Primeiro n
ltimo n
Irmos
Todos os irmos posteriores
Todos os irmos anteriores
Ns de um nivel superior
Filhos
Ns que a funo no retorne false

Como cada um desses mtodos retorna uma nova instncia de Crawler, voc pode restringir os ns selecionados
encadeando a chamada de mtodos:
$crawler
->filter(h1)
->reduce(function ($node, $i)
{
if (!$node->getAttribute(class)) {
return false;
}
})
->first();

Dica: Utilize a funo count() para pegar o nmero de ns armazenados no Crawler: count($crawler)

Extraindo Informaes

O Crawler pode extrair informaes dos ns:


// Retornar o valor do atributo para o primeiro n
$crawler->attr(class);
// Retorna o valor do n para o primeiro n
$crawler->text();
// Extrai um array de atributos para todos os ns (_text retorna o valor do n)
// retorna um array para cara elemento no crawler, cara um com o valor e href
$info = $crawler->extract(array(_text, href));
// Executa a lambda para cada n e retorna um array de resultados
$data = $crawler->each(function ($node, $i)
{
return $node->attr(href);
});

Links

Para selecionar links, voc pode usar os mtodos acima ou o conveniente atalho selectLink():
$crawler->selectLink(Click here);

2.1. Livro

123

Symfony Docs pt-BR Documentation, Verso 2.4

Isso seleciona todos os links que contm o texto, ou imagens que o atributo alt contm o determinado texto. Como
outros mtodos de filtragem, esse retorna outro objeto Crawler.
Uma vez selecionado um link, voc pode ter acesso a um objeto especial Link, que tem mtodos especificos muito
ties para links (como getMethod() e getUri()). Para clicar no link, use o mtodo do Client click() e passe
um objeto do tipo Link:
$link = $crawler->selectLink(Click here)->link();
$client->click($link);

Formulrios

Assim como nos links, voc seleciona o form com o mtodo selectButton():
$buttonCrawlerNode = $crawler->selectButton(submit);

Nota: Note que selecionamos os botes do formulrio e no os forms, pois o form pode ter vrios botes; se voc
usar a API para examinar, tenha em mente que voc deve procurar por um boto.
O mtodo selectButton() pode selecionar tags button e submit tags input. Ele usa diversas partes diferentes
do boto para encontr-los:
O atributo value;
O atributo id ou alt para imagens;
O valor do atributo id ou name para tags button.
Uma vez que voc tenha o Crawler representanto um boto, chame o mtodo form() para pegar a instancia de Form
do form que envolve o n do boto:
$form = $buttonCrawlerNode->form();

Quando chamar o mtodo form(), voc pode tambm passar uma array com valores dos campos para sobreescrever
os valores padres:
$form = $buttonCrawlerNode->form(array(
name
=> Fabien,
my_form[subject] => Symfony rocks!,
));

E se voc quiser simular algum mtodo HTTP especifico para o form, passe-o como um segundo argumento:
$form = $crawler->form(array(), DELETE);

O Client pode submeter instancias de Form:


$client->submit($form);

Os valores dos campos tambm posem ser passsados como um segundo argumento do mtodo submit():
$client->submit($form, array(
name
=> Fabien,
my_form[subject] => Symfony rocks!,
));

Para situaes mais complexas, use a instancia de Form como um array para setar o valor de cada campo individualmente:

124

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

// Muda o valor do campo


$form[name] = Fabien;
$form[my_form[subject]] = Symfony rocks!;

Tambm existe uma API para manipular os valores do campo de acordo com o seu tipo:
// Seleciona um option ou um radio
$form[country]->select(France);
// Marca um checkbox
$form[like_symfony]->tick();
// Faz o upload de um arquivo
$form[photo]->upload(/path/to/lucas.jpg);

Dica: Voc pode pegar os valores que sero submetidos chamando o mtodo getValues() no objeto Form.
Os arquivos do upload esto disponiveis em um array separado retornado por getFiles(). Os mtodos
getPhpValues() e getPhpFiles() tambm retorna valores submetidos, mas no formato PHP (ele converte
as chaves para a notao de colchetes - ex. my_form[subject] - para PHP arrays).

Configurao de Testes
O Client usado pelos testes funcionais cria um Kernel que roda em um ambiente especial chamado test. Uma
vez que o Symfony carrega o app/config/config_test.yml no ambiente test, voc pode ajustar qualquer
configurao de sua aplicao especificamente para testes.
Por exemplo, por padro, o swiftmailer configurado para no enviar realmente os e-mails no ambiente test. Voc
pode ver isso na opo de configurao swiftmailer:
YAML
# app/config/config_test.yml
# ...
swiftmailer:
disable_delivery: true

XML
<!-- app/config/config_test.xml -->
<container>
<!-- ... -->
<swiftmailer:config disable-delivery="true" />
</container>

PHP
// app/config/config_test.php
// ...
$container->loadFromExtension(swiftmailer, array(
disable_delivery => true
));

Voc tambm pode usar um ambiente completamente diferente, ou sobrescrever o modo de debug (true) passando
cada um como uma opo para o mtodo createClient():

2.1. Livro

125

Symfony Docs pt-BR Documentation, Verso 2.4

$client = static::createClient(array(
environment => my_test_env,
debug
=> false,
));

Se a sua aplicao se comporta de acordo com alguns cabealhos HTTP, passe eles como o segundo argumento de
createClient():
$client = static::createClient(array(), array(
HTTP_HOST
=> en.example.com,
HTTP_USER_AGENT => MySuperBrowser/1.0,
));

Voc tambm pode sobrescrever cabealhos HTTP numa base por requisies:
$client->request(GET, /, array(), array(), array(
HTTP_HOST
=> en.example.com,
HTTP_USER_AGENT => MySuperBrowser/1.0,
));

Dica: O cliente de testes est disponvel como um servio no container no ambiente teste (ou em qualquer lugar
que a opo framework.test esteja habilitada). Isso significa que voc pode sobrescrever o servio inteiramente se voc
precisar.

Configurao do PHPUnit

Cada aplicao tem a sua prpria configurao do PHPUnit, armazenada no arquivo phpunit.xml.dist. Voc
pode editar o arquivo para mudar os valores padres ou criar um arquivo phpunit.xml para ajustar a configurao
para sua mquina local.
Dica: Armazene o arquivo phpunit.xml.dist no seu repositrio de cdigos e ignore o arquivo phpunit.xml.
Por padro, somente os testes armazenados nos bundles standard so rodados pelo comando phpunit (standard
sendo os testes nos diretrios src/*/Bundle/Tests ou src/*/Bundle/*Bundle/Tests) Mas voc pode
facilmente adicionar mais diretrios. Por exemplo, a seguinte configurao adiciona os testes de um bundle de terceiros
instalado:
<!-- hello/phpunit.xml.dist -->
<testsuites>
<testsuite name="Project Test Suite">
<directory>../src/*/*Bundle/Tests</directory>
<directory>../src/Acme/Bundle/*Bundle/Tests</directory>
</testsuite>
</testsuites>

Para incluir outros diretrios no code coverage, edite tambm a sesso <filter>:
<filter>
<whitelist>
<directory>../src</directory>
<exclude>
<directory>../src/*/*Bundle/Resources</directory>
<directory>../src/*/*Bundle/Tests</directory>
<directory>../src/Acme/Bundle/*Bundle/Resources</directory>
<directory>../src/Acme/Bundle/*Bundle/Tests</directory>
</exclude>

126

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

</whitelist>
</filter>

Aprenda mais no Cookbook


/cookbook/testing/http_authentication
/cookbook/testing/insulating_clients
/cookbook/testing/profiling

2.1.9 Validao
Validao uma tarefa muito comum em aplicaes web. Dado inserido em formulrio precisa ser validado. Dado
tambm precisa ser revalidado antes de ser escrito num banco de dados ou passado a um servio web.
Symfony2 vem acompanhado com um componente Validator que torna essa tarefa fcil e transparente. Esse componente baseado na especificao JSR303 Bean Validation_. O qu ? Uma especificao Java no PHP? Voc ouviu
corretamente, mas no to ruim quanto parece. Vamos olhar como isso pode ser usado no PHP.
As bases da validao
A melhor forma de entender validao v-la em ao. Para comear, suponha que voc criou um bom e velho objeto
PHP que voc precisa usar em algum lugar da sua aplicao:
// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;
class Author
{
public $name;
}

At agora, essa somente uma classe comum que serve para alguns propsitos dentro de sua aplicao. O objetivo
da validao avisar voc se um dado de um objeto vlido ou no. Para esse trabalho, voc ir configura uma lista
de regras (chamada constraints) em que o objeto deve seguir em ordem para ser validado. Essas regras podem ser
especificadas por um nmero de diferentes formatos (YAML, XML, annotations, ou PHP).
Por exemplo, para garantir que a propriedade $name no vazia, adicione o seguinte:
YAML
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
properties:
name:
- NotBlank: ~

Annotations
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**

2.1. Livro

127

Symfony Docs pt-BR Documentation, Verso 2.4

* @Assert\NotBlank()
*/
public $name;
}

XML

<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->


<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/sche
<class name="Acme\BlogBundle\Entity\Author">
<property name="name">
<constraint name="NotBlank" />
</property>
</class>
</constraint-mapping>

PHP
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
class Author
{
public $name;
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint(name, new NotBlank());
}
}

Dica:
Propriedades protected e private podem tambm ser validadas, bem como os mtodos
getter (veja validator-constraint-targets

Usando o servio validator

Prximo passo, para realmente validar um objetoAuthor, use o mtodo validate no servio validator (classe
Validator). A tarefa do validator fcil: ler as restries (i.e. regras) de uma classe e verificar se o dado no
objeto satisfaz ou no aquelas restries. Se a validao falhar, retorna um array de erros. Observe esse simples
exemplo de dentro do controller:
use Symfony\Component\HttpFoundation\Response;
use Acme\BlogBundle\Entity\Author;
// ...
public function indexAction()
{
$author = new Author();

128

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

// ... do something to the $author object


$validator = $this->get(validator);
$errors = $validator->validate($author);
if (count($errors) > 0) {
return new Response(print_r($errors, true));
} else {
return new Response(The author is valid! Yes!);
}
}

Se a propriedade $name vazia, voc ver a seguinte mensagem de erro:


Acme\BlogBundle\Author.name:
This value should not be blank

Se voc inserir um valor na propriedade name, aparecer a feliz mensagem de sucesso.


Dica: A maior parte do tempo, voc no ir interagir diretamente com o servio validator ou precisar se
preocupar sobre imprimir os erros. A maior parte do tempo, voc ir usar a validao indiretamente quando lidar com
dados enviados do formulrio. Para mais informaes, veja: ref:book-validation-forms.
Voc tambm poderia passar o conjunto de erros em um template.
if (count($errors) > 0) {
return $this->render(AcmeBlogBundle:Author:validate.html.twig, array(
errors => $errors,
));
} else {
// ...
}

Dentro do template, voc pode gerar a lista de erros exatamente necessria:


Twig
{# src/Acme/BlogBundle/Resources/views/Author/validate.html.twig #}
<h3>The author has the following errors</h3>
<ul>
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>

PHP
<!-- src/Acme/BlogBundle/Resources/views/Author/validate.html.php -->
<h3>The author has the following errors</h3>
<ul>
<?php foreach ($errors as $error): ?>
<li><?php echo $error->getMessage() ?></li>
<?php endforeach; ?>
</ul>

Nota:
Cada erro de validao (chamado de constraint violation), representado por um objeto
ConstraintViolation .

2.1. Livro

129

Symfony Docs pt-BR Documentation, Verso 2.4

Validao e formulrios

O servio validator pode ser usado a qualquer momento para validar qualquer objeto. Na realidade, entretanto,
voc ir trabalhar frequentemente com o validator indiretamente enquanto trabalhar com formulrio. A biblioteca
Symfonys form usa o servio validator internamente para validar o objeto oculto aps os valores terem sido
enviados e fixados. As violaes de restrio no objeto so convertidas em objetos FieldError que podem ser
facilmente exibidos com seu formulrio. O tipico fluxo de envio do formulrio parece o seguinte dentro do controller:
use Acme\BlogBundle\Entity\Author;
use Acme\BlogBundle\Form\AuthorType;
use Symfony\Component\HttpFoundation\Request;
// ...
public function updateAction(Request $request)
{
$author = new Acme\BlogBundle\Entity\Author();
$form = $this->createForm(new AuthorType(), $author);
if ($request->isMethod(POST)) {
$form->bind($request);
if ($form->isValid()) {
// the validation passed, do something with the $author object
$this->redirect($this->generateUrl(...));
}
}
return $this->render(BlogBundle:Author:form.html.twig, array(
form => $form->createView(),
));
}

Nota: Esse exemplo usa uma classe de formulrios AuthorType , que no mostrada aqui.
Para mais informaes, veja: doc:Forms</book/forms> chapter.
Configurao
O validador do Symfony2 abilitado por padro, mas voc deve abilitar explicitamente anotaes se voc usar o
mtodo de anotao para especificar suas restries:
YAML
# app/config/config.yml
framework:
validation: { enable_annotations: true }

XML
<!-- app/config/config.xml -->
<framework:config>
<framework:validation enable_annotations="true" />
</framework:config>

130

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

PHP
// app/config/config.php
$container->loadFromExtension(framework, array(validation => array(
enable_annotations => true,
)));

Restries
O validator designado para validar objtos perante restries (i.e. regras). Em ordem para validar um objeto,
simplesmente mapeie uma ou mais restries para aquela classe e ento passe para o servio validator.
Por trs dos bastidores, uma restrio simplesmente um objeto PHP que faz uma sentena afirmativa. Na vida real,
uma restrio poderia ser: O bolo no deve queimar. No Symfony2, restries so similares: elas so afirmaes
que uma condio verdadeira. Dado um valor, a restriao ir indicar a voc se o valor adere ou no s regras da
restrio.
Resties Suportadas

Symfony2 engloba um grande nmero de restries mais frequentemente usadas:


Voc tambm pode criar sua prpria restrio personalizada. Esse tpico coberto no artigo do cookbook Como
criar uma Constraint de Validao Personalizada .
Configurao de restries

Algumas restries, como NotBlank, so simples como as outras, como a restrio Choice , tem vrias opes de
configurao disponveis. Suponha que a classeAuthor tenha outra propriedade, gender que possa ser configurado
como male ou female:
YAML
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
properties:
gender:
- Choice: { choices: [male, female], message: Choose a valid gender. }

Annotations
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\Choice(
choices = { "male", "female" },
*
message = "Choose a valid gender."
*
* )
*/
public $gender;
}

XML

2.1. Livro

131

Symfony Docs pt-BR Documentation, Verso 2.4

<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->


<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/sche
<class name="Acme\BlogBundle\Entity\Author">
<property name="gender">
<constraint name="Choice">
<option name="choices">
<value>male</value>
<value>female</value>
</option>
<option name="message">Choose a valid gender.</option>
</constraint>
</property>
</class>
</constraint-mapping>

PHP
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
class Author
{
public $gender;
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint(gender, new Choice(array(
choices => array(male, female),
message => Choose a valid gender.,
)));
}
}

A opo de uma restrio pode sempre ser passada como um array. Algumas restries, entretanto, tambm permitem
a voc passar o valor de uma opo default no lugar do array. No cado da restrio Choice , as opes choices
podem ser espeficadas dessa forma.
YAML
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
properties:
gender:
- Choice: [male, female]

Annotations
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\Choice({"male", "female"})

132

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

*/
protected $gender;
}

XML

<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->


<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/sche
<class name="Acme\BlogBundle\Entity\Author">
<property name="gender">
<constraint name="Choice">
<value>male</value>
<value>female</value>
</constraint>
</property>
</class>
</constraint-mapping>

PHP
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\Choice;
class Author
{
protected $gender;
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint(gender, new Choice(array(male, female)));
}
}

Isso significa simplesmente fazer a configurao da opo mais comum de uma restrio mais curta e rpida.
Se voc est incerto de como especificar uma opo, ou verifique a documentao da API para a restrio ou faa de
forma segura sempre passando um array de opes (o primeiro mtodo mostrado acima).
Escopos da restrio
Restries podem ser aplicadas a uma propriedade de classe (e.g. name) ou um mtodo getter pblico (e.g.
getFullName). O primeiro mais comum e fcil de usar, mas o segundo permite voc especificar regras de validao mais complexas.
Propriedades

Validar as propriedades de uma classe a tcnica de validao mais bsica.Symfony2 permite a voc validar propriedades private, protected ou public. A prxima listagem mostra a voc como configurar a propriedade $firstName
da classe Author para ter ao menos 3 caracteres.
YAML

2.1. Livro

133

Symfony Docs pt-BR Documentation, Verso 2.4

# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
properties:
firstName:
- NotBlank: ~
- MinLength: 3

Annotations
// Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\NotBlank()
* @Assert\MinLength(3)
*/
private $firstName;
}

XML
<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\BlogBundle\Entity\Author">
<property name="firstName">
<constraint name="NotBlank" />
<constraint name="MinLength">3</constraint>
</property>
</class>

PHP
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\MinLength;
class Author
{
private $firstName;
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint(firstName, new NotBlank());
$metadata->addPropertyConstraint(firstName, new MinLength(3));
}
}

Getters

Restries podem tambm ser aplicadas no mtodo de retorno de um valor.Symfony2 permite a voc adicionar uma
restrio para qualquer mtodo public cujo nome comec com get ou is. Nesse guia, ambos os tipos de mtodos
so referidos como getters.
O benefcio dessa tcnica que permite a voc validar seu objeto dinamicamente. Por exemplo, suponha que voc
queira ter certeza que um campo de senha no coincida com o primeiro nome do usurio (por motivos de segurana).

134

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Voc pode fazer isso criando um mtodo isPasswordLegal, e ento afirmando que esse mtodo deva retornar
true:
YAML
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
getters:
passwordLegal:
- "True": { message: "The password cannot match your first name" }

Annotations
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\True(message = "The password cannot match your first name")
*/
public function isPasswordLegal()
{
// return true or false
}
}

XML
<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\BlogBundle\Entity\Author">
<getter property="passwordLegal">
<constraint name="True">
<option name="message">The password cannot match your first name</option>
</constraint>
</getter>
</class>

PHP
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\True;
class Author
{
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addGetterConstraint(passwordLegal, new True(array(
message => The password cannot match your first name,
)));
}
}

Agora, crie o mtodo isPasswordLegal() , e inclua a lgica que voc precisa:


public function isPasswordLegal()
{
return ($this->firstName != $this->password);
}

2.1. Livro

135

Symfony Docs pt-BR Documentation, Verso 2.4

Nota: Com uma viso apurada, voc ir perceber que o prefixo do getter (get ou is) omitido no mapeamento.
Isso permite voc mover a restrio para uma propriedade com o mesmo nome mais tarde (ou vice-versa) sem mudar
sua lgica de validao.

Classes

Algumas restries aplicam para a classe inteira ser validada. Por exemplo, a restrio Callback uma restrio
genrica que aplicada para a prpria classe. Quando a classe validada, mtodos especificados por aquela restrio
so simplesmente executadas ento cada um pode prover uma validao mais personalizada.
Grupos de validao
At agora, voc foi capaz de adicionar restries a uma classe e perguntar se aquela classe passa ou no por todas as
restries definidas. Em alguns casos, entretanto, voc precisar validar um objeto a somente algumas das restries
naqula classe. Para fazer isso, voc pode organizar cada restrio dentro de um ou mais grupos de validao, e ento
aplicar validao a apenas um grupo de restries.
Por exemplo, suponha que voc tenha uma classe User , que usada tanto quando um usurio registra e quando um
usurio atualiza sua informaes de contato posteriormente:
YAML
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\User:
properties:
email:
- Email: { groups: [registration] }
password:
- NotBlank: { groups: [registration] }
- MinLength: { limit: 7, groups: [registration] }
city:
- MinLength: 2

Annotations
// src/Acme/BlogBundle/Entity/User.php
namespace Acme\BlogBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
class User implements UserInterface
{
/**
* @Assert\Email(groups={"registration"})
*/
private $email;
/**
* @Assert\NotBlank(groups={"registration"})
* @Assert\MinLength(limit=7, groups={"registration"})
*/
private $password;
/**

136

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

* @Assert\MinLength(2)
*/
private $city;
}

XML
<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\BlogBundle\Entity\User">
<property name="email">
<constraint name="Email">
<option name="groups">
<value>registration</value>
</option>
</constraint>
</property>
<property name="password">
<constraint name="NotBlank">
<option name="groups">
<value>registration</value>
</option>
</constraint>
<constraint name="MinLength">
<option name="limit">7</option>
<option name="groups">
<value>registration</value>
</option>
</constraint>
</property>
<property name="city">
<constraint name="MinLength">7</constraint>
</property>
</class>

PHP
// src/Acme/BlogBundle/Entity/User.php
namespace Acme\BlogBundle\Entity;
use
use
use
use

Symfony\Component\Validator\Mapping\ClassMetadata;
Symfony\Component\Validator\Constraints\Email;
Symfony\Component\Validator\Constraints\NotBlank;
Symfony\Component\Validator\Constraints\MinLength;

class User
{
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint(email, new Email(array(
groups => array(registration)
)));
$metadata->addPropertyConstraint(password, new NotBlank(array(
groups => array(registration)
)));
$metadata->addPropertyConstraint(password, new MinLength(array(
limit => 7,
groups => array(registration)
)));

2.1. Livro

137

Symfony Docs pt-BR Documentation, Verso 2.4

$metadata->addPropertyConstraint(city, new MinLength(3));


}
}

Com essa configurao, existem dois grupos de validao:


Default - contm as restries no atribuidas a qualquer outro grupo;
registration - Contm as restries somente nos campos email e password.
Para avisar o validador a usar um grupo especfico, passe um ou mais nomes de trupos como um segundo argumento
para o mtodovalidate()
$errors = $validator->validate($author, array(registration));
Claro, voc ir frequentemente trabalhar com validao indiretamnte por meio da biblioteca do formulrio. Para
informaes em como usar grupos de validao dentro de formulrios, veja Grupos de Validao.
Validando Valores e Arrays
At agora, voc viu como pode validar objetos inteiros. Mas s vezes, voc somente quer validar um valor simples
- como verificar se uma string um endereo de e-mail vlido. Isso realmente muito fcil de fazer. De dentro do
controller, parece com isso:
// add this to the top of your class use SymfonyComponentValidatorConstraintsEmail;
public function addEmailAction($email) {
$emailConstraint = new Email(); // all constraint options can be set this way
$emailConstraint->message = Invalid email address;
// use the validator to validate the value $errorList = $this->get(validator)>validateValue($email, $emailConstraint);
if (count($errorList) == 0) { // this IS a valid email address, do something
} else { // this is not a valid email address $errorMessage = $errorList[0]->getMessage()
// do somethign with the error
}
// ...
}
Ao chamar validateValue no validador, voc pode passar um valor bruto e o objeto de restrio que voc com o
qual voc quer validar aquele valor. Uma lista completa de restries disponveis - bem como o nome inteiro da classe
para cada restrio - est disponvel em constraints reference section .
O mtodo validateValule retorna um objeto ConstraintViolationList , que age como um array de
erros. Cada erro na coleo um objeto : class:Symfony\Component\Validator\ConstraintViolation , que contm a
mensagem de erro no mtodo getMessage dele.
Consideraes Finais
O Symfony2 validator uma ferramenta poderosa que pode ser multiplicada para garantir que o dado de qualquer
objeto seja vlido. O poder por trs da validao rside em restries, que seo regras que voc pode aplicar a
propriedades ou mtodos getter de seus objetos. E enquanto voc ir usar mais frequentemente usar a validao do
framework indiretamente quando usar formulrios, lembre que isso pode ser usado em qualquer lugar para validar
qualquer objeto.
138

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Aprenda mais do Cookbook


Como criar uma Constraint de Validao Personalizada

2.1.10 Formulrios
Lidar com formulrios HTML uma das mais comuns - e desafiadoras - tarefas para um desenvolvedor web. O
Symfony2 integra um componente de formulrio que torna fcil a tarefa de lidar com formulrios. Neste captulo, voc
vai construir um formulrio complexo a partir do zero, aprendendo as caractersticas mais importantes da biblioteca
de formulrios ao longo do caminho.
Nota: O componente de formulrio do Symfony uma biblioteca independente que pode ser utilizada fora de projetos
Symfony2. Para mais informaes, consulte o Componente de Formulrio do Symfony2 no Github.

Criando um formulrio simples


Suponha que voc est construindo uma aplicao simples de lista de tarefas que precisar exibir tarefas. Devido
aos seus usurios terem que editar e criar tarefas, voc precisar construir um formulrio. Mas, antes de comear,
primeiro vamos focar na classe genrica Task que representa e armazena os dados de uma nica tarefa:
// src/Acme/TaskBundle/Entity/Task.php
namespace Acme\TaskBundle\Entity;
class Task
{
protected $task;
protected $dueDate;
public function getTask()
{
return $this->task;
}
public function setTask($task)
{
$this->task = $task;
}
public function getDueDate()
{
return $this->dueDate;
}
public function setDueDate(\DateTime $dueDate = null)
{
$this->dueDate = $dueDate;
}
}

Nota: Se voc est codificando junto com este exemplo, crie o AcmeTaskBundle primeiro, executando o seguinte
comando (e aceite todas as opes padro):
php app/console generate:bundle --namespace=Acme/TaskBundle

2.1. Livro

139

Symfony Docs pt-BR Documentation, Verso 2.4

Essa classe um antigo objeto PHP simples, porque, at agora, no tem nada a ver com Symfony ou qualquer outra
biblioteca. simplesmente um objeto PHP normal que, diretamente resolve um problema no interior da sua aplicao
(ou seja, a necessidade de representar uma tarefa na sua aplicao). Claro, at o final deste captulo, voc ser capaz
de enviar dados para uma instncia Task (atravs de um formulrio HTML), validar os seus dados, e persisti-los para
o banco de dados.
Construindo o Formulrio

Agora que voc j criou a classe Task, o prximo passo criar e renderizar o formulrio HTML real. No Symfony2,
isto feito atravs da construo de um objeto de formulrio e, em seguida, renderizando em um template. Por ora,
tudo isso pode ser feito dentro de um controlador:
// src/Acme/TaskBundle/Controller/DefaultController.php
namespace Acme\TaskBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\TaskBundle\Entity\Task;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function newAction(Request $request)
{
// create a task and give it some dummy data for this example
$task = new Task();
$task->setTask(Write a blog post);
$task->setDueDate(new \DateTime(tomorrow));
$form = $this->createFormBuilder($task)
->add(task, text)
->add(dueDate, date)
->getForm();
return $this->render(AcmeTaskBundle:Default:new.html.twig, array(
form => $form->createView(),
));
}
}

Dica: Este exemplo mostra como construir o seu formulrio diretamente no controlador. Mais tarde, na seo
Criando classes de formulrio, voc aprender como construir o seu formulrio em uma classe independente, que
o recomendado pois torna o seu formulrio reutilizvel.
A criao de um formulrio requer relativamente pouco cdigo porque os objetos de formulrio do Symfony2 so
construdos com um construtor de formulrios. A finalidade do construtor de formulrios permitir que voc
escreva receitas simples de formulrios, e ele fazer todo o trabalho pesado, de, realmente, construir o formulrio.
Neste exemplo, voc acrescentou dois campos ao seu formulrio - task e dueDate - que correspondem as propriedades task e dueDate da classe Task. Voc tambm atribuiu a cada um deles um type (exemplo: text, date),
que, entre outras coisas, determina qual(ais) tag(s) HTML de formulrio sero renderizadas para esse campo.
O Symfony2 vem com muitos tipos embutidos, que sero discutidos em breve (veja Tipos de campos integrados
(Built-in)).

140

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Renderizando o Formulrio

Agora que o formulrio foi criado, o prximo passo renderiz-lo. Isto feito passando um objeto view especial
para o seu template (note o $form->createView() no controlador acima) e usando um conjunto de funes
helper para o formulrio:
Twig
{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
<form action="{{ path(task_new) }}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<input type="submit" />
</form>

PHP
<!-- src/Acme/TaskBundle/Resources/views/Default/new.html.php -->

<form action="<?php echo $view[router]->generate(task_new) ?>" method="post" <?php echo $vie


<?php echo $view[form]->widget($form) ?>
<input type="submit" />
</form>

Nota:
Este exemplo assume que voc criou uma rota chamada task_new que aponta para o controlador
AcmeTaskBundle:Default:new o qual foi criado anteriormente.
isso! Ao imprimir o form_widget(form), cada campo do formulrio renderizado, juntamente com uma label
e uma mensagem de erro (se houver). Fcil assim, embora no muito flexvel (ainda). Normalmente, voc desejar
renderizar cada campo do formulrio individualmente, pois poder controlar como ser a aparncia do formulrio.
Voc aprender como fazer isso na seo Renderizando um formulrio em um Template.
Antes de prosseguirmos, observe como o campo input task renderizado tem o valor da propriedade task do objeto
$task (Ex. Write a blog post). Este o primeiro trabalho de um formulrio: pegar os dados de um objeto e
traduzi-lo em um formato que seja adequado para ser renderizado em um formulrio HTML.
Dica: O sistema de formulrios inteligente o suficiente para acessar o valor da propriedade protegida task atravs
dos mtodos getTask() e setTask() na classe Task. A menos que a propriedade seja pblica, ela deve ter um
mtodo getter e setter para que o componente de formulrio possa obter e definir os dados na propriedade. Para
uma propriedade Boolean, voc pode usar um mtodo isser (por exemplo, isPublished()) em vez de um getter
(Ex. getPublished()).

2.1. Livro

141

Symfony Docs pt-BR Documentation, Verso 2.4

Manipulando o envio de formulrios

O segundo trabalho de um formulrio traduzir os dados enviados pelo usurio de volta as propriedades de um objeto.
Para que isso acontea, os dados enviados pelo usurio devem ser vinculados (bound) ao formulrio. Adicione as
seguintes funcionalidades no seu controlador:
// ...
public function newAction(Request $request)
{
// just setup a fresh $task object (remove the dummy data)
$task = new Task();
$form = $this->createFormBuilder($task)
->add(task, text)
->add(dueDate, date)
->getForm();
if ($request->isMethod(POST)) {
$form->bind($request);
if ($form->isValid()) {
// perform some action, such as saving the task to the database
return $this->redirect($this->generateUrl(task_success));
}
}
// ...
}

Novo na verso 2.1: O mtodo bind tornou-se mais flexvel no Symfony 2.1. Ele aceita agora os dados brutos do
cliente (como antes) ou um objeto Request do Symfony. Ele preferido ao invs do mtodo obsoleto bindRequest.
Agora, quando enviar o formulrio, o controlador vincula (bind) ao formulrio os dados enviados, que traduz os dados
de volta as propriedades task e dueDate do objeto $task. Isso tudo acontece atravs do mtodo bind().
Nota: Assim que o bind() chamado, os dados enviados so transferidos imediatamente para o objeto implcito.
Isso acontece independentemente dos dados implcitos serem realmente vlidos.
Este controlador segue um padro comum para a manipulao de formulrios, e possui trs caminhos possveis:
1. Inicialmente quando se carrega a pgina em um navegador, o mtodo de solicitao (request) GET e o formulrio simplesmente criado e renderizado;
2. Quando o usurio envia o formulrio (Ex., o mtodo POST) mas os dados no so vlidos (a validao ser
discutida na prxima seo), o formulrio vinculado (bound) e ento processado, desta vez exibindo todos os
erros de validao;
3. Quando o usurio envia o formulrio com dados vlidos, o formulrio vinculado (bound) e voc tem a oportunidade de executar algumas aes usando o objeto $task (por exemplo, persisti-lo para o banco de dados)
antes de redirecionar o usurio para outra pgina (por exemplo, uma pgina de obrigado ou sucesso).
Nota: Redirecionar o usurio aps o envio bem sucedido do formulrio impede que ele, ao clicar em atualizar,
reenvie os dados do formulrio.

142

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Validao do formulrio
Na seo anterior, voc aprendeu como um formulrio pode ser enviado com dados vlidos ou invlidos. No Symfony2, a validao aplicada ao objeto implcito (Ex., Task). Em outras palavras, a questo no se o formulrio vlido, mas se o objeto $task vlido aps a aplicao dos dados enviados pelo formulrio. A chamada
$form->isValid() um atalho que pergunta ao objeto $task se ele possui ou no dados vlidos.
A validao feita adicionando um conjunto de regras (chamadas constraints) uma classe. Para ver isso em ao,
adicione constraints de validao para que o campo task no deve ser vazio e o campo dueDate no deve ser vazio
e deve ser um objeto DateTime vlido.
YAML
# Acme/TaskBundle/Resources/config/validation.yml
Acme\TaskBundle\Entity\Task:
properties:
task:
- NotBlank: ~
dueDate:
- NotBlank: ~
- Type: \DateTime

Annotations
// Acme/TaskBundle/Entity/Task.php
use Symfony\Component\Validator\Constraints as Assert;
class Task
{
/**
* @Assert\NotBlank()
*/
public $task;
/**
* @Assert\NotBlank()
* @Assert\Type("\DateTime")
*/
protected $dueDate;
}

XML
<!-- Acme/TaskBundle/Resources/config/validation.xml -->
<class name="Acme\TaskBundle\Entity\Task">
<property name="task">
<constraint name="NotBlank" />
</property>
<property name="dueDate">
<constraint name="NotBlank" />
<constraint name="Type">
<value>\DateTime</value>
</constraint>
</property>
</class>

PHP
// Acme/TaskBundle/Entity/Task.php
use Symfony\Component\Validator\Mapping\ClassMetadata;

2.1. Livro

143

Symfony Docs pt-BR Documentation, Verso 2.4

use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Type;
class Task
{
// ...
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint(task, new NotBlank());
$metadata->addPropertyConstraint(dueDate, new NotBlank());
$metadata->addPropertyConstraint(dueDate, new Type(\DateTime));
}
}

isso! Se voc reenviar o formulrio com dados invlidos, ver os erros correspondentes exibidos com o formulrio.
Validao HTML5
Com o HTML5, muitos navegadores podem, nativamente, impor certas constraints de validao no lado do cliente. A validao mais comum ativada renderizando um atributo required em campos que so obrigatrios.
Para navegadores que suportam HTML5, isso ir resultar em uma mensagem nativa do navegador sendo exibida
se o usurio tentar enviar o formulrio com o campo em branco.
Os formulrios gerados podem aproveitar ao mximo esta nova funcionalidade, adicionando atributos HTML
que disparam a validao. A validao ao lado do cliente, entretanto, pode ser desativada ao adicionar o atributo
novalidate na tag form ou formnovalidate na tag submit. Isto especialmente til quando voc
quiser testar suas constraints de validao ao lado do servidor, mas esto sendo impedidas pelo seu navegador,
por exemplo, ao enviar campos em branco.
A validao um recurso muito poderoso do Symfony2 e tem seu prprio captulo dedicado.
Grupos de Validao

Dica: Se voc no estiver usando grupos de validao, ento, voc pode pular esta seo.
Se o seu objeto aproveita a grupos de validao, voc precisa especificar qual(ais) grupo(s) de validao seu formulrio
deve usar:
$form = $this->createFormBuilder($users, array(
validation_groups => array(registration),
))->add(...)
;

Se voc est criando classes de formulrio (uma boa prtica), ento voc precisa adicionar o seguinte ao mtodo
setDefaultOptions():
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
validation_groups => array(registration)
));
}

144

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Em ambos os casos, apenas o grupo de validao registration ser usado para validar o objeto implcito.
Grupos com base nos dados submetidos

Novo na verso 2.1: A capacidade de especificar um callback ou Closure no validation_groups novo na


verso 2.1
Se voc precisar de alguma lgica avanada para determinar os grupos de validao (por exemplo, com base nos dados
submetidos), voc pode definir a opo validation_groups para um array callback ou uma Closure:
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

public function setDefaultOptions(OptionsResolverInterface $resolver)


{
$resolver->setDefaults(array(
validation_groups => array(Acme\\AcmeBundle\\Entity\\Client, determineValidationGroups)
));
}

Isso ir chamar o mtodo esttico determineValidationGroups() na classe Client aps o formulrio ser
vinculado (bound), mas antes da validao ser executada. O objeto do formulrio passado como um argumento para
esse mtodo (veja o exemplo seguinte). Voc tambm pode definir toda a lgica inline usando uma Closure:
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
validation_groups => function(FormInterface $form) {
$data = $form->getData();
if (Entity\Client::TYPE_PERSON == $data->getType()) {
return array(person)
} else {
return array(company);
}
},
));
}

Tipos de campos integrados (Built-in)


O Symfony vem, por padro, com um grande grupo de tipos de campos que cobrem todos os os campos comuns de
formulrio e tipos de dados que voc vai encontrar:
Voc tambm pode criar os seus prprios tipos de campo personalizados. Este tpico abordado no artigo Como
Criar um Tipo de Campo de Formulrio Personalizado do cookbook.
Opes dos tipos de campos

Cada tipo de campo possui um nmero de opes que podem ser usadas para configur-lo. Por exemplo, o campo
dueDate atualmente processado como 3 select boxes. No entanto, o campo date pode ser configurado para ser
renderizado como uma caixa de texto simples (onde o usurio deve digitar a data como uma string na caixa):

2.1. Livro

145

Symfony Docs pt-BR Documentation, Verso 2.4

->add(dueDate, date, array(widget => single_text))

Cada tipo de campo tem um nmero de opes diferentes que podem ser passadas ele. Muitas delas so especficas
para o tipo de campo e os detalhes podem ser encontrados na documentao de cada tipo.
A opo required
A opo mais comum a opo required, que pode ser aplicada qualquer campo. Por padro, a opo
required definida como true, o que significa que os navegadores prontos para o HTML5 aplicaro a
validao ao lado do cliente se o campo for deixado em branco. Se voc no deseja esse comportamento, defina
a opo required em seu campo para false ou desabilite a validao HTML5.
Alm disso, note que a configurao da opo required para true no resultar em validao aplicada ao
lado do servidor. Em outras palavras, se um usurio enviar um valor em branco para o campo (ou usar um
navegador antigo ou web service, por exemplo), ela ser aceita como um valor vlido, a menos que voc utilize
a constraint de validao do Symfony NotBlank ou NotNull.
Em outras palavras, a opo required agradvel, mas a validao verdadeira ao lado do servidor sempre
dever ser usada.

Adivinhando o tipo do campo


Agora que voc adicionou metadados de validao na classe Task, o Symfony j sabe um pouco sobre os seus
campos. Se voc permitir, o Symfony pode adivinhar o tipo do seu campo e configur-lo para voc. Neste exemplo,
o Symfony pode adivinhar a partir das regras de validao que o campo task um campo texto normal e o campo
dueDate um campo data:
public function newAction()
{
$task = new Task();
$form = $this->createFormBuilder($task)
->add(task)
->add(dueDate, null, array(widget => single_text))
->getForm();
}

A adivinhao ativada quando voc omitir o segundo argumento do mtodo add() (ou se voc passar null para
ele). Se voc passar um array de opes como o terceiro argumento (feito para o dueDate acima), estas opes so
aplicadas ao campo adivinhado.
Cuidado: Se o formulrio usa um grupo de validao especfico, o adivinhador do tipo de campo ainda vai
considerar todas as constraints de validao quando estiver adivinhando os seus tipos de campos (incluindo as
constraints que no fazem parte dos grupos de validao sendo utilizados).

146

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Adivinhando as opes dos tipos de campos

Alm de adivinhar o tipo para um campo, o Symfony tambm pode tentar adivinhar os valores corretos de uma srie
de opes do campo.
Dica: Quando essas opes so definidas, o campo ser renderizado com atributos HTML especiais que fornecem
para a validao HTML5 ao lado do cliente. Entretanto, ele no gera as constraints equivalentes ao lado do servidor
(Ex. Assert\MaxLength). E, embora voc precisar adicionar manualmente a validao ao lado do servidor, essas
opes de tipo de campo podem, ento, ser adivinhadas a partir dessa informao.
required: A opo required pode ser adivinhada com base nas regras de validao (ou seja, o campo
NotBlank ou NotNull) ou metadados do Doctrine (ou seja, o campo nullable). Isto muito til, pois
a sua validao ao lado do cliente ir corresponder automaticamente as suas regras de validao.
min_length: Se o campo uma espcie de campo de texto, ento, a opo min_length pode ser adivinhada
a partir das constraints de validao (se o MinLength ou Min usado) ou a partir dos metadados do Doctrine
(atravs do tamanho do campo).
max_length: Semelhante ao min_length, o tamanho mximo tambm pode ser adivinhado.
Nota: Estas opes de campo so adivinhadas apenas se voc estiver usando o Symfony para adivinhar o tipo de
campo (ou seja, omitir ou passar null como o segundo argumento para o add()).
Se voc desejar modificar um dos valores adivinhados, voc pode sobrescrev-lo passando a opo no array de opes
do campo:
->add(task, null, array(min_length => 4))

Renderizando um formulrio em um Template


At agora, voc viu como um formulrio inteiro pode ser renderizado com apenas uma linha de cdigo. Claro, voc
geralmente precisar de muito mais flexibilidade quando estiver renderizando:
Twig
{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
<form action="{{ path(task_new) }}" method="post" {{ form_enctype(form) }}>
{{ form_errors(form) }}
{{ form_row(form.task) }}
{{ form_row(form.dueDate) }}
{{ form_rest(form) }}
<input type="submit" />
</form>

PHP
<!-- // src/Acme/TaskBundle/Resources/views/Default/newAction.html.php -->

<form action="<?php echo $view[router]->generate(task_new) ?>" method="post" <?php echo $vie


<?php echo $view[form]->errors($form) ?>
<?php echo $view[form]->row($form[task]) ?>
<?php echo $view[form]->row($form[dueDate]) ?>

2.1. Livro

147

Symfony Docs pt-BR Documentation, Verso 2.4

<?php echo $view[form]->rest($form) ?>


<input type="submit" />
</form>

Vamos dar uma olhada em cada parte:


form_enctype(form) - Se pelo menos um campo for um campo para upload de arquivo, ele ir renderizar
o enctype="multipart/form-data" obrigatrio;
form_errors(form) - Renderiza quaisquer erros globais para todo o formulrio (erros especficos de campos so exibidos ao lado de cada campo);
form_row(form.dueDate) - Renderiza a label, qualquer erro, e o widget HTML do formulrio para o
campo informado (Ex. dueDate), por padro, um elemento div;
form_rest(form) - Renderiza quaisquer campos que ainda no tenham sido renderizados. Geralmente
uma boa idia fazer uma chamada deste helper na parte inferior de cada formulrio (no caso de voc ter
esquecido algum campo ou no quer se preocupar em renderizar manualmente os campos ocultos). Este helper
tambm til para aproveitar a Proteo CSRF automtica.
A maioria do trabalho feito pelo helper form_row, que renderiza a label, os erros e widgets HTML do formulrio
para cada campo dentro de uma tag div por padro. Na seo Tematizando os formulrios, voc aprender como a
sada do form_row pode ser personalizada em muitos nveis diferentes.
Dica: Voc pode acessar os dados atuais do seu formulrio via form.vars.value:
Twig
{{ form.vars.value.task }}

PHP
<?php echo $view[form]->get(value)->getTask() ?>

Renderizando cada campo manualmente

O helper form_row timo porque voc pode renderizar rapidamente cada campo de seu formulrio (e tambm
possvel personalizar a marcao utilizada para a linha ). Mas, como a vida nem sempre to simples, voc tambm
pode renderizar cada campo inteiramente mo. O produto final do que segue o mesmo de quando voc usou o
helper form_row:
Twig
{{ form_errors(form) }}
<div>
{{ form_label(form.task) }}
{{ form_errors(form.task) }}
{{ form_widget(form.task) }}
</div>
<div>
{{ form_label(form.dueDate) }}
{{ form_errors(form.dueDate) }}
{{ form_widget(form.dueDate) }}
</div>

148

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

{{ form_rest(form) }}

PHP
<?php echo $view[form]->errors($form) ?>
<div>
<?php echo $view[form]->label($form[task]) ?>
<?php echo $view[form]->errors($form[task]) ?>
<?php echo $view[form]->widget($form[task]) ?>
</div>
<div>
<?php echo $view[form]->label($form[dueDate]) ?>
<?php echo $view[form]->errors($form[dueDate]) ?>
<?php echo $view[form]->widget($form[dueDate]) ?>
</div>
<?php echo $view[form]->rest($form) ?>

Se a label auto-gerada para um campo no estiver correta, voc pode especific-la explicitamente:
Twig
{{ form_label(form.task, Task Description) }}

PHP
<?php echo $view[form]->label($form[task], Task Description) ?>

Finalmente, alguns tipos de campos tem opes de renderizao adicionais que podem ser passadas para o widget.
Estas opes esto documentadas com cada tipo, mas uma opo em comum o attr, que permite modificar atributos no elemento do formulrio. O seguinte cdigo adiciona a classe task_field para o campo texto de entrada
renderizado:
Twig
{{ form_widget(form.task, { attr: {class: task_field} }) }}

PHP
<?php echo $view[form]->widget($form[task], array(
attr => array(class => task_field),
)) ?>

Referncia de funes dos templates Twig

Se voc est usando o Twig, uma referncia completa das funes de renderizao do formulrio est disponvel no
manual de referncia. Leia ele para saber tudo sobre os helpers disponveis e as opes que podem ser usadas
com cada um.
Criando classes de formulrio
Como voc viu, um formulrio pode ser criado e usado diretamente em um controlador. No entanto, uma prtica
melhor construir o formulrio separadamente, em uma classe PHP independente, que poder, ento, ser reutilizada
em qualquer lugar na sua aplicao. Crie uma nova classe que vai abrigar a lgica da construo do formulrio de
tarefas:
2.1. Livro

149

Symfony Docs pt-BR Documentation, Verso 2.4

// src/Acme/TaskBundle/Form/Type/TaskType.php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(task);
$builder->add(dueDate, null, array(widget => single_text));
}
public function getName()
{
return task;
}
}

Esta nova classe contm todas as orientaes necessrias para criar o formulrio de tarefas (Note que o mtodo
getName() deve retornar um identificador exclusivo para esse tipo do formulrio). Ele pode ser usado para
construir rapidamente um objeto de formulrio no controlador:
// src/Acme/TaskBundle/Controller/DefaultController.php
// add this new use statement at the top of the class
use Acme\TaskBundle\Form\Type\TaskType;
public function newAction()
{
$task = // ...
$form = $this->createForm(new TaskType(), $task);
// ...
}

Colocando a lgica do formulrio em sua prpria classe significa que o formulrio pode ser facilmente reutilizado em
outros lugares no seu projeto. Esta a melhor forma de criar formulrios, mas, a deciso final depende de voc.
Setando o data_class
Todo formulrio precisa saber o nome da classe que contm os dados implcitos (Ex.
Acme\TaskBundle\Entity\Task). Normalmente, ele apenas adivinhado com base no objeto
passado no segundo argumento para o createForm (Ex. $task). Mais tarde, quando voc iniciar nos
formulrios embutidos, isto no ser suficiente. Ento, embora nem sempre necessrio, geralmente uma boa
idia especificar explicitamente a opo data_class adicionando o seguinte sua classe type de formulrio:
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
data_class => Acme\TaskBundle\Entity\Task,
));
}

150

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Dica: Ao mapear formulrios para objetos, todos os campos so mapeados. Qualquer campo do formulrio que no
existe no objeto mapeado ir fazer com que uma exceo seja gerada.
Nos casos em que voc precisa de campos extras na formulrio (por exemplo: um checkbox voc concorda com os
termos) que no ser mapeado para o objeto implcito, voc precisa definir a opo property_path como false:
use Symfony\Component\Form\FormBuilderInterface;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(task);
$builder->add(dueDate, null, array(property_path => false));
}

Alm disso, se houver quaiquer campos do formulrio que no esto includos nos dados submetidos, esses campos
sero definidos explicitamente como null.
Os dados do campo podem ser acessados em um controlador com:
$form->get(dueDate)->getData();

Formulrios e o Doctrine
O objetivo de um formulrio traduzir os dados de um objeto (Ex. Task) para um formulrio HTML e, em seguida,
traduzir os dados enviados pelo usurio de volta ao objeto original. Como tal, o tpico da persistncia do objeto Task
no banco de dados totalmente no relacionado ao tpico de formulrios. Mas, se voc configurou a classe Task
para ser persistida atravs do Doctrine (ou seja, voc adicionou metadados de mapeamento ele), ento, a persistncia
aps a submisso do formulrio pode ser feita quando o formulrio vlido:
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($task);
$em->flush();
return $this->redirect($this->generateUrl(task_success));
}

Se, por algum motivo, voc no tem acesso ao seu objeto $task original, voc pode busc-lo a partir do formulrio:
$task = $form->getData();

Para mais informaes, consulte o captulo Doctrine ORM.


A chave para entender que, quando o formulrio vinculado (bound), os dados submetidos so transferidos imediatamente para o objeto implcito. Se voc quiser persistir esses dados, basta persistir o objeto em si (que j contm os
dados submetidos).
Formulrios embutidos
Muitas vezes, voc desejar criar um formulrio que vai incluir campos de vrios objetos diferentes. Por exemplo, um
formulrio de inscrio pode conter dados que pertencem a um objeto User, bem como, muitos objetos Address.
Felizmente, isto fcil e natural com o componente de formulrio.

2.1. Livro

151

Symfony Docs pt-BR Documentation, Verso 2.4

Embutindo um nico objeto

Suponha que cada Task pertence a um simples objeto Category. Inicie, claro, criando o objeto Category:
// src/Acme/TaskBundle/Entity/Category.php
namespace Acme\TaskBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
class Category
{
/**
* @Assert\NotBlank()
*/
public $name;
}

Em seguida, adicione uma nova propriedade category na classe Task:


// ...
class Task
{
// ...
/**
* @Assert\Type(type="Acme\TaskBundle\Entity\Category")
*/
protected $category;
// ...
public function getCategory()
{
return $this->category;
}
public function setCategory(Category $category = null)
{
$this->category = $category;
}
}

Agora que a sua aplicao foi atualizada para refletir as novas exigncias, crie uma classe de formulrio para que o
objeto Category possa ser modificado pelo usurio:
// src/Acme/TaskBundle/Form/Type/CategoryType.php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(name);
}

152

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

public function setDefaultOptions(OptionsResolverInterface $resolver)


{
$resolver->setDefaults(array(
data_class => Acme\TaskBundle\Entity\Category,
));
}
public function getName()
{
return category;
}
}

O objetivo final permitir que a Category de uma Task possa ser modificada direitamente dentro do prprio
formulrio da tarefa. Para fazer isso, adicione um campo category ao objeto TaskType cujo tipo uma instncia
da nova classe CategoryType:
use Symfony\Component\Form\FormBuilderInterface;
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder->add(category, new CategoryType());
}

Os campos do CategoryType podem agora ser renderizados juntamente com os campos da classe TaskType.
Para ativar a validao no CategoryType, adicione a opo cascade_validation:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
data_class => Acme\TaskBundle\Entity\Category,
cascade_validation => true,
));
}

Renderize os campos Category da mesma forma que os campos originais da Task:


Twig
{# ... #}
<h3>Category</h3>
<div class="category">
{{ form_row(form.category.name) }}
</div>
{{ form_rest(form) }}
{# ... #}

PHP
<!-- ... -->
<h3>Category</h3>
<div class="category">
<?php echo $view[form]->row($form[category][name]) ?>
</div>

2.1. Livro

153

Symfony Docs pt-BR Documentation, Verso 2.4

<?php echo $view[form]->rest($form) ?>


<!-- ... -->

Quando o usurio enviar o formulrio, os dados submetidos para os campos Category so usados para construir
uma instncia de Category, que ento definida no campo Category da instncia Task.
A instncia Category acessvel naturalmente via $task->getCategory() e pode ser persistida no banco de
dados ou usada como voc precisar.
Embutindo uma coleo de formulrios

Voc tambm pode embutir uma coleo de formulrios em um formulrio (imagine um formulrio Category com
muitos sub-formulrios Product). Isto feito usando o tipo de campo collection.
Para mais informaes consulte no Como embutir uma Coleo de Formulrios e na collection referncia dos tipos
de campo.
Tematizando os formulrios
Cada parte de como um formulrio renderizado pode ser personalizada. Voc est livre para mudar como cada linha
do formulrio renderizada, alterar a marcao usada para renderizar os erros, ou at mesmo, personalizar como uma
tag textarea deve ser renderizada. Nada est fora dos limites, e possvel utilizar diferentes personalizaes em
diferentes lugares.
O Symfony utiliza templates para renderizar todas e cada uma das partes de um formulrio, tais como tags label,
tags input, mensagens de erro e tudo mais.
No Twig, cada fragmento do formulrio representado por um bloco Twig. Para personalizar qualquer parte de
como um formulrio renderizado, voc s precisa substituir o bloco apropriado.
No PHP, cada fragmento do formulrio renderizado por um arquivo de template individual. Para personalizar
qualquer parte de como um formulrio renderizado, voc s precisa sobrescrever o template j existente, criando um
novo.
Para entender como isso funciona, vamos personalizar o fragmento form_row e adicionar um atributo class para o
elemento div que envolve cada linha. Para fazer isso, crie um novo arquivo template que ir armazenar a marcao
nova:
Twig
{# src/Acme/TaskBundle/Resources/views/Form/fields.html.twig #}
{% block field_row %}
{% spaceless %}
<div class="form_row">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock field_row %}

PHP
<!-- src/Acme/TaskBundle/Resources/views/Form/field_row.html.php -->
<div class="form_row">

154

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

<?php echo $view[form]->label($form, $label) ?>


<?php echo $view[form]->errors($form) ?>
<?php echo $view[form]->widget($form, $parameters) ?>
</div>

O fragmento field_row do formulrio utilizado para renderizar a maioria dos campos atravs da da funo
form_row. Para dizer ao componente de formulrio para utilizar o seu novo fragmento field_row definido acima,
adicione o seguinte no topo do template que renderiza o formulrio:
Twig
{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
{% form_theme form AcmeTaskBundle:Form:fields.html.twig %}
<form ...>

PHP
<!-- src/Acme/TaskBundle/Resources/views/Default/new.html.php -->
<?php $view[form]->setTheme($form, array(AcmeTaskBundle:Form)) ?>
<form ...>

A tag form_theme (no Twig) importa os fragmentos definidos no template informado e utiliza-os quando renderiza o formulrio. Em outras palavras, quando a funo form_row chamada mais tarde neste template, ela usar o
bloco field_row de seu tema personalizado (ao invs do bloco padro field_row que vem com o Symfony).
Para personalizar qualquer parte de um formulrio, voc s precisa substituir o fragmento apropriado. Saber exatamente qual bloco ou arquivo deve-se substituir o tema da prxima seo.
Novo na verso 2.1: Foi introduzida uma sintaxe alternativa do Twig para form_theme no 2.1. Ela aceita qualquer
expresso Twig vlida (a diferena mais notvel est no uso de um array quando utilizar vrios temas).
{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
{% form_theme form with AcmeTaskBundle:Form:fields.html.twig %}

{% form_theme form with [AcmeTaskBundle:Form:fields.html.twig, AcmeTaskBundle:Form:fields2.html.tw

Para uma discusso mais extensiva, consulte Como personalizar a Renderizao de Formulrios.
Nomeando os fragmentos do formulrio

No Symfony, cada parte de um formulrio que renderizada - elementos de formulrio HTML, erros, labels, etc -
definida em um tema base, que uma coleo de blocos no Twig e uma coleo de arquivos de template no PHP.
No Twig, cada bloco necessrio definido em um nico arquivo de template (form_div_layout.html.twig) que
encontra-se no interior do Twig Bridge. Dentro desse arquivo, voc pode ver todos os blocos necessrios para renderizar um formulrio e todo o tipo de campo padro.
No PHP, os fragmentos so arquivos de template individuais. Por padro, eles esto localizados no diretrio Resources/views/Form do framework bundle (veja no GitHub).
Cada nome de fragmento segue o mesmo padro bsico e dividido em duas partes, separadas por um nico caractere
de sublinhado (_). Alguns exemplos so:
field_row - usado pelo form_row para renderizar a maioria dos campos;

2.1. Livro

155

Symfony Docs pt-BR Documentation, Verso 2.4

textarea_widget - usado pelo form_widget para renderizar um campo do tipo textarea;


field_errors - usado pelo form_errors para renderizar os erros para um campo;
Cada fragmento segue o mesmo padro bsico: type_part. A poro type corresponde ao tipo do campo sendo
renderizado (Ex. textarea, checkbox, date, etc) enquanto a poro part corresponde a o que est sendo
renderizado (Ex., label, widget, errors, etc). Por padro, existem 4 partes possveis de um formulrio que
podem ser renderizadas:
label
widget
errors
row

(Ex.
(Ex.
(Ex.
(Ex.

field_label)
field_widget)
field_errors)
field_row)

renderiza label do campo


renderiza a representao HTML do campo
renderiza os errors do campo
renderiza a linha inteira do campo (label, widget e erros)

Nota: Na verdade, existem outras trs partes - rows, rest e enctype - mas voc raramente ou nunca vai precisar
se preocupar em sobrescrev-las
Ao conhecer o tipo do campo (Ex. textarea) e qual parte voc deseja personalizar (Ex. widget), voc pode
construir o nome do fragmento que precisa ser sobrescrito (Ex. textarea_widget).
Herana dos fragmentos de template

Em alguns casos, o fragmento que voc deseja personalizar parecer estar faltando. Por exemplo, no existe um
fragmento textarea_errors nos temas padro fornecidos com o Symfony. Ento, como so renderizados os
erros de um campo textarea?
A resposta : atravs do fragmento field_errors. Quando o Symfony renderiza os erros para um tipo textarea, ele
procura primeiro por um fragmento textarea_errors antes de voltar para o fragmento field_errors. Cada
tipo de campo tem um tipo pai (o tipo pai do textarea field), e o Symfony usa o fragmento para o tipo pai se
o fragmento base no existir.
Ento, para substituir os erros para apenas os campos textarea, copie o fragmento field_errors, renomeie
para textarea_errors e personalize-o. Para sobrescrever a renderizao de erro padro para todos os campos,
copie e personalize diretamente o fragmento field_errors.
Dica: O tipo pai de cada tipo de campo est disponvel na referncia de tipos do formulrio para cada tipo de
campo.

Tematizando os formulrios globalmente

No exemplo acima, voc usou o helper form_theme (no Twig) para importar os fragmentos personalizados somente para este formulrio. Voc tambm pode dizer ao Symfony para importar as personalizaes do formulrio para
todo o seu projeto.
Twig Para incluir automaticamente os blocos personalizados do template fields.html.twig criado anteriormente em todos os templates, modifique o seu arquivo de configurao da aplicao:
YAML
# app/config/config.yml
twig:
form:
resources:

156

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

- AcmeTaskBundle:Form:fields.html.twig
# ...

XML
<!-- app/config/config.xml -->
<twig:config ...>
<twig:form>
<resource>AcmeTaskBundle:Form:fields.html.twig</resource>
</twig:form>
<!-- ... -->
</twig:config>

PHP
// app/config/config.php
$container->loadFromExtension(twig, array(
form => array(resources => array(
AcmeTaskBundle:Form:fields.html.twig,
))
// ...
));

Quaisquer blocos dentro do template fields.html.twig agora so usados globalmente para definir a sada do
formulrio.
Personalizando toda a sada do formulrio em um nico arquivo com o Twig
No Twig, voc tambm pode personalizar um bloco de formulrio diretamente dentro do template onde a personalizao necessria:
{% extends ::base.html.twig %}
{# import "_self" as the form theme #}
{% form_theme form _self %}
{# make the form fragment customization #}
{% block field_row %}
{# custom field row output #}
{% endblock field_row %}
{% block content %}
{# ... #}
{{ form_row(form.task) }}
{% endblock %}

A tag {% form_theme form _self %} permite que blocos de formulrio sejam personalizados diretamente dentro do template que usar essas personalizaes. Utilize este mtodo para fazer personalizaes de
sada do formulrio rapidamente, que, somente sero necessrias em um nico template.

PHP Para
incluir
automaticamente
os
templates
personalizados
do
diretrio
Acme/TaskBundle/Resources/views/Form criado anteriormente em todos os templates, modifique o
seu arquivo de configurao da aplicao:

2.1. Livro

157

Symfony Docs pt-BR Documentation, Verso 2.4

YAML
# app/config/config.yml
framework:
templating:
form:
resources:
- AcmeTaskBundle:Form
# ...

XML
<!-- app/config/config.xml -->
<framework:config ...>
<framework:templating>
<framework:form>
<resource>AcmeTaskBundle:Form</resource>
</framework:form>
</framework:templating>
<!-- ... -->
</framework:config>

PHP
// app/config/config.php
$container->loadFromExtension(framework, array(
templating => array(form =>
array(resources => array(
AcmeTaskBundle:Form,
)))
// ...
));

Qualquer fragmento dentro do diretrio Acme/TaskBundle/Resources/views/Form agora ser usado globalmente para definir a sada do formulrio.
Proteo CSRF
CSRF - ou Cross-site request forgery - um mtodo pelo qual um usurio mal-intencionado tenta fazer com que os
seus usurios legtimos, sem saber, enviem dados que eles no pretendem enviar. Felizmente, os ataques CSRF podem
ser prevenidos usando um token CSRF dentro do seu formulrio.
A boa notcia que o Symfony, por padro, incorpora e valida os tokens CSRF automaticamente para voc. Isso
significa que voc pode aproveitar a proteo CSRF sem precisar fazer nada. Na verdade, todo formulrio neste
captulo aproveitou a proteo CSRF!
A proteo CSRF funciona adicionando um campo oculto ao seu formulrio - chamado _token por padro - que
contm um valor que s voc e seu usurio sabem. Isto garante que o usurio - e no alguma outra entidade - est
enviando os dados. O Symfony automaticamente valida a presena e exatido deste token.
O campo _token um campo oculto e ser automaticamente renderizado se voc incluir a funo form_rest()
em seu template, que garante a sada de todos os campos no-renderizados.
O token CSRF pode ser personalizado formulrio por formulrio. Por exemplo:

158

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TaskType extends AbstractType
{
// ...
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
data_class
=> Acme\TaskBundle\Entity\Task,
csrf_protection => true,
csrf_field_name => _token,
// a unique key to help generate the secret token
intention
=> task_item,
));
}
// ...
}

Para desativar a proteo CSRF, defina a opo csrf_protection para false. As personalizaes tambm podem
ser feitas globalmente em seu projeto. Para mais informaes veja a seo referncia de configurao do formulrio .
Nota: A opo intention opcional, mas aumenta muito a segurana do token gerado, tornando-o diferente para
cada formulrio.

Utilizando um formulrio sem uma classe


Na maioria dos casos, um formulrio vinculado a um objeto, e os campos do formulrio obtm e armazenam seus
dados nas propriedades desse objeto. Isto foi exatamente o que voc viu at agora neste captulo com a classe Task.
Mas, s vezes, voc pode desejar apenas utilizar um formulrio sem uma classe, e receber um array dos dados submetidos. Isso realmente muito fcil:
// Certifique-se que voc importou o namespace Request acima da classe
use Symfony\Component\HttpFoundation\Request
// ...
public function contactAction(Request $request)
{
$defaultData = array(message => Type your message here);
$form = $this->createFormBuilder($defaultData)
->add(name, text)
->add(email, email)
->add(message, textarea)
->getForm();
if ($request->isMethod(POST)) {
$form->bind($request);
// data is an array with "name", "email", and "message" keys
$data = $form->getData();
}
// ... render the form
}

2.1. Livro

159

Symfony Docs pt-BR Documentation, Verso 2.4

Por padro, um formulrio assume que voc deseja trabalhar com arrays de dados, em vez de um objeto. H exatamente
duas maneiras em que voc pode mudar esse comportamento e amarrar o formulrio um objeto:
1. Passar um objeto ao criar o formulrio (como o primeiro argumento para createFormBuilder ou o segundo
argumento para createForm);
2. Declarar a opo data_class no seu formulrio.
Se voc no fizer qualquer uma destas, ento o formulrio ir retornar os dados como um array. Neste exemplo, uma
vez que $defaultData no um objeto (e no foi definida a opo data_class), o $form->getData()
retorna um array.
Dica: Voc tambm pode acessar os valores POST (neste caso, name) diretamente atravs do objeto do pedido
(request), desta forma:
$this->get(request)->request->get(name);

Esteja ciente, no entanto, que, na maioria dos casos, usar o mtodo getData() uma melhor escolha, j que retorna os
dados (geralmente um objeto), aps ele ser transformado pelo framework de formulrio.

Adicionando a Validao

A pea que falta a validao. Normalmente, quando voc chama $form->isValid(), o objeto validado atravs
da leitura das constraints que voc aplicou classe. Mas, sem uma classe, como voc pode adicionar constraints para
os dados do seu formulrio?
A resposta configurar as constraints voc mesmo, e pass-las para o seu formulrio. A abordagem global explicada
um pouco mais no validation chapter, mas aqui est um pequeno exemplo:
// import the namespaces above your controller class
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\MinLength;
use Symfony\Component\Validator\Constraints\Collection;
$collectionConstraint = new Collection(array(
name => new MinLength(5),
email => new Email(array(message => Invalid email address)),
));
// create a form, no default values, pass in the constraint option
$form = $this->createFormBuilder(null, array(
validation_constraint => $collectionConstraint,
))->add(email, email)
// ...
;

Agora, quando voc chamar $form->bind($request), a configurao de constraints aqui ser executada em relao aos dados do seu formulrio. Se voc estiver usando uma classe de formulrio, sobrescreva o mtodo
setDefaultOptions para especificar a opo:
namespace Acme\TaskBundle\Form\Type;
use
use
use
use
use
use

160

Symfony\Component\Form\AbstractType;
Symfony\Component\Form\FormBuilder;
Symfony\Component\OptionsResolver\OptionsResolverInterface;
Symfony\Component\Validator\Constraints\Email;
Symfony\Component\Validator\Constraints\MinLength;
Symfony\Component\Validator\Constraints\Collection;

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

class ContactType extends AbstractType


{
// ...
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$collectionConstraint = new Collection(array(
name => new MinLength(5),
email => new Email(array(message => Invalid email address)),
));
$resolver->setDefaults(array(
validation_constraint => $collectionConstraint
));
}
}

Agora, voc tem a flexibilidade de criar formulrios - com validao - que retorna um array de dados, em vez de um
objeto. Na maioria dos casos, melhor - e certamente mais robusto - ligar (bind) o seu formulrio um objeto. Mas,
para formulrios simples, esta uma excelente abordagem.
Consideraes finais
Voc j conhece todos os blocos de construo necessrios para construir formulrios complexos e funcionais para a
sua aplicao. Ao construir formulrios, tenha em mente que a primeira meta de um formulrio traduzir os dados de
um objeto (Task) para um formulrio HTML, para que o usurio possa modificar os dados. O segundo objetivo de
um formulrio pegar os dados enviados pelo usurio e reaplic-los ao objeto.
Ainda h muito mais para aprender sobre o mundo poderoso das formulrios, tais como como lidar com uploads
de arquivos com o Doctrine ou como criar um formulrio onde um nmero dinmico de sub-formulrios podem ser
adicionados (por exemplo, uma lista de tarefas onde voc pode continuar a adicionar mais campos antes de enviar via
Javascript). Veja estes tpicos no cookbook. Alm disso, certifique-se de apoiar-se na documentao de referncia de
tipos de campo, que inclui exemplos de como usar cada tipo de campo e suas opes.
Aprenda mais no Cookbook
Como Manipular o Upload de Arquivos com o Doctrine
File Field Reference
Creating Custom Field Types
Como personalizar a Renderizao de Formulrios
/cookbook/form/dynamic_form_generation
Como usar os Transformadores de Dados

2.1.11 Segurana
Segurana um processo em dois passos principais. Seu objetivo evitar que um usurio tenha acesso a um recurso
que ele no deveria ter.
No primeiro passo do processo, o sistema de segurana identifica quem o usurio exigindo que o mesmo envie algum
tipo de identificao. Este primeiro passo chamado autenticao e signifcica que o sistema est tentando identificar
que o usurio.
2.1. Livro

161

Symfony Docs pt-BR Documentation, Verso 2.4

Uma vez que o sistema sabe quem est acessando, o prximo passo determinar se o usurio pode acessar determinado
recurso. Este segundo passo chamado de autorizao e significa que o sistema ir checar se o usurio tem permisso
para executar determinada ao.

Como a melhor maneira de aprender com um exemplo, vamos para ele.


Nota: O componente de segurana do Symfony est disponvel como uma biblioteca PHP podendo ser utilizada em
qualquer projeto PHP.

Exemplo: Autenticao Bsica HTTP


O componente de segurana pode ser configurado atravs da configurao de sua aplicao. Na verdade, a maioria dos esquemas comuns de segurana podem ser conseguidos apenas configurando adequadamente sua aplicao. A configurao a seguir diz ao Symfony para proteger qualquer URL que satisfaa /admin/* atravs
da autenticao bsica HTTP, solicitando do usurio credenciais (login/senha).
YAML
# app/config/security.yml
security:
firewalls:
secured_area:
pattern:
^/
anonymous: ~
http_basic:
realm: "Secured Demo Area"
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
providers:
in_memory:

162

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

memory:
users:
ryan: { password: ryanpass, roles: ROLE_USER }
admin: { password: kitten, roles: ROLE_ADMIN }
encoders:
Symfony\Component\Security\Core\User\User: plaintext

XML

<!-- app/config/security.xml -->


<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/ser
<config>
<firewall name="secured_area" pattern="^/">
<anonymous />
<http-basic realm="Secured Demo Area" />
</firewall>
<access-control>
<rule path="^/admin" role="ROLE_ADMIN" />
</access-control>
<provider name="in_memory">
<memory>
<user name="ryan" password="ryanpass" roles="ROLE_USER" />
<user name="admin" password="kitten" roles="ROLE_ADMIN" />
</memory>
</provider>
<encoder class="Symfony\Component\Security\Core\User\User" algorithm="plaintext" />
</config>
</srv:container>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
firewalls => array(
secured_area => array(
pattern => ^/,
anonymous => array(),
http_basic => array(
realm => Secured Demo Area,
),
),
),
access_control => array(
array(path => ^/admin, role => ROLE_ADMIN),
),
providers => array(
in_memory => array(
memory => array(
users => array(
ryan => array(password => ryanpass, roles => ROLE_USER),
admin => array(password => kitten, roles => ROLE_ADMIN),

2.1. Livro

163

Symfony Docs pt-BR Documentation, Verso 2.4

),
),
),
),
encoders => array(
Symfony\Component\Security\Core\User\User => plaintext,
),
));

Dica: A distribuio padro do Symfony coloca a configurao de segurana em um arquivo separado (e.g.
app/config/security.yml). Se voc no tem um arquivo separado para as configuraes de segurana, pode
colocar diretamente no arquivo de configurao principal (por exemplo, app/config/config.yml).
O resultado final desta configurao um completo sistema de segurana funcional com as seguintes caractersticas:
H dois usurios no sistema (ryan e admin);
Os usurios se autenticam atravs da janela de autenticao bsica HTTP;
Qualquer URL que comece com /admin/* ser protegida e somente o usurio admin ter acesso;
Todas URLs que no comecem com /admin/* so acessveis a todos usurios (e ao usurio nunca sero
solicitadas as credenciais de acesso).
Vamos dar uma olhada como funciona a segurana e como cada parte da configurao influencia no sistema.
Como funciona a segurana: Autenticao e Autorizao
O sistema de segurana do Symfony funciona determinando quem um usurio (autenticao) e depois checando se
o usurio tem acesso ao recurso especfico ou URL solicitado.
Firewalls (Autenticao)

Quando um usurio requisita uma URL que est protegida por um firewall, o sistema de segurana ativado. O
trabalho do firewall determinar se o usurio precisa ou no ser autenticado. Se ele precisar, envia a resposta de volta
e inicia o processo de autenticao.
Um firewall ser ativado quando a URL requisitada corresponda ao padro de caracteres da expresso regular configurada na configurao de segurana. Neste exemplo, o padro de caracteres (^/) corresponde a
qualquer solicitao. O fato do firewall ser ativado no significa, porm, que a janela de autenticao bsica HTTP
(solicitando login e senha) ser exibida para todas requisies. Por exemplo, qualquer usurio poder acessar /foo
sem que seja solicitada sua autenticao.

164

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Isto funciona primeiramente por que o firewall permite usurios annimos atravs do parmetro anonymous da
configurao. Em outras palavras, o firewall no exige que o usurio se autentique completamente. E por que nenhum
perfil (role) necessrio para acessar /foo (na seo access_control), a solicitao pode ser realizada
sem que o usurio sequer se identifique.
Se voc remover a chave anonymous, o firewall sempre far o usurio se identificar por completo imediatamente.
Controles de acesso (Autorizao)

Se o usurio solicitar /admin/foo, porm, o processo toma um rumo diferente. Isto acontecer por que a seo
access_control da configurao indica que qualquer URL que se encaixe no padro de caracteres ^/admin (isto
, /admin ou qualquer coisa do tipo /admin/*) deve ser acessada somente por usurios com o perfil ROLE_ADMIN.
Perfis so a base para a maioria das autorizaes: o usurio pode acessar /admin/foo somente se tiver o perfil
ROLE_ADMIN.

2.1. Livro

165

Symfony Docs pt-BR Documentation, Verso 2.4

Como antes, o firewall no solicita credenciais de acesso. Assim que a camada de controle de acesso nega o acesso
(por que o usurio no tem o perfil ROLE_ADMIN), porm, o firewall inicia o processo de autenticao. Este processo
depende do mecanismo de autenticao que estiver utilizando. Por exemplo, se estiver utilizando o mtodo de formulrio de autenticao (form login), o usurio ser redirecionado para a pgina de login. Se estiver utilizando o
mtodo bsico de autenticao HTTP, o navegador recebe uma resposta do tipo HTTP 401 para que ao usurio seja
exibida a janela de login/senha do navegador.
O usurio agora tem a oportunidade de digitar suas credenciais no aplicativo. Se as credenciais forem vlidas, a
requisio original ser solicitada novamente.

166

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

No exemplo, o usurio ryan se autentica com sucesso pelo firewall. Como, porm, ryan no tem o perfil
ROLE_ADMIN, ele ainda ter seu acesso negado ao recurso /admin/foo. Infelizmente, isto significa que o usurio
ver uma mensagem indicando que o acesso foi negado.
Dica: Quando o Symfony nega acesso a um usurio, o usurio v uma tela de erro e o navegador recebe uma resposta
com o HTTP status code 403 (Forbidden). possvel personalizar a tela de erro de acesso negado seguindo as
instrues em Error Pages do do texto do Symfony 2 - Passo-a-passo que ensina a personalizar a pgina de erro 403.
Finalmente, se o usurio admin requisitar /admin/foo, um processo similar entra em ao, mas neste caso, aps a
autenticao, a camada de controle de acesso permitir que a requisio seja completada:

2.1. Livro

167

Symfony Docs pt-BR Documentation, Verso 2.4

O fluxo de requisio quando um usurio solicita um recurso protegido direto, mas muito flexvel. Como ver
mais tarde, a autenticao pode acontecer de diversas maneiras, incluindo formulrio de login, certificado X.509, ou
autenticao pelo Twitter. Independente do mtodo de autenticao, o fluxo de requisiao sempre o mesmo:
1. Um usurio acessa um recurso protegido;
2. O aplicativo redireciona o usurio para o formulrio de login;
3. O usurio envia suas credenciais (e.g. login/senha);
4. O firewall autentica o usurio;
5. O usurio autenticado redirecionado para o recurso solicitado originalmente.
Nota: O processo exato na verdade depende um pouco do mecanismo de autenticao que estiver usando. Por
exemplo, quando estiver utilizando formulrio de login, o usurio envia suas credenciais para a URL que processa o
formulrio (por exemplo, /login_check) e depois redirecionado de volta para a URL solicitada originalmente
(por exemplo, /admin/foo). Se utilizar autenticao bsica HTTP, porm, o usurio envia suas credenciais diretamente para a URL original (por exemplo, /admin/foo) e depois a pgina retornada para o usurio na mesma
requisio (isto significa que no h redirecionamentos).
Estes detalhes tcnicos no devem ser relevantes no uso do sistema de segurana, mas bom ter uma idia a respeito.
Dica: Voc aprender mais tarde como qualquer coisa pode ser protegida no Symfony2, incluindo controladores
especficos, objetos, ou at mtodos PHP.

168

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Usando um formulrio de login em HTML


At agora, voc viu como cobrir seu aplicativo depois do firewall e assim restringir o acesso de certas reas a certos
perfis. Utilizando a autenticao bsica HTTP, possvel, sem esforos, submeter login/senha atravs da janela do
navegador. O Symfony, porm, suporta de fbrica muitos outros mecanismos de autenticao. Para detalhes sobre
todos eles, consulte Referncia Da Configurao De Segurana.
Nesta seo, voc aprimorar o processo permitindo que o usurio se autentique atravs de um formulrio de login
tradicional em HTML.
Primeiro habilite o formulrio no seu firewall:
YAML
# app/config/security.yml
security:
firewalls:
secured_area:
pattern:
^/
anonymous: ~
form_login:
login_path:
check_path:

/login
/login_check

XML

<!-- app/config/security.xml -->


<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/ser
<config>
<firewall name="secured_area" pattern="^/">
<anonymous />
<form-login login_path="/login" check_path="/login_check" />
</firewall>
</config>
</srv:container>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
firewalls => array(
secured_area => array(
pattern => ^/,
anonymous => array(),
form_login => array(
login_path => /login,
check_path => /login_check,
),
),
),
));

Dica: Se no precisar de personlizar os valores de login_path ou check_path (os valores utilizados acima so
os valores padro), voc pode encurtar seu configurao:
YAML

2.1. Livro

169

Symfony Docs pt-BR Documentation, Verso 2.4

form_login: ~

XML
<form-login />

PHP
form_login => array(),

Agora, quando o sistema de segurana inicia o processo de autenticao, ele redirecionar o usurio para o formulrio
de login (/login por padro). sua tarefa implementar o visual desse formulrio. Primeiro, crie duas rotas: uma
para a exibio do formulrio de login (no caso, /login) e outra para processar a submisso do formulrio (no caso,
/login_check):
YAML
# app/config/routing.yml
login:
pattern:
/login
defaults: { _controller: AcmeSecurityBundle:Security:login }
login_check:
pattern:
/login_check

XML
<!-- 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/rout
<route id="login" pattern="/login">
<default key="_controller">AcmeSecurityBundle:Security:login</default>
</route>
<route id="login_check" pattern="/login_check" />
</routes>

PHP
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(login, new Route(/login, array(
_controller => AcmeDemoBundle:Security:login,
)));
$collection->add(login_check, new Route(/login_check, array()));
return $collection;

Nota: No preciso implementar o controller para a URL /login_check pois o firewall interceptar e processar o
que foi submitido para essa URL. opcional, porm til, criar uma rota para que voc possa gerar o link de submisso
na template do formulrio de login.

170

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Novo na verso 2.1: Com o Symfony 2.1, voc deve possuir rotas configuradas para suas URLs login_path (ex.
/login), check_path (ex. /login_check) e logout (ex. /logout - veja Logging Out_).
Observe que o nome da rota login no importante. O que importa que a URL da rota corresponda o que foi
colocado na configurao login_path, pois para onde o sistema de segurana redirecionar os usurios que
precisarem se autenticar.
O prximo passo criar o controller que exibir o formulrio de login:
// src/Acme/SecurityBundle/Controller/Main;
namespace Acme\SecurityBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
class SecurityController extends Controller
{
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
// get the login error if there is one
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
}
return $this->render(AcmeSecurityBundle:Security:login.html.twig, array(
// last username entered by the user
last_username => $session->get(SecurityContext::LAST_USERNAME),
error
=> $error,
));
}
}

No se confunda com esse controller. Como ver, quando o usurio submete o formulrio, o sistema de segurana
automaticamente processar a submisso para voc. Se o usurio entrou com login e/ou senha invlidos, este controller
pega o erro ocorrido do sistema de segurana para poder exibir ao usurio.
Em outras palavras, seu trabalho exibir o formulrio de login e qualquer erro ocorrido durante a tentativa de autenticao, mas o sistema de segurana j toma conta de checar se as credenciais so vlidas e de autenticar o usurio.
Finalmente crie a template correspondente:
Twig
{# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig #}
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form action="{{ path(login_check) }}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />

2.1. Livro

171

Symfony Docs pt-BR Documentation, Verso 2.4

{#

If you want to control the URL the user is redirected to on success (more details below)
<input type="hidden" name="_target_path" value="/account" />
#}
<input type="submit" name="login" />
</form>

PHP
<?php // src/Acme/SecurityBundle/Resources/views/Security/login.html.php ?>
<?php if ($error): ?>
<div><?php echo $error->getMessage() ?></div>
<?php endif; ?>
<form action="<?php echo $view[router]->generate(login_check) ?>" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="_username" value="<?php echo $last_username ?>" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />

<!-If you want to control the URL the user is redirected to on success (more details below)
<input type="hidden" name="_target_path" value="/account" />
-->
<input type="submit" name="login" />
</form>

Dica: A varivel error passada para a template uma instncia de AuthenticationException. Esta pode
conter mais informaes - ou at informaes sensveis - sobre a falha na autenticao, por isso use-a com sabedoria!
O formulrio tem que atender alguns requisitos. Primeiro, ao submeter o formulrio para /login_check (atravs
da rota login_check), o sistema de segurana interceptar a submisso do formulrio e o processar. Segundo, o
sistema de segurana espera que os campos submetidos sejam chamados _username e _password (estes nomes
podem ser configured).
E isso! Quando submeter um formulrio, o sistema de segurana ir automaticamente checar as credenciais do
usurio e autentic-lo ou enviar o ele de volta ao formulrio de login para o erro ser exibido.
Vamos revisar o processo inteiro:
1. O usurio tenta acessar um recurso que est protegido;
2. O firewall inicia o processo de autenticao redirecionando o usurio para o formulrio de login(/login);
3. A pgina /login produz o formulrio de login atravs da rota e controlador criados neste exemplo;
4. O usurio submete o formulrio de login para /login_check;
5. O sistema de segurana intercepta a solicitao, verifica as credenciais submetidas pelo usurio, autentica o
mesmo se tiverem corretas ou envia de volta para o formulrio de login caso contrrio;
Por padro, se as credenciais estiverem corretas, o usurio ser redirecionado para a pgina que solicitou originalmente
(e.g. /admin/foo). Se o usurio originalmente solicitar a pgina de login, ele ser redirecionado para a pgina
principal. Isto pode ser modificado se necessrio, o que permitiria voc redirecionar o usurio para um outra URL
especfica.

172

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Para maiores detalhes sobre isso e como personalizar o processamento do formulrio de login acesse Como personalizar o seu Formulrio de Login.

2.1. Livro

173

Symfony Docs pt-BR Documentation, Verso 2.4

Evite os erros comuns


Quando estiver configurando seu formulrio de login, fique atendo aos seguintes erros comuns.
1. Crie as rotas corretas
Primeiro, tenha certeza que definiu as rotas /login e /login_check corretamente e que elas correspondem
aos calores das configuraes login_path e check_path. A configurao errada pode significar que voc
ser redirecionado para a pgina de erro 404 ao invs da pgina de login ou a submisso do formulrio de login
no faa nada (voc sempre v o formulrio sem sair dele).
2. Tenha certeza que a pgina de login no protegida
Tambm tenha certeza que a pgina de login no precisa de qualquer perfil para ser vizualizada. Por exemplo,
a seguinte configurao, que exige o perfil ROLE_ADMIN para todas as URLs (incluindo a URL /login),
causar um redirecionamento circular:
YAML
access_control:
- { path: ^/, roles: ROLE_ADMIN }

XML
<access-control>
<rule path="^/" role="ROLE_ADMIN" />
</access-control>

PHP
access_control => array(
array(path => ^/, role => ROLE_ADMIN),
),

Removendo o controle de acesso para a URL /login resolve o problema:


YAML
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_ADMIN }

XML
<access-control>
<rule path="^/login" role="IS_AUTHENTICATED_ANONYMOUSLY" />
<rule path="^/" role="ROLE_ADMIN" />
</access-control>

PHP
access_control => array(
array(path => ^/login, role => IS_AUTHENTICATED_ANONYMOUSLY),
array(path => ^/, role => ROLE_ADMIN),
),

Alm disso, se o seu firewall no permite usurios annimos, voc precisar criar um firewall especial para
permitir usurios annimos para a pgina de login:
YAML
firewalls:
login_firewall:
pattern:
anonymous:
secured_area:
pattern:
form_login:

^/login$
~
^/
~

XML

174

<firewall name="login_firewall" pattern="^/login$">


<anonymous />
</firewall>
<firewall name="secured_area" pattern="^/">
<form_login />
</firewall>

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Autorizao
O primeiro passo na segurana sempre a autenticao: o processo de verficar quem o usurio . No Symfony, a
autenticao pode ser feita de vrias maneiras - via formulrio de login, autenticao bsica HTTP ou at mesmo pelo
Facebook.
Uma vez que o usurio est autenticado, a autorizao comea. Autorizao fornece uma maneira padro e poderosa
de decidir se o usurio pode acessar algum recurso (uma URL, um objeto do modelo, um mtodo...). Isto funciona
com perfis atribudos para cada usurio e exigindo perfis diferentes para diferentes recursos.
O processo de autorizao tem dois lados diferentes:
1. O usurio tem um conjunto de perfis especfico;
2. Um recurso requer um perfil especfico para ser acessado.
Nesta seo, o foco ser em como tornar seguros diferentes recursos (por exemplo URLs, chamadas a mtodos, etc)
com diferentes perfis. Mais tarde, voc aprender mais como perfis so criados e atribudos aos usurios.
Protegendo padres de URLs

A maneira mais bsica de proteger seu aplicativo proteger um padro de URL. Voc j viu no primeiro exemplo deste
captulo que qualquer requisio que se encaixasse na expresso regular ^/admin exigiria o perfil ROLE_ADMIN.
Voc pode definir quantos padres precisar. Cada um uma expresso regular.
YAML
# app/config/config.yml
security:
# ...
access_control:
- { path: ^/admin/users, roles: ROLE_SUPER_ADMIN }
- { path: ^/admin, roles: ROLE_ADMIN }

XML
<!-- app/config/config.xml -->
<config>
<!-- ... -->
<rule path="^/admin/users" role="ROLE_SUPER_ADMIN" />
<rule path="^/admin" role="ROLE_ADMIN" />
</config>

PHP
// app/config/config.php
$container->loadFromExtension(security, array(
// ...
access_control => array(
array(path => ^/admin/users, role => ROLE_SUPER_ADMIN),
array(path => ^/admin, role => ROLE_ADMIN),
),
));

Dica: Iniciando o padro com ^ garante que somente URLs comeando com o padro ter uma comparao positiva.
Por exemplo, o padro simples /admin (sem o ^) resultaria em uma comparao positiva para /admin/foo, mas
tambm para URLs como /foo/admin.

2.1. Livro

175

Symfony Docs pt-BR Documentation, Verso 2.4

Para cada requisio que chega, o Symfony2 tenta encontrar uma regra de acesso correspondente, com comparao
positiva do padro (a primeira que encontrar ganha). Se o usurio no estiver autenticado ainda, a autenticao
iniciada (isto , o usurio tem a chance de fazer login). Se o usurio, porm, j estiver autenticado, mas no tiver
o perfil exigido, uma exceo disparada AccessDeniedException , que voc pode tratar e transformar em
uma apresentvel pgina de Acesso Negado para o usurio. Veja Como personalizar as pginas de erro para mais
informaes.
Como o Symfony utiliza a primeira regra de acesso que der uma comparao positiva, uma URL como
/admin/users/new corresponder a primeira regra e exigir somente o perfil ROLE_SUPER_ADMIN. Qualquer
URL como /admin/blog corresponder a segunda regra e exigir o perfil ROLE_ADMIN.
Protegendo por IP

Algumas situaes podem exigir que voc restrinja o acesso de uma determinada rota com base no IP. Isto particularmente relevante no caso de Edge Side Includes (ESI), por exemplo, que utiliza a rota com nome _internal. Quanto
ESI utilizado, a rota _internal requerida pelo gateway cache (gerente de cache) para possibilitar diferentes opes
de caching para subsees dentro de uma determinada pgina. Esta rota vem com o prefixo ^/_internal por padro na
edio padro (assumindo que voc ativou estas linhas do seu arquivo de configurao de rotas - routing.yml).
Aqui est um exemplo de como poderia proteger esta rota de acesso externo:
YAML
# app/config/security.yml
security:
# ...
access_control:
- { path: ^/_internal, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }

XML
<access-control>
<rule path="^/_internal" role="IS_AUTHENTICATED_ANONYMOUSLY" ip="127.0.0.1" />
</access-control>

PHP

access_control => array(


array(path => ^/_internal, role => IS_AUTHENTICATED_ANONYMOUSLY, ip => 127.0.0.1
),

Protegendo por canal

Assim como a proteo por IP, exigir o uso de SSL to simples quanto adicionar uma nova entrar em access_control:
YAML
# app/config/security.yml
security:
# ...
access_control:
- { path: ^/cart/checkout, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https

XML

<access-control>
<rule path="^/cart/checkout" role="IS_AUTHENTICATED_ANONYMOUSLY" requires_channel="https" />
</access-control>

176

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

PHP

access_control => array(


array(path => ^/cart/checkout, role => IS_AUTHENTICATED_ANONYMOUSLY, requires_chann
),

Protegendo um Controller

Proteger seu aplicativo baseado em padres de URL fcil, mas este mtodo pode no ser especfico o bastante em
certos casos. Quando necessrio, voc pode ainda facilmente forar autorizao de dentro de um controller:
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
// ...
public function helloAction($name)
{
if (false === $this->get(security.context)->isGranted(ROLE_ADMIN)) {
throw new AccessDeniedException();
}
// ...
}

Voc pode ainda instalar e utilizar opcionalmente o JMSSecurityExtraBundle, que te permite proteger controllers atravs de anotaes:
use JMS\SecurityExtraBundle\Annotation\Secure;
/**
* @Secure(roles="ROLE_ADMIN")
*/
public function helloAction($name)
{
// ...
}

Para mais informaes, veja a documentao JMSSecurityExtraBundle . Se voc a distribuio Standard do Symfony,
este bundle est habilitado por padro. Se no estiver, voc pode facilmente baixar e instal-lo.
Protegendo outros servios

De fato, qualquer coisa pode ser protegida em Symfony utilizando uma estratgia similar a apresentada na seo
anterior. Por exemplo, suponha que voc tem um servio (uma classe PHP, por exemplo) que seu trabalho enviar
e-mails de um usurio para outro. Voc pode restringir o uso dessa classe - no importa de onde est sendo utilizada a usurios que tenham um perfil especfico.
Para mais informaes sobre como voc pode utilizar o componente de segurana para proteger diferentes servios e
mtodos de seu aplicativo, consulte /cookbook/security/securing_services.
Listas De Controle De Acesso (ACLs): Protegendo Objetos Especficos Do Banco De Dados

Imagine que voc est projetando um sistema de blog onde seus usurios podem comentar seus posts. Agora, voc
quer que um usurio tenha a possibilidade de editar seus prprios comentrios, mas no aqueles de outros usurios.
Alm disso, como administrador, voc quer poder editar todos os comentrios.

2.1. Livro

177

Symfony Docs pt-BR Documentation, Verso 2.4

O componente de segurana possui um sistema de listas de controle de acesso (ACL) que te permite controlar acesso
a instncias individuais de um objeto no seu sistema. Sem ACL, voc consegue proteger seu sistema para que somente
usurios especficos possam editar os comentrios. Com ACL, porm, voc pode restringir ou permitir o acesso por
comentrio.
Para mais informao, veja o passo-a-passo: Listas de controle de acesso (ACLs).
Usurios
Nas sees anteriores, voc aprendeu como proteger diferentes recursos exigindo um conjunto de perfis para o acesso
a um recurso. Nesta seo exploraremos outro aspecto da autorizao: os usurios.
De onde os usurios vm? (User Providers)

Durante a autenticao, o usurio submete um conjunto de credenciais (normalmente login e senha). O trabalho do
sistema de autenticao verificar essas credenciais contra um conjunto de usurios. De onde essa lista de usurios
vem ento?
No Symfony2, usurios podem vir de qualquer lugar - um arquivo de configurao, um banco de dados, um servio
web ou qualquer outra fonte que desejar. Qualquer coisa que disponibiliza um ou mais usurios para o sistema de
autenticao conhecido como user provider. O Symfony2 vem por padro com os dois mais comuns: um que
carrega os usurios do arquivo de configurao e outro que carrega os usurios do banco de dados.
Especificando usurios no arquivo de configurao O jeito mais fcil de especificar usurios diretamnete no
arquivo de configurao. De fato, voc j viu isso em um exemplo neste captulo.
YAML
# app/config/config.yml
security:
# ...
providers:
default_provider:
memory:
users:
ryan: { password: ryanpass, roles: ROLE_USER }
admin: { password: kitten, roles: ROLE_ADMIN }

XML
<!-- app/config/config.xml -->
<config>
<!-- ... -->
<provider name="default_provider">
<memory>
<user name="ryan" password="ryanpass" roles="ROLE_USER" />
<user name="admin" password="kitten" roles="ROLE_ADMIN" />
</memory>
</provider>
</config>

PHP
// app/config/config.php
$container->loadFromExtension(security, array(
// ...

178

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

providers => array(


default_provider => array(
memory => array(
users => array(
ryan => array(password => ryanpass, roles => ROLE_USER),
admin => array(password => kitten, roles => ROLE_ADMIN),
),
),
),
),
));

Este user provider chamado de in-memory user provider, j que os usurios no esto armazenados em nenhum
banco de dados. O objeto usurio fornecido pelo Symfony (User).
Dica: Qualquer user provider pode carregar usurios diretamente da configurao se especificar o parmetro de
configurao users e listar os usurios abaixo dele.
Cuidado: Se seu login todo numrico (77, por exemplo) ou contm hfen (user-name, por exemplo), voc
deveria utilizar a sintaxe alternativa quando especificar usurios em YAML:
users:
- { name: 77, password: pass, roles: ROLE_USER }
- { name: user-name, password: pass, roles: ROLE_USER }

Para sites menores, este mtodo rpido e fcil de configurar. Para sistemas mais complexos, voc provavelmente
desejar carregar os usurios do banco de dados.
Carregando usurios do banco de dados Se voc desejar carregar seus usurios atravs do Doctrine ORM, voc
pode facilmente o fazer criando uma classe User e configurando o entity provider
Nessa abordagem, voc primeiro precisa criar sua prpria classe User, que ser persistida no banco de dados.
// src/Acme/UserBundle/Entity/User.php
namespace Acme\UserBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class User implements UserInterface
{
/**
* @ORM\Column(type="string", length="255")
*/
protected $username;
// ...
}

Ao que diz respeito ao sistema de segurana, o nico requisito para sua classe User personalizada que ela implemente a interface UserInterface . Isto significa que conceito de usurio pode ser qualquer um, desde que
implemente essa interface.

2.1. Livro

179

Symfony Docs pt-BR Documentation, Verso 2.4

Novo na verso 2.1: No Symfony 2.1, o mtodo equals foi removido do UserInterface. Se voc precisa sobrescrever a implementao default da lgica de comparao, implemente a nova interface EquatableInterface
.
Nota: O objeto User ser serializado e salvo na sesso entre requisies, por isso recomendado que voc implemente
a interface Serializable em sua classe User. Isto especialmente importante se sua classe User tem uma classe pai
com propriedades private.
Em seguida, configure um user provider entity e aponte-o para sua classe User:
YAML
# app/config/security.yml
security:
providers:
main:
entity: { class: Acme\UserBundle\Entity\User, property: username }

XML
<!-- app/config/security.xml -->
<config>
<provider name="main">
<entity class="Acme\UserBundle\Entity\User" property="username" />
</provider>
</config>

PHP

// app/config/security.php
$container->loadFromExtension(security, array(
providers => array(
main => array(
entity => array(class => Acme\UserBundle\Entity\User, property => username
),
),
));

Com a introduo desse novo provider, o sistema de autenticao tentar carregar o objeto User do banco de dados a
partir do campo username da classe.
Nota: Este exemplo somente para demonstrar a idia bsica por trs do provider entity. Para um exemplo
completo, consulte /cookbook/security/entity_provider.
Para mais informaes sobre como criar seu prprio provider (se precisar carregar usurios do seu servio web por
exemplo), consulte Como criar um Provider de Usurio Personalizado.
Protegendo a senha do usurio

At agora, por simplicidade, todos os exemplos armazenavam as senhas dos usurios em texto puro (sendo armazenados no arquivo de configurao ou no banco de dados). Claro que em um aplicativo profissional voc desejar
proteger as senhas dos seus usurios por questes de segurana. Isto facilmente conseguido mapeando sua classe
User para algum encoder disponvel. Por exemplo, para armazenar seus usurio em memria, mas proteger a senha
deles atravs da funo de hash sha1, faa o seguinte:
YAML

180

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

# app/config/config.yml
security:
# ...
providers:
in_memory:
memory:
users:
ryan: { password: bb87a29949f3a1ee0559f8a57357487151281386, roles: ROLE_US
admin: { password: 74913f5cd5f61ec0bcfdb775414c2fb3d161b620, roles: ROLE_AD
encoders:
Symfony\Component\Security\Core\User\User:
algorithm:
sha1
iterations: 1
encode_as_base64: false

XML

<!-- app/config/config.xml -->


<config>
<!-- ... -->
<provider name="in_memory">
<memory>
<user name="ryan" password="bb87a29949f3a1ee0559f8a57357487151281386" roles="ROLE_US
<user name="admin" password="74913f5cd5f61ec0bcfdb775414c2fb3d161b620" roles="ROLE_A
</memory>
</provider>

<encoder class="Symfony\Component\Security\Core\User\User" algorithm="sha1" iterations="1" e


</config>

PHP

// app/config/config.php
$container->loadFromExtension(security, array(
// ...
providers => array(
in_memory => array(
memory => array(
users => array(
ryan => array(password => bb87a29949f3a1ee0559f8a57357487151281386, r
admin => array(password => 74913f5cd5f61ec0bcfdb775414c2fb3d161b620,
),
),
),
),
encoders => array(
Symfony\Component\Security\Core\User\User => array(
algorithm
=> sha1,
iterations
=> 1,
encode_as_base64 => false,
),
),
));

Ao definir iterations como 1 e encode_as_base64 como false, a senha codificada simplesmente obtida
como o resultado de sha1 aps uma iterao apenas, sem codificao extra. Voc pode agora calcular a senha
codificada por cdigo PHP (e.g. hash(sha1, ryanpass)) ou atravs de alguma ferramenta online como
functions-online.com .
2.1. Livro

181

Symfony Docs pt-BR Documentation, Verso 2.4

Se voc estiver criando seus usurio dinamicamente e os armazenando no banco de dados, voc pode usar algoritmos
the hash ainda mais complexos e ento delegar em um objeto encoder para ajudar a codificar as senhas. Por exemplo, suponha que seu objeto User Acme\UserBundle\Entity\User (como no exemplo acima). Primeiro,
configure o encoder para aquele usurio:
YAML
# app/config/config.yml
security:
# ...
encoders:
Acme\UserBundle\Entity\User: sha512

XML
<!-- app/config/config.xml -->
<config>
<!-- ... -->
<encoder class="Acme\UserBundle\Entity\User" algorithm="sha512" />
</config>

PHP
// app/config/config.php
$container->loadFromExtension(security, array(
// ...
encoders => array(
Acme\UserBundle\Entity\User => sha512,
),
));

Neste caso, voc est utilizando um algoritmo mais forte sha512. Alm disso, desde que voc especificou o algoritmo
(sha512) como um texto, o sistema ir, por padro, utilizar a funo de hash 5000 vezes em uma linha e ento
o codificar como base64. Em outras palavras, a senha foi muito codificada de maneira que a senha no pode ser
decodificada (isto , voc no pode determinar qual a senha a partir da senha codificada).
Se voc tem alguma espcie de formulrio de registro para os visitantes, voc precisar a senha codificada para poder armazenar. No importa o algoritmo que configurar para sua classe User, a senha codificada pode sempre ser
determinada da seguinte maneira a partir de um controller:
$factory = $this->get(security.encoder_factory);
$user = new Acme\UserBundle\Entity\User();
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword(ryanpass, $user->getSalt());
$user->setPassword($password);

Obtendo o objeto User

Aps a autenticao, o objeto User do usurio atual pode ser acessado atravs do servio security.context.
De dentro de um controller, faa o seguinte:
public function indexAction()
{

182

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

$user = $this->get(security.context)->getToken()->getUser();
}

No controller, tambm existe o atalho:


public function indexAction()
{
$user = $this->getUser();
}

Nota: Usurios annimos so tecnicamente autenticados, significando que o mdodo isAuthenticated() de um


objeto User autenticado anonimamente retornar verdadeiro. Para verificar se seu usurio est realmente autenticado,
verifique se o perfil IS_AUTHENTICATED_FULLY est atribudo ao mesmo.

Utilizando mltiplos User Providers

Cada mecanismo de autenticao (exemplos: Autenticao HTTP, formulrio de login, etc) usa exatamente um user
provider, e utilizar, por padro, o primeiro user provider configurado. O que acontece se voc quiser que alguns de
seus usurios sejam autenticados por arquivo de configurao e o resto por banco de dados? Isto possvel criando
um novo user provider que ativa os dois juntos:
YAML
# app/config/security.yml
security:
providers:
chain_provider:
chain:
providers: [in_memory, user_db]
in_memory:
users:
foo: { password: test }
user_db:
entity: { class: Acme\UserBundle\Entity\User, property: username }

XML
<!-- app/config/config.xml -->
<config>
<provider name="chain_provider">
<chain>
<provider>in_memory</provider>
<provider>user_db</provider>
</chain>
</provider>
<provider name="in_memory">
<user name="foo" password="test" />
</provider>
<provider name="user_db">
<entity class="Acme\UserBundle\Entity\User" property="username" />
</provider>
</config>

PHP
// app/config/config.php
$container->loadFromExtension(security, array(

2.1. Livro

183

Symfony Docs pt-BR Documentation, Verso 2.4

providers => array(


chain_provider => array(
chain => array(
providers => array(in_memory, user_db),
),
),
in_memory => array(
users => array(
foo => array(password => test),
),
),
user_db => array(
entity => array(class => Acme\UserBundle\Entity\User, property => username
),
),
));

Agora, todos mecanismos de autenticao utilizaro o chain_provider, j que o primeiro configurado. O


chain_provider tentar carregar o usurio de ambos providers in_memory e user_db.
Dica: Se voc no tem razes para separar seus usurios in_memory dos seus usurios user_db, voc pode
conseguir o mesmo resultado mais facilmente, combinando as duas origens em um nico provider:
YAML
# app/config/security.yml
security:
providers:
main_provider:
memory:
users:
foo: { password: test }
entity:
class: Acme\UserBundle\Entity\User,
property: username

XML
<!-- app/config/config.xml -->
<config>
<provider name=="main_provider">
<memory>
<user name="foo" password="test" />
</memory>
<entity class="Acme\UserBundle\Entity\User" property="username" />
</provider>
</config>

PHP
// app/config/config.php
$container->loadFromExtension(security, array(
providers => array(
main_provider => array(
memory => array(
users => array(
foo => array(password => test),
),
),

184

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

entity => array(class => Acme\UserBundle\Entity\User, property => username


),
),
));

Voc pode ainda configurar o firewall ou mecanismos de autenticao individuais para utilizar um user provider especfico. Novamente, a menos que um provider seja especificado explicitamente, o primeiro ser sempre utilizado:
YAML
# app/config/config.yml
security:
firewalls:
secured_area:
# ...
provider: user_db
http_basic:
realm: "Secured Demo Area"
provider: in_memory
form_login: ~

XML
<!-- app/config/config.xml -->
<config>
<firewall name="secured_area" pattern="^/" provider="user_db">
<!-- ... -->
<http-basic realm="Secured Demo Area" provider="in_memory" />
<form-login />
</firewall>
</config>

PHP
// app/config/config.php
$container->loadFromExtension(security, array(
firewalls => array(
secured_area => array(
// ...
provider => user_db,
http_basic => array(
// ...
provider => in_memory,
),
form_login => array(),
),
),
));

Neste exemplo, se um usurio tentar se autenticar atravs de autenticao HTTP, o sistema utilizar o user provider
in_memory. Se o usurio tentar, porm, se autenticar atravs do formulrio de login, o provider user_db ser
usado (pois o padro para todo o firewall).
Para mais informaes sobre a configurao
/reference/configuration/security.

2.1. Livro

do

user

provider

do

firewall,

veja

185

Symfony Docs pt-BR Documentation, Verso 2.4

Perfis (Roles)
A idia de um perfil chave no processo de autorizao. Para cada usurio atribudo um conjunto de perfis e ento
cada recurso exige um ou mais perfis. Se um usurio tem os perfis requeridos, o acesso concedido. Caso contrrio,
o acesso negado.
Perfis so muito simples e basicamente textos que voc pode inventar e utilizar de acordo com suas necessidades
(embora perfis sejam objetos PHP internamente). Por exemplo, se precisar limitar acesso a uma seo administrativa
do blog de seu website, voc pode proteger a seo utilizando o perfil ROLE_BLOG_ADMIN. Este perfil no precisa
de estar definido em lugar nenhum - voc pode simplesmente usar o mesmo.
Nota: Todos os perfis devem comear com o prefixo ROLE_ para serem gerenciados pelo Symfony2. Se voc definir
seus prprios perfis com uma classe Role dedicada (mais avanado), no utilize o prefixo ROLE_.

Hierarquia de Perfis

Ao invs de associar muitos perfis aos usurios, voc pode defined regras de herana ao criar uma hierarquia de perfis:
YAML
# app/config/security.yml
security:
role_hierarchy:
ROLE_ADMIN:
ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

XML
<!-- app/config/security.xml -->
<config>
<role id="ROLE_ADMIN">ROLE_USER</role>
<role id="ROLE_SUPER_ADMIN">ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH</role>
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
role_hierarchy => array(
ROLE_ADMIN
=> ROLE_USER,
ROLE_SUPER_ADMIN => array(ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH),
),
));

Na configurao acima, usurios com o perfil ROLE_ADMIN tero tambm o perfil ROLE_USER. O perfil ROLE_SUPER_ADMIN tem os ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH e ROLE_USER (herdado do
ROLE_ADMIN).
Saindo do sistema
Normalmente, voc tambm quer que seus usurios possam sair do sistema. Felizmente, o firewall consegue lidar com
isso automaticamente quando o parmetro de configurao logout est ativo:
YAML

186

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

# app/config/config.yml
security:
firewalls:
secured_area:
# ...
logout:
path:
/logout
target: /
# ...

XML
<!-- app/config/config.xml -->
<config>
<firewall name="secured_area" pattern="^/">
<!-- ... -->
<logout path="/logout" target="/" />
</firewall>
<!-- ... -->
</config>

PHP
// app/config/config.php
$container->loadFromExtension(security, array(
firewalls => array(
secured_area => array(
// ...
logout => array(path => logout, target => /),
),
),
// ...
));

Uma vez que est configurado no seu firewall, redirecionando o usurio para /logout (ou qualquer outro caminho
que configurar em path), o usurio no estar mais autenticado. O usurio ser ento redirecionado para a pgina
principal (o valor definido no parmetro target). Ambas configuraes path e target tem valor padro iguais
ao especificado aqui. Em outras palavras, a menos que precise personalizar, voc pode simplesmente os omitir completamente e simplificar sua configurao:
YAML
logout: ~

XML
<logout />

PHP
logout => array(),

Note que voc no precisar implementar o controller para a URL /logout j que o firewall cuida disso. Voc deve,
entretante, precisar criar uma rota para que possa usar para gerar a URL:
Aviso: Com o Symfony 2.1, voc deve ter uma rota que corresponde ao seu caminho para logout. Sem esta rota,
o logout no ir funcionar.
YAML

2.1. Livro

187

Symfony Docs pt-BR Documentation, Verso 2.4

# app/config/routing.yml
logout:
pattern:
/logout

XML
<!-- 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/rout
<route id="logout" pattern="/logout" />
</routes>

PHP
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(logout, new Route(/logout, array()));
return $collection;

Uma vez que o usurio no est mais autenticado, ele ser redirecionado para o que tiver definido no parmetro target. Para mais informaes sobre a configurao de logout, veja Security Configuration
Reference.
Controle de Acesso em Templates
Se voc quiser checar se o usurio atual tem um determinado perfil de dentro de uma template, use a funo:
Twig
{% if is_granted(ROLE_ADMIN) %}
<a href="...">Delete</a>
{% endif %}

PHP
<?php if ($view[security]->isGranted(ROLE_ADMIN)): ?>
<a href="...">Delete</a>
<?php endif; ?>

Nota: Se voc usar esta funo e no estiver em uma URL que est atrs de um firewall ativo, uma exceo ser
gerada. Novamente, quase sempre uma boa idia ter um firewall principal que protege todas as URLs (como visto
neste captulo).

Controle de Acesso em Controllers


Se voc quer verificar se o usurio atual tem um perfil de dentro de um controller, use o mtodo isGranted do
contexto de segurana:

188

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

public function indexAction()


{
// show different content to admin users
if ($this->get(security.context)->isGranted(ADMIN)) {
// Load admin content here
}
// load other regular content here
}

Nota: Um firewall deve estar ativo ou uma exceo ser gerada quanto o mtodo isGranted for chamado. Veja a
nota acima sobre templates para mais detalhes.

Passando por outro usurio


As vezes, til poder trocar de um usurio para outro sem ter que sair e se autenticar novamente (por exemplo quando
voc est depurando or tentando entender uma falha que um usurio v e voc no consegue reproduzir). Isto pode ser
feito ativando o listener switch_user do firewall:
YAML
# app/config/security.yml
security:
firewalls:
main:
# ...
switch_user: true

XML
<!-- app/config/security.xml -->
<config>
<firewall>
<!-- ... -->
<switch-user />
</firewall>
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
firewalls => array(
main=> array(
// ...
switch_user => true
),
),
));

Para mudar para outro usurio, basta adicionar o parmetro de URL _switch_user indicando o usurio (username)
na URL atual:
http://example.com/somewhere?_switch_user=thomas
Para voltar ao usurio original, use como nome de usurio o texto _exit:
http://example.com/somewhere?_switch_user=_exit

2.1. Livro

189

Symfony Docs pt-BR Documentation, Verso 2.4

Claro que esta funcionalidade precisar estar disponvel para um grupo reduzido de usurios. Por padro, o acesso
restrito a usurios que tem o perfil ROLE_ALLOWED_TO_SWITCH. O nome deste perfil pode ser modificado atravs
do parmetro de configurao role. Para segurana extra, voc pode ainda mudar o nome do parmetro de URL
atravs da configurao parameter:
YAML
# app/config/security.yml
security:
firewalls:
main:
// ...
switch_user: { role: ROLE_ADMIN, parameter: _want_to_be_this_user }

XML
<!-- app/config/security.xml -->
<config>
<firewall>
<!-- ... -->
<switch-user role="ROLE_ADMIN" parameter="_want_to_be_this_user" />
</firewall>
</config>

PHP

// app/config/security.php
$container->loadFromExtension(security, array(
firewalls => array(
main=> array(
// ...
switch_user => array(role => ROLE_ADMIN, parameter => _want_to_be_this_user
),
),
));

Autenticao Sem Estado


Por padro, o Symfony2 confia a um cookie (a Session) para persistir o contexto de segurana de um usurio. Se voc
utiliza, porm, certificados ou autenticao HTTP, por exemplo, persistncia no necessrio j que as credenciais
esto disponveis em cada requisio. Neste caso, e se no precisar de armazenar nada entre as requisies, voc pode
ativar a autenticao sem estado (que significa que nenhum cookie ser criado pelo Symfony2):
YAML
# app/config/security.yml
security:
firewalls:
main:
http_basic: ~
stateless: true

XML
<!-- app/config/security.xml -->
<config>
<firewall stateless="true">
<http-basic />

190

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

</firewall>
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
firewalls => array(
main => array(http_basic => array(), stateless => true),
),
));

Nota: Se utiliza formulrio de login, o Symfony2 criar um cookie mesmo se voc definir stateless como true.

Palavras Finais
Segurana pode ser um assunto profundo e complexo de se resolver em uma aplicao. Felizmente, o componente
de segurana do Symfony segue um bom modelo baseado em autenticao e autorizao. Autenticao, que sempre acontece antes, gerenciada pelo firewall cujo trabalho determinar a identidade do usurio atravs de diversos
possveis mtodos (exemplo, autenticao HTTP, formulrio de login, etc). No passo-a-passo, voc encontrar exemplos de como outros mtodos de autenticao podem ser utilizados, incluindo como implementar o funcionalidade de
Lembrar de mim baseada em cookie.
Uma vez que o usurio est autenticado, a camada de autorizao pode determinar se o usurio deve ou no deve ter
acesso a um recurso especfico. Comumente, perfis so aplicados a URLs, classes ou mtodos e se o usurio atual
no possuir o perfil, o acesso negado. A camada de autorizao, porm, muito mais extensa e segue o sistema de
votao onde vrias partes podem determinar se o usurio atual deve ter acesso a determinado recurso. Saiba mais
sobre este e outros tpicos no passo-a-passo.
Aprenda mais do Passo-a-Passo
Forando HTTP/HTTPS
Coloque usurios por IP na lista negra com um voter personalizado
Listas de Controle de Acesso (ACLs)
/cookbook/security/remember_me

2.1.12 HTTP Cache


A natureza das aplicaes web ricas que elas sejam dinmicas. No importa quo eficiente seja sua aplicao, cada
uma das requisies sempre ter uma carga maior do que se ela servisse um arquivo esttico.
E, para a maior parte das aplicaes web, isso no problema. O Symfony 2 extremamente rpido e, a menos que
voc esteja fazendo algo extramente pesado, as requisies sero retornadas rapidamente sem sobrecarregar demais o
seu servidor.
Mas, a medida que seu site cresce, essa carga adicional pode se tornar um problema. O processamento que normalmente efetuado a cada requisio deveria ser feito apenas uma vez. exatamente esse o objetivo do cache.

2.1. Livro

191

Symfony Docs pt-BR Documentation, Verso 2.4

Fazendo Cache nos Ombros de Gigantes


O modo mais efetivo de melhorar a performance de uma aplicao fazendo cache da sada completa de uma pgina e
ento ignorar a aplicao totalmente nas requisies seguintes. claro, nem sempre isso possvel para sites altamente
dinmicos. Ou ser que ? Nesse captulo, veremos como o sistema de cache do Symfony2 trabalha e por que ns
acreditamos que esta a melhor abordagem possvel.
O sistema de cache do Symfony2 diferente porque ele se baseia na simplicidade e no poder do cache HTTP como
definido na especificao HTTP. Em vez de inventar uma nova metodologia de cache, o Symfony2 segue o padro que
define a comunicao bsica na Web. Quando voc entender os modelos fundamentais de validao e expirao de
cache HTTP estar pronto para dominar o sistema de cache do Symfony2.
Para os propsitos de aprender como fazer cache com o Symfony2, cobriremos o assunto em quatro passos:
Passo 1: Um gateway cache, ou proxy reverso, uma camada independente que fica na frente da sua aplicao.
O proxy reverso faz o cache das respostas quando elas so retornadas pela sua aplicao e responde as requisies com respostas cacheadas antes que elas atinjam sua aplicao. O Symfony2 fornece um proxy reverso
prprio, mas qualquer proxy reverso pode ser usado.
Passo 2: Cabealhos de cache:ref:cache HTTP<http-cache-introduction> so usados para comunicar com o
gateway cache e qualquer outro cache entre sua aplicao e o cliente. O Symfony2 fornece padres razoveis e
uma interface poderosa para interagir com os cabealhos de cache.
Passo 3: Expirao e validao HTTP so dois modelos usados para determinar se o contedo cacheado
atual/fresh (pode ser reutilizado a partir do cache) ou se o contedo antigo/stale (deve ser recriado pela
aplicao).
Passo 4: Edge Side Includes (ESI) permitem que sejam usados caches HTTP para fazer o cache de fragmentos
de pginas (mesmo fragmentos aninhados) independentemente. Com o ESI, voc pode at fazer o cache de uma
pgina inteira por 60 minutos, e uma barra lateral embutida por apenas 5 minutos.
Como fazer cache com HTTP no uma coisa apenas do Symfony, j existem muitos artigos sobre o assunto. Se voc
for iniciante em cache HTTP, recomendamos fortemente o artigo Things Caches Do do Ryan Tomayko. Outra fonte
aprofundada o Cache Tutorial do Mark Nottingham.
Fazendo Cache com um Gateway Cache
Quando se faz cache com HTTP, o cache separado completamente da sua aplicao e se coloca entre sua aplicao
e o cliente que est fazendo a requisio.
O trabalho do cache receber requisies do cliente e transferi-las para sua aplicao. O cache tambm receber
de volta respostas da sua aplicao e as encaminhar para o cliente. O cache o middle-man da comunicao
requisio-resposta entre o cliente e sua aplicao.
Ao longo do caminho, o cache guardar toda resposta que seja considerada cachevel (Veja Introduo ao Cache HTTP). Se o mesmo recurso for requisitado novamente, o cache ir mandar a resposta cacheada para o cliente,
ignorando completamente sua aplicao.
Esse tipo de cache conhecido como um gateway cache HTTP e existem vrios como o Varnish, o Squid in reverse
proxy mode e o proxy reverso do Symfony2.
Tipos de Cache

Mas um gateway cache no o nico tipo de cache. Na verdade, os cabealhos de cache HTTP enviados pela sua
aplicao so consumidos e interpretados por trs tipos diferentes de cache:

192

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Caches de Navegador: Todo navegador vem com seu prprio cache local que til principalmente quando voc
aperta o voltar ou para imagens e outros assets. O cache do navegador um cache privado assim os recursos
cacheados no so compartilhados com ningum mais.
Caches de Proxy: Um proxy um cache compartilhado assim muitas pessoas podem utilizar um nico deles.
Ele geralmente instalado por grandes empresas e ISPs para reduzir a latncia e o trfego na rede.
Caches Gateway: Como um proxy, ele tambm um cache compartilhado mas no lado do servidor. Instalado
por administradores de rede, ele torna os sites mais escalveis, confiveis e performticos.
Dica: Caches gateway algumas vezes so referenciados como caches de proxy reverso, surrogate caches ou at
aceleradores HTTP.
Nota: A diferena entre os caches privados e os compartilhados se torna mais bvia a medida que comeamos a falar
sobre fazer cache de respostas com contedo que especfico para exatamente um usurio (e.g. informao de uma
conta).
Toda resposta da sua aplicao ir provavelmente passar por um ou ambos os dois primeiros tipos de cache. Esses
caches esto fora de seu controle mas eles seguem o direcionamento do cache HTTP definido na resposta.
Proxy Reverso do Symfony2

O Symfony2 vem com um proxy reverso (tambm chamado de gateway cache) escrito em PHP. s habilit-lo e as
respostas cacheveis da sua aplicao comearam a ser cacheadas no mesmo momento. Sua instalao bem simples.
Toda nova aplicao Symfony2 vem com um kernel de cache pr-configurado (AppCache) que encapsula o kernel
padro (AppKernel). O Kernel de cache o proxy reverso.
Para habilitar o cache, altere o cdigo do front controller para utilizar o kernel de cache:
// web/app.php
require_once __DIR__./../app/bootstrap.php.cache;
require_once __DIR__./../app/AppKernel.php;
require_once __DIR__./../app/AppCache.php;
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel(prod, false);
$kernel->loadClassCache();
// wrap the default AppKernel with the AppCache one
$kernel = new AppCache($kernel);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

O kernel de cache funcionar imediatamente como um proxy reverso - fazendo cache das respostas da sua aplicao e
retornando-as para o cliente.
Dica: O kernel de cache tem um mtodo especial getLog() que retorna uma representao em texto do que ocorreu
na camada de cache. No ambiente de desenvolvimento, utilize-o para depurar e validar sua estratgia de cache:
error_log($kernel->getLog());

O objeto AppCache tem uma configurao padro razovel, mas ela pode receber um ajuste fino por meio de um
conjunto de opes que podem ser definidas sobrescrevendo o mtodo getOptions():
2.1. Livro

193

Symfony Docs pt-BR Documentation, Verso 2.4

// app/AppCache.php
class AppCache extends Cache
{
protected function getOptions()
{
return array(
debug
default_ttl
private_headers
allow_reload
allow_revalidate
stale_while_revalidate
stale_if_error
);
}
}

=>
=>
=>
=>
=>
=>
=>

false,
0,
array(Authorization, Cookie),
false,
false,
2,
60,

Dica: A menos que seja sobrescrita em getOptions(), a opo debug ser definida como o valor padro de
depurao no AppKernel envolvido.
Aqui vai uma lista das opes principais:
default_ttl: O nmero de segundos que uma entrada do cache deve ser considerada como atual quando
nenhuma informao de atualizao for passada na resposta. Os cabealhos explcitos Cache-Control e
Expires sobrescrevem esse valor (padro: 0);
private_headers:
Conjunto de cabealhos de requisio que acionam o comportamento
Cache-Control privado nas respostas que no declaram explicitamente se a resposta public ou
private por meio de uma diretiva Cache-Control. (padro: Authorization e Cookie);
allow_reload: Diz se o cliente pode forar um recarregamento do cache incluindo uma diretiva
Cache-Control no-cache na requisio. Defina ele como true para seguir a RFC 2616 (padro: false);
allow_revalidate: Diz se o cliente pode forar uma revalidao do cache incluindo uma diretiva
Cache-Control max-age=0 na requisio. Defina ele como true para seguir a RFC 2616 (padro: false);
stale_while_revalidate: Diz o nmero padro de segundos (a granularidade o segundo como na
preciso da Resposta TTL) durante o qual o cache pode retornar imediatamente uma resposta antiga enquanto ele faz a revalidao dela no segundo plano (padro: 2); essa configurao sobrescrita pela extenso
stale-while-revalidate do Cache-Control HTTP (veja RFC 5861);
stale_if_error: Diz o nmero padro de segundos (a granularidade o segundo) durante o qual o cache
pode fornecer uma resposta antiga quando um erro for encontrado (padro: 60). Essa configurao sobrescrita
pela extenso stale-if-error do Cache-Control HTTP (veja RFC 5861).
Se debug for true, o Symfony2 adiciona automaticamente um cabealho X-Symfony-Cache na resposta contendo informaes teis sobre o que o cache serviu ou deixou passar.

194

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Mudando de um Proxy Reverso para Outro


O proxy reverso do Symfony2 uma ferramenta importante quando estiver desenvolvendo o seu site ou quando
voc faz o deploy de seu site num servidor compartilhado onde voc no pode instalar nada mais do que cdigo
PHP. Mas como ele escrito em PHP, no h como ele ser to rpido quanto um proxy escrito em C. por isso
que recomendamos fortemente que voc utilize o Varnish ou o Squid no seu servidor de produo quando for
possvel. A boa notcia que mudar entre um servidor de proxy para outro fcil e transparente pois nenhuma
alterao de cdigo necessria em sua aplicao. Inicie de forma simples com o proxy reverso do Symfony2 e
depois atualize para o Varnish quando o seu trfego aumentar.
Para mais informaes de como usar o Varnish com o Symfony2, veja o captulo How to use Varnish do cookbook.
Nota: A performance do proxy reverso do Symfony2 no depende da complexidade da sua aplicao. Isso acontece
porque o kernel da aplicao s carregado quando a requisio precisar ser passada para ele.

Introduo ao Cache HTTP


Para tirar vantagem das camadas de cache disponveis, sua aplicao precisa ser capaz de informar quais respostas so
cacheveis e as regras que governam quando/como o cache o se torna antigo. Isso feito configurando os cabealhos
HTTP na sua resposta.
Dica: Lembre que o HTTP nada mais do que uma linguagem (um linguagem de texto simples) que os clientes web
(e.g navegadores) e os servidores web utilizam para se comunicar uns com os outros. Quando falamos sobre o cache
HTTP, estamos falando sobre a parte dessa linguagem que permite que os clientes e servidores troquem informaes
relacionadas ao cache.
O HTTP define quatro cabealhos de cache para as respostas que devemos nos preocupar:
Cache-Control
Expires
ETag
Last-Modified
O cabealho mais importante e verstil o cabealho Cache-Control, que na verdade uma coleo de vrias
informaes de cache.
Nota: Cada um dos cabealhos ser explicado detalhadamente na seo Expirao e Validao HTTP.

O Cabealho Cache-Control

O cabealho Cache-Control nico pois ele contm no um, mas vrios pedaos de informao sobre a possibilidade de cache de uma resposta. Cada pedao de informao separada por uma vrgula:
Cache-Control: private, max-age=0, must-revalidate
Cache-Control: max-age=3600, must-revalidate
O Symfony fornece uma abstrao em volta do cabealho Cache-Control para deixar sua criao mais gerencivel:
$response = new Response();
// mark the response as either public or private

2.1. Livro

195

Symfony Docs pt-BR Documentation, Verso 2.4

$response->setPublic();
$response->setPrivate();
// set the private or shared max age
$response->setMaxAge(600);
$response->setSharedMaxAge(600);
// set a custom Cache-Control directive
$response->headers->addCacheControlDirective(must-revalidate, true);

Respostas Pblicas vs Privadas

Tanto o gateway cache quando o proxy cache so considerados caches compartilhados pois o contedo cacheado
compartilhado por mais de um usurio. Se uma resposta especfica de um usurio for incorretamente armazenada por
um cache compartilhado, ela poderia ser retornada posteriormente para um nmero incontvel de usurios diferentes.
Imagine se a informao da sua conta fosse cacheada e depois retornada para todos os usurios que em seguida
solicitassem a pgina da conta deles!
Para lidar com essa situao, cada resposta precisa ser configurada para ser pblica ou privada:
public: Indica que a resposta pode ser cacheada tanto por caches privados quanto pelos compartilhados;
private: Indica que a mensagem toda ou parte da resposta destinada para um nico usurio e no deve ser
cacheada por um cache compartilhado.
O Symfony tem como padro conservador definir toda resposta como privada. Para se beneficiar dos caches compartilhados (como o proxy reverso do Symfony2), a resposta precisa ser definida como pblica explicitamente.
Mtodos Seguros

O cache HTTP s funciona para os mtodos HTTP seguros (como o GET e o HEAD). Ser seguro significa que voc
no consegue alterar o estado da aplicao no servidor quando estiver respondendo a requisio ( claro que voc pode
logar a informao, fazer cache dos dados etc). Isso tem duas consequncias importantes:
Voc nunca deve alterar o estado de sua aplicao quando estiver respondendo uma requisio GET ou HEAD.
Mesmo se voc no usar um gateway cache, a presena de caches proxy faz com que qualquer requisio GET
ou HEAD possa atingir ou no seu servidor.
No espere que os mtodos PUT, POST ou DELETE sejam cacheados. Esses mtodos so destinados para serem
utilizados quando se quer alterar o estado da sua aplicao (e.g. excluir uma postagem de um blog). Fazer cache
desses mtodos poderia fazer com que certas requisies no chegassem na sua aplicao e a alterasse.
Regras e Padres de Cache

O HTTP 1.1 permite por padro fazer o cache de qualquer coisa a menos que seja explcito que no num cabealho
Cache-Control. Na prtica, a maioria dos caches no faz nada quando as requisies tem um cookie, um cabealho
de autorizao, usam um mtodo inseguro (i.e PUT, POST, DELETE) ou quando as respostas tem cdigo de estado
para redirecionamento.
O Symfony2 define automaticamente um cabealho Cache-Control conservador quando o desenvolvedor no
definir nada diferente seguindo essas regras:
Se no for definido cabealho de cache (Cache-Control, Expires, ETag or Last-Modified),
Cache-Control configurado como no-cache, indicando que no ser feito cache da resposta;

196

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Se Cache-Control estiver vazio (mas outros cabealhos de cache estiverem presentes), seu valor configurado como private, must-revalidate;
Mas se pelo menos uma diretivaCache-Control estiver definida, e nenhuma diretiva public ou privada tiver sido adicionada explicitamente, o Symfony2 adiciona automaticamente a diretiva private (exceto quando
s-maxage estiver definido).
Expirao e Validao HTTP
A especificao HTTP define dois modelos de cache:
Com o expiration model, voc especifica simplesmente quanto tempo uma resposta deve ser considerada atual
incluindo um cabealho Cache-Control e/ou um Expires. Os caches que entendem a expirao no faro
a mesma requisio at que a verso cacheada atinja o tempo de expirao e se torne antiga.
Quando as pginas so realmente dinmicas (i.e. sua representao muda constantemente), o validation model
frequentemente necessrio. Com esse modelo, o cache armazena a resposta, mas pergunta ao servidor a cada
requisio se a resposta cacheada continua vlida ou no. A aplicao utiliza um identificador nico da resposta
(o cabealho Etag) e/ou um timestamp (o cabealho Last-Modified) para verificar se a pgina mudou
desde quando tinha sido cacheada.
O objetivo de ambos os modelos nunca ter que gerar a mesma resposta duas vezes contando com o cache para guardar
e retornar respostas atuais.
Lendo a Especificao HTTP
A especificao HTTP define uma linguagem simples mas poderosa com a qual clientes e servidores podem se
comunicar. Como um desenvolvedor web, o modelo requisio-resposta da especificao domina o seu trabalho.
Infelizmente o documento real da especificao - RFC 2616 - pode ser difcil de ler.
Existe um trabalho em andamento (HTTP Bis) para reescrever a RFC 2616. Ele no descreve uma nova verso
do HTTP, mas principalmente esclarece a especificao HTTP original. A organizao tambm melhorou pois a
especificao foi dividida em sete partes; tudo que for relacionado ao cache HTTP pode ser encontrado em duas
partes dedicadas ( P4 - Conditional Requests e P6 - Caching: Browser and intermediary caches)
Como desenvolvedor web, ns recomendamos fortemente que voc leia a especificao. Sua clareza e poder ainda mais depois de dez anos da sua criao - incalculvel. No se engane com a aparncia da especificao
- o contedo dela muito mais bonito que sua capa.

Expirao

O modelo de expirao o mais eficiente e simples dos dois modelos de cache e deve ser usado sempre que possvel.
Quando uma resposta cacheada com uma expirao, o cache ir armazenar a resposta e retorn-la diretamente sem
acessar a aplicao at que a resposta expire.
O modelo de expirao pode ser aplicado usando um desses dois, quase idnticos, cabealhos HTTP: Expires ou
Cache-Control.
Expirao com o Cabealho Expires

De acordo com a especificao HTTP, o campo do cabealho Expires diz a data/horrio a partir do qual a resposta
considerada antiga. O cabealho Expires pode ser definido com o mtodo setExpires() Response. Ele
recebe uma instncia de DateTime como argumento:

2.1. Livro

197

Symfony Docs pt-BR Documentation, Verso 2.4

$date = new DateTime();


$date->modify(+600 seconds);
$response->setExpires($date);

O cabealho HTTP resultante se parecer com isso:


Expires: Thu, 01 Mar 2011 16:00:00 GMT

Nota: O mtodo setExpires() converte automaticamente a data para o fuso horrio GMT como exigido na
especificao.
O cabealho Expires sofre com duas limitaes. Primeiro, o relgio no servidor web e o cache (e.g. o navegador)
precisam estar sincronizados. A outra que a especificao define que servidores HTTP/1.1 no devem mandar datas
Expires com mais de um ano no futuro.
Expirao com o Cabealho Cache-Control

Devido s limitaes do cabealho Expires, na maioria das vezes, voc deve usar no lugar dele o cabealho
Cache-Control. Lembre-se que o cabealho Cache-Control usado para especificar vrias diretivas de cache
diferentes. Para expirao, existem duas diretivas: max-age e s-maxage. A primeira usada para todos os caches
enquanto a segunda somente utilizada por caches compartilhados:
// Define o nmero de segundos aps o qual a resposta
// no ser mais considerada atual
$response->setMaxAge(600);
// O mesmo que acima, mas apenas para caches compartilhados
$response->setSharedMaxAge(600);

O cabealho Cache-Control deve ter o seguinte formato (ele pode ter diretivas adicionais):
Cache-Control: max-age=600, s-maxage=600

Validao

Quando um recurso precisa ser atualizado logo que uma mudana for feita em dados relacionados, o modelo de
expirao insuficiente. Com o modelo de expirao, a aplicao no ser acionada para retornar a resposta atualizada
at que o cache finalmente se torne antigo.
O modelo de validao resolve esse problema. Nesse modelo, o cache continua a armazenar as respostas. A diferena
que, para cada requisio, o cache pede para a aplicao verificar se a respostas cacheada continua vlida ou no.
Se o cache ainda for vlido, sua aplicao deve retornar um cdigo de estado 304 e nenhum contedo. Isso diz para o
cache que ele pode retornar a resposta cacheada.
Nesse modelo, voc economiza principalmente banda pois a representao no enviada duas vezes para o mesmo
cliente (uma resposta 304 mandada no lugar). Mas se projetar sua aplicao com cuidado, voc pode ser capaz
de pegar o mnimo de dados necessrio para enviar uma resposta 304 e tambm economizar CPU (veja abaixo um
exemplo de uma implementao).
Dica: O cdigo de estado 304 significa Not Modified. Isso importante pois com esse cdigo de estado no
colocado o contedo real que est sendo requisitado. Em vez disso, a resposta simplesmente um conjunto leve de
direes que dizem ao cache para ele utilizar uma verso armazenada.

198

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Assim como com a expirao, existem dois cabealhos HTTP diferentes que podem ser utilizados para implementar o
modelo de validao: ETag e Last-Modified.
Validao com o Cabealho ETag

O cabealhoETag um cabealho em texto (chamado de entity-tag) que identifica de forma nica uma representao do recurso alvo. Ele totalmente gerado e configurado pela sua aplicao, dessa forma voc pode dizer,
por exemplo, se o recurso /about que foi armazenado pelo cache est atualizado com o que sua aplicao poderia
retornar. Uma ETag como uma impresso digital e utilizada para comparar rapidamente se duas verses diferentes
de um recurso so equivalentes. Como as impresses digitais, cada ETag precisa ser nica em todos as representaes
do mesmo recurso.
Vamos analisar uma implementao simples que gera a ETag como o hash md5 do contedo:
public function indexAction()
{
$response = $this->render(MyBundle:Main:index.html.twig);
$response->setETag(md5($response->getContent()));
$response->isNotModified($this->getRequest());
return $response;
}

O mtodo Response::isNotModified() compara o ETag enviado na Request com o que est definido na
Response. Se os dois combinarem, o mtodo define automaticamente o cdigo de estado da Response como 304.
Esse algoritmo simples o suficiente e bem genrico, mas voc precisa criar a Response inteira antes de ser capaz
de calcular a Etag, o que no o melhor possvel. Em outras palavras, isso economiza banda de rede mas no faz o
mesmo com os ciclos de CPU.
Na seo Otimizando seu Cdigo com Validao, ns mostraremos como a validao pode ser utilizada de forma mais
inteligente para determinar a validade de um cache sem muito trabalho.
Dica:
O Symfony2 tambm suporta ETags fracas passando true como segundo argumento para o mtodo
setETag().

Validao com o Cabealho Last-Modified

O cabealho Last-Modified a segunda forma de validao. De acordo com a especificao HTTP, O campo
do cabealho Last-Modified indica a data e o horrio que o servidor de origem acredita que a representao foi
modificada pela ltima vez. Em outras palavras, a aplicao decide se o contedo cacheado foi atualizado ou no
tendo como base se ele foi atualizado desde que a resposta foi cacheada.
Por exemplo, voc pode usar a ltima data de atualizao de todos os objetos necessrios para calcular a representao
do recurso como o valor para o cabealho Last-Modified:
public function showAction($articleSlug)
{
// ...
$articleDate = new \DateTime($article->getUpdatedAt());
$authorDate = new \DateTime($author->getUpdatedAt());
$date = $authorDate > $articleDate ? $authorDate : $articleDate;
$response->setLastModified($date);

2.1. Livro

199

Symfony Docs pt-BR Documentation, Verso 2.4

$response->isNotModified($this->getRequest());
return $response;
}

O mtodo Response::isNotModified() compara o cabealho If-Modified-Since mandado pela requisio com o cabealho Last-Modified definido na resposta. Se eles forem equivalentes, a Response ser configurada com um cdigo de estado 304.
Nota: O cabealho da requisio If-Modified-Since igual ao cabealho Last-Modified de uma resposta
enviada ao cliente para um recurso
especfico. Essa a forma como o cliente e o servidor se comunicam entre si e decidem se o recurso foi
ou no atualizado desde que ele foi
cacheado.

Otimizando seu Cdigo com Validao

O objetivo principal de qualquer estratgia de cache aliviar a carga sobre a aplicao. Colocando de outra
forma, quanto menos coisas voc fizer na sua aplicao para retornar uma resposta 304, melhor. O mtodo
Response::isNotModified() faz exatamente isso expondo um padro simples e eficiente:
public
{
//
//
//
//

function showAction($articleSlug)
Pega a informao mnima para calcular
a ETag ou o valor Last-Modified
(baseado na Requisio, o dado recuperado
de um banco de dados ou de um armazenamento chave-valor)

$article = // ...
// cria uma Resposta com uma ETag e/ou um cabealho Last-Modified
$response = new Response();
$response->setETag($article->computeETag());
$response->setLastModified($article->getPublishedAt());
// Verifica se a Resposta no diferente da Requisio
if ($response->isNotModified($this->getRequest())) {
// retorna imediatamente a Resposta 304
return $response;
} else {
// faa mais algumas coisas aqui - como buscar mais dados
$comments = // ...
// ou renderize um template com a $response que voc j
// inicializou
return $this->render(
MyBundle:MyController:article.html.twig,
array(article => $article, comments => $comments),
$response
);
}
}

Quando a Response no tiver sido modificada, isNotModified() automaticamente define o cdigo de estado
para 304, remove o contedo e remove alguns cabealhos que no podem estar presentes em respostas 304 (veja
200

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

setNotModified()).
Variando a Resposta

At agora assumimos que cada URI tem exatamente uma representao do recurso alvo. Por padro, o cache HTTP
feito usando a URI do recurso como a chave do cache. Se duas pessoas requisitarem a mesma URI de um recurso
passvel de cache, a segunda pessoa receber a verso cacheada.
Algumas vezes isso no suficiente e diferentes verses da mesma URI precisam ser cacheadas baseando-se em um
ou mais valores dos cabealhos de requisio. Por exemplo, se voc comprimir a pgina quando o cliente suportar
compresso, cada URI ter duas representaes: uma quando o cliente suportar compresso e uma quando o cliente
no suportar. Essa deciso feita usando o valor do cabealho Accept-Encoding da requisio.
Nesse caso, precisamos que o cache armazene ambas as verses da requisio, para uma determinada URI, comprimida
e no, e retorne-as se baseando no valor Accept-Encoding da requisio. Isso feito utilizando o cabealho Vary
da resposta, que uma lista separada por vrgulas dos diferentes cabealhos cujos valores acionam um representao
diferente do recurso requisitado:
Vary: Accept-Encoding, User-Agent

Dica: Esse cabealho Vary especfico pode fazer o cache de diferentes verses de cada recurso baseado na URI e no
valor dos cabealhos Accept-Encoding e User-Agent da requisio.
O objeto Response fornece um interface limpa para gerenciar o cabealho Vary:
// define um cabealho vary
$response->setVary(Accept-Encoding);
// define mltiplos cabealhos vary
$response->setVary(array(Accept-Encoding, User-Agent));

O mtodo setVary() recebe o nome de um cabealho ou um array de nomes de cabealhos para os quais a resposta
varia.
Expirao e Validao

claro que voc pode usar ambas a validao e a expirao dentro da mesma Response. Como a expirao mais
importante que a validao, voc pode se beneficiar facilmente do melhor dos dois mundos. Em outras palavras,
utilizando ambas a expirao e a validao, voc pode ordenar que o cache sirva o contedo cacheado, ao mesmo
tempo que verifica em algum intervalo de tempo (a expirao) para ver se o contedo continua vlido.
Mais Mtodos Response

A classe Response fornece muitos outros mtodos relacionados ao cache. Aqui seguem os mais teis:
// Marca a Resposta como antiga
$response->expire();
// Fora a resposta para retornar um 304 apropriado sem contedo
$response->setNotModified();

Adicionalmente, a maioria dos cabealhos HTTP relacionados ao cache podem ser definidos por meio do mtodo
setCache() apenas:

2.1. Livro

201

Symfony Docs pt-BR Documentation, Verso 2.4

// Define as configuraes de cache em uma nica chamada


$response->setCache(array(
etag
=> $etag,
last_modified => $date,
max_age
=> 10,
s_maxage
=> 10,
public
=> true,
// private
=> true,
));

Usando Edge Side Includes


Os gateway caches so uma excelente maneira de fazer o seu site ficar mais performtico. Mas ele tem uma limitao:
s conseguem fazer cache de pginas completas. Se voc no conseguir cachear pginas completas ou se partes de
uma pgina tiverem partes mais dinmicas, voc est sem sorte. Felizmente, o Symfony2 fornece uma soluo para
esses casos, baseado numa tecnologia chamada ESI, ou Edge Side Includes. A Akamai escreveu essa especificao
quase 10 anos atrs, e ela permite que partes especficas de uma pgina tenham estratgias de cache diferentes da
pgina principal.
A especificao ESI descreve tags que podem ser embutidas em suas pginas para comunicar com o gateway cache.
Apenas uma tag implementada no Symfony2, include, pois ela a nica que til fora do contexto da Akamai:
<html>
<body>
Some content
<!-- Embed the content of another page here -->
<esi:include src="http://..." />
More content
</body>
</html>

Nota: Perceba pelo exemplo que cada tag ESI tem uma URL completamente vlida. Uma tag ESI representa um
fragmento de pgina que pode ser recuperado pela URL informada.
Quando uma requisio tratada, o gateway cache busca a pgina inteira do cache ou faz a requisio no backend da
aplicao. Se a resposta contiver uma ou mais tags ESI, elas so processadas da mesma forma. Em outras palavras, o
cache gateway pega o fragmento da pgina inserido ou do seu cache ou faz a requisio novamente para o backend da
aplicao. Quado todas as tags ESI forem resolvidas, o gateway cache mescla cada delas na pgina principal e envia
o contedo finalizado para o cliente.
Tudo isso acontece de forma transparente no nvel do gateway cache (i.e. fora de sua aplicao). Como voc pode ver,
se voc escolher tirar proveito das tags ESI, o Symfony2 faz com que o processo de inclu-las seja quase sem esforo.
Usando ESI no Symfony2

Primeiro, para usar ESI, tenha certeza de ter feito sua habilitao na configurao da sua aplicao:
YAML
# app/config/config.yml
framework:
# ...
esi: { enabled: true }

202

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

XML
<!-- app/config/config.xml -->
<framework:config ...>
<!-- ... -->
<framework:esi enabled="true" />
</framework:config>

PHP
// app/config/config.php
$container->loadFromExtension(framework, array(
// ...
esi
=> array(enabled => true),
));

Agora, suponha que temos uma pgina que seja relativamente esttica, exceto por um atualizador de notcias na parte
inferior do contedo. Com o ESI, podemos fazer o cache do atualizador de notcias de forma independente do resto
da pgina.
public function indexAction()
{
$response = $this->render(MyBundle:MyController:index.html.twig);
$response->setSharedMaxAge(600);
return $response;
}

Nesse exemplo, informamos o tempo de vida para o cache da pgina inicial como dez minutos. Em seguida, vamos incluir o atualizador de notcias no template embutindo uma action. Isso feito pelo helper render (Veja Incorporao
de Controllers para mais detalhes).
Como o contedo embutido vem de outra pgina (ou controller nesse caso), o Symfony2 usa o helper padro render
para configurar as tags ESI:
Twig
{% render ...:news with {}, {standalone: true} %}

PHP
<?php echo $view[actions]->render(...:news, array(), array(standalone => true)) ?>

Definindo standalone como true, voc diz ao Symfony2 que a action deve ser renderizada como uma tag ESI.
Voc pode estar imaginando porque voc iria querer utilizar um helper em vez de escrever a tag ESI voc mesmo. Isso
acontece porque a utilizao de um helper faz a sua aplicao funcionar mesmo se no existir nenhum gateway cache
instalado. Vamos ver como isso funciona.
Quando standalone est false (o padro), o Symfony2 mescla o contedo da pgina includa com a principal antes
de mandar a resposta para o cliente. Mas quando standalone est true, e se o Symfony detectar que ele est falando
com um gateway cache que suporta ESI, ele gera uma tag ESI de incluso. Mas se no houver um gateway cache ou
se ele no suportar ESI, o Symfony2 apenas mesclar o contedo da pgina includa com a principal como se tudo
tivesse sido feito com o standalone definido como false.
Nota: O Symfony2 detecta se um gateway cache suporta ESI por meio de outra especificao da Akamai que
suportada nativamente pelo proxy reverso do Symfony2.
A action embutida agora pode especificar suas prprias regras de cache, de forma totalmente independente da pgina
principal.

2.1. Livro

203

Symfony Docs pt-BR Documentation, Verso 2.4

public function newsAction()


{
// ...
$response->setSharedMaxAge(60);
}

Com o ESI, o cache da pgina completa pode ficar vlido por 600 segundos, mas o cache do componente de notcias
ser vlido apenas nos ltimos 60 segundos.
Um requisito do ESI, no entanto, que a action embutida seja acessvel por uma URL dessa forma o gateway cache
pode acess-la independentemente do restante da pgina. claro que uma action no pode ser acessada pela URL
a menos que exista uma rota que aponte para ela. O Symfony2 trata disso por meio de uma rota e um controller
genricos. Para que a tag ESI de incluso funcione adequadamente, voc precisa definir a rota _internal:
YAML
# app/config/routing.yml
_internal:
resource: "@FrameworkBundle/Resources/config/routing/internal.xml"
prefix:
/_internal

XML
<!-- 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/rout

<import resource="@FrameworkBundle/Resources/config/routing/internal.xml" prefix="/_internal


</routes>

PHP
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection->addCollection($loader->import(@FrameworkBundle/Resources/config/routing/internal.x
return $collection;

Dica: Como essa rota permite que todas as actions sejam acessadas por uma URL, talvez voc queira proteg-la
utilizando a funcionalidade de firewall do Symfony2 (restringindo o acesso para a faixa de IP do seu proxy reverso).
Veja a seo Securing by IP do Security Chapter para mais informaes de como fazer isso.
Uma grande vantagem dessa estratgia de cache que voc pode fazer com que sua aplicao seja to dinmica quanto
for necessrio e ao mesmo tempo, acessar a aplicao o mnimo possvel.
Nota: Assim que voc comear a utilizar ESI, lembre-se de sempre usar a diretiva s-maxage em vez de max-age.
Como o navegador somente recebe o recurso agregado, ele no tem cincia dos sub-componentes e assim ele ir
obedecer a diretiva max-age e fazer o cache da pgina inteira. E voc no quer isso.
O helper render suporta duas outras opes teis:

204

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

alt: usada como o atributo alt na tag ESI, que permite que voc especifique uma URL alternativa para ser
usada se o src no for encontrado;
ignore_errors: se for configurado como true, um atributo onerror ser adicionado ao ESI com um valor
continue indicando que, em caso de falha, o gateway cache ir simplesmente remover a tag ESI silenciosamente.
Invalidao do Cache
S tem duas coisas difceis em Cincia da Computao: invalidao de cache e nomear coisas. Phil
Karlton
Voc nunca deveria ter que invalidar dados em cache porque a invalidao j feita nativamente nos modelos de cache
HTTP. Se voc usar validao, por definio voc nunca precisaria invalidar algo; e se voc usar expirao e precisar
invalidar um recurso, isso significa que voc definiu a data de expirao com um valor muito longe.
Nota: Como invalidao um tpico especfico para cada tipo de proxy reverso, se voc no se preocupa com
invalidao, voc pode alternar entre proxys reversos sem alterar nada no cdigo da sua aplicao.
Na verdade, todos os proxys reversos fornecem maneiras de expurgar dados do cache, mas voc deveria evit-los o
mximo possvel. A forma mais padronizada expurgar o cache de uma determinada URL requisitando-a com o
mtodo HTTP especial PURGE.
Aqui vai como voc pode configurar o proxy reverso do Symfony2 para suportar o mtodo HTTP PURGE:
// app/AppCache.php
class AppCache extends Cache
{
protected function invalidate(Request $request)
{
if (PURGE !== $request->getMethod()) {
return parent::invalidate($request);
}
$response = new Response();
if (!$this->getStore()->purge($request->getUri())) {
$response->setStatusCode(404, Not purged);
} else {
$response->setStatusCode(200, Purged);
}
return $response;
}
}

Cuidado: Voc tem que proteger o mtodo HTTP PURGE de alguma forma para evitar que pessoas aleatrias
expurguem seus dados cacheados.

Sumrio
O Symfony2 foi desenhado para seguir as regras j testadas: HTTP. O cache no exceo. Dominar o sistema de
cache do Symfony2 significa se familiarizar com os modelos de cache HTTP e utiliz-los de forma efetiva. Para isso,
em vez de depender apenas da documentao do Symfony2 e exemplos de cdigo, voc deveria buscar mais contedo
relacionado com o cache HTTP e caches gateway como o Varnish.

2.1. Livro

205

Symfony Docs pt-BR Documentation, Verso 2.4

Learn more from the Cookbook


Como usar Varnish para aumentar a velocidade do meu Website

2.1.13 Tradues
O termo internacionalizao se refere ao processo de abstrair strings e outras peas com localidades especficas para
fora de sua aplicao e dentro de uma camada onde eles podem ser traduzidos e convertidos baseados na localizao
do usurio (em outras palavras, idioma e pis). Para o texto, isso significa englobar cada um com uma funo capaz
de traduzir o texto (ou messagem) dentro do idioma do usurio:
// text will *always* print out in English
echo Hello World;
// text can be translated into the end-users language or default to English
echo $translator->trans(Hello World);

Nota: O termo localidade se refere rigorosamente ao idioma e pas do usurio. Isto pode ser qualquer string que
sua aplicao usa ento para gerenciar tradues e outras diferenas de formato (ex: formato de moeda). Ns recomendamos o cdigo de linguagem ISO639-1 , um underscore (_), ento o cdigo de pas ISO3166 (ex: fr_FR para
Francs/Frana).
Nesse captulo, ns iremos aprender como preparar uma aplicao para suportar mltiplas localidades e ento como
criar tradues para localidade e ento como criar tradues para mltiplas localidades. No geral, o processo tem
vrios passos comuns:
1. Abilitar e configurar o componente Translation do Symfony;
2. Abstrair strings (em outras palavras, messagens) por englob-las em chamadas para o Translator;
3. Criar translation resources para cada localidade suportada que traduza cada mensagem na aplicao;
4. Determinar, definir e gerenciar a localidade do usurio para o pedido e opcionalmente em toda a sesso do
usurio.
Configurao
Tradues so suportadas por um Translator service que usa o localidadedo usurio para observar e retornar
mensagens traduzidas. Antes de usar isto, abilite o Translator na sua configurao:
YAML
# app/config/config.yml
framework:
translator: { fallback: en }

XML
<!-- app/config/config.xml -->
<framework:config>
<framework:translator fallback="en" />
</framework:config>

PHP

206

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

// app/config/config.php
$container->loadFromExtension(framework, array(
translator => array(fallback => en),
));

A opo fallback define a localidade alternativa quando uma traduo no existe no localidadedo usurio.
Dica: Quando a traduo no existe para uma localidade, o tradutor primeiro tenta encontrar a traduo para o
idioma (fr se o localidade fr_FR por exemplo). Se isto tambm falhar, procura uma traduo usando a localidade
alternativa.
A localidade usada em tradues a que est armazenada no pedido. Isto tipicamente definido atravs do atributo
_locale em suas rotas (veja A localidade e a URL).
Traduo bsica
Traduo do texto feita done atravs do servio translator (Translator). Para traduzir um bloco de texto
(chamado de messagem), use o mtodo trans(). Suponhamos, por exemplo, que estamos traduzindo uma simples
mensagem de dentro do controller:
public function indexAction()
{
$t = $this->get(translator)->trans(Symfony2 is great);
return new Response($t);
}

Quando esse cdigo executado, Symfony2 ir tentar traduzir a mensagem Symfony2 is great baseada na
localidade do usurio. Para isto funcionar, ns precisamos informar o Symfony2 como traduzir a mensagem
por um translation resource, que uma coleo de tradues de mensagens para um localidade especificada. Esse
dicionrio de tradues pode ser criado em diferentes formatos variados, sendo XLIFF o formato recomendado:
XML
<!-- messages.fr.xliff -->
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>Symfony2 is great</source>
<target>Jaime Symfony2</target>
</trans-unit>
</body>
</file>
</xliff>

PHP
// messages.fr.php
return array(
Symfony2 is great => J\aime Symfony2,
);

YAML

2.1. Livro

207

Symfony Docs pt-BR Documentation, Verso 2.4

# messages.fr.yml
Symfony2 is great: Jaime Symfony2

Agora, se o idioma do localidade do usurio Francs (ex: fr_FR ou fr_BE), a mensagem ir ser traduzida para
Jaime Symfony2.
O processo de traduo

Para realmente traduzir a mensagem, Symfony2 usa um processo simples:


A localidade do usurio atual, que est armazenada no pedido (ou armazenada como _locale na sesso),
determinada;
Um catlogo de mensagens traduzidas carregado de translation resources definidos pelo locale (ex:
fr_FR). Messagens da localidade alternativa so tambm carregadas e adiconadas ao catalogo se elas realmente no existem. O resultado final um grande dicionrio de tradues. Veja Catlogo de Mensagens para
mais detalhes;
Se a mensagem localizada no catlogo, retorna a traduo. Se no, o tradutor retorna a mensagem original.
Quando usa o mtodo trans(), Symfony2 procura pelo string exato dentro do catlogo de mensagem apropriada e
o retorna (se ele existir).
Espaos reservados de mensagem

s vezes, uma mensagem contedo uma varivel precisa ser traduzida:


public function indexAction($name)
{
$t = $this->get(translator)->trans(Hello .$name);
return new Response($t);
}

Entretanto criar uma traduo para este string impossvel visto que o tradutor ir tentar achar a mensagem exata,
incluindo pores da varivel (ex: Hello Ryan ou Hello Fabien). Ao invs escrever uma traduo para toda
interao possvel da mesma varivel $name , podemos substituir a varivel com um espao reservado:
public function indexAction($name)
{
$t = $this->get(translator)->trans(Hello %name%, array(%name% => $name));
new Response($t);
}

Symfony2 ir procurar uma traduo da mensagem pura (Hello %name%) e ento substitui o espao reservado com
os valores deles. Criar uma traduo exatamente como foi feito anteriormente:
XML
<!-- messages.fr.xliff -->
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>Hello %name%</source>

208

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

<target>Bonjour %name%</target>
</trans-unit>
</body>
</file>
</xliff>

PHP
// messages.fr.php
return array(
Hello %name% => Bonjour %name%,
);

YAML
# messages.fr.yml
Hello %name%: Hello %name%

Nota: Os espaos reservados podem suportar qualquer outro forma j que a mensagem inteira reconstruda usando
a funo PHP strtr function. Entretanto, a notao %var% requerida quando traduzir em templates Twig, e no geral
uma conveno sensata a seguit.
Como podemos ver, criar uma traduo um processo de dois passos:
1. Abstrair a mensagem que precisa ser traduzida por processamento atravs do Translator.
2. Criar uma traduo para a mensagem em cada localidade que voc escolha dar suporte.
O segundo passo feito mediante criar catlogos de mensagem que definem as tradues para qualquer nmero de
localidades diferentes.
Catlogo de Mensagens
Quando uma mensagem traduzida, Symfony2 compila um catlogo de mensagem para a localidade do usurio e
investiga por uma traduo da mensagem. Um catlogo de mensagens como um dicionrio de tradues para uma
localidade especfica. Por exemplo, o catlogo para a localidadefr_FR poderia conter a seguinte traduo:
Symfony2 is Great => Jaime Symfony2
responsabilidade do desenvolvedor (ou tradutor) de uma aplicao internacionalizada criar essas tradues. Tradues so armazenadas no sistema de arquivos e descoberta pelo Symfony, graas a algumas convenes.
Dica: Cada vez que voc criar um novo translation resource (ou instalar um pacote que inclua o translation resource),
tenha certeza de limpar o cache ento aquele Symfony poder detectar o novo translation resource:
php app/console cache:clear

Localizao de Tradues e Convenes de Nomenclatura

O Symfony2 procura por arquivos de mensagem (em outras palavras, tradues) nas seguintes localizaes:
o diretrio <kernel root directory>/Resources/translations;
o diretrio <kernel root directory>/Resources/<bundle name>/translations;
o diretrio Resources/translations/ do bundle.

2.1. Livro

209

Symfony Docs pt-BR Documentation, Verso 2.4

Os locais so apresentados com a prioridade mais alta em primeiro lugar. Isso significa que voc pode sobrescrever as
mensagens de traduo de um bundle em qualquer um dos 2 diretrios no topo.
O mecanismo de substituio funciona em um nvel chave: apenas as chaves sobrescritas precisam ser listadas em um
arquivo de mensagem de maior prioridade. Quando a chave no encontrada em um arquivo de mensagem, o tradutor
automaticamente alternar para os arquivos de mensagem menos prioritrios.
O nome de arquivo das tradues tambm importante, j que Symfony2 usa uma conveno para determinar
detalhes sobre as tradues. Cada arquivo de messagem deve ser nomeado de acordo com o seguinte padro:
domnio.localidade.carregador:
domnio: Uma forma opcional de organizar mensagens em grupos (ex: admin, navigation ou o padro
messages) - veja Usando Domnios de Mensagem;
localidade: A localidade para a qual a traduo feita (ex: en_GB, en, etc);
carregador: Como Symfony2 deveria carregar e analisar o arquivo (ex: xliff, php or yml).
O carregador poder ser o nome de qualquer carregador registrado. Por padro, Symfony providencia os seguintes
carregadores:
xliff: arquivo XLIFF;
php: arquivo PHP;
yml: arquivo YAML.
A escolha de qual carregador inteiramente tua e uma questo de gosto.
Nota: Voc tambm pode armazenar tradues em um banco de dados, ou outro armazenamento ao providenciar uma
classe personalizada implementando a interface LoaderInterface. Veja Custom Translation Loaders
abaixo para aprender como registrar carregadores personalizados.

Criando tradues

Cada arquivo consiste de uma srie de pares de traduo de id para um dado domnio e locale. A id o identificador
para a traduo individual, e pode ser a mensagem da localidade principal (ex: Symfony is great) de sua aplicap
ou um identificador nico (ex: symfony2.great - veja a barra lateral abaixo):
XML
<!-- src/Acme/DemoBundle/Resources/translations/messages.fr.xliff -->
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>Symfony2 is great</source>
<target>Jaime Symfony2</target>
</trans-unit>
<trans-unit id="2">
<source>symfony2.great</source>
<target>Jaime Symfony2</target>
</trans-unit>
</body>
</file>
</xliff>

PHP

210

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

// src/Acme/DemoBundle/Resources/translations/messages.fr.php
return array(
Symfony2 is great => J\aime Symfony2,
symfony2.great
=> J\aime Symfony2,
);

YAML
# src/Acme/DemoBundle/Resources/translations/messages.fr.yml
Symfony2 is great: Jaime Symfony2
symfony2.great:
Jaime Symfony2

Symfony2 ir descobrir esses arquivos e us-los quando ou traduzir Symfony2 is great ou symfony2.great no
localidade do idioma Francs (ex: fr_FR ou fr_BE).

2.1. Livro

211

Symfony Docs pt-BR Documentation, Verso 2.4

Usando Mensagens Reais ou Palavras-Chave


Esse exemplo ilustra duas diferentes filosofias quando criar mensagens para serem traduzidas:
$t = $translator->trans(Symfony2 is great);
$t = $translator->trans(symfony2.great);

No primeiro mtodo, mensagens so escritas no idioma do localidade padro (Ingls neste caso). Aquela mensagem ento usada como id quando criar tradues.
No segundo mtodo, mensagens so realmente palavras-chave que contm a idia da mensagem. A mensagem
de palavras-chave ento usada como o id de qualquer traduo. Neste caso, tradues devem ser feitas para
o locale padro (em outras palavras, traduzir symfony2.great para Symfony2 is great).
O segundo mtodo prtico porque a chave da mensagem no precisar ser mudada em cada arquivo de traduo
se decidirmos que a mensagem realmente deveria ler Symfony2 is really great no localidade padro.
A escolha de qual mtodo usar inteiramente sua, mas o formato de palavra-chave frequentemente recomendada.
Adicionalmente, os formatos de arquivo php e yaml suportam ids encaixadas para que voc evite repeties se
voc usar palavras-chave ao invs do texto real para suas ids:
YAML
symfony2:
is:
great: Symfony2 is great
amazing: Symfony2 is amazing
has:
bundles: Symfony2 has bundles
user:
login: Login

PHP
return array(
symfony2 => array(
is => array(
great => Symfony2 is great,
amazing => Symfony2 is amazing,
),
has => array(
bundles => Symfony2 has bundles,
),
),
user => array(
login => Login,
),
);

Os nveis mltiplos so achatados em id unitria / pares de traduo ao adicionar um ponto (.) entre cada nvel,
portanto os exemplos acima so equivalentes ao seguinte:
YAML
symfony2.is.great: Symfony2 is great
symfony2.is.amazing: Symfony2 is amazing
symfony2.has.bundles: Symfony2 has bundles
user.login: Login

PHP

212

return array(
symfony2.is.great => Symfony2 is great,
symfony2.is.amazing => Symfony2 is amazing,
symfony2.has.bundles => Symfony2 has bundles,
user.login => Login,
);

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Usando Domnios de mensagem


Como ns vimos, arquivos de mensagem so organizados em diferentes localidades que eles traduzem. Os arquivos
de mensagem podem tambm ser organizados alm de domnios. O domnio padro messages. Por exemplo, suponha que, para organizao, tradues foram divididas em trs domnios diferentes: messages, admin e
navigation. A traduo Francesa teria os seguintes arquivos de mensagem:
messages.fr.xliff
admin.fr.xliff
navigation.fr.xliff
Quando traduzir strings que no esto no domnio padro (messages), voc deve especificar o domnio como terceiro
argumento de trans():
$this->get(translator)->trans(Symfony2 is great, array(), admin);

Symfony2 ir pesquisar pela mensagem no domnioadmin da localidade do usurio.


Tratando a localidade do Usurio
A localidade do usurio atual armazenada no pedido e acessvel atravs do objeto request:
// access the reqest object in a standard controller
$request = $this->getRequest();
$locale = $request->getLocale();
$request->setLocale(en_US);

Tambm possvel armazenar a localidade na sesso em vez do pedido. Se voc fizer isso, cada pedido posterior ter
esta localidade.
$this->get(session)->set(_locale, en_US);

Veja a seo A localidade e a URL abaixo sobre como setar a localidade atravs de roteamento.
Localidade padro e alternativa

Se a localidade no foi fixada explicitamente na sesso , o parmetro de configurao fallback_locale ser


usada pelo Translator. O parmetro padronizado para en (veja Configurao).
Alternativamente, voc pode garantir que uma localidade definida em cada pedido do usurio definindo um
default_locale para o framework:
YAML
# app/config/config.yml
framework:
default_locale: en

XML
<!-- app/config/config.xml -->
<framework:config>
<framework:default-locale>en</framework:default-locale>
</framework:config>

2.1. Livro

213

Symfony Docs pt-BR Documentation, Verso 2.4

PHP
// app/config/config.php
$container->loadFromExtension(framework, array(
default_locale => en,
));

Novo na verso 2.1: O parmetro default_locale foi definido debaixo da chave session originalmente, entretanto,
com o 2.1 isto foi movido. Foi movido porque a localidade agora definida no pedido ao invs da sesso.
A localidade e a URL

Uma vez que voc pode armazenar a localidade do usurio na sesso, pode ser tentador usar a mesma
URL para mostrar o recurso em muitos idiomas diferentes baseados na localidade do usurio.Por exemplo,
http://www.example.com/contact poderia mostrar contedo em Ingls para um usurio e Francs para
outro usurio. Infelizmente, isso viola uma regra fundamental da Web: que um URL particular retorne o mesmo recurso independente do usurio. Para complicar ainda o problema, qual verso do contedo deveria ser indexado pelas
ferramentas de pesquisa ?
Uma melhor poltica incluir a localidade na URL. Isso totalmente suportado pelo sistema de roteamnto usando o
parmetro especial _locale:
YAML
contact:
pattern:
/{_locale}/contact
defaults: { _controller: AcmeDemoBundle:Contact:index, _locale: en }
requirements:
_locale: en|fr|de

XML
<route id="contact" pattern="/{_locale}/contact">
<default key="_controller">AcmeDemoBundle:Contact:index</default>
<default key="_locale">en</default>
<requirement key="_locale">en|fr|de</requirement>
</route>

PHP
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(contact, new Route(/{_locale}/contact, array(
_controller => AcmeDemoBundle:Contact:index,
_locale
=> en,
), array(
_locale
=> en|fr|de
)));
return $collection;

Quando usar o parmetro especial _locale numa rota, a localidade encontrada ser automaticamente estabelecida na
sesso do usurio. Em outras palavras, se um usurio visita a URI /fr/contact, a localidadefr ser automaticamente estabelecida como a localidade para a sesso do usurio.
Voc pode agora usar a localidade do usurio para criar rotas para outras pginas traduzidas na sua aplicao.

214

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Pluralizao
Pluralizao de mensagem um tpico difcil j que as regras podem ser bem complexas. Por conveno, aqui est a
representao matemtica das regrad de pluralizao Russa:
(($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) &&

Como voc viu, em Russo, voc pode ter trs formas diferentes de plural, cada uma com index de 0, 1 ou 2. Para cada
forma, o plural diferente, e ento a traduo tambm diferente.
Quando uma traduo tem formas diferentes devido pluralizao, voc pode providenciar todas as formas como
string separadas por barra vertical (|):
There is one apple|There are %count% apples

Para traduzir mensagens pluralizadas, use o mtodo: transChoice()


$t = $this->get(translator)->transChoice(
There is one apple|There are %count% apples,
10,
array(%count% => 10)
);

O segundo argumento (10 neste exemplo), o nmero de objetos sendo descritos e usado para determinar qual
traduo usar e tambm para preencher o espao reservado %count%.
Baseado em certo nmero, o tradutor escolhe a forma correta do plural. Em Ingls, muitas palavras tem uma forma
singular quando existe exatamente um objeto e uma forma no plural para todos os outros nmeros (0, 2, 3...). Ento,
se count 1, o tradutor usar a primeira string (There is one apple) como traduo. Seno ir usar There
are %count% apples.
Aqui est a traduo Francesa:
Il y a %count% pomme|Il y a %count% pommes

Mesmo se a string parecer similar ( feita de duas substrings separadas por barra vertical), as regras Francesas so
diferentes: a primeira forma (sem plural) usada quando count is 0 or 1. Ento, o tradutor ir automaticamente usar
a primeira string (Il y a %count% pomme) quando count 0 ou 1.
Cada localidade tem sua prpria lista de regras, com algumas tendo tanto quanto seis formas diferentes de plural com
regras complexas por trs de quais nmeros de mapas de quais formas no plural. As regras so bem simples para
Ingls e Francs, mas para Russo, voc poderia querer um palpite para conhecer quais regras combinam com qual
string. Para ajudar tradutores, voc pode opcionalmente atribuir uma tag a cada string:
one: There is one apple|some: There are %count% apples
none_or_one: Il y a %count% pomme|some: Il y a %count% pommes

As tags so realmente as nicas pistas para tradutores e no afetam a lgica usada para determinar qual forma plural
usar. As tags podem ser qualquer string descritiva que termine com dois pontos (:). As tags traduzidas tambm no
so necessariamente a mesma que as da mensagem original.
Pluralizao de Intervalo Explcito

A maneira mais fcil de pluralizar uma mensagem deixar o Symfony2 usar lgica interna para escolher qual string
usar, baseando em um nmero fornecido. s vezes, voc ir precisar de mais controle ou querer uma traduo diferente
para casos especficos (para 0, ou quando o contador negativo, por exemplo). Para certos casos, voc pode usar
intervalos matemticos explcitos:

2.1. Livro

215

Symfony Docs pt-BR Documentation, Verso 2.4

{0} There are no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf] There are ma

Os intervalos seguem a notao ISO 31-11. A string acima especifica quatro intervalos diferentes: exatamente 0,
exatamente 1, 2-19, e 20 e mais altos.
Voc tambm pode misturar regras matemticas explcitas e regras padro. Nesse caso, se o contador no combinar
com um intervalo especfico, as regras padro, tero efeito aps remover as regras explcitas:

{0} There are no apples|[20,Inf] There are many apples|There is one apple|a_few: There are %count% a

Por exemplo, para 1 ma, a regra padro There is one apple ser usada. Para 2-19 apples, a segunda regra
padro There are %count% apples ser selecionada.
Uma classe Interval pode representar um conjunto finito de nmeros:
{1,2,3,4}

Ou nmeros entre dois outros nmeros:


[1, +Inf[
]-1,2[

O delimitador esquerdo pode ser [ (inclusivo) or ] (exclusivo). O delimitador direito pode ser [ (exclusivo) or ]
(inclusivo). Alm de nmeros, voc pode usar -Inf e +Inf para infinito.
Tradues em Templates
A maior parte do tempo, tradues ocorrem em templates. Symfony2 providencia suporte nativo para ambos os
templates PHP e Twig.
Templates Twig

Symfony2 providencia tags Twig especializadas (trans e transchoice) para ajudar com traduo de mensagem
de blocos estticos de texto:
{% trans %}Hello %name%{% endtrans %}
{% transchoice count %}
{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples
{% endtranschoice %}

A tag transchoice automaticamente obtm a varivel %count% do contexto atual e a passa para o tradutor. Esse
mecanismo s funciona quando voc usa um espao reservado seguindo o padro %var%.
Dica: Se voc precisa usar o caractere de percentual (%) em uma string, escape dela ao dobr-la: {% trans
%}Percent: %percent%%%{% endtrans %}
Voc tambm pode especificar o domnio da mensagem e passar algumas variveis adicionais:
{% trans with {%name%: Fabien} from "app" %}Hello %name%{% endtrans %}
{% trans with {%name%: Fabien} from "app" into "fr" %}Hello %name%{% endtrans %}
{% transchoice count with {%name%: Fabien} from "app" %}
{0} There is no apples|{1} There is one apple|]1,Inf] There are %count% apples
{% endtranschoice %}

216

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Os filtros trans e transchoice podem ser usados para traduzir textos de variveis e expresses complexas:
{{ message | trans }}
{{ message | transchoice(5) }}
{{ message | trans({%name%: Fabien}, "app") }}
{{ message | transchoice(5, {%name%: Fabien}, app) }}

Dica: Usando as tags de traduo ou filtros que tenham o mesmo efeito, mas com uma diferena sutil: sada para
escape automtico s aplicada para variveis traduzidas por utilizao de filtro. Em outras palavras, se voc precisar
estar certo que sua varivel traduzida no uma sada para escape, voc precisa aplicar o filtro bruto aps o filtro de
traduo.
{# text translated between tags is never escaped #}
{% trans %}
<h3>foo</h3>
{% endtrans %}
{% set message = <h3>foo</h3> %}
{# a variable translated via a filter is escaped by default #}
{{ message | trans | raw }}
{# but static strings are never escaped #}
{{ <h3>foo</h3> | trans }}

Novo na verso 2.1: Agora voc pode definir o domnio de traduo para um template Twig inteiro com uma nica
tag:
{% trans_default_domain "app" %}

Note que isso somente influencia o template atual, e no qualquer template includo (para evitar efeitos colaterais).
Templates PHP

O servio tradutor acessvel em templates PHP atravs do helper translator:


<?php echo $view[translator]->trans(Symfony2 is great) ?>
<?php echo $view[translator]->transChoice(
{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples,
10,
array(%count% => 10)
) ?>

Forando a Localidade Tradutora


Quando traduzir a mensagem, Symfony2 usa a localidade do pedido atual ou a localidade alternativa se necessria. Voc tambm pode especificar manualmente a localidade a usar para a traduo:
$this->get(translator)->trans(
Symfony2 is great,
array(),

2.1. Livro

217

Symfony Docs pt-BR Documentation, Verso 2.4

messages,
fr_FR,
);
$this->get(translator)->trans(
{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples,
10,
array(%count% => 10),
messages,
fr_FR,
);

Traduzindo Contedo de Banco de Dados


Quando a traduo do contedo do banco de dados deveria ser manipulada pelo Doctrine atravs do Translatable
Extension. Para mais informaes, veja a documentao para aquela biblioteca.
Sumrio
Com o componente Translation do Symfony2, criar uma aplicao internacionalizada no precisa mais ser um processo
dolorido e desgastante para somente uns passos bsicos:
Mensagens abstratas na sua aplicao ao englobar cada uma ou com mtodos trans() ou transChoice();
Traduza cada mensagem em localidades mltiplas ao criar arquivos de traduo de mensagem. Symfony2
descobre e processa cada arquivo porque o nome dele segue uma conveno especfica;
Gerenciar a localidade do usurio, que armazenada no pedido, mas tambm pode ser definida na sesso do
usurio.

2.1.14 Container de Servio


Uma aplicao PHP moderna cheia de objetos. Um objeto pode facilitar a entrega de mensagens de e-mail enquanto
outro pode permitir persistir as informaes em um banco de dados. Em sua aplicao, voc pode criar um objeto que
gerencia seu estoque de produtos, ou outro objeto que processa dados de uma API de terceiros. O ponto que uma
aplicao moderna faz muitas coisas e organizada em muitos objetos que lidam com cada tarefa.
Neste captulo, vamos falar sobre um objeto PHP especial no Symfony2 que ajuda voc a instanciar, organizar e
recuperar os muitos objetos da sua aplicao. Esse objeto, chamado de container de servio, lhe permitir padronizar
e centralizar a forma como os objetos so construdos em sua aplicao. O container facilita a sua vida, super rpido
e enfatiza uma arquitetura que promove cdigo reutilizvel e desacoplado. E, como todas as classes principais do
Symfony2 usam o container, voc vai aprender como estender, configurar e usar qualquer objeto no Symfony2. Em
grande parte, o container de servio o maior contribuinte velocidade e extensibilidade do Symfony2.
Finalmente, configurar e usar o container de servio fcil. Ao final deste captulo, voc estar confortvel criando os
seus prprios objetos atravs do container e personalizando objetos a partir de qualquer bundle de terceiros. Voc vai
comear a escrever cdigo que mais reutilizvel, testvel e desacoplado, simplesmente porque o container de servio
torna fcil escrever bom cdigo.
O que um Servio?
Simplificando, um Servio qualquer objeto PHP que realiza algum tipo de tarefa global. um nome genrico
proposital, usado em cincia da computao, para descrever um objeto que criado para uma finalidade especfica
(por exemplo, entregar e-mails). Cada servio usado em qualquer parte da sua aplicao sempre que precisar da
218

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

funcionalidade especfica que ele fornece. Voc no precisa fazer nada de especial para construir um servio: basta
escrever uma classe PHP com algum cdigo que realiza uma tarefa especfica. Parabns, voc acabou de criar um
servio!
Nota: Como regra geral, um objeto PHP um servio se ele usado globalmente em sua aplicao. Um nico
servio Mailer utilizado globalmente para enviar mensagens de e-mail, enquanto os muitos objetos Message que
ele entrega no so servios. Do mesmo modo, um objeto Product no um servio, mas um objeto que persiste os
objetos Product para um banco de dados um servio.
Ento, porque ele especial? A vantagem de pensar em servios que voc comea a pensar em separar cada
pedao de funcionalidade de sua aplicao em uma srie de servios. Uma vez que cada servio realiza apenas um
trabalho, voc pode facilmente acessar cada servio e usar a sua funcionalidade, sempre que voc precisar. Cada
servio pode tambm ser mais facilmente testado e configurado j que ele est separado das outras funcionalidades
em sua aplicao. Esta idia chamada de arquitetura orientada servios e no exclusiva do Symfony2 ou at
mesmo do PHP. Estruturar a sua aplicao em torno de um conjunto de classes de servios independentes uma das
melhores prticas de orientao objeto bem conhecida e confivel. Essas habilidades so a chave para ser um bom
desenvolvedor em praticamente qualquer linguagem.
O que um Service Container?
Um Container de Servio (ou container de injeo de dependncia) simplesmente um objeto PHP que gerencia a
instanciao de servios (ex. objetos). Por exemplo, imagine que temos uma classe PHP simples que envia mensagens
de e-mail. Sem um container de servio, precisamos criar manualmente o objeto sempre que precisarmos dele:
use Acme\HelloBundle\Mailer;
$mailer = new Mailer(sendmail);
$mailer->send(ryan@foobar.net, ... );

Isso bastante fcil. A classe imaginria Mailer nos permite configurar o mtodo utilizado para entregar as mensagens de e-mail (por exemplo: sendmail, smtp, etc). Mas, e se quisssemos usar o servio de mailer em outro lugar?
Ns certamente no desejamos repetir a configurao do mailer sempre que ns precisamos usar o objeto Mailer.
E se precisarmos mudar o transport de sendmail para smtp em toda a aplicao? Ns precisaremos localizar
cada lugar em que adicionamos um servio Mailer e alter-lo.
Criando/Configurando Servios no Container
Uma resposta melhor deixar o container de servio criar o objeto Mailer para voc. Para que isso funcione,
preciso ensinar o container como criar o servio Mailer. Isto feito atravs de configurao, que pode ser
especificada em YAML, XML ou PHP:
YAML
# app/config/config.yml
services:
my_mailer:
class:
Acme\HelloBundle\Mailer
arguments:
[sendmail]

XML
<!-- app/config/config.xml -->
<services>
<service id="my_mailer" class="Acme\HelloBundle\Mailer">
<argument>sendmail</argument>

2.1. Livro

219

Symfony Docs pt-BR Documentation, Verso 2.4

</service>
</services>

PHP
// app/config/config.php
use Symfony\Component\DependencyInjection\Definition;
$container->setDefinition(my_mailer, new Definition(
Acme\HelloBundle\Mailer,
array(sendmail)
));

Nota: Quando o Symfony2 inicializado, ele constri o container de servio usando a configurao da aplicao (por padro (app/config/config.yml). O arquivo exato que carregado ditado pelo mtodo
AppKernel::registerContainerConfiguration() , que carrega um arquivo de configurao do ambiente especfico (por exemplo config_dev.yml para o ambiente dev ou config_prod.yml para o prod).
Uma instncia do objeto Acme\HelloBundle\Mailer est agora disponvel atravs do container de servio. O
container est disponvel em qualquer controlador tradicional do Symfony2 onde voc pode acessar os servios do
container atravs do mtodo de atalho get():
class HelloController extends Controller
{
// ...
public function sendEmailAction()
{
// ...
$mailer = $this->get(my_mailer);
$mailer->send(ryan@foobar.net, ... );
}
}

Quando requisitamos o servio my_mailer a partir do container, o container constri o objeto e o retorna. Esta
outra vantagem importante do uso do container de servio. Ou seja, um servio nunca construdo at que ele seja
necessrio. Se voc definir um servio e nunca us-lo em um pedido, o servio nunca ser criado. Isso economiza
memria e aumenta a velocidade de sua aplicao. Isto tambm significa que no h nenhuma perda ou apenas uma
perda insignificante de desempenho ao definir muitos servios. Servios que nunca so usados, nunca so construdos.
Como um bnus adicional, o servio Mailer criado apenas uma vez e a mesma instncia retornada cada vez que
voc requisitar o servio. Isso quase sempre o comportamento que voc precisa ( mais flexvel e poderoso), mas
vamos aprender, mais tarde, como voc pode configurar um servio que possui vrias instncias.
Parmetros do Servio
A criao de novos servios (objetos, por exemplo) atravs do container bastante simples. Os parmetros tornam a
definio dos servios mais organizada e flexvel:
YAML
# app/config/config.yml
parameters:
my_mailer.class:
my_mailer.transport:

Acme\HelloBundle\Mailer
sendmail

services:

220

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

my_mailer:
class:
arguments:

%my_mailer.class%
[%my_mailer.transport%]

XML
<!-- app/config/config.xml -->
<parameters>
<parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter>
<parameter key="my_mailer.transport">sendmail</parameter>
</parameters>
<services>
<service id="my_mailer" class="%my_mailer.class%">
<argument>%my_mailer.transport%</argument>
</service>
</services>

PHP
// app/config/config.php
use Symfony\Component\DependencyInjection\Definition;
$container->setParameter(my_mailer.class, Acme\HelloBundle\Mailer);
$container->setParameter(my_mailer.transport, sendmail);
$container->setDefinition(my_mailer, new Definition(
%my_mailer.class%,
array(%my_mailer.transport%)
));

O resultado final exatamente o mesmo de antes - a diferena est apenas em como definimos o servio. Quando
as strings my_mailer.class e my_mailer.transport esto envolvidas por sinais de porcentagem (%), o
container sabe que deve procurar por parmetros com esses nomes. Quando o container construdo, ele procura o
valor de cada parmetro e utiliza-o na definio do servio.
Nota: O sinal de porcentagem dentro de um parmetro ou argumento, como parte da string, deve ser escapado com
um outro sinal de porcentagem:
<argument type="string">http://symfony.com/?foo=%%s&bar=%%d</argument>

A finalidade dos parmetros alimentar informao para os servios. Naturalmente, no h nada de errado em definir
o servio sem o uso de quaisquer parmetros. Os parmetros, no entanto, possuem vrias vantagens:
separao e organizao de todas as opes dos servios sob uma nica chave parameters;
os valores do parmetro podem ser usados em mltiplas definies de servios;
ao criar um servio em um bundle (vamos mostrar isso em breve), o uso de parmetros permite que o servio
seja facilmente customizado em sua aplicao.
A escolha de usar ou no os parmetros com voc. Bundles de terceiros de alta qualidade sempre utilizaro parmetros, pois, eles tornam mais configurvel o servio armazenado no container. Para os servios em sua aplicao,
entretanto, voc pode no precisar da flexibilidade dos parmetros.

2.1. Livro

221

Symfony Docs pt-BR Documentation, Verso 2.4

Parmetros Array

Os parmetros no precisam ser strings, eles tambm podem ser arrays. Para o formato XML , voc precisar usar o
atributo type=collection em todos os parmetros que so arrays.
YAML
# app/config/config.yml
parameters:
my_mailer.gateways:
- mail1
- mail2
- mail3
my_multilang.language_fallback:
en:
- en
- fr
fr:
- fr
- en

XML
<!-- app/config/config.xml -->
<parameters>
<parameter key="my_mailer.gateways" type="collection">
<parameter>mail1</parameter>
<parameter>mail2</parameter>
<parameter>mail3</parameter>
</parameter>
<parameter key="my_multilang.language_fallback" type="collection">
<parameter key="en" type="collection">
<parameter>en</parameter>
<parameter>fr</parameter>
</parameter>
<parameter key="fr" type="collection">
<parameter>fr</parameter>
<parameter>en</parameter>
</parameter>
</parameter>
</parameters>

PHP
// app/config/config.php
use Symfony\Component\DependencyInjection\Definition;
$container->setParameter(my_mailer.gateways, array(mail1, mail2, mail3));
$container->setParameter(my_multilang.language_fallback,
array(en => array(en, fr),
fr => array(fr, en),
));

Importando outros Recursos de Configurao do Container


Dica: Nesta seo, vamos referenciar os arquivos de configurao de servio como recursos. Isto para destacar o
fato de que, enquanto a maioria dos recursos de configurao ser em arquivos (por exemplo, YAML, XML, PHP), o

222

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Symfony2 to flexvel que a configurao pode ser carregada de qualquer lugar (por exemplo, de um banco de dados
ou at mesmo atravs de um web service externo).
O service container construdo usando um nico recurso de configurao (por padro
app/config/config.yml). Todas as outras configuraes de servio (incluindo o ncleo do Symfony2 e
configuraes de bundles de terceiros) devem ser importadas dentro desse arquivo de uma forma ou de outra. Isso lhe
d absoluta flexibilidade sobre os servios em sua aplicao.
Configuraes de servios externos podem ser importadas de duas maneiras distintas. Primeiro, vamos falar sobre o
mtodo que voc vai usar mais frequentemente na sua aplicao: a diretiva imports. Na seo seguinte, vamos falar
sobre o segundo mtodo, que mais flexvel e preferido para a importao de configurao de servios dos bundles de
terceiros.
Importando configurao com imports

At agora, adicionamos a nossa definio do container de servio my_mailer diretamente no arquivo de configurao
da aplicao (por exemplo: app/config/config.yml). Naturalmente, j que a prpria classe Mailer reside
dentro do AcmeHelloBundle, faz mais sentido colocar a definio do container my_mailer dentro do bundle
tambm.
Primeiro, mova a definio do container my_mailer dentro de um novo arquivo container de recurso dentro do
AcmeHelloBundle. Se os diretrios Resources ou Resources/config no existirem, adicione eles.
YAML
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
my_mailer.class:
Acme\HelloBundle\Mailer
my_mailer.transport: sendmail
services:
my_mailer:
class:
arguments:

%my_mailer.class%
[%my_mailer.transport%]

XML
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
<parameters>
<parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter>
<parameter key="my_mailer.transport">sendmail</parameter>
</parameters>
<services>
<service id="my_mailer" class="%my_mailer.class%">
<argument>%my_mailer.transport%</argument>
</service>
</services>

PHP
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
$container->setParameter(my_mailer.class, Acme\HelloBundle\Mailer);
$container->setParameter(my_mailer.transport, sendmail);
$container->setDefinition(my_mailer, new Definition(

2.1. Livro

223

Symfony Docs pt-BR Documentation, Verso 2.4

%my_mailer.class%,
array(%my_mailer.transport%)
));

A definio em si no mudou, apenas a sua localizao. Claro que o container de servio no sabe sobre o novo
arquivo de recurso. Felizmente, ns podemos facilmente importar o arquivo de recurso usando a chave imports na
configurao da aplicao.
YAML
# app/config/config.yml
imports:
- { resource: @AcmeHelloBundle/Resources/config/services.yml }

XML
<!-- app/config/config.xml -->
<imports>
<import resource="@AcmeHelloBundle/Resources/config/services.xml"/>
</imports>

PHP
// app/config/config.php
$this->import(@AcmeHelloBundle/Resources/config/services.php);

A diretiva imports permite sua aplicao incluir os recursos de configurao do container de servio de qualquer outra localizao (mais comumente, de bundles). A localizao do resource, para arquivos, o caminho
absoluto para o arquivo de recurso. A sintaxe especial @AcmeHello resolve o caminho do diretrio do bundle
AcmeHelloBundle. Isso ajuda voc a especificar o caminho para o recurso sem preocupar-se mais tarde caso
mover o AcmeHelloBundle para um diretrio diferente.
Importando Configurao atravs de Extenses do Container

Ao desenvolver no Symfony2, voc vai mais comumente usar a diretiva imports para importar a configurao do
container dos bundles que voc criou especificamente para a sua aplicao. As configuraes de container de bundles
de terceiros, incluindo os servios do ncleo do Symfony2, so geralmente carregadas usando um outro mtodo que
mais flexvel e fcil de configurar em sua aplicao.
Veja como ele funciona. Internamente, cada bundle define os seus servios de forma semelhante a que vimos at agora.
Ou seja, um bundle utiliza um ou mais arquivos de configuraes de recurso (normalmente XML) para especificar os
parmetros e servios para aquele bundle. No entanto, em vez de importar cada um desses recursos diretamente a
partir da sua configurao da aplicao usando a diretiva imports, voc pode simplesmente invocar uma extenso
do container de servio dentro do bundle que faz o trabalho para voc. Uma extenso do container de servio uma
classe PHP criada pelo autor do bundle para realizar duas coisas:
importar todos os recursos de container de servio necessrios para configurar os servios para o bundle;
fornecer configurao simples e semntica para que o bundle possa ser configurado sem interagir com os parmetros da configurao do container de servio.
Em outras palavras, uma extenso do container de servio configura os servios para um bundle em seu nome. E,
como veremos em breve, a extenso fornece uma interface sensvel e de alto nvel para configurar o bundle.
Considere o FrameworkBundle - o bundle do ncleo do framework Symfony2 - como um exemplo. A presena do cdigo a seguir na configurao da sua aplicao invoca a extenso do container de servio dentro do
FrameworkBundle:
YAML
224

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

# app/config/config.yml
framework:
secret:
xxxxxxxxxx
form:
true
csrf_protection: true
router:
{ resource: "%kernel.root_dir%/config/routing.yml" }
# ...

XML
<!-- app/config/config.xml -->
<framework:config secret="xxxxxxxxxx">
<framework:form />
<framework:csrf-protection />
<framework:router resource="%kernel.root_dir%/config/routing.xml" />
<!-- ... -->
</framework>

PHP
// app/config/config.php
$container->loadFromExtension(framework, array(
secret
=> xxxxxxxxxx,
form
=> array(),
csrf-protection => array(),
router
=> array(resource => %kernel.root_dir%/config/routing.php),
// ...
));

Quando realizado o parse da configurao, o container procura uma extenso que pode lidar com a diretiva de
configurao framework. A extenso em questo, que reside no FrameworkBundle, chamada e a configurao
do servio para o FrameworkBundle carregada. Se voc remover totalmente a chave framework de seu arquivo
de configurao da aplicao, os servios do ncleo do Symfony2 no sero carregados. O ponto que voc est no
controle: o framework Symfony2 no contm qualquer tipo de magia ou realiza quaisquer aes que voc no tenha
controle.
Claro que voc pode fazer muito mais do que simplesmente ativar a extenso do container de servio do
FrameworkBundle. Cada extenso permite que voc facilmente personalize o bundle, sem se preocupar com a
forma como os servios internos so definidos.
Neste caso, a extenso permite que voc personalize as configuraes error_handler, csrf_protection,
router e muito mais. Internamente, o FrameworkBundle usa as opes especificadas aqui para definir e configurar os servios especficos para ele. O bundle se encarrega de criar todos os parameters e services necessrios
para o container de servio, permitindo ainda que grande parte da configurao seja facilmente personalizada. Como
um bnus adicional, a maioria das extenses do container de servio tambm so inteligentes o suficiente para executar
a validao - notificando as opes que esto faltando ou que esto com o tipo de dados incorreto.
Ao instalar ou configurar um bundle, consulte a documentao do bundle para verificar como os servios para o
bundle devem ser instalados e configurados. As opes disponveis para os bundles do ncleo podem ser encontradas
no Reference Guide.
Nota: Nativamente, o container de servio somente reconhece as diretivas parameters, services e imports.
Quaisquer outras diretivas so manipuladas pela extenso do container de servio.
Se voc deseja expor configurao amigvel em seus prprios bundles, leia o Como expor uma Configurao Semntica para um Bundle do cookbook.

2.1. Livro

225

Symfony Docs pt-BR Documentation, Verso 2.4

Referenciando (Injetando) Servios


At agora, nosso servio original my_mailer simples: ele recebe apenas um argumento em seu construtor, que
facilmente configurvel. Como voc ver, o poder real do container percebido quando voc precisa criar um servio
que depende de um ou mais outros servios no container.
Vamos comear com um exemplo. Suponha que temos um novo servio, NewsletterManager, que ajuda
a gerenciar a preparao e entrega de uma mensagem de e-mail para uma coleo de endereos. Claro que
o servio my_mailer j est muito bom ao entregar mensagens de e-mail, por isso vamos us-lo dentro do
NewsletterManager para lidar com a entrega das mensagens. Esta simulao de classe seria parecida com:
namespace Acme\HelloBundle\Newsletter;
use Acme\HelloBundle\Mailer;
class NewsletterManager
{
protected $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
// ...
}

Sem usar o container de servio, podemos criar um novo NewsletterManager facilmente dentro de um controlador:
public function sendNewsletterAction()
{
$mailer = $this->get(my_mailer);
$newsletter = new Acme\HelloBundle\Newsletter\NewsletterManager($mailer);
// ...
}

Esta abordagem boa, mas, e se decidirmos mais tarde que a classe NewsletterManager precisa de um segundo
ou terceiro argumento? E se decidirmos refatorar nosso cdigo e renomear a classe? Em ambos os casos, voc precisa
encontrar todos os locais onde o NewsletterManager instanciado e modific-lo. Certamente, o container de
servio nos d uma opo muito mais atraente:
YAML
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
# ...
newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager
services:
my_mailer:
# ...
newsletter_manager:
class:
%newsletter_manager.class%
arguments: [@my_mailer]

XML

226

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

<!-- src/Acme/HelloBundle/Resources/config/services.xml -->


<parameters>
<!-- ... -->
<parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</par
</parameters>
<services>
<service id="my_mailer" ... >
<!-- ... -->
</service>
<service id="newsletter_manager" class="%newsletter_manager.class%">
<argument type="service" id="my_mailer"/>
</service>
</services>

PHP
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

// ...
$container->setParameter(newsletter_manager.class, Acme\HelloBundle\Newsletter\NewsletterMana
$container->setDefinition(my_mailer, ... );
$container->setDefinition(newsletter_manager, new Definition(
%newsletter_manager.class%,
array(new Reference(my_mailer))
));

Em YAML, a sintaxe especial @my_mailer diz ao container para procurar por um servio chamado my_mailer
e passar esse objeto para o construtor do NewsletterManager. Neste caso, entretanto, o servio especificado
my_mailer deve existir. Caso ele no existir, ser gerada uma exceo. Voc pode marcar suas dependncias como
opcionais - o que ser discutido na prxima seo.
O uso de referncias uma ferramenta muito poderosa que lhe permite criar classes de servios independentes com
dependncias bem definidas. Neste exemplo, o servio newsletter_manager precisa do servio my_mailer
para funcionar. Quando voc define essa dependncia no container de servio, o container cuida de todo o trabalho de
instanciar os objetos.
Dependncias Opcionais: Injeo do Setter

Injetando dependncias no construtor dessa maneira uma excelente forma de assegurar que a dependncia estar
disponvel para o uso. No entanto, se voc possuir dependncias opcionais para uma classe, a injeo do setter pode
ser uma opo melhor. Isto significa injetar a dependncia usando uma chamada de mtodo ao invs do construtor. A
classe ficaria assim:
namespace Acme\HelloBundle\Newsletter;
use Acme\HelloBundle\Mailer;
class NewsletterManager
{
protected $mailer;
public function setMailer(Mailer $mailer)
{

2.1. Livro

227

Symfony Docs pt-BR Documentation, Verso 2.4

$this->mailer = $mailer;
}
// ...
}

Para injetar a dependncia pelo mtodo setter somente necessria uma mudana da sintaxe:
YAML
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
# ...
newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager
services:
my_mailer:
# ...
newsletter_manager:
class:
%newsletter_manager.class%
calls:
- [ setMailer, [ @my_mailer ] ]

XML

<!-- src/Acme/HelloBundle/Resources/config/services.xml -->


<parameters>
<!-- ... -->
<parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</par
</parameters>
<services>
<service id="my_mailer" ... >
<!-- ... -->
</service>
<service id="newsletter_manager" class="%newsletter_manager.class%">
<call method="setMailer">
<argument type="service" id="my_mailer" />
</call>
</service>
</services>

PHP
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

// ...
$container->setParameter(newsletter_manager.class, Acme\HelloBundle\Newsletter\NewsletterMana
$container->setDefinition(my_mailer, ... );
$container->setDefinition(newsletter_manager, new Definition(
%newsletter_manager.class%
))->addMethodCall(setMailer, array(
new Reference(my_mailer)
));

Nota: As abordagens apresentadas nesta seo so chamadas de injeo de construtor e injeo de setter. O

228

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

container de servio do Symfony2 tambm suporta a injeo de propriedade.

Tornando Opcionais as Referncias


s vezes, um de seus servios pode ter uma dependncia opcional, ou seja, a dependncia no exigida por seu
servio para funcionar corretamente. No exemplo acima, o servio my_mailer deve existir, caso contrrio, uma
exceo ser gerada. Ao modificar a definio do servio newsletter_manager , voc pode tornar esta referncia
opcional. O container ir, ento, injet-lo se ele existir e no ir fazer nada caso contrrio:
YAML
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
# ...
services:
newsletter_manager:
class:
%newsletter_manager.class%
arguments: [@?my_mailer]

XML
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
<services>
<service id="my_mailer" ... >
<!-- ... -->
</service>
<service id="newsletter_manager" class="%newsletter_manager.class%">
<argument type="service" id="my_mailer" on-invalid="ignore" />
</service>
</services>

PHP
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerInterface;

// ...
$container->setParameter(newsletter_manager.class, Acme\HelloBundle\Newsletter\NewsletterMana
$container->setDefinition(my_mailer, ... );
$container->setDefinition(newsletter_manager, new Definition(
%newsletter_manager.class%,
array(new Reference(my_mailer, ContainerInterface::IGNORE_ON_INVALID_REFERENCE))
));

Em YAML, a sintaxe especial @? diz ao container de servio que a dependncia opcional. Naturalmente, o
NewsletterManager tambm deve ser escrito para permitir uma dependncia opcional:
public function __construct(Mailer $mailer = null)
{
// ...
}

2.1. Livro

229

Symfony Docs pt-BR Documentation, Verso 2.4

Servios do Ncleo do Symfony e de Bundles de Terceiros


Desde que o Symfony2 e todos os bundles de terceiros configuram e recuperam os seus servios atravs do container, voc pode acess-los facilmente ou at mesmo us-los em seus prprios servios. Para manter tudo simples, o
Symfony2, por padro, no exige que controladores sejam definidos como servios. Alm disso, o Symfony2 injeta
o container de servio inteiro em seu controlador. Por exemplo, para gerenciar o armazenamento de informaes em
uma sesso do usurio, o Symfony2 fornece um servio session, que voc pode acessar dentro de um controlador
padro da seguinte forma:
public function indexAction($bar)
{
$session = $this->get(session);
$session->set(foo, $bar);
// ...
}

No Symfony2, voc vai usar constantemente os servios fornecidos pelo ncleo do Symfony ou outros bundles de
terceiros para executar tarefas como renderizao de templates (templating), envio de e-mails (mailer), ou
acessar informaes sobre o pedido(request).
Podemos levar isto um passo adiante, usando esses servios dentro dos servios que voc criou para a sua aplicao. Vamos modificar o NewsletterManager para utilizar o servio de mailer real do Symfony2 (no lugar
do my_mailer). Vamos tambm passar o servio de templating engine para o NewsletterManager ento ele
poder gerar o contedo de e-mail atravs de um template:
namespace Acme\HelloBundle\Newsletter;
use Symfony\Component\Templating\EngineInterface;
class NewsletterManager
{
protected $mailer;
protected $templating;
public function __construct(\Swift_Mailer $mailer, EngineInterface $templating)
{
$this->mailer = $mailer;
$this->templating = $templating;
}
// ...
}

A configurao do service container fcil:


YAML
services:
newsletter_manager:
class:
%newsletter_manager.class%
arguments: [@mailer, @templating]

XML
<service id="newsletter_manager" class="%newsletter_manager.class%">
<argument type="service" id="mailer"/>
<argument type="service" id="templating"/>
</service>

230

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

PHP
$container->setDefinition(newsletter_manager, new Definition(
%newsletter_manager.class%,
array(
new Reference(mailer),
new Reference(templating)
)
));

O servio newsletter_manager agora tem acesso aos servios do ncleo mailer and templating. Esta
uma forma comum de criar servios especficos para sua aplicao que aproveitam o poder de diferentes servios
dentro do framework.
Dica: Certifique-se de que a entrada swiftmailer aparece na configurao da sua aplicao. Como mencionamos
em Importando Configurao atravs de Extenses do Container, a chave swiftmailer invoca a extenso de
servio do SwiftmailerBundle, que registra o servio mailer.

Configurao Avanada do Container


Como vimos, a definio de servios no interior do container fcil, geralmente envolvendo uma chave de configurao service e alguns parmetros. No entanto, o container possui vrias outras ferramentas disponveis que ajudam a
adicionar uma tag para uma funcionalidade especial nos servios, criar servios mais complexos e executar operaes
aps o container estar construdo.
Marcando Servios como pblico / privado

Ao definir os servios, normalmente voc vai desejar poder acessar essas definies dentro do cdigo da sua aplicao.
Esses servios so chamados publicos. Por exemplo, o servio doctrine registrado com o container quando se
utiliza o DoctrineBundle um servio pblico, j que voc pode acess-lo via:
$doctrine = $container->get(doctrine);

No entanto, existem casos de uso em que voc no deseja que um servio seja pblico. Isto comum quando um
servio definido apenas porque poderia ser usado como um argumento para um outro servio.
Nota: Se voc usar um servio privado como um argumento para mais de um servio, isto ir resultar em duas
instncias diferentes sendo usadas, j que, a instanciao do servio privado realizada inline (por exemplo: new
PrivateFooBar()).
Simplesmente falando: Um servio ser privado quando voc no quiser acess-lo diretamente em seu cdigo.
Aqui est um exemplo:
YAML
services:
foo:
class: Acme\HelloBundle\Foo
public: false

XML

2.1. Livro

231

Symfony Docs pt-BR Documentation, Verso 2.4

<service id="foo" class="Acme\HelloBundle\Foo" public="false" />

PHP
$definition = new Definition(Acme\HelloBundle\Foo);
$definition->setPublic(false);
$container->setDefinition(foo, $definition);

Agora que o servio privado, voc no pode chamar:


$container->get(foo);

No entanto, se o servio foi marcado como privado, voc ainda pode adicionar um alias para ele (veja abaixo) para
acessar este servio (atravs do alias).
Nota: Os servios so pblicos por padro.

Definindo Alias

Ao usar bundles do ncleo ou de terceiros dentro de sua aplicao, voc pode desejar usar atalhos para acessar alguns
servios. Isto possvel adicionando alias eles e, alm disso, voc pode at mesmo adicionar alias para servios que
no so pblicos.
YAML
services:
foo:
class: Acme\HelloBundle\Foo
bar:
alias: foo

XML
<service id="foo" class="Acme\HelloBundle\Foo"/>
<service id="bar" alias="foo" />

PHP
$definition = new Definition(Acme\HelloBundle\Foo);
$container->setDefinition(foo, $definition);
$containerBuilder->setAlias(bar, foo);

Isto significa que, ao usar o container diretamente, voc pode acessar o servio foo pedindo pelo servio bar como
segue:
$container->get(bar); // retorna o servio foo

Incluindo Arquivos

Podem haver casos de uso quando necessrio incluir outro arquivo antes do servio em si ser carregado. Para fazer
isso, voc pode usar a diretiva file.
YAML

232

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

services:
foo:
class: Acme\HelloBundle\Foo\Bar
file: %kernel.root_dir%/src/path/to/file/foo.php

XML
<service id="foo" class="Acme\HelloBundle\Foo\Bar">
<file>%kernel.root_dir%/src/path/to/file/foo.php</file>
</service>

PHP
$definition = new Definition(Acme\HelloBundle\Foo\Bar);
$definition->setFile(%kernel.root_dir%/src/path/to/file/foo.php);
$container->setDefinition(foo, $definition);

Observe que o symfony ir, internamente, chamar a funo PHP require_once o que significa que seu arquivo ser
includo apenas uma vez por pedido.
Tags (tags)

Da mesma forma que podem ser definidas tags para um post de um blog na web com palavras como Symfony ou
PHP, tambm podem ser definidas tags para os servios configurados em seu container. No container de servio, a
presena de uma tag significa que o servio destina-se ao uso para um fim especfico. Veja o seguinte exemplo:
YAML
services:
foo.twig.extension:
class: Acme\HelloBundle\Extension\FooExtension
tags:
- { name: twig.extension }

XML
<service id="foo.twig.extension" class="Acme\HelloBundle\Extension\FooExtension">
<tag name="twig.extension" />
</service>

PHP
$definition = new Definition(Acme\HelloBundle\Extension\FooExtension);
$definition->addTag(twig.extension);
$container->setDefinition(foo.twig.extension, $definition);

A tag twig.extension uma tag especial que o TwigBundle usa durante a configurao. Ao definir a tag
twig.extension para este servio, o bundle saber que o servio foo.twig.extension deve ser registrado como uma extenso Twig com o Twig. Em outras palavras, o Twig encontra todos os servios com a tag
twig.extension e automaticamente registra-os como extenses.
Tags, ento, so uma forma de dizer ao Symfony2 ou outros bundles de terceiros que o seu servio deve ser registrado
ou usado de alguma forma especial pelo bundle.
Segue abaixo uma lista das tags disponveis com os bundles do ncleo do Symfony2. Cada uma delas tem um efeito
diferente no seu servio e muitas tags requerem argumentos adicionais (alm do parmetro name).
assetic.filter
assetic.templating.php
2.1. Livro

233

Symfony Docs pt-BR Documentation, Verso 2.4

data_collector
form.field_factory.guesser
kernel.cache_warmer
kernel.event_listener
monolog.logger
routing.loader
security.listener.factory
security.voter
templating.helper
twig.extension
translation.loader
validator.constraint_validator
Saiba mais
/components/dependency_injection/factories
/components/dependency_injection/parentservices
Como definir Controladores como Servios

2.1.15 Performance
O Symfony2 rpido, logo aps a sua instalao. Claro, se voc realmente precisa de mais velocidade, h muitas
maneiras para tornar o Symfony ainda mais rpido. Neste captulo, voc vai explorar muitas das formas mais comuns
e poderosas para tornar a sua aplicao Symfony ainda mais rpida.
Use um Cache de Cdigo Byte (ex.: APC)
Uma das melhores (e mais fceis) coisas que voc deve fazer para melhorar a sua performance usar um cache de
cdigo byte. A idia de um cache de cdigo byte remover a necessidade de constantemente recompilar o cdigo
fonte PHP. H uma srie de Caches de cdigo byte _ disponveis, alguns dos quais so de cdigo aberto. O cache de
cdigo byte mais amplamente usado , provavelmente, o APC
Usar um cache de cdigo byte no tem um lado negativo, e o Symfony2 foi arquitetado para executar realmente bem
neste tipo de ambiente.
Mais otimizaes

Caches de cdigo byte normalmente monitoram as alteraes nos arquivos fonte. Isso garante que, se o fonte de um
arquivo for alterado, o cdigo byte recompilado automaticamente. Isto realmente conveniente, mas, obviamente,
adiciona uma sobrecarga.
Por este motivo, alguns caches de cdigo byte oferecem uma opo para desabilitar estas verificaes. Obviamente,
quando desabilitar estas verificaes, caber ao administrador do servidor garantir que o cache seja limpo sempre que
houver qualquer alterao nos arquivos fonte. Caso contrrio, as atualizaes que voc fizer nunca sero vistas.

234

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Por exemplo, para desativar estas verificaes no APC, basta adicionar apc.stat=0 ao seu arquivo de configurao
php.ini.
Utilize um Autoloader com cache (ex.: ApcUniversalClassLoader)
Por padro, a edio standard do Symfony2 usa o UniversalClassLoader no arquivo autoloader.php . Este autoloader fcil de usar, pois ele encontra automaticamente as novas classes que voc colocou nos diretrios registrados.
Infelizmente, isso tem um custo, devido ao carregador iterar sobre todos os namespaces configurados para encontrar um determinado arquivo, fazendo chamadas file_exists at que, finalmente, encontre o arquivo que estiver
procurando.
A soluo mais simples armazenar em cache a localizao de cada classe aps ter sido localizada pela primeira
vez. O Symfony vem com um carregador de classe - ApcClassLoader - que faz exatamente isso. Para us-lo,
basta adaptar seu arquivo front controller. Se voc estiver usando a Distribuio Standard, este cdigo j deve estar
disponvel com comentrios neste arquivo:
// app.php
// ...
$loader = require_once __DIR__./../app/bootstrap.php.cache;
// Use APC for autoloading to improve performance
// Change sf2 by the prefix you want in order to prevent key conflict with another application
/*
$loader = new ApcClassLoader(sf2, $loader);
$loader->register(true);
*/
// ...

Nota: Ao usar o autoloader APC, se voc adicionar novas classes, elas sero encontradas automaticamente e tudo
vai funcionar da mesma forma como antes (ou seja, sem razo para limpar o cache). No entanto, se voc alterar a
localizao de um namespace ou prefixo em particular, voc vai precisar limpar o cache do APC. Caso contrrio, o
autoloader ainda estar procurando pelo local antigo para todas as classes dentro desse namespace.

Utilize arquivos de inicializao (bootstrap)


Para garantir a mxima flexibilidade e reutilizao de cdigo, as aplicaes do Symfony2 aproveitam uma variedade
de classes e componentes de terceiros. Mas, carregar todas essas classes de arquivos separados em cada requisio
pode resultar em alguma sobrecarga. Para reduzir essa sobrecarga, a Edio Standard do Symfony2 fornece um script
para gerar o chamado arquivo de inicializao, que consiste em mltiplas definies de classes em um nico arquivo.
Ao incluir este arquivo (que contm uma cpia de muitas das classes core), o Symfony no precisa incluir nenhum dos
arquivos fonte contendo essas classes. Isto reduzir bastante a E/S no disco.
Se voc estiver usando a Edio Standard do Symfony2, ento, voc provavelmente j est usando o arquivo de
inicializao. Para ter certeza, abra o seu front controller (geralmente app.php) e, certifique-se que existe a
seguinte linha:
require_once __DIR__./../app/bootstrap.php.cache;

Note que existem duas desvantagens ao usar um arquivo de inicializao:


O arquivo precisa ser regerado, sempre que houver qualquer mudana nos fontes originais (ex.: quando voc
atualizar o fonte do Symfony2 ou as bibliotecas vendor);

2.1. Livro

235

Symfony Docs pt-BR Documentation, Verso 2.4

Quando estiver debugando, necessrio colocar break points dentro do arquivo de inicializao.
Se voc estiver usando a Edio Standard do Symfony2, o arquivo de inicializao automaticamente reconstrudo
aps a atualizao das bibliotecas vendor atravs do comando php composer.phar install .
Arquivos de inicializao e caches de cdigo byte

Mesmo quando se utiliza um cache de cdigo byte, o desempenho ir melhorar quando se utiliza um arquivo de
inicializao, pois, haver menos arquivos para monitorar as mudanas. Claro, se este recurso est desativado no
cache de cdigo byte (ex.: apc.stat=0 no APC), no h mais motivo para usar um arquivo de inicializao.

2.1.16 API estvel do Symfony2


A API estvel do Symfony2 um subconjunto de todos mtodos pblicos publicados (componentes e bundles principais) que possuem as seguintes propriedades:
O namespace e o nome da classe no mudaro;
O nome do mtodo no mudar;
A assinatura do mtodo (parmetros e tipo de retorno) no mudar;
A funo do mtodo (o que ele faz) no mudar;
A implementao em si pode mudar. O nico caso que pode causar alterao na API estvel ser para correo de
algum problema de segurana.
A API estvel baseada em uma lista (whitelist), marcada com @api. Assim, tudo que no esteja marcado com @api
no parte da API estvel.
Dica: Qualquer bundle de terceiros deveria tambm publicar sua prpria API estvel.
Atualmente na verso Symfony 2.0 os seguintes componentes tm API pblica marcada:
BrowserKit
ClassLoader
Console
CssSelector
DependencyInjection
DomCrawler
EventDispatcher
Finder
HttpFoundation
HttpKernel
Locale
Process
Routing
Templating
Translation

236

Captulo 2. Livro

Symfony Docs pt-BR Documentation, Verso 2.4

Validator
Yaml
Fundamentos de Symfony e HTTP
Symfony2 versus o PHP puro
Instalando e Configurando o Symfony
Controlador
Roteamento
Criando e usando Templates
Bancos de Dados e Doctrine
Testes
Validao
Formulrios
Segurana
HTTP Cache
Tradues
Container de Servio
Performance
API estvel do Symfony2
Fundamentos de Symfony e HTTP
Symfony2 versus o PHP puro
Instalando e Configurando o Symfony
Controlador
Roteamento
Criando e usando Templates
Bancos de Dados e Doctrine
Testes
Validao
Formulrios
Segurana
HTTP Cache
Tradues
Container de Servio
Performance
API estvel do Symfony2

2.1. Livro

237

Symfony Docs pt-BR Documentation, Verso 2.4

238

Captulo 2. Livro

CAPTULO 3

Cookbook

3.1 Cookbook
3.1.1 Workflow
Como Criar e Armazenar um Projeto Symfony2 no git
Dica: Embora este artigo seja especificamente sobre git, os mesmos princpios genricos aplicam-se caso voc estiver
armazenando o seu projeto no Subversion.
Uma vez que voc leu /book/page_creation e familiarizou-se com o Symfony, j est, sem dvida, pronto para
comear o seu prprio projeto. Neste artigo do cookbook, voc aprender a melhor maneira de iniciar um novo projeto
Symfony2 que ser armazenado usando o sistema gerenciador de controle de verso git.
Configurao Inicial do Projeto

Para comear, voc precisa baixar o Symfony e inicializar o seu repositrio git local:
1. Baixe a Edio Standard do Symfony2 sem vendors.
2. Descompacte a distribuio. Ser criado um diretrio chamado Symfony com a sua nova estrutura do projeto,
arquivos de configurao, etc. Renomeie-o como desejar.
3. Crie um novo arquivo chamado .gitignore no raiz de seu novo projeto (Ex.: junto com o arquivo
composer.json) e cole o contedo seguinte nele. Os arquivos correspondentes a estes padres sero ignorados pelo git:
/web/bundles/
/app/bootstrap*
/app/cache/*
/app/logs/*
/vendor/
/app/config/parameters.yml

4. Copie app/config/parameters.yml para app/config/parameters.yml.dist. O arquivo


parameters.yml ignorado pelo git (veja acima), de modo que as configuraes especficas da mquina,
como senhas de banco de dados, no sejam comitadas. Criando o arquivo parameters.yml.dist, novos desenvolvedores podem, rapidamente, clonar o projeto, copiar este arquivo para parameters.yml,
personaliz-lo e iniciar o desenvolvimento.

239

Symfony Docs pt-BR Documentation, Verso 2.4

5. Inicialize o seu repositrio git:


$ git init

6. Adicione todos os arquivos iniciais ao git:


$ git add .

7. Crie um commit inicial para o seu projeto:


$ git commit -m "Initial commit"

8. Finalmente, baixe todas as bibliotecas vendor de terceiros executando o composer. Para detalhes, veja Atualizando os Vendors.
Neste ponto, voc tem um projeto Symfony2 totalmente funcional que est corretamente comitado com o git. Voc
pode iniciar imediatamente o desenvolvimento, comitando as novas alteraes em seu repositrio git.
Voc pode continuar acompanhando o captulo /book/page_creation para saber mais sobre como configurar e
desenvolver dentro da sua aplicao.
Dica: A Edio Standard do Symfony2 vem com algumas funcionalidades exemplo. Para remover o cdigo de
exemplo, siga as instrues contidas no Readme da Edio Standard.

Gerenciando Bibliotecas Vendor com bin/vendors e deps

Cada projeto Symfony usa um grande grupo de bibliotecas vendor de terceiros.


Por padro, estas bibliotecas so baixadas executando o script php bin/vendors install. Este script l o
arquivo deps, e baixa as bibliotecas ali informadas no diretrio vendor/. Ele tambm l o arquivo deps.lock,
fixando cada biblioteca listada ao respectivo hash do commit git.
Nesta configurao, as bibliotecas vendor no fazem parte de seu repositrio git, nem mesmo como sub-mdulos. Em
vez disso, contamos com os arquivos deps e deps.lock e o script bin/vendors para gerenciar tudo. Esses
arquivos so parte de seu repositrio, ento, as verses necessrias de cada biblioteca de terceiros tem controle de
verso no git, e voc pode usar o script vendors para trazer o seu projeto atualizado.
Sempre que um desenvolvedor clona um projeto, ele(a) deve executar o script php bin/vendors install para
garantir que todas as bibliotecas vendor necessrias foram baixadas.
Atualizando o Symfony
Uma vez que o Symfony apenas um grupo de bibliotecas de terceiros e estas bibliotecas so totalmente controladas atravs do deps e deps.lock, atualizar o Symfony significa, simplesmente, atualizar cada um desses
arquivos para combinar seu estado na ltima Edio Standard do Symfony.
Claro, se voc adicionou novas entradas ao deps ou deps.lock, certifique-se de substituir apenas as partes
originais (ou seja, no excluir as suas entradas personalizadas).
Cuidado: H tambm um comando php bin/vendors update, mas isso no tem nada a ver com a atualizao do seu projeto e voc normalmente no precisar utiliz-lo. Este comando usado para congelar as verses
de todas as suas bibliotecas vendor atualizando-as para a verso especificada em deps e gravando-as no arquivo
deps.lock.
Alm disso, se voc deseja simplesmente atualizar o arquivo deps.lock para o que j tem instalado, ento, voc
pode simplesmente executar php bin/vendors lock para armazenar os identificadores git SHA apropriados
no arquivo deps.lock.

240

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Vendors e sub-mdulos Em vez de usar o sistema composer.json para gerenciar suas bibliotecas vendor,
voc pode optar por usar o git submodules nativo. No h nada de errado com esta abordagem, embora o sistema
composer.json a forma oficial de resolver este problema e provavelmente mais fcil de lidar. Ao contrrio do
git submodules, o Composer esperto o suficiente para calcular quais bibliotecas dependem de outras bibliotecas.
Armazenando o seu Projeto em um Servidor Remoto

Agora, voc tem um projeto Symfony2 totalmente funcional armazenado com o git. Entretanto, na maioria dos casos,
voc tambm vai querer guardar o seu projeto em um servidor remoto tanto para fins de backup quanto para que outros
desenvolvedores possam colaborar com o projeto.
A maneira mais fcil para armazenar o seu projeto em um servidor remoto via GitHub. Repositrios pblicos so
gratuitos, porm, voc ter que pagar uma taxa mensal para hospedar repositrios privados.
Alternativamente, voc pode armazenar seu repositrio git em qualquer servidor, criando um repositrio barebones e,
ento, envi-lo. Uma biblioteca que ajuda neste gerenciamento a Gitolite.

3.1.2 Controlador
Como personalizar as pginas de erro
Quando qualquer exceo lanada no Symfony2, ela capturada dentro da classe Kernel e, eventualmente, encaminhada para um controlador especial, TwigBundle:Exception:show, para o tratamento. Este controlador,
que reside no interior do ncleo do TwigBundle, determina qual template de erro ser exibido e o cdigo de status
que deve ser definido para a exceo.
Pginas de erro pode ser personalizadas de duas maneiras diferentes, dependendo da quantidade de controle que voc
precisa:
1. Personalizar os templates de erro das diferentes pginas de erro (explicado abaixo);
2. Substituir o controlador de exceo padro TwigBundle::Exception:show pelo seu prprio controlador
e manipul-lo como quiser (veja exception_controller in the Twig reference);
Dica: A personalizao da manipulao de exceo , na verdade, muito mais poderosa do que o que est escrito
aqui. Um evento interno lanado, kernel.exception, que permite controle completo sobre o tratamento de
exceo. Para mais informaes, veja kernel-kernel.exception.
Todos os templates de erro residem dentro do TwigBundle. Para sobrescrever os templates, simplesmente contamos
com o mtodo padro para sobrescrever templates que reside dentro de um bundle. Para maiores informaes, visite
Sobrepondo Templates de Pacote.
Por exemplo, para sobrescrever o template padro de erro que exibido ao usurio final, adicione um novo template
em app/Resources/TwigBundle/views/Exception/error.html.twig:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>An Error Occurred: {{ status_text }}</title>
</head>
<body>
<h1>Oops! An Error Occurred</h1>
<h2>The server returned a "{{ status_code }} {{ status_text }}".</h2>

3.1. Cookbook

241

Symfony Docs pt-BR Documentation, Verso 2.4

</body>
</html>

Dica: Se voc no estiver familiarizado com o Twig, no se preocupe. O Twig uma templating engine simples,
poderosa e opcional que integra o Symfony2. Para mais informaes sobre o Twig, consulte Criando e usando
Templates.
Alm da pgina HTML de erro padro, o Symfony fornece uma pgina padro de erro para muitos dos formatos de
resposta mais comuns, incluindo JSON (error.json.twig), XML (error.xml.twig) e at mesmo JavaScript
(error.js.twig), somente para citar alguns. Para sobrescrever qualquer um destes templates, apenas adicione um
novo arquivo com o mesmo nome no diretrio app/Resources/TwigBundle/views/Exception. Esta a
forma padro de sobrescrever qualquer template que reside dentro de um bundle.
Personalizando a Pgina 404 e outras Pginas de Erro

Voc tambm pode personalizar os templates de erro especficos de acordo com o cdigo de status HTTP. Por exemplo,
crie um template app/Resources/TwigBundle/views/Exception/error404.html.twig para exibir
uma pgina especial para erros 404 (pgina no encontrada).
O Symfony usa o seguinte algoritmo para determinar qual o template que deve usar:
Primeiro, ele procura por um template para o formato e cdigo de status especificado (como
error404.json.twig);
Se no existir, ele procura um template para o formato especificado (como error.json.twig);
Se ainda no existir, ele volta para o template HTML (como error.html.twig).
Dica: Para ver a lista completa de templates de erro padro, consulte o diretrio Resources/views/Exception
do TwigBundle.
Na instalao Standard do Symfony2, o TwigBundle pode ser encontrado em vendor/symfony/src/Symfony/Bundle/TwigBundle.
Muitas vezes,
a maneira mais fcil para personalizar uma pgina de erro copi-la do TwigBundle para o
app/Resources/TwigBundle/views/Exception e ento modific-la.
Nota: As pginas de exceo amigveis de depurao, mostradas para o desenvolvedor, podem ser personalizadas
da mesma forma, criando templates como exception.html.twig para a pgina de exceo padro HTML ou
exception.json.twig para a pgina de exceo JSON.

Como definir Controladores como Servios


No livro, voc aprendeu como um controlador pode ser facilmente usado quando ele estende a classe base
Controller. Mesmo isso funcionando bem, os controladores tambm podem ser especificados como servios.
Para referir-se a um controlador que definido como um servio, utilize a notao de um nico dois pontos (:) . Por
exemplo, supondo que voc definiu um servio chamado my_controller e que deseja encaminhar para um mtodo
chamado indexAction() dentro do servio:
$this->forward(my_controller:indexAction, array(foo => $bar));

Voc precisa usar a mesma notao ao definir o valor da rota _controller:


my_controller:
pattern:
/
defaults: { _controller: my_controller:indexAction }

242

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Para utilizar um controlador desta forma, ele deve estar definido na configurao do container de servio. Para mais
informaes, consulte o captulo Service Container
Ao usar um controlador definido como um servio, ele provavelmente no estender a classe base Controller.
Em vez de contar com seus mtodos de atalho, voc vai interagir diretamente com os servios que voc precisa.
Felizmente, isto normalmente muito fcil e a classe base Controller em si uma grande fonte sobre a forma de
realizar muitas das tarefas comuns.
Nota: Especificar um controlador como um servio leva um pouco mais de trabalho. A principal vantagem que
todo o controlador ou quaisquer servios passados para o controlador podem ser modificados atravs da configurao
do container de servio. Isto especialmente til no desenvolvimento de um bundle open-source ou qualquer bundle
que ser utilizado em vrios projetos diferentes. Assim, mesmo se voc no especificar os seus controladores como
servios, provavelmente ver isto feito em alguns bundles open-source do Symfony2.

Usando Anotao no Roteamento

Ao usar anotaes para configurar as rotas em um controlador definido como um servio, necessrio especificar o
servio da seguinte forma:
/**
* @Route("/blog", service="my_bundle.annot_controller")
* @Cache(expires="tomorrow")
*/
class AnnotController extends Controller
{
}

Neste
exemplo,
my_bundle.annot_controller
deve
ser
AnnotController definida no container de servio.
Isto est
/bundles/SensioFrameworkExtraBundle/annotations/routing .

o
id
da
instncia
documentado no captulo

3.1.3 Roteamento
Como forar as rotas a usar sempre HTTPS ou HTTP
s vezes, voc deseja proteger algumas rotas e ter certeza de que elas sero sempre acessadas atravs do protocolo
HTTPS. O componente de Roteamento permite que voc aplique o esquema URI atravs da condio _scheme:
YAML
secure:
pattern: /secure
defaults: { _controller: AcmeDemoBundle:Main:secure }
requirements:
_scheme: https

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/rout
<route id="secure" pattern="/secure">

3.1. Cookbook

243

Symfony Docs pt-BR Documentation, Verso 2.4

<default key="_controller">AcmeDemoBundle:Main:secure</default>
<requirement key="_scheme">https</requirement>
</route>
</routes>

PHP
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(secure, new Route(/secure, array(
_controller => AcmeDemoBundle:Main:secure,
), array(
_scheme => https,
)));
return $collection;

A configurao acima fora a rota secure sempre usar HTTPS.


Ao gerar a URL secure, e se o schema atual for HTTP, o Symfony ir gerar automaticamente uma URL absoluta
com HTTPS como esquema:
# If the current scheme is HTTPS
{{ path(secure) }}
# generates /secure
# If the current scheme is HTTP
{{ path(secure) }}
# generates https://example.com/secure

A condio tambm aplicada para as solicitaes de entrada. Se voc tentar acessar o caminho /secure com
HTTP, voc ser automaticamente redirecionado para a mesma URL, mas com o esquema HTTPS.
O exemplo acima utiliza https para o _scheme, mas voc tambm pode forar uma URL sempre utilizar http.
Nota:
O componente Security fornece outra forma de aplicar HTTP ou HTTPs atravs da configurao
requires_channel. Este mtodo alternativo mais adequado para proteger uma rea do seu site (todas as
URLs sob o /admin) ou quando voc quiser proteger URLs definidas em um bundle de terceiros.

Como permitir um caractere / em um parmetro de rota


s vezes, voc precisa compor URLs com parmetros que podem conter uma barra /. Por exemplo, considere a rota clssica /hello/{name}. Por padro, /hello/Fabien ir corresponder a esta rota, mas
/hello/Fabien/Kris no ir. Isso ocorre porque o Symfony usa esse caractere como separador entre as partes da rota.
Este guia aborda como voc pode modificar a rota para que /hello/Fabien/Kris corresponda rota
/hello/{name}, onde {name} iguala Fabien/Kris.
Configure a Rota

Por padro, o componente de roteamento do Symfony requer que os parmetros correspondam com o seguinte path de
expresso regular: [^/]+. Isso significa que todos os caracteres so permitidos, exceto /.

244

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Voc deve explicitamente permitir que a / faa parte de seu parmetro, especificando um path regex mais permissivo.
YAML
_hello:
path: /hello/{name}
defaults: { _controller: AcmeDemoBundle:Demo:hello }
requirements:
name: ".+"

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/rout
<route id="_hello" path="/hello/{name}">
<default key="_controller">AcmeDemoBundle:Demo:hello</default>
<requirement key="name">.+</requirement>
</route>
</routes>

PHP
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(_hello, new Route(/hello/{name}, array(
_controller => AcmeDemoBundle:Demo:hello,
), array(
name => .+,
)));
return $collection;

Annotations
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class DemoController
{
/**
* @Route("/hello/{name}", name="_hello", requirements={"name" = ".+"})
*/
public function helloAction($name)
{
// ...
}
}

isso! Agora, o parmetro {name} pode conter o caractere /.


Como configurar um redirecionamento para outra rota sem um controlador personalizado
Este guia explica como configurar um redirecionamento de uma rota para outra sem o uso de um controlador personalizado.

3.1. Cookbook

245

Symfony Docs pt-BR Documentation, Verso 2.4

Suponha que no h nenhum controlador padro til para o caminho / da sua aplicao e voc quer redirecionar os
pedidos para /app.
Sua configurao ser parecida com a seguinte:
AppBundle:
resource: "@App/Controller/"
type:
annotation
prefix:
/app
root:
pattern: /
defaults:
_controller: FrameworkBundle:Redirect:urlRedirect
path: /app
permanent: true

Neste
exemplo,
voc
configura
uma
rota
para
o
caminho
class:Symfony\Bundle\FrameworkBundle\Controller\RedirectController lidar com ela.
com o Symfony e oferece duas aes para redirecionar o pedido:

/
e
deixa
o
Este controlador vem

urlRedirect redireciona para outro caminho. Voc deve fornecer o parmetro path contendo o caminho
do recurso para o qual deseja redirecionar.
redirect (no mostrado aqui) redireciona para outra rota. Voc deve fornecer o parmetro route com o
nome da rota para a qual voc quer redirecionar.
O permanent informa ambos os mtodos para emitir um cdigo de status HTTP 301 em vez do cdigo de status
padro 302.
Como usar mtodos HTTP alm do GET e POST em Rotas
O mtodo de um pedido HTTP um dos requisitos que pode ser verificado ao ver se ele corresponde a uma rota. Isto
introduzido no captulo de roteamento do livro Roteamento com exemplos usando GET e POST. Voc tambm pode
usar outros verbos HTTP desta forma. Por exemplo, se voc tem um post de blog, ento, voc pode usar o mesmo
padro de URL para mostr-lo, fazer alteraes e remov-lo pela correspondncia nos mtodos GET, PUT e DELETE.
YAML
blog_show:
pattern: /blog/{slug}
defaults: { _controller: AcmeDemoBundle:Blog:show }
requirements:
_method: GET
blog_update:
pattern: /blog/{slug}
defaults: { _controller: AcmeDemoBundle:Blog:update }
requirements:
_method: PUT
blog_delete:
pattern: /blog/{slug}
defaults: { _controller: AcmeDemoBundle:Blog:delete }
requirements:
_method: DELETE

XML

246

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

<?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/rout
<route id="blog_show" pattern="/blog/{slug}">
<default key="_controller">AcmeDemoBundle:Blog:show</default>
<requirement key="_method">GET</requirement>
</route>
<route id="blog_update" pattern="/blog/{slug}">
<default key="_controller">AcmeDemoBundle:Blog:update</default>
<requirement key="_method">PUT</requirement>
</route>
<route id="blog_delete" pattern="/blog/{slug}">
<default key="_controller">AcmeDemoBundle:Blog:delete</default>
<requirement key="_method">DELETE</requirement>
</route>
</routes>

PHP
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(blog_show, new Route(/blog/{slug}, array(
_controller => AcmeDemoBundle:Blog:show,
), array(
_method => GET,
)));
$collection->add(blog_update, new Route(/blog/{slug}, array(
_controller => AcmeDemoBundle:Blog:update,
), array(
_method => PUT,
)));
$collection->add(blog_delete, new Route(/blog/{slug}, array(
_controller => AcmeDemoBundle:Blog:delete,
), array(
_method => DELETE,
)));
return $collection;

Infelizmente, a vida no to simples assim, j que a maioria dos navegadores no suporta o envio de solicitaes
PUT e DELETE. Felizmente o Symfony2 fornece uma maneira simples de trabalhar com esta limitao. Ao incluir um
parmetro _method na query string ou nos parmetros de um pedido HTTP, o Symfony2 ir us-lo como o mtodo
ao fazer a correspondncia de rotas. Isto pode ser feito facilmente em formulrios com um campo oculto. Suponha
que voc tenha um formulrio para editar um post no blog:
<form action="{{ path(blog_update, {slug: blog.slug}) }}" method="post">
<input type="hidden" name="_method" value="PUT" />
{{ form_widget(form) }}
<input type="submit" value="Update" />
</form>

3.1. Cookbook

247

Symfony Docs pt-BR Documentation, Verso 2.4

O pedido submetido agora vai corresponder rota blog_update e a updateAction ser utilizada para processar
o formulrio.
Do mesmo modo, o formulrio de excluso pode ser alterado para parecer com o seguinte:
<form action="{{ path(blog_delete, {slug: blog.slug}) }}" method="post">
<input type="hidden" name="_method" value="DELETE" />
{{ form_widget(delete_form) }}
<input type="submit" value="Delete" />
</form>

Ele ir ento corresponder rota blog_delete.


Como usar os Parmetros do Container de Servio em suas Rotas
Novo na verso 2.1: A possibilidade de usar parmetros em suas rotas foi adicionada no Symfony 2.1.
s vezes pode ser til tornar algumas partes de suas rotas configurveis globalmente. Por exemplo, se voc construir
um site internacionalizado , voc provavelmente vai comear com uma ou duas localidades. Certamente voc ir
adicionar um requisito em suas rotas para evitar que um usurio utilize uma localidade alm das suportadas.
Voc pode codificar manualmente seu requisito _locale em todas as suas rotas. Mas uma soluo melhor usar um
parmetro configurvel do container de servio dentro de sua configurao de roteamento:
YAML
contact:
path: /{_locale}/contact
defaults: { _controller: AcmeDemoBundle:Main:contact }
requirements:
_locale: %acme_demo.locales%

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/rout
<route id="contact" path="/{_locale}/contact">
<default key="_controller">AcmeDemoBundle:Main:contact</default>
<requirement key="_locale">%acme_demo.locales%</requirement>
</route>
</routes>

PHP
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(contact, new Route(/{_locale}/contact, array(
_controller => AcmeDemoBundle:Main:contact,
), array(
_locale => %acme_demo.locales%,
)));

248

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

return $collection;

Agora voc pode controlar e definir o parmetro acme_demo.locales em algum lugar no seu container:
YAML
# app/config/config.yml
parameters:
acme_demo.locales: en|es

XML
<!-- app/config/config.xml -->
<parameters>
<parameter key="acme_demo.locales">en|es</parameter>
</parameters>

PHP
// app/config/config.php
$container->setParameter(acme_demo.locales, en|es);

Voc tambm pode usar um parmetro para definir o seu path da rota (ou parte do seu path):
YAML
some_route:
path: /%acme_demo.route_prefix%/contact
defaults: { _controller: AcmeDemoBundle:Main:contact }

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/rout
<route id="some_route" path="/%acme_demo.route_prefix%/contact">
<default key="_controller">AcmeDemoBundle:Main:contact</default>
</route>
</routes>

PHP
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(some_route, new Route(/%acme_demo.route_prefix%/contact, array(
_controller => AcmeDemoBundle:Main:contact,
)));
return $collection;

Nota: Assim como em arquivos normais de configurao do container de servio, se voc precisar de um % na sua
rota, voc pode escapar o sinal de porcentagem duplicando ele , por exemplo, /score-50%%, ir converter para
/score-50%.
No entanto, como os caracteres % includos em qualquer URL so automaticamente codificados, a URL resultante
desse exemplo seria /score-50%25 (%25 o resultado da codificao do caractere %).
3.1. Cookbook

249

Symfony Docs pt-BR Documentation, Verso 2.4

Como criar um Loader de Rota personalizado


Um loader de rota personalizado permite que voc adicione rotas a uma aplicao sem inclu-las, por exemplo, num
arquivo yaml. Isto til quando voc tem um bundle, mas no quer adicionar manualmente as rotas ao bundle em
app/config/routing.yml. Isso pode ser especialmente importante quando voc quer tornar o bundle reutilizvel, ou quando o disponibilizar como cdigo aberto, porque iria retardar o processo de instalao e torn-lo suscetvel
a erros.
Alternativamente, voc tambm pode usar um loader de rota personalizado quando quiser que as suas rotas sejam
geradas ou localizadas automaticamente com base em alguma conveno ou padro. Um exemplo o FOSRestBundle
onde o roteamento gerado com base nos nomes dos mtodos de ao em um controlador.
Nota: H muitos bundles que usam seus prprios loaders de rotas para realizar casos como os descritos acima, por
exemplo FOSRestBundle, KnpRadBundle e SonataAdminBundle.

Carregando Rotas

As rotas em uma aplicao Symfony so carregadas pelo DelegatingLoader. Este loader usa vrios outros
loaders (delegados) para carregar recursos de diferentes tipos, por exemplo, arquivos YAML ou anotaes @Route e
@Method em arquivos de controlador. Os loaders especializados implementam a LoaderInterface e, portanto,
tem dois mtodos importantes: supports() e load().
Considere essas linhas do routing.yml:
_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 mtodo supports() com
o determinado recurso (@AcmeDemoBundle/Controller/DemoController.php) e tipo (annotation)
como argumentos. Quando um dos loaders retorna true, seu mtodo 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 anotaes, 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
no importante - voc pode simplesmente inventar qualquer tipo de recurso que desejar. O prprio nome do recurso
no realmente usado no exemplo:
namespace Acme\DemoBundle\Routing;
use
use
use
use

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

class ExtraLoader implements LoaderInterface


{

250

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

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
}
}

Nota: Certifique-se que o controlador que voc especificou realmente existe.


Agora defina um servio para o ExtraLoader:
YAML
services:
acme_demo.routing_loader:
class: Acme\DemoBundle\Routing\ExtraLoader
tags:
- { name: routing.loader }

XML

3.1. Cookbook

251

Symfony Docs pt-BR Documentation, Verso 2.4

<?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/ser
<services>
<service id="acme_demo.routing_loader" class="Acme\DemoBundle\Routing\ExtraLoader">
<tag name="routing.loader" />
</service>
</services>
</container>

PHP
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 servios com esta tag sero marcados como potenciais loaders de rota e
adicionados como roteadores especializados para o DelegatingLoader.
Usando o Loader Personalizado Se voc no fez nada mais, seu loader de roteamento personalizado no ser
chamado. Em vez disso, voc s precisa adicionar algumas linhas extras para a configurao de roteamento:
YAML
# app/config/routing.yml
AcmeDemoBundle_Extra:
resource: .
type: extra

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/rout
<import resource="." type="extra" />
</routes>

PHP
// 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 mtodo load() chamado. A chave resource insignificante para o ExtraLoader,
252

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

ento defina ela como ..


Nota: O cache das rotas definidas usando loaders personalizados ser feita automaticamente pelo framework. Assim,
sempre que voc mudar alguma coisa na classe do loader, no se esquea de limpar o cache.

Loaders mais Avanados

Na maioria dos casos melhor no implementar a LoaderInterface voc mesmo, mas estender do Loader.
Esta classe sabe como usar um LoaderResolver para carregar os recursos de roteamento secundrios.
Claro que voc ainda precisa implementar supports() e load(). Sempre que quiser carregar outro recurso - por
exemplo, um arquivo de configurao 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;
}
}

Nota: O nome e o tipo do recurso da configurao de roteamento importada pode ser qualquer coisa que normalmente suportada pelo loader de configurao de roteamento (YAML, XML, PHP, anotao, etc.)

Redirecionar URLs com uma Barra no Final


O objetivo deste cookbook demonstrar como redirecionar URLs com uma barra no final para a mesma URL sem a
barra no final (por exemplo, /en/blog/ para /en/blog).
Crie um controlador que ir corresponder a qualquer URL com uma barra no final, remova a barra do final (mantendo
os parmetros de consulta, se houver) e redirecione para a nova URL com um cdigo de status 301 de resposta:
// src/Acme/DemoBundle/Controller/RedirectingController.php
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

3.1. Cookbook

253

Symfony Docs pt-BR Documentation, Verso 2.4

use Symfony\Component\HttpFoundation\Request;
class RedirectingController extends Controller
{
public function removeTrailingSlashAction(Request $request)
{
$pathInfo = $request->getPathInfo();
$requestUri = $request->getRequestUri();
$url = str_replace($pathInfo, rtrim($pathInfo, /), $requestUri);
return $this->redirect($url, 301);
}
}

Depois disso, crie uma rota para este controlador que corresponde sempre que uma URL com uma barra no final
solicitada. No se esquea de colocar esta rota por ltimo no seu sistema, como explicado a seguir:
YAML
remove_trailing_slash:
path: /{url}
defaults: { _controller: AcmeDemoBundle:Redirecting:removeTrailingSlash }
requirements:
url: .*/$
_method: GET

XML
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing">
<route id="remove_trailing_slash" path="/{url}">
<default key="_controller">AcmeDemoBundle:Redirecting:removeTrailingSlash</default>
<requirement key="url">.*/$</requirement>
<requirement key="_method">GET</requirement>
</route>
</routes>

PHP
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add(
remove_trailing_slash,
new Route(
/{url},
array(
_controller => AcmeDemoBundle:Redirecting:removeTrailingSlash,
),
array(
url => .*/$,
_method => GET,
)
)
);

Nota: Redirecionar uma solicitao POST no funciona bem em navegadores antigos. Um 302 em uma solicitao

254

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

POST iria enviar uma solicitao GET aps o redirecionamento por motivos legados. Por essa razo, a rota aqui
corresponde apenas solicitaes GET.
Cuidado: Certifique-se de incluir esta rota em sua configurao de roteamento no final de sua lista de rotas. Caso
contrrio, voc corre o risco de redirecionar rotas reais (incluindo as rotas principais do Symfony2) que realmente
tm uma barra no final do seu caminho.

3.1.4 Assetic
Como usar o Assetic para o Gerenciamento de Assets
O Assetic combina duas idias principais: assets e filtros. Os assets so arquivos CSS, JavaScript e arquivos de
imagem. Os filtros so coisas que podem ser aplicadas esses arquivos antes deles serem servidos ao navegador.
Isto permite uma separao entre os arquivos asset armazenados na aplicao e os arquivos que so efetivamente
apresentados ao usurio.
Sem o Assetic, voc somente poderia servir os arquivos que so armazenados diretamente na aplicao:
Twig
<script src="{{ asset(js/script.js) }}" type="text/javascript" />

PHP
<script src="<?php echo $view[assets]->getUrl(js/script.js) ?>" type="text/javascript" />

Mas com o Assetic, voc pode manipular esses assets da forma que desejar (ou carreg-los de qualquer lugar) antes de
serv-los. Isto significa que voc pode:
Minificar e combinar todos os seus arquivos CSS e JS
Executar todos (ou apenas alguns) dos seus arquivos CSS ou JS atravs de algum tipo de compilador, como o
LESS, SASS ou CoffeeScript
Executar otimizaes em suas imagens
Assets

O uso do Assetic oferece muitas vantagens sobre servir diretamente os arquivos. Os arquivos no precisam ser armazenados onde eles so servidos e podem ser buscados a partir de vrias fontes, como, por exemplo, a partir de um
bundle:
Twig
{% javascripts @AcmeFooBundle/Resources/public/js/* %}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}

PHP
<?php foreach ($view[assetic]->javascripts(
array(@AcmeFooBundle/Resources/public/js/*)) as $url): ?>
<script type="text/javascript" src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach; ?>

3.1. Cookbook

255

Symfony Docs pt-BR Documentation, Verso 2.4

Dica: Para buscar folhas de estilo CSS, voc pode usar as mesmas metodologias vistas aqui, exceto com a tag
stylesheets :
Twig
{% stylesheets @AcmeFooBundle/Resources/public/css/* %}
<link rel="stylesheet" href="{{ asset_url }}" />
{% endstylesheets %}

PHP
<?php foreach ($view[assetic]->stylesheets(
array(@AcmeFooBundle/Resources/public/css/*)
) as $url): ?>
<link rel="stylesheet" href="<?php echo $view->escape($url) ?>" />
<?php endforeach; ?>

Neste exemplo, todos os arquivos no diretrio Resources/public/js/ do AcmeFooBundle sero carregados


e servidos em um local diferente. A tag atual renderizada pode parecer simplesmente com:
<script src="/app_dev.php/js/abcd123.js"></script>

Nota: Este um ponto-chave: uma vez que voc deixar o Assetic lidar com seus assets, os arquivos so servidos
a partir de um local diferente. Isto pode causar problemas com os arquivos CSS que referenciam imagens pelo seu
caminho relativo. No entanto, isso pode ser corrigido usando o filtro cssrewrite, que atualiza os caminhos nos
arquivos CSS para refletir a sua nova localizao.

Combinando Assets Voc tambm pode combinar vrios arquivos em um nico. Isto ajuda a reduzir o nmero de
solicitaes HTTP, o que timo para o desempenho front-end. Tambm permite que voc mantenha os arquivos mais
facilmente, dividindo-os em partes gerenciveis. Isso pode ajudar com a possibilidade de reutilizao, uma vez que
voc pode facilmente dividir os arquivos especficos do projeto daqueles que podem ser usados em outras aplicaes,
mas ainda serv-los como um nico arquivo:
Twig
{% javascripts
@AcmeFooBundle/Resources/public/js/*
@AcmeBarBundle/Resources/public/js/form.js
@AcmeBarBundle/Resources/public/js/calendar.js %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}

PHP
<?php foreach ($view[assetic]->javascripts(
array(@AcmeFooBundle/Resources/public/js/*,
@AcmeBarBundle/Resources/public/js/form.js,
@AcmeBarBundle/Resources/public/js/calendar.js)) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach; ?>

No ambiente dev, cada arquivo ainda servido individualmente, de modo que voc pode depurar problemas mais
facilmente. No entanto, no ambiente prod, sero processados como uma nica tag script.
Dica: Se voc novo no Assetic e tentar usar a sua aplicao no ambiente prod (utilizando o controlador app.php),

256

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

voc provavelmente ver que todos os seus CSS e JS esto corrompidos. No se preocupe! Isso de propsito. Para
detalhes sobre a utilizao do Assetic no ambiente prod, consulte Dump dos arquivos de asset.
E a combinao de arquivos no se aplica apenas para seus arquivos. Voc tambm pode usar o Assetic para combinar
assets de terceiros, tais como jQuery, como seu prprio em um nico arquivo:
Twig
{% javascripts
@AcmeFooBundle/Resources/public/js/thirdparty/jquery.js
@AcmeFooBundle/Resources/public/js/* %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}

PHP
<?php foreach ($view[assetic]->javascripts(
array(@AcmeFooBundle/Resources/public/js/thirdparty/jquery.js,
@AcmeFooBundle/Resources/public/js/*)) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach; ?>

Filtros

Uma vez que so gerenciados pelo Assetic, voc pode aplicar filtros em seus assets antes deles serem servidos. Isso
inclui filtros que comprimem a sada de seus assets para tamanhos de arquivos menores (e melhor otimizao do frontend). Outros filtros podem compilar os arquivos JavaScript a partir de arquivos CoffeeScript e processar SASS em
CSS. Na verdade, o Assetic tem uma longa lista de filtros disponveis.
Muitos dos filtros no fazem o trabalho diretamente, mas usam bibliotecas existentes de terceiros para fazer o trabalho
pesado. Isto significa que muitas vezes voc vai precisar instalar uma biblioteca de terceiro para usar um filtro. A
grande vantagem de usar o Assetic para chamar estas bibliotecas (em oposio a us-las diretamente) que, em vez
de ter que execut-las manualmente depois de trabalhar nos arquivos, o Assetic ir cuidar disto para voc e remover
completamente esta etapa do seu processo de desenvolvimento e implantao.
Para usar um filtro, primeiro voc precisa especific-lo na configurao do Assetic. Adicionar um filtro aqui no
significa que ele est sendo usado - apenas significa que est disponvel para uso (vamos usar o filtro abaixo).
Por exemplo, para usar o JavaScript YUI Compressor, a configurao seguinte deve ser acrescentada:
YAML
# app/config/config.yml
assetic:
filters:
yui_js:
jar: "%kernel.root_dir%/Resources/java/yuicompressor.jar"

XML
<!-- app/config/config.xml -->
<assetic:config>
<assetic:filter
name="yui_js"
jar="%kernel.root_dir%/Resources/java/yuicompressor.jar" />
</assetic:config>

PHP

3.1. Cookbook

257

Symfony Docs pt-BR Documentation, Verso 2.4

// app/config/config.php
$container->loadFromExtension(assetic, array(
filters => array(
yui_js => array(
jar => %kernel.root_dir%/Resources/java/yuicompressor.jar,
),
),
));

Agora, para efetivamente usar o filtro em um grupo de arquivos JavaScript, adicione-o em seu template:
Twig
{% javascripts @AcmeFooBundle/Resources/public/js/* filter=yui_js %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}

PHP
<?php foreach ($view[assetic]->javascripts(
array(@AcmeFooBundle/Resources/public/js/*),
array(yui_js)) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach; ?>

Um guia mais detalhado sobre a configurao e uso dos filtros Assetic, bem como detalhes do modo de depurao do
Assetic pode ser encontrado em Como Minificar JavaScripts e Folhas de Estilo com o YUI Compressor.
Controlando a URL usada

Se desejar, voc pode controlar as URLs que o Assetic produz. Isto feito a partir do template e relativo raiz do
documento pblico:
Twig
{% javascripts @AcmeFooBundle/Resources/public/js/* output=js/compiled/main.js %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}

PHP
<?php foreach ($view[assetic]->javascripts(
array(@AcmeFooBundle/Resources/public/js/*),
array(),
array(output => js/compiled/main.js)
) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach; ?>

Nota: O Symfony tambm contm um mtodo de busting de cache, onde a URL final gerada pelo Assetic contm um
parmetro de query, que pode ser incrementado atravs de configurao em cada implantao. Para mais informaes,
consulte a opo de configurao ref-framework-assets-version.

258

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Dump dos arquivos de asset

No ambiente dev, o Assetic gera caminhos para os arquivos CSS e JavaScript que no existem fisicamente em seu
computador. Mas, eles renderizam mesmo assim porque um controlador interno do Symfony abre os arquivos e serve
de volta o contedo (aps a execuo de quaisquer filtros).
Este tipo de publicao dinmica dos assets processados tima porque significa que voc pode ver imediatamente o
novo estado de quaisquer arquivos de assets que foram alterados. Tambm ruim, porque pode ser muito lento. Se
voc estiver usando uma srie de filtros, pode ser francamente frustrante.
Felizmente, o Assetic fornece uma forma de fazer o dump de seus assets para arquivos reais, em vez de ser gerado
dinamicamente.
Dump dos arquivos asset no ambiente prod No ambiente prod, seus JS e CSS so representados por uma nica
tag cada. Em outras palavras, em vez de ver cada arquivo JavaScript que voc est incluindo no seu cdigo fonte,
provvel que voc s veja algo semelhante a:
<script src="/app_dev.php/js/abcd123.js"></script>

Alm disso, esse arquivo no existe realmente, nem renderizado de forma dinmica pelo Symfony (pois os arquivos
de asset esto no ambiente dev). Isto de propsito - deixar o Symfony gerar esses arquivos dinamicamente em um
ambiente de produo muito lento.
Em vez disso, cada vez que voc usar a sua aplicao no ambiente prod (e, portanto, cada vez que voc implantar),
voc deve executar o seguinte comando:
$ php app/console assetic:dump --env=prod --no-debug

Isso vai gerar fisicamente e escrever cada arquivo que voc precisa (por exemplo, /js/abcd123.js). Se voc
atualizar qualquer um de seus assets, necessrio executar o comando novamente para gerar o novo arquivo.
Dump dos arquivos de asset no ambiente dev Por padro, cada caminho de asset gerado no ambiente dev gerenciado dinamicamente pelo Symfony. Isso no tem desvantagem (voc pode ver as suas alteraes imediatamente),
com exceo de que os assets podem visivelmente carregar mais lentos. Se voc sentir que seus assets esto carregando
muito lentamente, siga este guia.
Primeiro, diga ao Symfony para parar de tentar processar estes arquivos dinamicamente. Faa a seguinte alterao em
seu arquivo config_dev.yml:
YAML
# app/config/config_dev.yml
assetic:
use_controller: false

XML
<!-- app/config/config_dev.xml -->
<assetic:config use-controller="false" />

PHP
// app/config/config_dev.php
$container->loadFromExtension(assetic, array(
use_controller => false,
));

3.1. Cookbook

259

Symfony Docs pt-BR Documentation, Verso 2.4

Em seguida, uma vez que o Symfony no est mais gerando esses assets para voc, voc vai precisar fazer o dump
deles manualmente. Para isso, execute o seguinte:
$ php app/console assetic:dump

Esta fisicamente grava todos os arquivos ativos que voc precisa para seu dev produo. A grande desvantagem
que voc precisa executar este cada vez voc atualizar um ativo. Felizmente, passando o - assistir opo , o
comando automaticamente regenerar ativos * como eles mudam *:
$ php app/console assetic:dump --watch

Uma vez que executar este comando no ambiente dev pode gerar vrios arquivos, geralmente uma boa idia apontar
os seus arquivos assets gerados para algum diretrio isolado (por exemplo, /js/compiled), para manter as coisas
organizadas:
Twig
{% javascripts @AcmeFooBundle/Resources/public/js/* output=js/compiled/main.js %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}

PHP
<?php foreach ($view[assetic]->javascripts(
array(@AcmeFooBundle/Resources/public/js/*),
array(),
array(output => js/compiled/main.js)
) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach; ?>

Como Minificar JavaScripts e Folhas de Estilo com o YUI Compressor


A Yahoo! oferece um excelente utilitrio, chamado YUI Compressor, para minificar JavaScripts e folhas de estilo,
assim, eles so carregados mais rapidamente. Graas ao Assetic, voc pode tirar proveito desta ferramenta de forma
muito fcil.
Baixe o JAR do YUI Compressor

O YUI Compressor escrito em Java e distribudo como um JAR. Faa o download do JAR no site da Yahoo! e salve-o
em app/Resources/java/yuicompressor.jar.
Configure os Filtros do YUI

Agora voc precisa configurar dois filtros Assetic em sua aplicao, um para minificar os JavaScripts com o YUI
Compressor e um para minificar as folhas de estilo:
YAML
# app/config/config.yml
assetic:
filters:
yui_css:
jar: "%kernel.root_dir%/Resources/java/yuicompressor.jar"
yui_js:
jar: "%kernel.root_dir%/Resources/java/yuicompressor.jar"

260

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

XML
<!-- app/config/config.xml -->
<assetic:config>
<assetic:filter
name="yui_css"
jar="%kernel.root_dir%/Resources/java/yuicompressor.jar" />
<assetic:filter
name="yui_js"
jar="%kernel.root_dir%/Resources/java/yuicompressor.jar" />
</assetic:config>

PHP
// app/config/config.php
$container->loadFromExtension(assetic, array(
filters => array(
yui_css => array(
jar => %kernel.root_dir%/Resources/java/yuicompressor.jar,
),
yui_js => array(
jar => %kernel.root_dir%/Resources/java/yuicompressor.jar,
),
),
));

Voc agora tem acesso a dois novos filtros Assetic em sua aplicao: yui_css e yui_js. Eles utilizaro o YUI
Compressor para minificar as folhas de estilo e JavaScripts, respectivamente.
Minifique os seus Assets

Voc agora tem o YUI Compressor configurado, mas nada vai acontecer at aplicar um desses filtros para um asset.
Uma vez que os seus assets fazem parte da camada de viso, este trabalho feito em seus templates:
Twig
{% javascripts @AcmeFooBundle/Resources/public/js/* filter=yui_js %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}

PHP
<?php foreach ($view[assetic]->javascripts(
array(@AcmeFooBundle/Resources/public/js/*),
array(yui_js)) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach; ?>

Nota: O exemplo acima assume que voc tem um bundle chamado AcmeFooBundle e os seus arquivos JavaScript
esto no diretrioResources/public/js sob o seu bundle. Entretante, isso no importante - voc pode incluir os
seus arquivos JavaScript, no importa onde eles estiverem.
Com a adio do filtro yui_js para as tags asset acima, voc deve agora ver os JavaScripts minificados sendo
carregados muito mais rpido. O mesmo processo pode ser repetido para minificar as suas folhas de estilo.
Twig

3.1. Cookbook

261

Symfony Docs pt-BR Documentation, Verso 2.4

{% stylesheets @AcmeFooBundle/Resources/public/css/* filter=yui_css %}


<link rel="stylesheet" type="text/css" media="screen" href="{{ asset_url }}" />
{% endstylesheets %}

PHP

<?php foreach ($view[assetic]->stylesheets(


array(@AcmeFooBundle/Resources/public/css/*),
array(yui_css)) as $url): ?>
<link rel="stylesheet" type="text/css" media="screen" href="<?php echo $view->escape($url) ?>" /
<?php endforeach; ?>

Desative a minificao no modo de depurao

Os JavaScripts e as folhas de estilo minificados so muito difceis de ler, e muito menos depurar. Devido a isso, o
Assetic permite desabilitar um certo filtro quando a sua aplicao est no modo de depurao. Voc pode fazer isso
prefixando o nome do filtro em seu template com um ponto de interrogao: ?. Isto diz ao Assetic para apenas aplicar
esse filtro quando o modo de depurao est desligado.
Twig
{% javascripts @AcmeFooBundle/Resources/public/js/* filter=?yui_js %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}

PHP
<?php foreach ($view[assetic]->javascripts(
array(@AcmeFooBundle/Resources/public/js/*),
array(?yui_js)) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach; ?>

Como usar o Assetic para otimizao de imagem com funes do Twig


Dentre os seus vrios filtros, o Assetic possui quatro que podem ser utilizados para a otimizao de imagens on-thefly. Isso permite obter os benefcios de tamanhos menores dos arquivos sem ter que usar um editor de imagens para
processar cada imagem. Os resultados so armazenados em cache e pode ser feito o dump para produo de modo que
no h impacto no desempenho para seus usurios finais.
Usando o jpegoptim

Jpegoptim um utilitrio para otimizar arquivos JPEG. Para us-lo com o Assetic, adicione o seguinte na configurao
do Assetic:
YAML
# app/config/config.yml
assetic:
filters:
jpegoptim:
bin: path/to/jpegoptim

XML

262

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

<!-- app/config/config.xml -->


<assetic:config>
<assetic:filter
name="jpegoptim"
bin="path/to/jpegoptim" />
</assetic:config>

PHP
// app/config/config.php
$container->loadFromExtension(assetic, array(
filters => array(
jpegoptim => array(
bin => path/to/jpegoptim,
),
),
));

Nota: Observe que, para usar o jpegoptim, voc deve instal-lo em seu sistema. A opo bin aponta para a
localizao do binrio compilado.
Ele agora pode ser usado em um template:
Twig
{% image @AcmeFooBundle/Resources/public/images/example.jpg
filter=jpegoptim output=/images/example.jpg
%}
<img src="{{ asset_url }}" alt="Example"/>
{% endimage %}

PHP
<?php foreach ($view[assetic]->images(
array(@AcmeFooBundle/Resources/public/images/example.jpg),
array(jpegoptim)) as $url): ?>
<img src="<?php echo $view->escape($url) ?>" alt="Example"/>
<?php endforeach; ?>

Removendo todos os dados EXIF Por padro, a execuo desse filtro remove apenas algumas das informaes
meta armazenadas no arquivo. Os dados EXIF e comentrios no so removidos, mas voc pode remov-los usando a
opo strip_all:
YAML
# app/config/config.yml
assetic:
filters:
jpegoptim:
bin: path/to/jpegoptim
strip_all: true

XML
<!-- app/config/config.xml -->
<assetic:config>
<assetic:filter
name="jpegoptim"

3.1. Cookbook

263

Symfony Docs pt-BR Documentation, Verso 2.4

bin="path/to/jpegoptim"
strip_all="true" />
</assetic:config>

PHP
// app/config/config.php
$container->loadFromExtension(assetic, array(
filters => array(
jpegoptim => array(
bin => path/to/jpegoptim,
strip_all => true,
),
),
));

Diminuindo a qualidade mxima Por padro, o nvel de qualidade do JPEG no afetado. Voc pode ganhar
redues adicionais no tamanho dos arquivos ao ajustar a configurao de qualidade mxima para um valor inferior ao
nvel atual das imagens. Isto ir, claro, custar a qualidade de imagem:
YAML
# app/config/config.yml
assetic:
filters:
jpegoptim:
bin: path/to/jpegoptim
max: 70

XML
<!-- app/config/config.xml -->
<assetic:config>
<assetic:filter
name="jpegoptim"
bin="path/to/jpegoptim"
max="70" />
</assetic:config>

PHP
// app/config/config.php
$container->loadFromExtension(assetic, array(
filters => array(
jpegoptim => array(
bin => path/to/jpegoptim,
max => 70,
),
),
));

Sintaxe curta: Funo Twig

Se voc estiver usando o Twig, possvel conseguir tudo isso com uma sintaxe curta, ao habilitar e usar uma funo
especial do Twig. Comece adicionando a seguinte configurao:
YAML

264

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

# app/config/config.yml
assetic:
filters:
jpegoptim:
bin: path/to/jpegoptim
twig:
functions:
jpegoptim: ~

XML
<!-- app/config/config.xml -->
<assetic:config>
<assetic:filter
name="jpegoptim"
bin="path/to/jpegoptim" />
<assetic:twig>
<assetic:twig_function
name="jpegoptim" />
</assetic:twig>
</assetic:config>

PHP
// app/config/config.php
$container->loadFromExtension(assetic, array(
filters => array(
jpegoptim => array(
bin => path/to/jpegoptim,
),
),
twig => array(
functions => array(jpegoptim),
),
),
));

O template Twig pode agora ser alterado para o seguinte:


<img src="{{ jpegoptim(@AcmeFooBundle/Resources/public/images/example.jpg) }}"
alt="Example"/>

Voc pode especificar o diretrio de sada na configurao da seguinte forma:


YAML
# app/config/config.yml
assetic:
filters:
jpegoptim:
bin: path/to/jpegoptim
twig:
functions:
jpegoptim: { output: images/*.jpg }

XML
<!-- app/config/config.xml -->
<assetic:config>
<assetic:filter

3.1. Cookbook

265

Symfony Docs pt-BR Documentation, Verso 2.4

name="jpegoptim"
bin="path/to/jpegoptim" />
<assetic:twig>
<assetic:twig_function
name="jpegoptim"
output="images/*.jpg" />
</assetic:twig>
</assetic:config>

PHP
// app/config/config.php
$container->loadFromExtension(assetic, array(
filters => array(
jpegoptim => array(
bin => path/to/jpegoptim,
),
),
twig => array(
functions => array(
jpegoptim => array(
output => images/*.jpg
),
),
),
));

Como Aplicar um filtro Assetic a uma extenso de arquivo especfica


Os filtros Assetic podem ser aplicados arquivos individuais, grupos de arquivos ou at mesmo, como voc ver
aqui, aos arquivos que possuem uma extenso especfica. Para mostrar como lidar com cada opo, vamos supor que
voc deseja usar o filtro CoffeeScript do Assetic, que compila arquivos CoffeeScript em Javascript.
A configurao principal apenas os caminhos para coffee e node.
/usr/bin/coffee e /usr/bin/node, respectivamente:

Elas apontam, por padro, para

YAML
# app/config/config.yml
assetic:
filters:
coffee:
bin: /usr/bin/coffee
node: /usr/bin/node

XML
<!-- app/config/config.xml -->
<assetic:config>
<assetic:filter
name="coffee"
bin="/usr/bin/coffee"
node="/usr/bin/node" />
</assetic:config>

PHP

266

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

// app/config/config.php
$container->loadFromExtension(assetic, array(
filters => array(
coffee => array(
bin => /usr/bin/coffee,
node => /usr/bin/node,
),
),
));

Filtrando um nico arquivo

Agora, voc pode servir um nico arquivo CoffeeScript como JavaScript a partir de seus templates:
Twig
{% javascripts @AcmeFooBundle/Resources/public/js/example.coffee
filter=coffee
%}
<script src="{{ asset_url }} type="text/javascript"></script>
{% endjavascripts %}

PHP
<?php foreach ($view[assetic]->javascripts(
array(@AcmeFooBundle/Resources/public/js/example.coffee),
array(coffee)) as $url): ?>
<script src="<?php echo $view->escape($url) ?>" type="text/javascript"></script>
<?php endforeach; ?>

Isso tudo o que necessrio para compilar este arquivo CoffeeScript e servir ele como JavaScript compilado.
Filtrando vrios arquivos

Voc tambm pode combinar vrios arquivos CoffeeScript em um nico arquivo de sada:
Twig
{% javascripts @AcmeFooBundle/Resources/public/js/example.coffee
@AcmeFooBundle/Resources/public/js/another.coffee
filter=coffee
%}
<script src="{{ asset_url }} type="text/javascript"></script>
{% endjavascripts %}

PHP
<?php foreach ($view[assetic]->javascripts(
array(@AcmeFooBundle/Resources/public/js/example.coffee,
@AcmeFooBundle/Resources/public/js/another.coffee),
array(coffee)) as $url): ?>
<script src="<?php echo $view->escape($url) ?>" type="text/javascript"></script>
<?php endforeach; ?>

Ambos os arquivos agora sero servidos como um nico arquivo compilado em JavaScript regular.

3.1. Cookbook

267

Symfony Docs pt-BR Documentation, Verso 2.4

Filtrando com base em uma extenso de arquivo

Uma das grandes vantagens de usar o Assetic minimizar o nmero de arquivos asset para reduzir as solicitaes
HTTP. A fim de fazer seu pleno uso, seria bom combinar todos os seus arquivos JavaScript e CoffeeScript juntos,
uma vez que, todos sero servidos como JavaScript. Infelizmente, apenas adicionar os arquivos JavaScript aos arquivos
combinados como acima no funcionar, pois, os arquivos JavaScript regulares no sobrevivero a compilao do
CoffeeScript.
Este problema pode ser evitado usando a opo apply_to na configurao, que permite especificar que filtro dever
ser sempre aplicado determinadas extenses de arquivo. Neste caso, voc pode especificar que o filtro Coffee ser
aplicado todos os arquivos .coffee:
YAML
# app/config/config.yml
assetic:
filters:
coffee:
bin: /usr/bin/coffee
node: /usr/bin/node
apply_to: "\.coffee$"

XML
<!-- app/config/config.xml -->
<assetic:config>
<assetic:filter
name="coffee"
bin="/usr/bin/coffee"
node="/usr/bin/node"
apply_to="\.coffee$" />
</assetic:config>

PHP
// app/config/config.php
$container->loadFromExtension(assetic, array(
filters => array(
coffee => array(
bin => /usr/bin/coffee,
node => /usr/bin/node,
apply_to => \.coffee$,
),
),
));

Com isso, voc no precisa especificar o filtro coffee no template. Voc tambm pode listar os arquivos JavaScript
regulares, todos os quais sero combinados e renderizados como um nico arquivo JavaScript (apenas com os arquivos
.coffee sendo executados atravs do filtro CoffeeScript):
Twig
{% javascripts @AcmeFooBundle/Resources/public/js/example.coffee
@AcmeFooBundle/Resources/public/js/another.coffee
@AcmeFooBundle/Resources/public/js/regular.js
%}
<script src="{{ asset_url }} type="text/javascript"></script>
{% endjavascripts %}

PHP
268

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

<?php foreach ($view[assetic]->javascripts(


array(@AcmeFooBundle/Resources/public/js/example.coffee,
@AcmeFooBundle/Resources/public/js/another.coffee,
@AcmeFooBundle/Resources/public/js/regular.js),
as $url): ?>
<script src="<?php echo $view->escape($url) ?>" type="text/javascript"></script>
<?php endforeach; ?>

3.1.5 Doctrine
Como Manipular o Upload de Arquivos com o Doctrine
Gerenciar o upload de arquivos utilizando entidades do Doctrine no diferente de manusear qualquer outro upload
de arquivo. Em outras palavras, voc livre para mover o arquivo em seu controlador aps a manipulao do envio de
um formulrio. Para exemplos de como fazer isso, veja a pgina de referncia do tipo arquivo.
Se voc quiser, tambm pode integrar o upload de arquivo no ciclo de vida de sua entidade (ou seja, criao, atualizao
e remoo). Neste caso, como a sua entidade criada, atualizada e removida pelo Doctrine, o tratamento do upload e
da remoo de arquivos ser realizado automaticamente (sem precisar fazer nada em seu controlador);
Para fazer este trabalho, voc precisa cuidar de uma srie de detalhes, que sero abordados neste artigo do cookbook.
Configurao Bsica

Primeiro, crie uma classe Entity simples do Doctrine para voc trabalhar:
// src/Acme/DemoBundle/Entity/Document.php
namespace Acme\DemoBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity
*/
class Document
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @ORM\Column(type="string", length=255)
* @Assert\NotBlank
*/
public $name;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
public $path;

3.1. Cookbook

269

Symfony Docs pt-BR Documentation, Verso 2.4

public function getAbsolutePath()


{
return null === $this->path
? null
: $this->getUploadRootDir()./.$this->path;
}
public function getWebPath()
{
return null === $this->path
? null
: $this->getUploadDir()./.$this->path;
}
protected function getUploadRootDir()
{
// the absolute directory path where uploaded
// documents should be saved
return __DIR__./../../../../web/.$this->getUploadDir();
}
protected function getUploadDir()
{
// get rid of the __DIR__ so it doesnt screw up
// when displaying uploaded doc/image in the view.
return uploads/documents;
}
}

A entidade Document tem um nome e ele associado a um arquivo. A propriedade path armazena o caminho
relativo para o arquivo e persistida no banco de dados. O getAbsolutePath() um mtodo de convenincia
que retorna o caminho absoluto para o arquivo enquanto o getWebPath() um mtodo de convenincia que retorna
o caminho web, que podem ser utilizados em um template para obter o link do arquivo que foi feito o upload.
Dica: Se no tiver feito isso, voc deve ler primeiro a documentao sobre o tipo arquivo file para entender como
funciona o processo bsico de upload.
Nota: Se voc estiver usando anotaes para especificar as suas regras de validao (como mostrado neste exemplo),
certifique-se de que tenha ativado a validao por anotao (veja configurao de validao).
Para lidar com o upload do arquivo no formulrio, use um campo virtual file. Por exemplo, se voc est construindo o seu formulrio diretamente em um controlador, ele poderia parecer com o seguinte:
public function uploadAction()
{
// ...
$form = $this->createFormBuilder($document)
->add(name)
->add(file)
->getForm();
// ...
}

Em seguida, crie essa propriedade em sua classe Document e adicione algumas regras de validao:

270

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

// src/Acme/DemoBundle/Entity/Document.php
// ...
class Document
{
/**
* @Assert\File(maxSize="6000000")
*/
public $file;
// ...
}

Nota: Como voc est usando a constraint File, o Symfony2 ir adivinhar automaticamente que o campo do
formulrio do tipo para upload de arquivos. por isso que voc no tem que defini-lo explicitamente ao criar o
formulrio acima (->add(file)).
O controlador a seguir mostra como lidar com todo o processo:
use Acme\DemoBundle\Entity\Document;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
// ...
/**
* @Template()
*/
public function uploadAction()
{
$document = new Document();
$form = $this->createFormBuilder($document)
->add(name)
->add(file)
->getForm()
;
if ($this->getRequest()->isMethod(POST)) {
$form->bind($this->getRequest());
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($document);
$em->flush();
$this->redirect($this->generateUrl(...));
}
}
return array(form => $form->createView());
}

Nota: Ao escrever o template, no esquea de definir o atributo enctype:


<h1>Upload File</h1>
<form action="#" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<input type="submit" value="Upload Document" />

3.1. Cookbook

271

Symfony Docs pt-BR Documentation, Verso 2.4

</form>

O controlador anterior ir persistir automaticamente a entidade Document com o nome submetido, mas ele no far
nada a respeito do arquivo e a propriedade path ficar em branco.
Uma maneira fcil de lidar com o upload do arquivo mov-lo pouco antes da entidade ser persistida e, em seguida,
definir a propriedade path de acordo. Comece chamando o novo mtodo upload() na classe Document, que
voc vai criar no momento para lidar com o upload do arquivo:
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$document->upload();
$em->persist($document);
$em->flush();
$this->redirect(...);
}

O mtodo upload() ir aproveitar o objeto UploadedFile , que o retornado aps um campo file ser submetido:
public function upload()
{
// the file property can be empty if the field is not required
if (null === $this->file) {
return;
}
// use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the
// target filename to move to
$this->file->move(
$this->getUploadRootDir(),
$this->file->getClientOriginalName()
);
// set the path property to the filename where youve saved the file
$this->path = $this->file->getClientOriginalName();
// clean up the file property as you wont need it anymore
$this->file = null;
}

Utilizando Lifecycle Callbacks

Mesmo esta aplicao funcionando, ela sofre de uma grande falha: E se houver um problema quando a entidade for
persistida? O arquivo j teria sido movido para seu local definitivo, apesar da propriedade path da entidade no ter
sido persistida corretamente.
Para evitar esses problemas, voc deve alterar a implementao de forma que as operaes do banco de dados e a cpia
do arquivo tornem-se atmicas: se h um problema persistindo a entidade ou se o arquivo no pode ser movido, ento
nada deve ser feito.

272

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Para fazer isso, voc precisa mover o arquivo no mesmo momento em que o Doctrine persistir a entidade no banco de
dados. Isto pode ser feito lifecycle da entidade:
/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks
*/
class Document
{
}

Em seguida, refatore a classe Document para aproveitar esses callbacks:


use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks
*/
class Document
{
/**
* @ORM\PrePersist()
* @ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
// do whatever you want to generate a unique name
$filename = sha1(uniqid(mt_rand(), true));
$this->path = $filename...$this->file->guessExtension();
}
}
/**
* @ORM\PostPersist()
* @ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
// if there is an error when moving the file, an exception will
// be automatically thrown by move(). This will properly prevent
// the entity from being persisted to the database on error
$this->file->move($this->getUploadRootDir(), $this->path);
unset($this->file);
}
/**
* @ORM\PostRemove()
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath()) {
unlink($file);

3.1. Cookbook

273

Symfony Docs pt-BR Documentation, Verso 2.4

}
}
}

A classe agora faz tudo o que voc precisa: ela gera um nome de arquivo nico antes de persistir, move o arquivo
depois de persistir e remove o arquivo sempre que a entidade for excluda.
Agora que a cpia do arquivo tratada atomicamente pela entidade, a chamada $document->upload() deve ser
removida do controlador:
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($document);
$em->flush();
$this->redirect(...);
}

Nota: Os callbacks dos eventos @ORM\PrePersist() e @ORM\PostPersist() so acionados antes e depois da entidade ser persistida no banco de dados. Por outro lado, a callback dos eventos @ORM\PreUpdate() e
@ORM\PostUpdate() so chamadas quando a entidade atualizada.
Cuidado: As callbacks PreUpdate e PostUpdate so acionadas somente se houver uma alterao em um dos
campos de uma entidade que persistida. Isto significa que, por padro, se voc modificar apenas a propriedade
$file, esses eventos no sero disparados, pois a propriedade no diretamente persistida via Doctrine. Uma
soluo seria a utilizao de um campo updated que persistido pelo Doctrine e modific-lo manualmente
quando alterar o arquivo.

Usando o id como nome do arquivo

Se voc quiser usar o id como nome do arquivo, a implementao ligeiramente diferente pois voc precisa salvar a
extenso na propriedade path, em vez do nome real:
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks
*/
class Document
{
// a property used temporarily while deleting
private $filenameForRemove;
/**
* @ORM\PrePersist()
* @ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
$this->path = $this->file->guessExtension();
}
}

274

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

/**
* @ORM\PostPersist()
* @ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
// you must throw an exception here if the file cannot be moved
// so that the entity is not persisted to the database
// which the UploadedFile move() method does
$this->file->move(
$this->getUploadRootDir(),
$this->id...$this->file->guessExtension()
);
unset($this->file);
}
/**
* @ORM\PreRemove()
*/
public function storeFilenameForRemove()
{
$this->filenameForRemove = $this->getAbsolutePath();
}
/**
* @ORM\PostRemove()
*/
public function removeUpload()
{
if ($this->filenameForRemove) {
unlink($this->filenameForRemove);
}
}
public function getAbsolutePath()
{
return null === $this->path
? null
: $this->getUploadRootDir()./.$this->id...$this->path;
}
}

Voc vai notar que, neste caso, necessrio um pouco mais de trabalho a fim de remover o arquivo. Antes que seja
removido, voc deve armazenar o caminho do arquivo (pois ele depende do id). Ento, uma vez que o objeto foi
totalmente removido do banco de dados, voc pode apagar o arquivo com segurana (em PostRemove).
Como usar as extens?es do Doctrine: Timestampable, Sluggable, Translatable, etc.
O Doctrine2 ? muito flex?vel e a comunidade j? criou um s?rie de extens?es do Doctrine ?teis para ajudar voc? com
tarefas comuns relacionadas a entidades.
Uma biblioteca em particular - a biblioteca DoctrineExtensions - fornece funcionalidade de integra??o para os com3.1. Cookbook

275

Symfony Docs pt-BR Documentation, Verso 2.4

portamentos Sluggable, Translatable, Timestampable, Loggable, Tree e Sortable.


O uso de cada uma destas extens?es ? explicado no reposit?rio.
No entanto, para instalar/ativar cada extens?o voc? deve se registrar e ativar um Listener de Evento. Para fazer isso,
voc? tem duas op??es:
1. Usar o StofDoctrineExtensionsBundle, que integra a biblioteca acima.
2. Implementar este servi?o diretamente seguindo a documenta??o para a integra??o com o Symfony2: Instalando
extens?es Gedmo Doctrine2 no Symfony2
Como Registrar Ouvintes e Assinantes de Eventos
O Doctrine contm um valioso sistema de evento que dispara eventos quando quase tudo acontece dentro do sistema.
Para voc, isso significa que poder criar servios arbitrrios e dizer ao Doctrine para notificar os objetos sempre que
uma determinada ao (ex. prePersist) acontecer. Isto pode ser til, por exemplo, para criar um ndice de pesquisa
independente sempre que um objeto em seu banco de dados for salvo.
O Doctrine define dois tipos de objetos que podem ouvir eventos do Doctrine: ouvintes e assinantes. Ambos so muito
semelhantes, mas os ouvintes so um pouco mais simples. Para saber mais, consulte O Sistema de Eventos no site do
Doctrine.
Configurando o Ouvinte/Assinante

Para registrar um servio para agir como um ouvinte ou assinante de evento voc s tem que usar a tag com o nome
apropriado. Dependendo de seu caso de uso, voc pode ligar um ouvinte em cada conexo DBAL e gerenciador de
entidade ORM ou apenas em uma conexo especfica DBAL e todos os gerenciadores de entidades que usam esta
conexo.
YAML
doctrine:
dbal:
default_connection: default
connections:
default:
driver: pdo_sqlite
memory: true
services:
my.listener:
class: Acme\SearchBundle\EventListener\SearchIndexer
tags:
- { name: doctrine.event_listener, event: postPersist }
my.listener2:
class: Acme\SearchBundle\EventListener\SearchIndexer2
tags:
- { name: doctrine.event_listener, event: postPersist, connection: default }
my.subscriber:
class: Acme\SearchBundle\EventListener\SearchIndexerSubscriber
tags:
- { name: doctrine.event_subscriber, connection: default }

XML

276

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

<?xml version="1.0" ?>


<container xmlns="http://symfony.com/schema/dic/services"
xmlns:doctrine="http://symfony.com/schema/dic/doctrine">
<doctrine:config>
<doctrine:dbal default-connection="default">
<doctrine:connection driver="pdo_sqlite" memory="true" />
</doctrine:dbal>
</doctrine:config>

<services>
<service id="my.listener" class="Acme\SearchBundle\EventListener\SearchIndexer">
<tag name="doctrine.event_listener" event="postPersist" />
</service>
<service id="my.listener2" class="Acme\SearchBundle\EventListener\SearchIndexer2">
<tag name="doctrine.event_listener" event="postPersist" connection="default" />
</service>
<service id="my.subscriber" class="Acme\SearchBundle\EventListener\SearchIndexerSubscrib
<tag name="doctrine.event_subscriber" connection="default" />
</service>
</services>
</container>

Criando a Classe Ouvinte

No exemplo anterior, um servio my.listener foi configurado como um ouvinte Doctrine no evento
postPersist. A classe atrs desse servio deve ter um mtodo postPersist, que ser chamado quando o
evento lanado:
// src/Acme/SearchBundle/EventListener/SearchIndexer.php
namespace Acme\SearchBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Acme\StoreBundle\Entity\Product;
class SearchIndexer
{
public function postPersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$entityManager = $args->getEntityManager();
// perhaps you only want to act on some "Product" entity
if ($entity instanceof Product) {
// do something with the Product
}
}
}

Em cada evento, voc tem acesso a um objeto LifecycleEventArgs, que d acesso tanto ao objeto entidade do
evento quanto ao gerenciador de entidade em si.
Algo importante a notar que um ouvinte estar ouvindo todas as entidades em sua aplicao. Ento, se voc est
interessado apenas em lidar com um tipo especfico de entidade (por exemplo, uma entidade Product, mas no
uma entidade BlogPost), voc deve verificar o nome da classe da entidade em seu mtodo (como mostrado
acima).

3.1. Cookbook

277

Symfony Docs pt-BR Documentation, Verso 2.4

Como usar a Camada DBAL do Doctrine


Nota: Este artigo sobre a camada DBAL do Doctrine. Normalmente, voc vai trabalhar com a camada de alto nvel
ORM do Doctrine, que simplesmente usa o DBAL, nos bastidores, para comunicar-se com o banco de dados. Para ler
mais sobre o ORM Doctrine, consulte Bancos de Dados e Doctrine.
A Camada de Abstrao de Banco de Dados Doctrine (DBAL) uma camada de abstrao que fica situada no topo
do PDO e oferece uma API intuitiva e flexvel para se comunicar com os bancos de dados relacionais mais populares.
Em outras palavras, a biblioteca DBAL torna mais fcil a execuo de consultas e de outras aes de banco de dados.
Dica: Leia a Documentao oficial do DBAL para aprender todos os detalhes e as capacidades da biblioteca DBAL
do Doctrine.
Para comear, configure os parmetros de conexo do banco de dados:
YAML
# app/config/config.yml
doctrine:
dbal:
driver:
pdo_mysql
dbname:
Symfony2
user:
root
password: null
charset: UTF8

XML
// app/config/config.xml
<doctrine:config>
<doctrine:dbal
name="default"
dbname="Symfony2"
user="root"
password="null"
driver="pdo_mysql"
/>
</doctrine:config>

PHP
// app/config/config.php
$container->loadFromExtension(doctrine, array(
dbal => array(
driver
=> pdo_mysql,
dbname
=> Symfony2,
user
=> root,
password => null,
),
));

Para ver todas as opes de configurao do DBAL, consulte reference-dbal-configuration.


Voc pode acessar a conexo DBAL do Doctrine acessando o servio database_connection:
class UserController extends Controller
{
public function indexAction()
{

278

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

$conn = $this->get(database_connection);
$users = $conn->fetchAll(SELECT * FROM users);
// ...
}
}

Registrando Tipos de Mapeamento Personalizados

Voc pode registrar tipos de mapeamento personalizados atravs de configurao do symfony. Eles sero adicionados
todas as conexes configuradas. Para mais informaes sobre tipos de mapeamento personalizados, leia a seo
Tipos de Mapeamento Personalizados na documentao do Doctrine.
YAML
# app/config/config.yml
doctrine:
dbal:
types:
custom_first: Acme\HelloBundle\Type\CustomFirst
custom_second: Acme\HelloBundle\Type\CustomSecond

XML

<!-- app/config/config.xml -->


<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:doctrine="http://symfony.com/schema/dic/doctrine"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/ser
http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doc
<doctrine:config>
<doctrine:dbal>
<doctrine:type name="custom_first" class="Acme\HelloBundle\Type\CustomFirst" />
<doctrine:type name="custom_second" class="Acme\HelloBundle\Type\CustomSecond" />
</doctrine:dbal>
</doctrine:config>
</container>

PHP
// app/config/config.php
$container->loadFromExtension(doctrine, array(
dbal => array(
types => array(
custom_first => Acme\HelloBundle\Type\CustomFirst,
custom_second => Acme\HelloBundle\Type\CustomSecond,
),
),
));

Registrando Tipos de Mapeamento Personalizados no SchemaTool

O SchemaTool usado para inspecionar o banco de dados para comparar o esquema. Para realizar esta tarefa, ele
precisa saber que tipo de mapeamento precisa ser usado para cada um dos tipos do banco de dados. O registro de
novos pode ser feito atravs de configurao.
3.1. Cookbook

279

Symfony Docs pt-BR Documentation, Verso 2.4

Vamos mapear o tipo ENUM (por padro no suportado pelo DBAL) para um tipo de mapeamento string:
YAML
# app/config/config.yml
doctrine:
dbal:
connections:
default:
// Other connections parameters
mapping_types:
enum: string

XML

<!-- app/config/config.xml -->


<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:doctrine="http://symfony.com/schema/dic/doctrine"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/ser
http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doc
<doctrine:config>
<doctrine:dbal>
<doctrine:dbal default-connection="default">
<doctrine:connection>
<doctrine:mapping-type name="enum">string</doctrine:mapping-type>
</doctrine:connection>
</doctrine:dbal>
</doctrine:config>
</container>

PHP
// app/config/config.php
$container->loadFromExtension(doctrine, array(
dbal => array(
connections => array(
default => array(
mapping_types => array(
enum => string,
),
),
),
),
));

Como gerar Entidades de uma base de dados existente


Quando se comea a trabalhar em um novo projeto que usa banco de dados, duas diferentes situaes so comuns. Na
maioria dos casos, o modelo de banco de dados projetado e construdo do zero. Mas algumas vezes, voc comear
com um modelo de banco de dados existente e provavelmente no poder alter-lo. Felizmente, o Doctrine possui
muitas ferramentas que podem te ajudar a gerar as classes model do seu de banco de dados j existente.
Nota: Como a Doctrine tools documentation diz, engenharia reversa um processo nico ao iniciar um projeto.
Doctrine capaz de converter aproximadamente 70-80% das instrues de mapeamento necessrias baseadas em
campos, ndices e chaves estrangeiras. Doctrine no pode descobrir associaes inversas, tipos de herana, entidades
com chaves estrangeiras como chaves primrias ou operaes semnticas em associaes como cascata ou eventos

280

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

de ciclo de vida. Alguns ajustes adicionais nas entidades geradas so necessrios posteriormente para adequar as
especificidades de cada modelo de domnio.
Este tutorial asssume que voc est utilizando uma simples aplicao de blog com as seguintes duas tabelas:
blog_post e blog_comment. Um comentrio gravado ligado a um post gravado graas a uma chave estrangeira.
CREATE TABLE blog_post (
id bigint(20) NOT NULL AUTO_INCREMENT,
title varchar(100) COLLATE utf8_unicode_ci NOT NULL,
content longtext COLLATE utf8_unicode_ci NOT NULL,
created_at datetime NOT NULL,
PRIMARY KEY (id),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE blog_comment (
id bigint(20) NOT NULL AUTO_INCREMENT,
post_id bigint(20) NOT NULL,
author varchar(20) COLLATE utf8_unicode_ci NOT NULL,
content longtext COLLATE utf8_unicode_ci NOT NULL,
created_at datetime NOT NULL,
PRIMARY KEY (id),
KEY blog_comment_post_id_idx (post_id),
CONSTRAINT blog_post_id FOREIGN KEY (post_id) REFERENCES blog_post (id) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Antes de comear a receita, verifique se seus parmetros de conexo com banco de dados esto configurados corretamente no arquivo app/config/parameters.yml (ou onde quer que sua configurao de banco de dados
mantida) e que voc tenha inicializado um bundle que ir receber sua futura classe de entidade. NEste tutorial, vamos
supor que um AcmeBlogBundle existe e est localizado na pasta src/Acme/BlogBundle.
O primeiro passo para construir as classes de entidade de uma base da dados solicitar que o Doctrine faa a introspeco do banco de dados e gere os arquivos de metadados. Os arquivos de metadados descrevem a classe de entidade
baseados nos campos da tabela.

php app/console doctrine:mapping:convert xml ./src/Acme/BlogBundle/Resources/config/doctrine/metadata

Esta ferramenta de linha de comando pede para o Doctrine fazer a introspeco do banco de dados e gerar os arquivos
de metadados na pasta src/Acme/BlogBundle/Resources/config/doctrine/metadata/orm do seu
bundle.
Dica: Tambm possivel gerar a classe de metadados no formato YAML alterando o primeiro argumento para yml.
O arquivo de metadados gerado BlogPost.dcm.xml semelhante a isto:
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping>
<entity name="BlogPost" table="blog_post">
<change-tracking-policy>DEFERRED_IMPLICIT</change-tracking-policy>
<id name="id" type="bigint" column="id">
<generator strategy="IDENTITY"/>
</id>
<field name="title" type="string" column="title" length="100"/>
<field name="content" type="text" column="content"/>
<field name="isPublished" type="boolean" column="is_published"/>
<field name="createdAt" type="datetime" column="created_at"/>
<field name="updatedAt" type="datetime" column="updated_at"/>
<field name="slug" type="string" column="slug" length="255"/>
<lifecycle-callbacks/>

3.1. Cookbook

281

Symfony Docs pt-BR Documentation, Verso 2.4

</entity>
</doctrine-mapping>

Uma vez que os arquivos de metados foram gerados, voc pode pedir para Doctrine importar o esquema e construir as
classes de entidade relacionadas com a execuo dos dois comandos a seguir.
php app/console doctrine:mapping:import AcmeBlogBundle annotation
php app/console doctrine:generate:entities AcmeBlogBundle

O primeiro comando gera as classes de entidade com um mapeamento de anotaes, msa voc poder alterar o argumento annotationpara xml ou yml. A classe de entidade BlogComment recm-criada semelhante a
isto:
<?php
// src/Acme/BlogBundle/Entity/BlogComment.php
namespace Acme\BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Acme\BlogBundle\Entity\BlogComment
*
* @ORM\Table(name="blog_comment")
* @ORM\Entity
*/
class BlogComment
{
/**
* @var bigint $id
*
* @ORM\Column(name="id", type="bigint", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string $author
*
* @ORM\Column(name="author", type="string", length=100, nullable=false)
*/
private $author;
/**
* @var text $content
*
* @ORM\Column(name="content", type="text", nullable=false)
*/
private $content;
/**
* @var datetime $createdAt
*
* @ORM\Column(name="created_at", type="datetime", nullable=false)
*/
private $createdAt;
/**

282

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

* @var BlogPost
*
* @ORM\ManyToOne(targetEntity="BlogPost")
* @ORM\JoinColumn(name="post_id", referencedColumnName="id")
*/
private $post;
}

Como voc pode ver, Doctrine converte todos os campos da tabela para propriedades privadas e anotadas da classe. O
mais impressionante que ele descobre o relacionamento com a classe de entidade BlogPost baseada na restrio
de chave estrangeira. Conseqentemente, voc pode encontrar uma propriedade privada $post mapeada com por
uma entidade BlogPost na classe de entidade BlogComment.
O ltimo comando gerou todos os getters e setters para todas as propriedades das duas classes de entidade BlogPost
e BlogComment. As entidades geradas agora esto prontas para serem usadas. Divirta-se!
Como trabalhar com Mltiplos Gerenciadores de Entidade
Voc pode usar mltiplos gerenciadores de entidades em uma aplicao Symfony2. Isto necessrio se voc est
usando bancos de dados diferentes ou algum vendor com conjuntos de entidades completamente diferentes. Em outras
palavras, um gerenciador de entidades que conecta em um banco de dados manipular algumas entidades enquanto
um outro gerenciador de entidades que conecta a um outro banco de dados ir manupular as entidades restantes.
Nota: Usar mltiplos gerenciadores de entidade muito fcil, mas mais avanado e geralmente no necessrio.
Certifique se voc realmente precisa de mltiplos gerenciadores de entidades antes de adicionar esta camada de complexibilidade.
O cdigo de configurao seguinte mostra como configurar dois gerenciadores de entidade:
YAML
doctrine:
orm:
default_entity_manager:
default
entity_managers:
default:
connection:
default
mappings:
AcmeDemoBundle: ~
AcmeStoreBundle: ~
customer:
connection:
customer
mappings:
AcmeCustomerBundle: ~

Neste caso, voc deve definir dois gerenciadores de entidade e cham-los de default e customer. O gerenciador
de entidade default manipula as entidades em AcmeDemoBundle e AcmeStoreBundle, enquanto o gerenciador de entidades customer manipula as entidades AcmeCustomerBundle.
Quando estiver trabalhando com mltiplos gerenciadores de entidade, voc deve ser explcito sobre qual gerenciador
de entidade voc quer. Se voc omitir o nome do gerenciador de entidade quando voc atualizar o seu schema, ser
usado o padro (ou seja, default):
# Play only with "default" mappings
php app/console doctrine:schema:update --force

3.1. Cookbook

283

Symfony Docs pt-BR Documentation, Verso 2.4

# Play only with "customer" mappings


php app/console doctrine:schema:update --force --em=customer

Se voc omitir o nome do gerenciador de entidade ao solicitar ele, o gerenciador de entidade padro (ou seja,
default) retornado:
class UserController extends Controller
{
public function indexAction()
{
// both return the "default" em
$em = $this->get(doctrine)->getManager();
$em = $this->get(doctrine)->getManager(default);
$customerEm =

$this->get(doctrine)->getManager(customer);

}
}

Agora voc pode usar Doctrine exatamente da mesma forma que voc fez antes - usando o gerenciador de entidade
default para persistir e buscar as entidades que ele gerencia, e o gerenciador de entidade customer para persistir
e buscar suas entidades.
O mesmo se aplica para chamadas de repositrio:
class UserController extends Controller
{
public function indexAction()
{
// Retrieves a repository managed by the "default" em
$products = $this->get(doctrine)
->getRepository(AcmeStoreBundle:Product)
->findAll();
// Explicit way to deal with the "default" em
$products = $this->get(doctrine)
->getRepository(AcmeStoreBundle:Product, default)
->findAll();
// Retrieves a repository managed by the "customer" em
$customers = $this->get(doctrine)
->getRepository(AcmeCustomerBundle:Customer, customer)
->findAll();
}
}

Como Registrar Funes DQL Personalizadas


O Doctrine permite especificar funes DQL personalizadas. Para mais informaes sobre este assunto, leia o artigo
Funes DQL Definidas pelo Usurio no cookbook do Doctrine.
No Symfony, voc pode registrar suas funes DQL personalizadas da seguinte forma:
YAML
# app/config/config.yml
doctrine:
orm:
# ...

284

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

entity_managers:
default:
# ...
dql:
string_functions:
test_string: Acme\HelloBundle\DQL\StringFunction
second_string: Acme\HelloBundle\DQL\SecondStringFunction
numeric_functions:
test_numeric: Acme\HelloBundle\DQL\NumericFunction
datetime_functions:
test_datetime: Acme\HelloBundle\DQL\DatetimeFunction

XML

<!-- app/config/config.xml -->


<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:doctrine="http://symfony.com/schema/dic/doctrine"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/ser
http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doc

<doctrine:config>
<doctrine:orm>
<!-- ... -->
<doctrine:entity-manager name="default">
<!-- ... -->
<doctrine:dql>
<doctrine:string-function name="test_string>Acme\HelloBundle\DQL\StringFunct
<doctrine:string-function name="second_string>Acme\HelloBundle\DQL\SecondStr
<doctrine:numeric-function name="test_numeric>Acme\HelloBundle\DQL\NumericFu
<doctrine:datetime-function name="test_datetime>Acme\HelloBundle\DQL\Datetim
</doctrine:dql>
</doctrine:entity-manager>
</doctrine:orm>
</doctrine:config>
</container>

PHP
// app/config/config.php
$container->loadFromExtension(doctrine, array(
orm => array(
...,
entity_managers => array(
default => array(
...,
dql => array(
string_functions => array(
test_string
=> Acme\HelloBundle\DQL\StringFunction,
second_string => Acme\HelloBundle\DQL\SecondStringFunction,
),
numeric_functions => array(
test_numeric => Acme\HelloBundle\DQL\NumericFunction,
),
datetime_functions => array(
test_datetime => Acme\HelloBundle\DQL\DatetimeFunction,
),
),
),

3.1. Cookbook

285

Symfony Docs pt-BR Documentation, Verso 2.4

),
),
));

Como Implementar um Formulrio Simples de Registro


Alguns formulrios possuem campos extras cujos valores no precisam ser armazenados no banco de dados. Por
exemplo, voc pode criar um formulrio de registo com alguns campos extras (como um campo checkbox termos de
aceite) e incorporar o formulrio que realmente armazena as informaes da conta.
O modelo User

Voc tem uma entidade simples User mapeada para o banco de dados:
// src/Acme/AccountBundle/Entity/User.php
namespace Acme\AccountBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* @ORM\Entity
* @UniqueEntity(fields="email", message="Email already taken")
*/
class User
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="string", length=255)
* @Assert\NotBlank()
* @Assert\Email()
*/
protected $email;
/**
* @ORM\Column(type="string", length=255)
* @Assert\NotBlank()
*/
protected $plainPassword;
public function getId()
{
return $this->id;
}
public function getEmail()
{
return $this->email;
}

286

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

public function setEmail($email)


{
$this->email = $email;
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function setPlainPassword($password)
{
$this->plainPassword = $password;
}
}

Esta entidade User contm trs campos, e dois deles (email e plainPassword) devem ser exibos no formulrio.
A propriedade e-mail deve ser nica no banco de dados, isto aplicado atravs da adio da validao no topo da
classe.
Nota: Se voc quiser integrar este User com o sistema de segurana, voc precisa implementar a UserInterface do
componente de segurana.

Criando um Formulrio para o Modelo

Em seguida, crie o formulrio para o modelo User:


// src/Acme/AccountBundle/Form/Type/UserType.php
namespace Acme\AccountBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(email, email);
$builder->add(plainPassword, repeated, array(
first_name => password,
second_name => confirm,
type => password,
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
data_class => Acme\AccountBundle\Entity\User
));
}
public function getName()
{
return user;

3.1. Cookbook

287

Symfony Docs pt-BR Documentation, Verso 2.4

}
}

H apenas dois campos:email e plainPassword (repetido para confirmar a senha digitada).


data_class diz ao formulrio o nome da classe de dados (ou seja, a sua entidade User).

A opo

Dica: Para explorar mais sobre o componente de formulrio, leia Formulrios.

Incorporando o Formulrio do User no Formulrio de Registro

O formulrio que voc vai usar para a pgina de registo no ser o mesmo usado para modificar o User (ou seja,
UserType). O formulrio de registro conter novos campos como o aceitar os termos, cujo valor no ser armazenado no banco de dados.
Comece criando uma classe simples que representa o registro:
// src/Acme/AccountBundle/Form/Model/Registration.php
namespace Acme\AccountBundle\Form\Model;
use Symfony\Component\Validator\Constraints as Assert;
use Acme\AccountBundle\Entity\User;
class Registration
{
/**
* @Assert\Type(type="Acme\AccountBundle\Entity\User")
*/
protected $user;
/**
* @Assert\NotBlank()
* @Assert\True()
*/
protected $termsAccepted;
public function setUser(User $user)
{
$this->user = $user;
}
public function getUser()
{
return $this->user;
}
public function getTermsAccepted()
{
return $this->termsAccepted;
}
public function setTermsAccepted($termsAccepted)
{
$this->termsAccepted = (Boolean) $termsAccepted;
}
}

288

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Em seguida, crie o formulrio para este modelo Registration:


// src/Acme/AccountBundle/Form/Type/RegistrationType.php
namespace Acme\AccountBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(user, new UserType());
$builder->add(
terms,
checkbox,
array(property_path => termsAccepted)
);
}
public function getName()
{
return registration;
}
}

Voc no precisa usar um mtodo especial para incorporar o formulrio UserType. Um formulrio tambm
um campo - logo, voc pode adicionar ele como qualquer outro campo, com a certeza de que a propriedade
Registration.user ir manter uma instncia da classe User.
Manuseando a Submisso do Formulrio

Em seguida, voc precisa de um controlador para lidar com o formulrio. Comece criando um controlador simples
para exibir o formulrio de registro:
// src/Acme/AccountBundle/Controller/AccountController.php
namespace Acme\AccountBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Acme\AccountBundle\Form\Type\RegistrationType;
use Acme\AccountBundle\Form\Model\Registration;
class AccountController extends Controller
{
public function registerAction()
{
$form = $this->createForm(
new RegistrationType(),
new Registration()
);
return $this->render(
AcmeAccountBundle:Account:register.html.twig,
array(form => $form->createView())
);

3.1. Cookbook

289

Symfony Docs pt-BR Documentation, Verso 2.4

}
}

e o seu template:
{# src/Acme/AccountBundle/Resources/views/Account/register.html.twig #}
<form action="{{ path(create)}}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<input type="submit" />
</form>

Por fim, adicione o controlador que lida com a submisso do formulrio. Ele realiza a validao e salva os dados no
banco de dados:
public function createAction()
{
$em = $this->getDoctrine()->getEntityManager();
$form = $this->createForm(new RegistrationType(), new Registration());
$form->bind($this->getRequest());
if ($form->isValid()) {
$registration = $form->getData();
$em->persist($registration->getUser());
$em->flush();
return $this->redirect(...);
}
return $this->render(
AcmeAccountBundle:Account:register.html.twig,
array(form => $form->createView())
);
}

Pronto! O seu formulrio agora valida e permite que voc salve o objeto User no banco de dados. O checkbox extra
terms na classe de modelo Registration utilizado durante a validao, mas no utilizado posteriormente
quando salvamos o usurio no banco de dados.

3.1.6 Formulrio
Como personalizar a Renderizao de Formulrios
O Symfony dispe de uma grande variedade de maneiras para personalizar como um formulrio renderizado. Neste
guia, voc vai aprender a personalizar cada parte possvel do seu formulrio com o menor esforo, se voc usa o Twig
ou PHP como sua templating engine.
Noes Bsicas sobre a Renderizao de Formulrios

Lembre-se que o widget HTML, a label e o erro de um campo do formulrio podem ser facilmente renderizados
usando a funo Twig form_row ou o mtodo helper PHP row:
Twig

290

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

{{ form_row(form.age) }}

PHP
<?php echo $view[form]->row($form[age]) }} ?>

Voc tambm pode renderizar cada uma das trs partes do campo individualmente:
Twig
<div>
{{ form_label(form.age) }}
{{ form_errors(form.age) }}
{{ form_widget(form.age) }}
</div>

PHP
<div>
<?php echo $view[form]->label($form[age]) }} ?>
<?php echo $view[form]->errors($form[age]) }} ?>
<?php echo $view[form]->widget($form[age]) }} ?>
</div>

Em ambos os casos, a label do formulrio, os erros e o widget HTML so renderizados usando um conjunto de
marcao que vem por padro com o symfony. Por exemplo, ambos os templates acima seriam renderizados da
seguinte forma:
<div>
<label for="form_age">Age</label>
<ul>
<li>This field is required</li>
</ul>
<input type="number" id="form_age" name="form[age]" />
</div>

Para prottipos rpidos e para testar um formulrio, voc pode renderizar todo o formulrio com apenas uma linha:
Twig
{{ form_widget(form) }}

PHP
<?php echo $view[form]->widget($form) }} ?>

O restante desta receita ir explicar como cada parte de marcao do formulrio pode ser modificada em vrios nveis
diferentes. Para mais informaes sobre a renderizao do formulrio em geral, consulte Renderizando um formulrio
em um Template.
O que so Temas de Formulrio?

O Symfony utiliza fragmentos de formulrio - um pequeno pedao de um template que renderiza apenas uma parte
de um formulrio - para renderizar cada parte de um formulrio - labels de campo, os erros, campos de texto input,
tags select, etc
Os fragmentos so definidos como blocos no Twig e como arquivos de template no PHP.

3.1. Cookbook

291

Symfony Docs pt-BR Documentation, Verso 2.4

Um tema nada mais do que um conjunto de fragmentos que voc deseja usar quando est renderizando um formulrio.
Em outras palavras, se voc quiser personalizar uma parte de como um formulrio processado, voc vai importar um
tema que contm uma personalizao dos fragmentos apropriados do formulrio.
O Symfony vem com um tema padro (form_div_layout.html.twig no Twig e FrameworkBundle:Form no PHP)
que define cada fragmento necessrio para renderizar cada parte de um formulrio.
Na seo seguinte, voc vai aprender a personalizar um tema, sobrescrevendo alguns ou todos os seus fragmentos.
Por exemplo, quando o widget de um campo do tipo integer renderizado, um campo input number gerado
Twig
{{ form_widget(form.age) }}

PHP
<?php echo $view[form]->widget($form[age]) ?>

renderiza:
<input type="number" id="form_age" name="form[age]" required="required" value="33" />

Internamente, o symfony usa o fragmento integer_widget para renderizar o campo. Isso ocorre porque o tipo de
campo integer e voc est renderizando seu widget (em oposio a sua label ou errors).
No Twig ele seria por padro o bloco integer_widget do template form_div_layout.html.twig.
No
PHP
ele
seria
o
arquivo
integer_widget.html.php
FrameworkBundle/Resources/views/Form.

localizado

no

diretrio

A implementao padro do fragmento integer_widget seria parecida com esta:


Twig
{# form_div_layout.html.twig #}
{% block integer_widget %}
{% set type = type|default(number) %}
{{ block(field_widget) }}
{% endblock integer_widget %}

PHP

<!-- integer_widget.html.php -->


<?php echo $view[form]->renderBlock(field_widget, array(type => isset($type) ? $type : "nu

Como voc pode ver, este prprio fragmento renderiza outro fragmento - field_widget:
Twig
{# form_div_layout.html.twig #}
{% block field_widget %}
{% set type = type|default(text) %}
<input type="{{ type }}" {{ block(widget_attributes) }} value="{{ value }}" />
{% endblock field_widget %}

PHP
<!-- FrameworkBundle/Resources/views/Form/field_widget.html.php -->
<input
type="<?php echo isset($type) ? $view->escape($type) : "text" ?>"
value="<?php echo $view->escape($value) ?>"
<?php echo $view[form]->renderBlock(attributes) ?>
/>

292

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

O ponto , os fragmentos ditam a sada HTML de cada parte de um formulrio. Para personalizar a sada do formulrio,
voc s precisa identificar e substituir o fragmento apropriado. O conjunto dessas personalizaes de fragmentos de
formulrio conhecida como tema de formulrio. Ao renderizar um formulrio, voc pode escolher qual(ais)
tema(s) de formulrio deseja aplicar.
No Twig, um tema um nico arquivo de template e os fragmentos so os blocos definidos neste arquivo.
No PHP um tema um diretrio e os fragmentos so os arquivos de template individuais neste diretrio.
Sabendo qual bloco personalizar
Neste exemplo, o nome do fragmento personalizado integer_widget porque voc quis sobrescrever o
widget HTML para todos os tipos de campo integer. Se voc precisa personalizar campos textarea, voc
iria personalizar o textarea_widget.
Como voc pode ver, o nome do fragmento uma combinao do tipo do campo e de qual parte do campo est
sendo renderizada (ex.: widget, label, errors, row). Como tal, para personalizar a forma como os erros
so renderizados apenas para campos input text, voc deve personalizar o fragmento text_errors.
Mais comumente, no entanto, voc vai querer personalizar a forma como os erros so exibidos atravs de todos
os campos. Voc pode fazer isso personalizando o fragmento field_errors. Isso aproveita a herana do
tipo de campo. Especificamente, uma vez que o tipo text estende o tipo field, o componente de formulrio
vai procurar primeiro pelo fragmento de tipo especfico (ex., text_errors) antes de voltar ao seu nome de
fragmento pai, no caso dele no existir (ex., field_errors).
Para mais informaes sobre este tpico, consulte Nomeando os fragmentos do formulrio.

Tematizando os Formulrios

Para ver o poder da tematizao de formulrios, suponha que voc queira envolver cada campo input number com
uma tag div. A chave para fazer isso personalizar o fragmento integer_widget.
Tematizao de Formulrios no Twig

Ao personalizar o bloco de campo de formulrio no Twig, voc tem duas opes de onde o bloco de formulrio
personalizado pode residir:
Mtodo
Dentro do mesmo template que o
formulrio
Dentro de um template separado

Prs
Rpido e fcil
Pode ser reutilizado por muitos
templates

Contras
No pode ser reutilizado em outros
templates
Requer que um template extra seja
criado

Ambos os mtodos tm o mesmo efeito, mas so melhores em situaes diferentes.


Mtodo 1: Dentro do mesmo Template que o Formulrio A maneira mais fcil de personalizar o bloco
integer_widget personaliz-lo diretamente no mesmo template que est renderizando o formulrio.
{% extends ::base.html.twig %}
{% form_theme form _self %}
{% block integer_widget %}
<div class="integer_widget">
{% set type = type|default(number) %}
{{ block(field_widget) }}

3.1. Cookbook

293

Symfony Docs pt-BR Documentation, Verso 2.4

</div>
{% endblock %}
{% block content %}
{# ... render the form #}
{{ form_row(form.age) }}
{% endblock %}

Ao usar a tag especial {% form_theme form _self %}, o Twig procura dentro do mesmo template por qualquer blocos de formulrios sobrescritos. Assumindo que o campo form.age um tipo de campo integer, quando
o widget renderizado, o bloco integer_widget personalizado ir ser utilizado.
A desvantagem deste mtodo que o bloco de formulrio personalizado no pode ser reutilizado ao renderizar outros
formulrios em outros templates. Em outras palavras, este mtodo mais til ao fazer personalizaes de formulrios
que so especficas para um nico formulrio em sua aplicao. Se voc quiser reutilizar uma personalizao em
vrios (ou todos) os formulrios de sua aplicao, leia a prxima seo.
Mtodo 2: Dentro de um Template Separado Voc tambm pode optar por colocar o bloco de formulrio
integer_widget personalizado em um template totalmente separado. O cdigo e o resultado final o mesmo,
mas agora voc pode reutilizar a personalizao de formulrio em muitos templates:
{# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #}
{% block integer_widget %}
<div class="integer_widget">
{% set type = type|default(number) %}
{{ block(field_widget) }}
</div>
{% endblock %}

Agora que voc j criou o bloco de formulrio personalizado, voc precisa dizer ao Symfony para us-lo. Dentro
do template onde voc est renderizando o seu formulrio, diga ao Symfony para usar o template atravs da tag
form_theme:
{% form_theme form AcmeDemoBundle:Form:fields.html.twig %}
{{ form_widget(form.age) }}

Quando o widget form.age renderizado, o Symfony usar o bloco integer_widget do novo template e a tag
input vai ser envolvida no elemento div especificado no bloco personalizado.
Tematizando Formulrios em PHP

Ao usar o PHP como templating engine, o nico mtodo de personalizar um fragmento criar um novo arquivo
template - semelhante ao segundo mtodo utilizado pelo Twig.
O arquivo de template deve ser nomeado aps o fragmento.
Voc deve criar um arquivo
integer_widget.html.php para personalizar o fragmento integer_widget.

<!-- src/Acme/DemoBundle/Resources/views/Form/integer_widget.html.php -->


<div class="integer_widget">
<?php echo $view[form]->renderBlock(field_widget, array(type => isset($type) ? $type : "num
</div>

Agora que voc criou o template de formulrio personalizado, voc precisa dizer ao Symfony para us-lo. Dentro do
template onde voc est renderizando o seu formulrio, diga ao Symfony para usar o tema atravs do mtodo helper
setTheme:
294

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

<?php $view[form]->setTheme($form, array(AcmeDemoBundle:Form)) ;?>


<?php $view[form]->widget($form[age]) ?>

Quando o widget form.age renderizado, o Symfony vai usar o template integer_widget.html.php personalizado e a tag input ser envolvida pelo elemento div.
Referenciando Blocos de Formulrio Base (especfico para o Twig)

At agora, para sobrescrever um bloco de formulrio em particular, o melhor mtodo copiar o bloco padro de
form_div_layout.html.twig, col-lo em um template diferente, e, em seguida, personaliz-lo. Em muitos casos, voc
pode evitar ter que fazer isso atravs da referncia ao bloco base quando for personaliz-lo.
Isso fcil de fazer, mas varia um pouco dependendo se as personalizaes de seu bloco de formulrio esto no mesmo
template que o formulrio ou em um template separado.
Referenciando Blocos no interior do mesmo Template que o Formulrio
use no template onde voc est renderizando o formulrio:

Importe os blocos adicionando uma tag

{% use form_div_layout.html.twig with integer_widget as base_integer_widget %}

Agora, quando os blocos do form_div_layout.html.twig so importados, o bloco integer_widget chamado


base_integer_widget. Isto significa que quando voc redefinir o bloco integer_widget, voc pode fazer
referncia a marcao padro atravs do base_integer_widget:
{% block integer_widget %}
<div class="integer_widget">
{{ block(base_integer_widget) }}
</div>
{% endblock %}

Referenciando Blocos Base a partir de um Template Externo Se as personalizaes do formulrio residirem


dentro de um template externo, voc pode fazer referncia ao bloco base usando a funo parent() do Twig:
{# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #}
{% extends form_div_layout.html.twig %}
{% block integer_widget %}
<div class="integer_widget">
{{ parent() }}
</div>
{% endblock %}

Nota: No possvel fazer referncia ao bloco base quando se utiliza o PHP como template engine. Voc tem que
copiar manualmente o contedo do bloco base para o seu novo arquivo de template.

Fazendo Personalizaes para toda a Aplicao

Se voc deseja que uma certa personalizao de formulrio seja global para a sua aplicao, voc pode conseguir isso
fazendo as personalizaes de formulrio em um template externo e depois import-lo dentro da sua configurao da
aplicao:

3.1. Cookbook

295

Symfony Docs pt-BR Documentation, Verso 2.4

Twig Usando a seguinte configurao, quaisquer blocos de formulrios personalizados dentro do template
AcmeDemoBundle:Form:fields.html.twig sero usados globalmente quando um formulrio renderizado.
YAML
# app/config/config.yml
twig:
form:
resources:
- AcmeDemoBundle:Form:fields.html.twig
# ...

XML
<!-- app/config/config.xml -->
<twig:config ...>
<twig:form>
<resource>AcmeDemoBundle:Form:fields.html.twig</resource>
</twig:form>
<!-- ... -->
</twig:config>

PHP
// app/config/config.php
$container->loadFromExtension(twig, array(
form => array(
resources => array(
AcmeDemoBundle:Form:fields.html.twig,
),
),
// ...
));

Por padro, o Twig usa um layout div ao renderizar os formulrios. Algumas pessoas, no entanto, podem preferir
renderizar formulrios em um layout de tabela. Para isso use o recurso form_table_layout.html.twig como
layout:
YAML
# app/config/config.yml
twig:
form:
resources: [form_table_layout.html.twig]
# ...

XML
<!-- app/config/config.xml -->
<twig:config ...>
<twig:form>
<resource>form_table_layout.html.twig</resource>
</twig:form>
<!-- ... -->
</twig:config>

PHP
// app/config/config.php
$container->loadFromExtension(twig, array(

296

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

form => array(


resources => array(
form_table_layout.html.twig,
),
),
// ...
));

Se voc quer fazer a alterao somente em um template, adicione a seguinte linha em seu arquivo de template em vez
de adicionar o template como um recurso:
{% form_theme form form_table_layout.html.twig %}

Note que a varivel form no cdigo acima a varivel de viso do formulrio que voc passou para o seu template.
PHP Usando a seguinte configurao, quaisquer fragmentos de formulrios personalizados no interior do diretrio
src/Acme/DemoBundle/Resources/views/Form sero usados globalmente quando um formulrio renderizado.
YAML
# app/config/config.yml
framework:
templating:
form:
resources:
- AcmeDemoBundle:Form
# ...

XML
<!-- app/config/config.xml -->
<framework:config ...>
<framework:templating>
<framework:form>
<resource>AcmeDemoBundle:Form</resource>
</framework:form>
</framework:templating>
<!-- ... -->
</framework:config>

PHP
// app/config/config.php
// PHP
$container->loadFromExtension(framework, array(
templating => array(
form => array(
resources => array(
AcmeDemoBundle:Form,
),
),
),
// ...
));

3.1. Cookbook

297

Symfony Docs pt-BR Documentation, Verso 2.4

Por padro, a engine PHP usa um layout div ao renderizar formulrios. Algumas pessoas, no entanto, podem preferir
renderizar formulrios em um layout de tabela. Para isso use o recurso FrameworkBundle:FormTable como
layout:
YAML
# app/config/config.yml
framework:
templating:
form:
resources:
- FrameworkBundle:FormTable

XML
<!-- app/config/config.xml -->
<framework:config ...>
<framework:templating>
<framework:form>
<resource>FrameworkBundle:FormTable</resource>
</framework:form>
</framework:templating>
<!-- ... -->
</framework:config>

PHP
// app/config/config.php
$container->loadFromExtension(framework, array(
templating => array(
form => array(
resources => array(
FrameworkBundle:FormTable,
),
),
),
// ...
));

Se voc quer fazer a alterao somente em um template, adicione a seguinte linha em seu arquivo de template em vez
de adicionar o template como um recurso:
<?php $view[form]->setTheme($form, array(FrameworkBundle:FormTable)); ?>

Note que a varivel $form no cdigo acima a varivel de viso do formulrio que voc passou para o seu template.
Como personalizar um campo individual

At agora, voc viu as diferentes maneiras que pode personalizar a sada dos widgets de todos os tipos de campo
texto. Voc tambm pode personalizar campos individuais. Por exemplo, supondo que voc tenha dois campos
text - first_name e last_name - mas voc s quer personalizar um dos campos. Isto pode ser feito pela
personalizao de um fragmento cujo nome uma combinao do atributo id do campo e qual parte do campo est
sendo personalizada. Por exemplo:
Twig
{% form_theme form _self %}

298

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

{% block _product_name_widget %}
<div class="text_widget">
{{ block(field_widget) }}
</div>
{% endblock %}
{{ form_widget(form.name) }}

PHP
<!-- Main template -->
<?php echo $view[form]->setTheme($form, array(AcmeDemoBundle:Form)); ?>
<?php echo $view[form]->widget($form[name]); ?>
<!-- src/Acme/DemoBundle/Resources/views/Form/_product_name_widget.html.php -->
<div class="text_widget">
echo $view[form]->renderBlock(field_widget) ?>
</div>

Aqui, o fragmento _product_name_widget define o template a ser usado para o campo cujo id
product_name (e o nome product[name]).
Dica: A parte product do campo o nome do formulrio, que pode ser definido manualmente ou gerado automaticamente com base no nome do tipo de formulrio (por exemplo, ProductType equivale ao product). Se voc
no tem certeza de qual o nome de seu formulrio, apenas visualize o cdigo fonte do formulrio gerado.
Voc tambm pode sobrescrever a marcao de uma linha inteira de campo utilizando o mesmo mtodo:
Twig
{# _product_name_row.html.twig #}
{% form_theme form _self %}
{% block _product_name_row %}
<div class="name_row">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endblock %}

PHP
<!-- _product_name_row.html.php -->
<div class="name_row">
<?php echo $view[form]->label($form) ?>
<?php echo $view[form]->errors($form) ?>
<?php echo $view[form]->widget($form) ?>
</div>

Outras Personalizaes Comuns

At agora, esta receita tem demonstrado diversas maneiras de personalizar um nico pedao de como um formulrio
renderizado. A chave personalizar um fragmento especfico que corresponde parte do formulrio que voc deseja

3.1. Cookbook

299

Symfony Docs pt-BR Documentation, Verso 2.4

controlar (veja naming form blocks).


Nas prximas sees, voc vai ver como possvel fazer vrias personalizaes comuns de formulrio. Para aplicar
essas personalizaes, utilize um dos mtodos descritos na seo Tematizando os Formulrios.
Personalizando Sada de Erro
Nota: O componente de formulrio s lida com como os erros de validao so renderizados, e no com as mensagens
de erro de validao. As mensagens de erro em si so determinadas pelas restries de validao que voc aplicou aos
seus objetos. Para mais informaes, consulte o captulo sobre validation.
H muitas formas diferentes para personalizar como os erros so renderizados quando um formulrio enviado com
erros. As mensagens de erro para um campo so renderizadas quando voc usa o helper form_errors:
Twig
{{ form_errors(form.age) }}

PHP
<?php echo $view[form]->errors($form[age]); ?>

Por padro, os erros so renderizados dentro de uma lista no ordenada:


<ul>
<li>This field is required</li>
</ul>

Para sobrecrever como os erros so renderizados para todos os campos, basta copiar, colar e personalizar o fragmento
field_errors.
Twig

{# fields_errors.html.twig #}
{% block field_errors %}
{% spaceless %}
{% if errors|length > 0 %}
<ul class="error_list">
{% for error in errors %}
<li>{{ error.messageTemplate|trans(error.messageParameters, validators) }}</li
{% endfor %}
</ul>
{% endif %}
{% endspaceless %}
{% endblock field_errors %}

PHP
<!-- fields_errors.html.php -->
<?php if ($errors): ?>
<ul class="error_list">
<?php foreach ($errors as $error): ?>
<li><?php echo $view[translator]->trans(
$error->getMessageTemplate(),
$error->getMessageParameters(),
validators
) ?></li>
<?php endforeach; ?>
</ul>
<?php endif ?>

300

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Dica: Veja Tematizando os Formulrios para saber como aplicar essa personalizao.
Voc tambm pode personalizar a sada de erro de apenas um tipo de campo especfico. Por exemplo, certos erros que
so mais globais para o seu formulrio (ou seja, no especfico para apenas um campo) so renderizados separadamente, geralmente no topo do seu formulrio:
Twig
{{ form_errors(form) }}

PHP
<?php echo $view[form]->render($form); ?>

Para personalizar apenas a marcao usada para esses erros, siga as mesmas instrues acima, mas agora chame o
bloco form_errors (Twig) / o arquivo form_errors.html.php (PHP). Agora, quando os erros para o tipo
form so renderizados, o seu fragmento personalizado ser usado em vez do padro field_errors.
Personalizando a Linha de Formulrio Quando voc pode gerenci-lo, a maneira mais fcil de renderizar um
campo de formulrio atravs da funo form_row, que renderiza a label, os erros e o widget HTML de um campo.
Para personalizar a marcao usada para renderizar todas as linhas de campo do formulrio, sobrescreva o fragmento
field_row. Por exemplo, suponha que voc deseja adicionar uma classe para o elemento div que envolve cada
linha:
Twig
{# field_row.html.twig #}
{% block field_row %}
<div class="form_row">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endblock field_row %}

PHP
<!-- field_row.html.php -->
<div class="form_row">
<?php echo $view[form]->label($form) ?>
<?php echo $view[form]->errors($form) ?>
<?php echo $view[form]->widget($form) ?>
</div>

Dica: Veja Tematizando os Formulrios para saber como aplicar essa personalizao.

Adicionando um Asterisco para as Labels de Campo Obrigatrias Se voc quiser indicar todos os seus campos
obrigatrios com um asterisco (*), voc pode fazer isso personalizando o fragmento field_label.
No Twig, se voc estiver fazendo a personalizao de formulrio dentro do mesmo template que o seu formulrio,
modifique a tag use e adicione o seguinte:
{% use form_div_layout.html.twig with field_label as base_field_label %}
{% block field_label %}
{{ block(base_field_label) }}

3.1. Cookbook

301

Symfony Docs pt-BR Documentation, Verso 2.4

{% if required %}
<span class="required" title="This field is required">*</span>
{% endif %}
{% endblock %}

No Twig, se voc estiver fazendo a personalizao do formulrio dentro de um template separado, use o seguinte:
{% extends form_div_layout.html.twig %}
{% block field_label %}
{{ parent() }}
{% if required %}
<span class="required" title="This field is required">*</span>
{% endif %}
{% endblock %}

Ao usar o PHP como template engine voc tem que copiar o contedo do template original:
<!-- field_label.html.php -->

<!-- original content -->


<label for="<?php echo $view->escape($id) ?>" <?php foreach($attr as $k => $v) { printf(%s="%s" , $
<!-- customization -->
<?php if ($required) : ?>
<span class="required" title="This field is required">*</span>
<?php endif ?>

Dica: Veja Tematizando os Formulrios para saber como aplicar essa personalizao.

Adicionando mensagens de ajuda Voc tambm pode personalizar os widgets do formulrio para ter uma mensagem opcional de ajuda.
No Twig, se voc est fazendo a personalizao do formulrio dentro do mesmo template que o seu formulrio,
modifique a tag use e adicione o seguinte:
{% use form_div_layout.html.twig with field_widget as base_field_widget %}
{% block field_widget %}
{{ block(base_field_widget) }}
{% if help is defined %}
<span class="help">{{ help }}</span>
{% endif %}
{% endblock %}

No Twig, se voc est fazendo a personalizao do formulrio dentro de um template separado, use o seguinte:
{% extends form_div_layout.html.twig %}
{% block field_widget %}
{{ parent() }}
{% if help is defined %}
<span class="help">{{ help }}</span>

302

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

{% endif %}
{% endblock %}

Ao usar o PHP como template engine voc tem que copiar o contedo do template original:
<!-- field_widget.html.php -->
<!-- Original content -->
<input
type="<?php echo isset($type) ? $view->escape($type) : "text" ?>"
value="<?php echo $view->escape($value) ?>"
<?php echo $view[form]->renderBlock(attributes) ?>
/>
<!-- Customization -->
<?php if (isset($help)) : ?>
<span class="help"><?php echo $view->escape($help) ?></span>
<?php endif ?>

Para renderizar uma mensagem de ajuda abaixo de um campo, passe em uma varivel help:
Twig
{{ form_widget(form.title, {help: foobar}) }}

PHP
<?php echo $view[form]->widget($form[title], array(help => foobar)) ?>

Dica: Veja:ref:cookbook-form-theming-methods para saber como aplicar essa personalizao.

Usando Variveis de Formulrio

A maioria das funes disponveis para renderizar diferentes partes de um formulrio (por exemplo, o widget de
formulrio, a label do formulrio, os erros de formulrio, etc) tambm permitem que voc faa certas personalizaes
diretamente. Veja o exemplo a seguir:
Twig
{# render a widget, but add a "foo" class to it #}
{{ form_widget(form.name, { attr: {class: foo} }) }}

PHP
<!-- render a widget, but add a "foo" class to it -->
<?php echo $view[form]->widget($form[name], array(
attr => array(
class => foo,
),
)) ?>

O array passado como segundo argumento contm variveis de formulrio. Para mais detalhes sobre este conceito
no Twig, veja twig-reference-form-variables.

3.1. Cookbook

303

Symfony Docs pt-BR Documentation, Verso 2.4

Como usar os Transformadores de Dados


Voc frenquentemente encontrar a necessidade de transformar os dados inseridos pelo usurio em um formulrio para
um outro formato para uso em seu programa. Isto pode ser feito manualmente no seu controlador facilmente, mas, e
se voc pretende utilizar este formulrio especfico em locais diferentes?
Digamos que voc tenha uma relao um-para-um da Task para o Issue, por exemplo, uma Task opcionalmente tem
um issue ligado ela. Adicionando uma caixa de listagem com todos os possveis issues, eventualmente, pode levar a
uma caixa de listagem muito longa onde impossvel encontrar algo. Em vez disso, voc poderia adicionar uma caixa
de texto, onde o usurio pode simplesmente informar o nmero do issue.
Voc poderia tentar fazer isso no seu controlador, mas no a melhor soluo. Seria melhor se esse issue fosse
automaticamente convertido em um objeto Issue. onde os transformadores de dados entram em jogo.
Criando o Transformador

Primeiro, crie uma classe IssueToNumberTransformer - esta classe ser responsvel por converter de e para o nmero
do issue e o objeto Issue:
// src/Acme/TaskBundle/Form/DataTransformer/IssueToNumberTransformer.php
namespace Acme\TaskBundle\Form\DataTransformer;
use
use
use
use

Symfony\Component\Form\DataTransformerInterface;
Symfony\Component\Form\Exception\TransformationFailedException;
Doctrine\Common\Persistence\ObjectManager;
Acme\TaskBundle\Entity\Issue;

class IssueToNumberTransformer implements DataTransformerInterface


{
/**
* @var ObjectManager
*/
private $om;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
/**
* Transforms an object (issue) to a string (number).
*
* @param Issue|null $issue
* @return string
*/
public function transform($issue)
{
if (null === $issue) {
return "";
}
return $issue->getNumber();
}

304

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

/**
* Transforms a string (number) to an object (issue).
*
* @param string $number
* @return Issue|null
* @throws TransformationFailedException if object (issue) is not found.
*/
public function reverseTransform($number)
{
if (!$number) {
return null;
}
$issue = $this->om
->getRepository(AcmeTaskBundle:Issue)
->findOneBy(array(number => $number))
;
if (null === $issue) {
throw new TransformationFailedException(sprintf(
An issue with number "%s" does not exist!,
$number
));
}
return $issue;
}
}

Dica: Se voc deseja que um novo issue seja criado quando um nmero desconhecido informado, voc pode
instanci-lo ao invs de gerar uma TransformationFailedException.

Usando o Transformador

Agora que voc j tem o transformador construdo, voc s precisa adicion-lo ao seu campo issue de alguma forma.
Voc tambm pode usar transformadores sem criar um novo tipo de formulrio personalizado chamando
addModelTransformer (ou addViewTransformer - ver Transformadores de Modelo e Viso) )
em qualquer builder de campo:
use Symfony\Component\Form\FormBuilderInterface;
use Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformer;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
// this assumes that the entity manager was passed in as an option
$entityManager = $options[em];
$transformer = new IssueToNumberTransformer($entityManager);
// add a normal text field, but add our transformer to it
$builder->add(
$builder->create(issue, text)

3.1. Cookbook

305

Symfony Docs pt-BR Documentation, Verso 2.4

->addModelTransformer($transformer)
);
}
// ...
}

Este exemplo requer que voc passe no gerenciador de entidade como uma opo ao criar o seu formulrio. Mais tarde,
voc vai aprender como criar um tipo de campo issue personalizado para evitar ter de fazer isso no seu controlador:
$taskForm = $this->createForm(new TaskType(), $task, array(
em => $this->getDoctrine()->getEntityManager(),
));

Legal, est feito! O usurio poder informar um nmero de issue no campo texto e ele ser transformado novamente
em um objeto Issue. Isto significa que, aps um bind bem sucedido, o framework de Formulrio passar um objeto
Issue real para o Task::setIssue() em vez do nmero do issue.
Se o issue no for encontrado, um erro de formulrio ser criado para esse campo e sua mensagem de erro pode ser
controlada com a opo do campo invalid_message.
Cuidado: Note que a adio de um transformador exige a utilizao de uma sintaxe um pouco mais complicada
ao adicionar o campo. O cdigo seguinte est errado, j que o transformador ser aplicado todo o formulrio,
em vez de apenas este campo:
// ISTO EST ERRADO - O TRANSFORMADOR SER APLICADA A TODO O FORMULRIO
// Veja o exemplo acima para o cdigo correto
$builder->add(issue, text)
->addModelTransformer($transformer);

Transformadores de Modelo e Viso Novo na verso 2.1: Os nomes e mtodo de transformadores


foram alterados no Symfony 2.1. prependNormTransformer tornou-se addModelTransformer e
appendClientTransformer tornou-se addViewTransformer.
No exemplo acima, o transformador foi utilizado como um transformador de modelo. De fato, existem dois tipos
diferentes de transformadores e trs tipos diferentes de dados subjacentes.
Em qualquer formulrio, os 3 tipos de dados possveis so os seguintes:
1) Dados do modelo - Estes so os dados no formato usado em sua aplicao (ex., um objeto Issue). Se voc chamar
Form::getData ou Form::setData, voc est lidando com os dados do modelo.
2) Dados Normalizados - Esta uma verso normalizada de seus dados, e comumente o mesmo que os dados do
modelo (apesar de no no nosso exemplo). Geralmente ele no usado diretamente.
3) Dados da Viso - Este o formato que usado para preencher os campos do formulrio. tambm o formato no
qual o usurio ir enviar os dados. Quando voc chama Form::bind($data), o $data est no formato de dados
da viso.
Os 2 tipos diferentes de transformadores ajudam a converter de e para cada um destes tipos de dados:
Transformadores de Modelo:
transform: model data => norm data
reverseTransform: norm data => model data
Transformadores de Viso:

306

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

transform: norm data => view data


reverseTransform: view data => norm data
O transformador que voc vai precisar depende de sua situao.
Para utilizar o transformador de viso, chame addViewTransformer.
Ento, por que ns usamos o transformador de modelo?

No nosso exemplo, o campo um campo text, e ns sempre esperamos que um campo texto seja um formato escalar
simples, nos formatos normalizado e viso. Por esta razo, o transformador mais apropriado o transformador
de modelo (que converte de/para o formato normalizado - nmero de issue string - para o formato modelo - objeto
Issue).
A diferena entre os transformadores sutil e voc deve sempre pensar sobre o que o dado normalizado para um
campo deve realmente ser. Por exemplo, o dado normalizado para um campo text uma string, mas um objeto
DateTime para um campo date.
Usando Transformadores em um tipo de campo personalizado

No exemplo acima, voc aplicou o transformador para um campo text normal. Isto foi fcil, mas tem duas desvantagens:
1) Voc precisa lembrar de aplicar o transformador sempre que voc est adicionando um campo para nmeros de
issue
2) Voc precisa se preocupar em sempre passar a opo em quando voc est criando um formulrio que usa o transformador.
Devido isto, voc pode optar por criar um tipo de campo personalizado. Primeiro, crie a classe do tipo de campo
personalizado:
// src/Acme/TaskBundle/Form/Type/IssueSelectorType.php
namespace Acme\TaskBundle\Form\Type;
use
use
use
use
use

Symfony\Component\Form\AbstractType;
Symfony\Component\Form\FormBuilderInterface;
Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformer;
Doctrine\Common\Persistence\ObjectManager;
Symfony\Component\OptionsResolver\OptionsResolverInterface;

class IssueSelectorType extends AbstractType


{
/**
* @var ObjectManager
*/
private $om;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
public function buildForm(FormBuilderInterface $builder, array $options)

3.1. Cookbook

307

Symfony Docs pt-BR Documentation, Verso 2.4

{
$transformer = new IssueToNumberTransformer($this->om);
$builder->addModelTransformer($transformer);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
invalid_message => The selected issue does not exist,
));
}
public function getParent()
{
return text;
}
public function getName()
{
return issue_selector;
}
}

Em seguida, registre o seu tipo como um servio e use a tag form.type, para que ele seja reconhecido como um
tipo de campo personalizado:
YAML
services:
acme_demo.type.issue_selector:
class: Acme\TaskBundle\Form\Type\IssueSelectorType
arguments: ["@doctrine.orm.entity_manager"]
tags:
- { name: form.type, alias: issue_selector }

XML

<service id="acme_demo.type.issue_selector" class="Acme\TaskBundle\Form\Type\IssueSelectorType">


<argument type="service" id="doctrine.orm.entity_manager"/>
<tag name="form.type" alias="issue_selector" />
</service>

Agora, sempre que voc precisa usar o seu tipo de campo especial issue_selector, muito fcil:
// src/Acme/TaskBundle/Form/Type/TaskType.php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(task)
->add(dueDate, null, array(widget => single_text));
->add(issue, issue_selector);
}

308

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

public function getName()


{
return task;
}
}

Como Modificar Formulrios dinamicamente usando Eventos de Formulrio


Antes de saltar diretamente para a gerao dinmica de formulrio, vamos fazer uma reviso rpida do como uma
classe de formulrio parece:
// src/Acme/DemoBundle/Form/Type/ProductType.php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(name);
$builder->add(price);
}
public function getName()
{
return product;
}
}

Nota: Se esta parte de cdigo em particular ainda no lhe familiar, voc provavelmente ter que dar um passo para
trs e primeiro rever o captulo Formulrios antes de prosseguir.
Vamos assumir, por um momento, que este formulrio utiliza uma classe imaginria Product que possui apenas duas
propriedades relevantes (name e price). O formulrio gerado desta classe ter exatamente a mesma aparncia,
independentemente se um novo Product est sendo criado ou se um produto j existente est sendo editado (por
exemplo, um produto obtido a partir do banco de dados).
Suponha agora, que voc no deseja que o usurio possa alterar o valor de name uma vez que o objeto foi criado. Para
fazer isso, voc pode contar com o sistema de Dispatcher de Evento do Symfony para analisar os dados sobre
o objeto e modificar o formulrio com base nos dados do objeto Product. Neste artigo, voc vai aprender como
adicionar este nvel de flexibilidade aos seus formulrios.
Adicionando um Assinante (Subscriber) de evento uma Classe de Formulrio

Assim, em vez de adicionar diretamente o widget name atravs da sua classe de formulrio ProductType, vamos
delegar a responsabilidade de criar esse campo especfico para um Assinante de Evento:
// src/Acme/DemoBundle/Form/Type/ProductType.php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType
use Symfony\Component\Form\FormBuilderInterface;

3.1. Cookbook

309

Symfony Docs pt-BR Documentation, Verso 2.4

use Acme\DemoBundle\Form\EventListener\AddNameFieldSubscriber;
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$subscriber = new AddNameFieldSubscriber($builder->getFormFactory());
$builder->addEventSubscriber($subscriber);
$builder->add(price);
}
public function getName()
{
return product;
}
}

passado o objeto FormFactory ao construtor do assinante de evento para que o seu novo assinante seja capaz de criar
o widget de formulrio, uma vez que ele notificado do evento despachado durante a criao do formulrio.
Dentro da Classe do Assinante de Evento

O objetivo criar um campo name apenas se o objeto Product subjacente novo (por exemplo, no tenha sido
persistido no banco de dados). Com base nisso, o assinante pode parecer com o seguinte:
// src/Acme/DemoBundle/Form/EventListener/AddNameFieldSubscriber.php
namespace Acme\DemoBundle\Form\EventListener;
use
use
use
use

Symfony\Component\Form\Event\DataEvent;
Symfony\Component\Form\FormFactoryInterface;
Symfony\Component\EventDispatcher\EventSubscriberInterface;
Symfony\Component\Form\FormEvents;

class AddNameFieldSubscriber implements EventSubscriberInterface


{
private $factory;
public function __construct(FormFactoryInterface $factory)
{
$this->factory = $factory;
}
public static function getSubscribedEvents()
{
// Tells the dispatcher that you want to listen on the form.pre_set_data
// event and that the preSetData method should be called.
return array(FormEvents::PRE_SET_DATA => preSetData);
}
public function preSetData(DataEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
// During form creation setData() is called with null as an argument
// by the FormBuilder constructor. Youre only concerned with when
// setData is called with an actual Entity object in it (whether new

310

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

// or fetched with Doctrine). This if statement lets you skip right


// over the null condition.
if (null === $data) {
return;
}
// check if the product object is "new"
if (!$data->getId()) {
$form->add($this->factory->createNamed(name, text));
}
}
}

Cuidado: fcil entender mal o propsito do segmento if (null === $data) deste assinante de evento.
Para entender plenamente o seu papel, voc pode considerar tambm verificar a classe Form e prestar ateno
especial onde o setData() chamado no final do construtor, bem como o mtodo setData() em si.
A linha FormEvents::PRE_SET_DATA resolve para a string form.pre_set_data. A classe FormEvents
serve para propsito organizacional. um local centralizado em que voc pode encontrar todos os vrios eventos
disponveis.
Enquanto este exemplo poderia ter usado o evento form.set_data ou at mesmo o form.post_set_data
com a mesma eficcia, usando o form.pre_set_data voc garante que os dados que esto sendo recuperados
do objeto Event no foram de modo algum modificados por quaisquer outros assinantes ou ouvintes. Isto porque o form.pre_set_data passa um objeto DataEvent em vez do objcto FilterDataEvent passado pelo evento
form.set_data. O DataEvent, ao contrrio de seu filho FilterDataEvent, no tem um mtodo setData().
Nota: Voc pode ver a lista completa de eventos de formulrio atravs da classe FormEvents, encontrada no bundle
de formulrio.

Como embutir uma Coleo de Formulrios


Neste artigo, voc vai aprender como criar um formulrio que incorpora uma coleo de muitos outros formulrios.
Isto pode ser til, por exemplo, se voc tem uma classe Task onde voc deseja editar/criar/remover muitos objetos
Tag relacionados a Task, dentro do mesmo formulrio.
Nota: Neste artigo, livremente assumido que voc est usando o Doctrine para armazenar em seu banco de dados.
Mas se voc no est usando o Doctrine (por exemplo, Propel ou apenas uma conexo de banco de dados), tudo
muito semelhante. H apenas algumas partes deste tutorial que realmente se preocupam com persistncia.
Se voc est usando o Doctrine, voc vai precisar adicionar os metadados do Doctrine, incluindo a definio de
mapeamento da associao ManyToMany na propriedade tags da Task.
Vamos comear: suponha que cada Task pertence a vrios objetos Tags. Comece criando uma classe simples Task:
// src/Acme/TaskBundle/Entity/Task.php
namespace Acme\TaskBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
class Task
{
protected $description;

3.1. Cookbook

311

Symfony Docs pt-BR Documentation, Verso 2.4

protected $tags;
public function __construct()
{
$this->tags = new ArrayCollection();
}
public function getDescription()
{
return $this->description;
}
public function setDescription($description)
{
$this->description = $description;
}
public function getTags()
{
return $this->tags;
}
public function setTags(ArrayCollection $tags)
{
$this->tags = $tags;
}
}

Nota: O ArrayCollection especfico do Doctrine e basicamente o mesmo que usar um array (mas deve
ser um ArrayCollection se voc est usando o Doctrine).
Agora, crie uma classe Tag. Como voc viu acima, uma Task pode ter muitos objetos Tag:
// src/Acme/TaskBundle/Entity/Tag.php
namespace Acme\TaskBundle\Entity;
class Tag
{
public $name;
}

Dica: A propriedade name pblica aqui, mas ela pode facilmente ser protegida ou privada (ento seriam necessrios
os mtodos getName e setName).
Agora, vamos para os formulrios. Crie uma classe de formulrio para que um objeto Tag possa ser modificado pelo
usurio:
// src/Acme/TaskBundle/Form/Type/TagType.php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TagType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)

312

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

{
$builder->add(name);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
data_class => Acme\TaskBundle\Entity\Tag,
));
}
public function getName()
{
return tag;
}
}

Com isso, voc tem o suficiente para renderizar um formulrio tag. Mas, uma vez que o objetivo final permitir que
as tags de uma Task sejam modificadas dentro do prprio formulrio da task, crie um formulrio para a classe Task.
Observe que voc embutiu uma coleo de formulrios TagType usando o tipo de campo collection:
// src/Acme/TaskBundle/Form/Type/TaskType.php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(description);
$builder->add(tags, collection, array(type => new TagType()));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
data_class => Acme\TaskBundle\Entity\Task,
));
}
public function getName()
{
return task;
}
}

Em seu controlador, voc ir agora inicializar uma nova instncia do TaskType:


// src/Acme/TaskBundle/Controller/TaskController.php
namespace Acme\TaskBundle\Controller;
use Acme\TaskBundle\Entity\Task;
use Acme\TaskBundle\Entity\Tag;
use Acme\TaskBundle\Form\Type\TaskType;

3.1. Cookbook

313

Symfony Docs pt-BR Documentation, Verso 2.4

use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class TaskController extends Controller
{
public function newAction(Request $request)
{
$task = new Task();
// dummy code - this is here just so that the Task has some tags
// otherwise, this isnt an interesting example
$tag1 = new Tag();
$tag1->name = tag1;
$task->getTags()->add($tag1);
$tag2 = new Tag();
$tag2->name = tag2;
$task->getTags()->add($tag2);
// end dummy code
$form = $this->createForm(new TaskType(), $task);
// process the form on POST
if ($request->isMethod(POST)) {
$form->bind($request);
if ($form->isValid()) {
// ... maybe do some form processing, like saving the Task and Tag objects
}
}
return $this->render(AcmeTaskBundle:Task:new.html.twig, array(
form => $form->createView(),
));
}
}

O template correspondente agora capaz de renderizar tanto o campo description para o formulrio da task,
quanto todos os formulrios TagType para quaisquer tags que j esto relacionadas com esta Task. No controlador
acima, foi adicionado algum cdigo fictcio para que voc possa ver isso em ao (uma vez que uma Task no tem
nenhuma tag quando ela criada pela primeira vez).
Twig
{# src/Acme/TaskBundle/Resources/views/Task/new.html.twig #}
{# ... #}
<form action="..." method="POST" {{ form_enctype(form) }}>
{# render the tasks only field: description #}
{{ form_row(form.description) }}
<h3>Tags</h3>
<ul class="tags">
{# iterate over each existing tag and render its only field: name #}
{% for tag in form.tags %}
<li>{{ form_row(tag.name) }}</li>
{% endfor %}
</ul>
{{ form_rest(form) }}

314

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

{# ... #}
</form>

PHP
<!-- src/Acme/TaskBundle/Resources/views/Task/new.html.php -->
<!-- ... -->
<form action="..." method="POST" ...>
<h3>Tags</h3>
<ul class="tags">
<?php foreach($form[tags] as $tag): ?>
<li><?php echo $view[form]->row($tag[name]) ?></li>
<?php endforeach; ?>
</ul>
<?php echo $view[form]->rest($form) ?>
</form>
<!-- ... -->

Quando o usurio submeter o formulrio, os dados submetidos para os campos Tags so usados para construir um
ArrayCollection de objetos Tag, o qual ento definido no campo tag da instncia Task.
A coleo Tags acessvel naturalmente via $task->getTags() e pode ser persistida no banco de dados ou
utilizada da forma que voc precisar.
At agora, isso funciona muito bem, mas no permite que voc adicione dinamicamente novas tags ou exclua as
tags existentes. Ento, enquanto a edio de tags existentes ir funcionar perfeitamente, o usurio no pode, ainda,
adicionar quaisquer tags novas.
Cuidado:
Neste artigo, voc embutiu apenas uma coleo, mas voc no est limitado a apenas isto.
Voc tambm pode incorporar coleo aninhada com a quantidade de nveis abaixo que desejar. Mas, se
voc usar o Xdebug em sua configurao de desenvolvimento, voc pode receber erro Maximum function
nesting level of 100 reached, aborting!.
Isto ocorre devido a configurao do PHP
xdebug.max_nesting_level, que tem como padro 100.
Esta diretiva limita recurso para 100 chamadas, o que pode no ser o suficiente para renderizar o formulrio
no template se voc renderizar todo o formulrio de uma vez (por exemplo, usando form_widget(form)).
Para corrigir isso, voc pode definir esta diretiva para um valor maior (atravs do arquivo ini do PHP ou via
ini_set, por exemplo em app/autoload.php) ou renderizar cada campo do formulrio manualmente
usando form_row.

Permitindo novas tags com o prototype

Permitir ao usurio adicionar dinamicamente novas tags significa que voc vai precisar usar algum JavaScript. Anteriormente, voc adicionou duas tags ao seu formulrio no controlador. Agora, para permitir ao usurio adicionar a
quantidade de formulrios tag que precisar diretamente no navegador, vamos utilizar um pouco de JavaScript.
A primeira coisa que voc precisa fazer tornar a coleo de formulrio ciente de que ela vai receber um nmero
desconhecido de tags. At agora, voc adicionou duas tags e o tipo formulrio espera receber exatamente duas, caso
contrrio, um erro ser lanado: Este formulrio no deve conter campos extras. Para tornar isto
flexvel, adicione a opo allow_add no seu campo de coleo:
// src/Acme/TaskBundle/Form/Type/TaskType.php

3.1. Cookbook

315

Symfony Docs pt-BR Documentation, Verso 2.4

// ...
use Symfony\Component\Form\FormBuilderInterface;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(description);
$builder->add(tags,
type
=>
allow_add
=>
by_reference =>
));

collection, array(
new TagType(),
true,
false,

Note que by_reference => false tambm foi adicionado. Normalmente, o framework de formulrio ir
modificar as tags em um objeto Task sem realmente nunca chamar setTags. Definindo by_reference para false, o
setTags ser chamado. Voc ver que isto ser importante mais tarde.
Alm de dizer ao campo para aceitar qualquer nmero de objetos submetidos, o allow_add tambm disponibiliza
para voc uma varivel prototype. Este prototype um template que contm todo o HTML para poder renderizar
quaisquer formulrios tag novos. Para renderiz-lo, faa a seguinte alterao no seu template:
Twig
<ul class="tags" data-prototype="{{ form_widget(form.tags.vars.prototype)|e }}">
...
</ul>

PHP

<ul class="tags" data-prototype="<?php echo $view->escape($view[form]->row($form[tags]->getV


...
</ul>

Nota: Se voc renderizar todo o seu sub-formulrio tags de uma vez (por exemplo form_row(form.tags)),
ento o prototype est automaticamente disponvel na div externa, no atributo data-prototype, semelhante ao
que voc v acima.
Dica: O form.tags.vars.prototype um elemento de formulrio com o aspecto semelhante aos elementos
individuais form_widget(tag) dentro do seu lao for. Isso significa que voc pode chamar form_widget,
form_row ou form_label nele. Voc pode at mesmo optar por renderizar apenas um de seus campos (por
exemplo, o campo name):
{{ form_widget(form.tags.vars.prototype.name)|e }}

Na pgina renderizada, o resultado ser algo parecido com o seguinte:

<ul class="tags" data-prototype="&lt;div&gt;&lt;label class=&quot; required&quot;&gt;__name__&lt;/lab

O objetivo desta seo ser usar JavaScript para ler este atributo e dinamicamente adicionar novos formulrios tag
quando o usurio clicar no link Adicionar uma tag. Para tornar as coisas simples, este exemplo usa jQuery e assume
que voc o incluiu em algum lugar na sua pgina.
Adicione uma tag script em algum lugar na sua pgina para que voc possa comear a escrever um pouco de
JavaScript.

316

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Primeiro, adicione um link no final da lista tags via JavaScript. Segundo, faa o bind do evento click desse link
para que voc possa adicionar um novo formulrio de tag (addTagForm ser exibido em seguida):
// Get the ul that holds the collection of tags
var collectionHolder = $(ul.tags);
// setup an "add a tag" link
var $addTagLink = $(<a href="#" class="add_tag_link">Add a tag</a>);
var $newLinkLi = $(<li></li>).append($addTagLink);
jQuery(document).ready(function() {
// add the "add a tag" anchor and li to the tags ul
collectionHolder.append($newLinkLi);
// count the current form inputs we have (e.g. 2), use that as the new
// index when inserting a new item (e.g. 2)
collectionHolder.data(index, collectionHolder.find(:input).length);
$addTagLink.on(click, function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// add a new tag form (see next code block)
addTagForm(collectionHolder, $newLinkLi);
});
});

O trabalho da funo addTagForm ser usar o atributo data-prototype para adicionar dinamicamente um
novo formulrio quando clicado neste link. O HTML data-prototype contm o elemento de entrada text
com um nome de task[tags][__name__][name] e com o id task_tags___name___name. O nome
__name__ um pequeno placeholder, que voc vai substituir por um nmero nico, incrementado (por exemplo: task[tags][3][name]).
Novo na verso 2.1: O placeholder foi alterado de $$name$$ para __name__ no Symfony 2.1
O cdigo real necessrio para fazer todo este trabalho pode variar um pouco, mas aqui est um exemplo:
function addTagForm(collectionHolder, $newLinkLi) {
// Get the data-prototype explained earlier
var prototype = collectionHolder.data(prototype);
// get the new index
var index = collectionHolder.data(index);
// Replace __name__ in the prototypes HTML to
// instead be a number based on the current collections length.
var newForm = prototype.replace(/__name__/g, collectionHolder.children().length);
// increase the index with one for the next item
collectionHolder.data(index, index + 1);
// Display the form in the page in an li, before the "Add a tag" link li
var $newFormLi = $(<li></li>).append(newForm);
$newLinkLi.before($newFormLi);
}

Nota: melhor separar o seu javascript em arquivos JavaScript do que escrev-lo dentro do HTML como foi feito
aqui.

3.1. Cookbook

317

Symfony Docs pt-BR Documentation, Verso 2.4

Agora, cada vez que um usurio clicar no link Adicionar uma tag, um novo sub-formulrio vai aparecer na
pgina. Quando o formulrio submetido, todos os novos formulrios de tag sero convertidos em novos objetos Tag
e adicionados propriedade tags do objeto Task.

318

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Doctrine: Relaes em Cascata e salvando o lado Inverso


Para obter as novas tags para salvar no Doctrine, preciso considerar algumas coisas a mais. Em primeiro lugar,
a menos que voc iterar sobre todos os novos objetos Tag e chamar $em->persist($tag) em cada um,
voc receber um erro do Doctrine:
Uma nova entidade foi encontrada atravs da relao AcmeTaskBundleEntityTask#tags que no foi
configurada para operaes de persistir em cascata para a entidade...
Para corrigir isso, voc pode optar pela operao de persistir em cascata automaticamente a partir do objeto Task para todas as tags relacionadas. Para fazer isso, adicione a opo cascade em seu metadado
ManyToMany:
Annotations
// src/Acme/TaskBundle/Entity/Task.php
// ...
/**
* @ORM\ManyToMany(targetEntity="Tag", cascade={"persist"})
*/
protected $tags;

YAML
# src/Acme/TaskBundle/Resources/config/doctrine/Task.orm.yml
Acme\TaskBundle\Entity\Task:
type: entity
# ...
oneToMany:
tags:
targetEntity: Tag
cascade:
[persist]

XML
<!-- src/Acme/TaskBundle/Resources/config/doctrine/Task.orm.xml -->
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Acme\TaskBundle\Entity\Task" ...>
<!-- ... -->
<one-to-many field="tags" target-entity="Tag">
<cascade>
<cascade-persist />
</cascade>
</one-to-many>
</entity>
</doctrine-mapping>

Um segundo problema potencial aborda o Lado Proprietrio e Lado Inverso dos relacionamentos do Doctrine.
Neste exemplo, se o lado proprietrio da relao Task, ento a persistncia ir funcionar bem pois as tags
so devidamente adicionadas Task. No entanto, se o lado proprietrio a Tag, ento voc vai ter um pouco
mais de trabalho para garantir que o lado correto da relao ser modificado.
O truque ter certeza de que uma nica Task definida em cada Tag. Uma maneira fcil de fazer isso
adicionar alguma lgica extra ao setTags(), que chamada pelo framework de formulrio desde que
by_reference esteja definido como false:
// src/Acme/TaskBundle/Entity/Task.php
// ...
public function setTags(ArrayCollection $tags)

3.1.
{ Cookbook

foreach ($tags as $tag) {


$tag->addTask($this);
}

319

Symfony Docs pt-BR Documentation, Verso 2.4

Permitindo que as tags sejam removidas

O passo seguinte permitir a remoo de um item em particular na coleo. A soluo similar a que permite que as
tags sejam adicionadas.
Comece adicionando a opo allow_delete no tipo do formulrio:
// src/Acme/TaskBundle/Form/Type/TaskType.php
// ...
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add(description);
$builder->add(tags,
type
=>
allow_add
=>
allow_delete =>
by_reference =>
));

collection, array(
new TagType(),
true,
true,
false,

Modificaes nos Templates A opo allow_delete tem uma consequncia: se um item de uma coleo no
for enviado na submisso, o dado relacionado removido da coleo no servidor. A soluo , portanto, remover o
elemento de formulrio do DOM.
Primeiro, adicione um link excluir esta tag para cada formulrio de tag:
jQuery(document).ready(function() {
// add a delete link to all of the existing tag form li elements
collectionHolder.find(li).each(function() {
addTagFormDeleteLink($(this));
});
// ... the rest of the block from above
});
function addTagForm() {
// ...
// add a delete link to the new form
addTagFormDeleteLink($newFormLi);
}

A funo addTagFormDeleteLink ser parecida com a seguinte:


function addTagFormDeleteLink($tagFormLi) {
var $removeFormA = $(<a href="#">delete this tag</a>);
$tagFormLi.append($removeFormA);
$removeFormA.on(click, function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// remove the li for the tag form
$tagFormLi.remove();

320

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

});
}

Quando um formulrio de tag removido do DOM e submetido, o objeto Tag removido no ser includo na coleo
passada ao setTags. Dependendo de sua camada de persistncia, isto pode ou no ser o suficiente para remover
efetivamente a relao entre os objetos Tag e Task.

3.1. Cookbook

321

Symfony Docs pt-BR Documentation, Verso 2.4

Doctrine: Garantir a persistncia de dados


Ao remover objetos dessa forma, voc pode precisar fazer um pouco mais de trabalho para garantir que a relao
entre a Task e Tag seja removida adequadamente.
No Doctrine, voc tem dois lados da relao: o lado proprietrio e o lado inverso. Normalmente, neste caso,
voc vai ter uma relao ManyToMany e as tags excludas desaparecero e ser persistido corretamente (a adio
de novas tags tambm funciona sem esforo).
Mas, se voc tem uma relao OneToMany ou uma ManyToMany com um mappedBy na entidade Task
(significando que Task o lado inverso), voc vai ter mais trabalho para que as tags removidas persistam
corretamente.
Neste caso, voc pode modificar o controlador para remover a relao na tag removida. Isso pressupe que voc
tenha algum editAction que est lidando com a atualizao da sua Task:
// src/Acme/TaskBundle/Controller/TaskController.php
// ...
public function editAction($id, Request $request)
{
$em = $this->getDoctrine()->getManager();
$task = $em->getRepository(AcmeTaskBundle:Task)->find($id);
if (!$task) {
throw $this->createNotFoundException(No task found for is .$id);
}
$originalTags = array();
// Create an array of the current Tag objects in the database
foreach ($task->getTags() as $tag) $originalTags[] = $tag;
$editForm = $this->createForm(new TaskType(), $task);
if ($request->isMethod(POST)) {
$editForm->bind($this->getRequest());
if ($editForm->isValid()) {
// filter $originalTags to contain tags no longer present
foreach ($task->getTags() as $tag) {
foreach ($originalTags as $key => $toDel) {
if ($toDel->getId() === $tag->getId()) {
unset($originalTags[$key]);
}
}
}
// remove the relationship between the tag and the Task
foreach ($originalTags as $tag) {
// remove the Task from the Tag
$tag->getTasks()->removeElement($task);
// if it were a ManyToOne relationship, remove the relationship like this
// $tag->setTask(null);
$em->persist($tag);
// if you wanted to delete the Tag entirely, you can also do that
// $em->remove($tag);
}

322

$em->persist($task);
$em->flush();
// redirect back to some edit page

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Como Criar um Tipo de Campo de Formulrio Personalizado


O Symfony vem com vrios tipos de campo disponveis para a construo de formulrios. No entanto, existem
situaes em que voc pode desejar criar um tipo de campo de formulrio personalizado para um propsito especfico.
Esta receita assume que voc precisa de uma definio de campo que possui o gnero de uma pessoa, com base no
campo choice existente. Esta seo explica como o campo definido, como voc pode personalizar o seu layout e,
finalmente, como registr-lo para uso em sua aplicao.
Definindo o Tipo de Campo

A fim de criar o tipo de campo personalizado, primeiro voc precisa criar a classe que o representa. Nesta situao, a
classe que contm o tipo de campo ser chamada GenderType e o arquivo ser armazenado no local padro para campos
de formulrio, que <BundleName>\Form\Type. Verifique se o campo estende a classe AbstractType:
// src/Acme/DemoBundle/Form/Type/GenderType.php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class GenderType extends AbstractType
{
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
choices => array(
m => Male,
f => Female,
)
));
}
public function getParent()
{
return choice;
}
public function getName()
{
return gender;
}
}

Dica: A localizao deste arquivo no importante - o diretrio Form\Type apenas uma conveno.
Aqui, o valor de retorno da funo getParent indica que voc est estendendo o tipo de campo choice. Isto
significa que, por padro, voc herda toda a lgica e renderizao deste tipo de campo. Para ver um pouco da lgica,
confira a classe ChoiceType. Existem trs mtodos que so particularmente importantes:
buildForm() - Cada tipo de campo possui um mtodo buildForm, que onde voc configura e constri
qualquer campo(s). Note que este o mesmo mtodo que voc usa para configurar seus formulrios, e ele
funciona da mesma forma aqui.
buildView() - Este mtodo usado para definir quaisquer variveis extras que voc precisa ao renderizar
o seu campo em um template. Por exemplo, no ChoiceType, uma varivel multiple definida e utilizada

3.1. Cookbook

323

Symfony Docs pt-BR Documentation, Verso 2.4

no template para setar (ou no) o atributo multiple no campo select. Veja Criando um template para o
Campo para mais detalhes.
setDefaultOptions() - Define opes para o seu tipo do formulrio, que podem ser usadas no buildForm() e no buildView(). H vrias opes comuns a todos os campos (veja
/reference/forms/types/form), mas voc pode criar quaisquer outras que voc precisar aqui.
Dica: Se voc est criando um campo que consiste de muitos campos, ento no se esquea de definir o seu tipo pai
como form ou algo que estenda form. Alm disso, se voc precisar modificar a viso de qualquer um dos tipos
filho a partir de seu tipo pai, use o mtodo finishView().
O mtodo getName() retorna um identificador que deve ser nico na sua aplicao. Isto usado em vrios lugares,
como ao personalizar a forma que o seu tipo de formulrio ser renderizado.
O objetivo deste campo foi estender o tipo choice para ativar a seleo de um gnero. Isto alcanado atravs do
ajuste das choices para uma lista de gneros possveis.
Criando um Template para o Campo

Cada tipo de campo renderizado por um fragmento de template, o qual determinado, em parte, pelo valor do seu
mtodo getName(). Para maiores informaes, visite O que so Temas de Formulrio?.
Neste caso, uma vez que o campo pai choice, voc no precisa fazer qualquer trabalho pois o tipo de campo
personalizado ser automaticamente renderizado como um tipo choice. Mas, para o propsito deste exemplo, vamos
supor que, quando o seu campo expandido (ou seja, botes de opo ou caixas de seleo em vez de um campo de
seleo), voc quer sempre renderiz-lo em um elemento ul. Em seu template tema de formulrio (veja o link acima
para mais detalhes), crie um bloco gender_widget para lidar com isso:
{# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #}
{% block gender_widget %}
{% spaceless %}
{% if expanded %}
<ul {{ block(widget_container_attributes) }}>
{% for child in form %}
<li>
{{ form_widget(child) }}
{{ form_label(child) }}
</li>
{% endfor %}
</ul>
{% else %}
{# just let the choice widget render the select tag #}
{{ block(choice_widget) }}
{% endif %}
{% endspaceless %}
{% endblock %}

Nota: Certifique-se que usado o prefixo widget correto. Neste exemplo, o nome deve ser gender_widget, de
acordo com o valor retornado pelo getName. Alm disso, o arquivo de configurao principal deve apontar para o
template de formulrio personalizado, assim, ele ser usado ao renderizar todos os formulrios.
# app/config/config.yml
twig:
form:
resources:
- AcmeDemoBundle:Form:fields.html.twig

324

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Usando o Tipo de Campo

Agora voc pode usar o seu tipo de campo personalizado imediatamente, simplesmente criando uma nova instncia do
tipo em um de seus formulrios:
// src/Acme/DemoBundle/Form/Type/AuthorType.php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class AuthorType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(gender_code, new GenderType(), array(
empty_value => Choose a gender,
));
}
}

Mas isso s funciona porque o GenderType() muito simples. E se os cdigos do gnero foram armazenados em
configurao ou num banco de dados? A prxima seo explica como os tipos de campos mais complexos resolvem
este problema.
Criando o seu Tipo de Campo como um Servio

At agora, este artigo assumiu que voc tem um tipo de campo personalizado bem simples. Mas se voc precisar
acessar a configurao, uma conexo de banco de dados ou algum outro servio, ento, voc vai querer registrar o seu
tipo personalizado como um servio. Por exemplo, suponha que voc est armazenando os parmetros de gnero em
configurao:
YAML
# app/config/config.yml
parameters:
genders:
m: Male
f: Female

XML
<!-- app/config/config.xml -->
<parameters>
<parameter key="genders" type="collection">
<parameter key="m">Male</parameter>
<parameter key="f">Female</parameter>
</parameter>
</parameters>

Para usar o parmetro, defina o seu tipo de campo personalizado como um servio, injetando o valor do parmetro
genders como o primeiro argumento para a sua funo recm-criada __construct:
YAML
# src/Acme/DemoBundle/Resources/config/services.yml
services:
acme_demo.form.type.gender:

3.1. Cookbook

325

Symfony Docs pt-BR Documentation, Verso 2.4

class: Acme\DemoBundle\Form\Type\GenderType
arguments:
- "%genders%"
tags:
- { name: form.type, alias: gender }

XML
<!-- src/Acme/DemoBundle/Resources/config/services.xml -->
<service id="acme_demo.form.type.gender" class="Acme\DemoBundle\Form\Type\GenderType">
<argument>%genders%</argument>
<tag name="form.type" alias="gender" />
</service>

Dica: Certifique-se que o arquivo de servios est sendo importado. Para mais detalhes consulte Importando configurao com imports.
Certifique-se tambm que o atributo alias da tag corresponde ao valor retornado pelo mtodo getName definido
anteriormente. Voc vai ver a importncia disto logo que usar o tipo de campo personalizado. Mas, primeiro, adicione
um mtodo __construct para o GenderType, o qual recebe a configurao do gnero:
// src/Acme/DemoBundle/Form/Type/GenderType.php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
// ...
class GenderType extends AbstractType
{
private $genderChoices;
public function __construct(array $genderChoices)
{
$this->genderChoices = $genderChoices;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
choices => $this->genderChoices,
));
}
// ...
}

timo! O GenderType alimentado agora por parmetros de configurao e registrado como um servio. Alm
disso, devido a voc ter usado o alias form.type na sua configurao, a utilizao do campo muito mais fcil
agora:
// src/Acme/DemoBundle/Form/Type/AuthorType.php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
// ...

326

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

class AuthorType extends AbstractType


{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(gender_code, gender, array(
empty_value => Choose a gender,
));
}
}

Observe que em vez de criar uma nova instncia, voc pode apenas referir-se ela pelo alias usado na sua configurao
do servio, gender. Divirta-se!
Como criar uma Extenso do Tipo de Formulrio
Tipos de campo de formulrio personalizados so timos quando voc precisa de tipos de campo para um propsito
especfico, por exemplo para selecionar o gnero ou uma entrada de nmero de IVA.
Mas, s vezes, voc realmente no precisa adicionar novos tipos de campo - voc quer adicionar funcionalidades aos
tipos existentes. onde o tipo de formulrio usado.
As extenses do tipo de formulrio tm dois casos de uso principais:
1. Voc quer adicionar uma funcionalidade genrica para vrios tipos (como a adio de um texto de ajuda
para cada tipo de campo);
2. Voc quer adicionar uma funcionalidade especfica para um nico tipo (tal como a adio de uma funcionalidade download para o tipo de campo file).
Em ambos os casos, possvel alcanar o seu objetivo com a renderizao personalizada de formulrio. Mas, usar
extenses do tipo de formulrio pode ser mais limpo (limitando a quantidade de lgica de negcios nos templates) e
mais flexvel (voc pode adicionar vrias extenses do tipo a um nico tipo de formulrio.
Extenses do tipo de formulrio podem alcanar mais do que os tipos de campos personalizados podem fazer, mas,
em vez de serem tipos de campos prprios, elas conectam em tipos existentes.
Imagine que voc gerencia uma entidade Media, e que cada mdia est associada a um arquivo. Seu formulrio
Media usa um tipo file, mas, ao editar a entidade, voc gostaria de ver sua imagem processada automaticamente ao
lado do campo arquivo.
Voc poderia, naturalmente, fazer isso personalizando a forma como este campo renderizado em um template. Mas
as extenses do tipo de formulrio permitem que voc faa isso de uma forma DRY agradvel.
Definindo a Extenso do Tipo de Formulrio

Sua primeira tarefa ser criar a classe da extenso do tipo de formulrio. Vamos cham-la ImageTypeExtension.
Por padro, as extenses de formulrio geralmente residem no diretrio Form\Extension de um de seus bundles.
Ao criar uma extenso de tipo de formulrio,
voc pode implementar a interface
FormTypeExtensionInterface ou estender a classe AbstractTypeExtension. Na maioria dos
casos, mais fcil estender a classe abstrata:
// src/Acme/DemoBundle/Form/Extension/ImageTypeExtension.php
namespace Acme\DemoBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
class ImageTypeExtension extends AbstractTypeExtension

3.1. Cookbook

327

Symfony Docs pt-BR Documentation, Verso 2.4

{
/**
* Returns the name of the type being extended.
*
* @return string The name of the type being extended
*/
public function getExtendedType()
{
return file;
}
}

O nico mtodo que voc deve implementar o getExtendedType. Ele usado para indicar o nome do tipo de
formulrio que ser estendido pela sua extenso.
Dica: O valor que voc retorna no mtodo getExtendedType corresponde ao valor retornado pelo mtodo
getName na classe do tipo de formulrio que voc deseja estender.
Alm da funogetExtendedType, provavelmente voc vai querer sobrescrever um dos seguintes mtodos:
buildForm()
buildView()
setDefaultOptions()
finishView()
Para mais informaes sobre o que esses mtodos fazem, voc pode consultar o artigo do cookbook Criando Tipos de
Campo Personalizados.
Registrando a sua Extenso do Tipo de Formulrio como um Servio

O prximo passo tornar o Symfony ciente da sua extenso. Tudo o que voc precisa fazer declar-la como um servio
usando a tag form.type_extension.
YAML
services:
acme_demo_bundle.image_type_extension:
class: Acme\DemoBundle\Form\Extension\ImageTypeExtension
tags:
- { name: form.type_extension, alias: file }

XML
<service id="acme_demo_bundle.image_type_extension"
class="Acme\DemoBundle\Form\Extension\ImageTypeExtension"
>
<tag name="form.type_extension" alias="file" />
</service>

PHP
$container
->register(
acme_demo_bundle.image_type_extension,
Acme\DemoBundle\Form\Extension\ImageTypeExtension

328

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

)
->addTag(form.type_extension, array(alias => file));

A chave alias da tag o tipo de campo que essa extenso deve ser aplicada. No seu caso, como voc deseja estender
o tipo de file, voc vai usar o file como um alias.
Adicionando a lgica de negcio da extenso

O objetivo da sua extenso exibir imagens agradveis ao lado de campos arquivo (quando o modelo subjacente
contm imagens). Para esta finalidade, vamos supor que voc usa uma abordagem semelhante descrita em Como
manusear o upload de arquivos com o Doctrine: voc tem um modelo Media com uma propriedade file (correspondente ao campo de arquivo, no formulrio) e uma propriedade path (correspondendo ao caminho da imagem, no banco
de dados):
// src/Acme/DemoBundle/Entity/Media.php
namespace Acme\DemoBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
class Media
{
// ...
/**
* @var string The path - typically stored in the database
*/
private $path;
/**
* @var \Symfony\Component\HttpFoundation\File\UploadedFile
* @Assert\File(maxSize="2M")
*/
public $file;
// ...
/**
* Get the image url
*
* @return null|string
*/
public function getWebPath()
{
// ... $webPath being the full image url, to be used in templates
return $webPath;
}
}

Sua classe da extenso do tipo de formulrio ter que fazer duas coisas, a fim de estender o tipo de formulrio file:
1. Sobrescrever o mtodo setDefaultOptions, a fim de adicionar uma opo image_path;
2. Sobrescrever os mtodos buildForm e buildView a fim de passar a url da imagem para a viso.
A lgica a seguinte: quando adicionar um campo de formulrio do tipo file, voc poder especificar uma nova
opo: image_path . Esta opo ir dizer ao campo arquivo como obter o endereo real da imagem, a fim de
exib-la na viso:
3.1. Cookbook

329

Symfony Docs pt-BR Documentation, Verso 2.4

// src/Acme/DemoBundle/Form/Extension/ImageTypeExtension.php
namespace Acme\DemoBundle\Form\Extension;
use
use
use
use
use

Symfony\Component\Form\AbstractTypeExtension;
Symfony\Component\Form\FormView;
Symfony\Component\Form\FormInterface;
Symfony\Component\Form\Util\PropertyPath;
Symfony\Component\OptionsResolver\OptionsResolverInterface;

class ImageTypeExtension extends AbstractTypeExtension


{
/**
* Returns the name of the type being extended.
*
* @return string The name of the type being extended
*/
public function getExtendedType()
{
return file;
}
/**
* Add the image_path option
*
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setOptional(array(image_path));
}
/**
* Pass the image url to the view
*
* @param FormView $view
* @param FormInterface $form
* @param array $options
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
if (array_key_exists(image_path, $options)) {
$parentData = $form->getParent()->getData();
if (null !== $parentData) {
$propertyPath = new PropertyPath($options[image_path]);
$imageUrl = $propertyPath->getValue($parentData);
} else {
$imageUrl = null;
}
// set an "image_url" variable that will be available when rendering this field
$view->set(image_url, $imageUrl);
}
}
}

330

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Sobrescrevendo o Fragmento de Template do Widget File

Cada tipo de campo renderizado por um fragmento de template. Esses fragmentos de template podem ser sobrescritos, para personalizar a renderizao formulrio. Para mais informaes voc pode consultar o artigo O que so Temas
de Formulrio?.
Em sua classe de extenso, voc adicionou uma nova varivel (image_url), mas voc ainda precisa aproveitar esta
nova varivel em seus templates. Especificamente, voc precisa sobrescrever o bloco file_widget:
Twig
{# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #}
{% extends form_div_layout.html.twig %}
{% block file_widget %}
{% spaceless %}
{{ block(form_widget) }}
{% if image_url is not null %}
<img src="{{ asset(image_url) }}"/>
{% endif %}
{% endspaceless %}
{% endblock %}

PHP
<!-- src/Acme/DemoBundle/Resources/views/Form/file_widget.html.php -->
<?php echo $view[form]->widget($form) ?>
<?php if (null !== $image_url): ?>
<img src="<?php echo $view[assets]->getUrl($image_url) ?>"/>
<?php endif ?>

Nota: Voc precisar mudar o seu arquivo de configurao ou especificar explicitamente como voc quer o tema do
seu formulrio, para que o Symfony utilize o seu bloco sobrecrito. Veja O que so Temas de Formulrio? para mais
informaes.

Usando a Extenso do Tipo de Formulrio

A partir de agora, ao adicionar um campo do tipo file no seu formulrio, voc pode especificar uma opo
image_path que ser usada para exibir uma imagem prxima ao campo arquivo. Por exemplo:
// src/Acme/DemoBundle/Form/Type/MediaType.php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class MediaType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(name, text)
->add(file, file, array(image_path => webPath));
}

3.1. Cookbook

331

Symfony Docs pt-BR Documentation, Verso 2.4

public function getName()


{
return media;
}
}

Ao exibir o formulrio, se o modelo subjacente j foi associado com uma imagem, voc vai v-la ao lado do campo
arquivo.
Como usar a opo de campo de formulrio Virtual
A opo de campo de formulrio virtual pode ser muito til quando voc possui alguns campos duplicados em
entidades diferentes.
Por exemplo, imagine que voc tem duas entidades:Company e Customer:
// src/Acme/HelloBundle/Entity/Company.php
namespace Acme\HelloBundle\Entity;
class Company
{
private $name;
private $website;
private
private
private
private

$address;
$zipcode;
$city;
$country;

}
// src/Acme/HelloBundle/Entity/Customer.php
namespace Acme\HelloBundle\Entity;
class Customer
{
private $firstName;
private $lastName;
private
private
private
private

$address;
$zipcode;
$city;
$country;

Como pode-se ver, as entidades possuem alguns campos iguais: address, zipcode, city e country.
Agora, voc deseja construir dois formulrios: um para Company e outro para Customer.
Comece criando classes simples de tipo de formulrio para CompanyType e CustomerType:
// src/Acme/HelloBundle/Form/Type/CompanyType.php
namespace Acme\HelloBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
class CompanyType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{

332

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

$builder
->add(name, text)
->add(website, text);
}
}
// src/Acme/HelloBundle/Form/Type/CustomerType.php
namespace Acme\HelloBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
class CustomerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(firstName, text)
->add(lastName, text);
}
}

Agora, temos que lidar com os quatro campos duplicados. Aqui est um formulrio (simples) para localidade
(Location):
// src/Acme/HelloBundle/Form/Type/LocationType.php
namespace Acme\HelloBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LocationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(address, textarea)
->add(zipcode, text)
->add(city, text)
->add(country, text);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
virtual => true
));
}
public function getName()
{
return location;
}
}

Ns no temos realmente um campo de localidade em cada uma das nossas entidades, de modo que no podemos ligar
diretamente LocationType ao nosso CompanyType ou CustomerType. Mas, com certeza, queremos um tipo
de formulrio prprio para lidar com a localidade (lembre-se, DRY!).
A opo de campo de formulrio virtual a soluo.
3.1. Cookbook

333

Symfony Docs pt-BR Documentation, Verso 2.4

Podemos definir a opo virtual => true no mtodo setDefaultOptions() da LocationType e


comear a us-lo diretamente nos dois tipos de formulrios originais.
Verifique o resultado:
// CompanyType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(foo, new LocationType(), array(
data_class => Acme\HelloBundle\Entity\Company
));
}
// CustomerType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(bar, new LocationType(), array(
data_class => Acme\HelloBundle\Entity\Customer
));
}

Com a opo virtual definida para false (comportamento padro) , o Componente de Formulrio espera que cada
objeto subjacente tenha uma propriedade foo (ou bar) que algum objeto ou array que contm os quatro campos da
localidade. Claro, no temos este objeto/array em nossas entidades e ns no queremos isso!
Com a opo virtual definida como true, o componente de Formulrio ignora a propriedade foo (ou bar), e, em vez
disso, aplica gets e sets aos quatro campos de localidade diretamente no objeto subjacente!
Nota: Ao invs de definir a opo virtual dentro de LocationType, voc pode (assim como com todas as
outras opes) tambm pass-la como uma opo de array no terceiro argumento de $builder->add().

Como configurar Dados Vazios para uma Classe de Formulrio


A opo empty_data permite que voc especifique um conjunto de dados vazios para a sua classe de formulrio.
Este conjunto de dados vazios ser usado se voc fez o bind do seu formulrio, mas no chamou o setData() nele
ou no passou dados quando criou ele. Por exemplo:
public function indexAction()
{
$blog = // ...
// $blog is passed in as the data, so the empty_data option is not needed
$form = $this->createForm(new BlogType(), $blog);
// no data is passed in, so empty_data is used to get the "starting data"
$form = $this->createForm(new BlogType());
}

Por padro, o empty_data setado como null. Ou, se voc especificou a opo data_class para a sua classe
de formulrio, ele ser, por padro, uma nova instncia dessa classe. Essa instncia ser criada chamando o construtor
sem argumentos.
Se voc quiser sobrescrever esse comportamento padro, existem duas formas de fazer isso.

334

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Opo 1: Instanciar uma nova Classe

Uma razo para usar esta opo se voc quer usar um construtor que possui argumentos. Lembre-se, a opo
data_class padro chama o construtor sem argumentos:
// src/Acme/DemoBundle/Form/Type/BlogType.php
// ...
use Symfony\Component\Form\AbstractType;
use Acme\DemoBundle\Entity\Blog;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class BlogType extends AbstractType
{
private $someDependency;
public function __construct($someDependency)
{
$this->someDependency = $someDependency;
}
// ...
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
empty_data => new Blog($this->someDependency),
));
}
}

Voc pode instanciar sua classe como desejar. Neste exemplo, ns passamos algumas dependncias para o BlogType
ao instanci-lo, ento, use isso para instanciar o objeto Blog. O ponto , voc pode setar o empty_data para o
objeto novo que voc deseja usar.
Opo 2: Fornecer uma Closure

Usar uma closure o mtodo preferido, uma vez que ir criar o objeto apenas se for necessrio.
A closure deve aceitar uma instncia FormInterface como seu primeiro argumento:
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormInterface;
// ...
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
empty_data => function (FormInterface $form) {
return new Blog($form->get(title)->getData());
},
));
}

3.1. Cookbook

335

Symfony Docs pt-BR Documentation, Verso 2.4

3.1.7 Validao
Como criar uma Constraint de Validao Personalizada
Voc pode criar uma constraint personalizada estendendo uma classe base de constraint Constraint. Como exemplo vamos criar um validador simples que verifica se uma string contm apenas caracteres alfanumricos.
Criando a Classe Constraint
Primeiro voc precisa criar uma classe de Constraint e estender Constraint:
// src/Acme/DemoBundle/Validator/constraints/ContainsAlphanumeric.php
namespace Acme\DemoBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;

/**
* @Annotation
*/
class ContainsAlphanumeric extends Constraint
{
public $message = The string "%string%" contains an illegal character: it can only contain lette
}

Nota: A annotation @Annotation necessria para esta nova constraint para torn-la disponvel para uso em
classes atravs de annotations. Opes para a sua constraint so representadas como propriedades pblicas na classe
constraint.

Criando o Validador em si
Como voc pode ver, uma classe de constraint muito curta. A validao real realizada por uma outra classe validadora de constraint. A classe validadora de constraint especificada pelo mtodo de constraint validatedBy(),
que inclui alguma lgica padro simples:
// in the base Symfony\Component\Validator\Constraint class
public function validatedBy()
{
return get_class($this).Validator;
}

Em outras palavras, se voc criar uma Constraint personalizada (Ex. MyConstraint), o Symfony2 automaticamente ir procurar a outra classe MyConstraintValidator quando realmente executar a validao.
A classe validadora tambm simples, e s contm um mtodo necessrio: validate:
// src/Acme/DemoBundle/Validator/Constraints/ContainsAlphanumericValidator.php
namespace Acme\DemoBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class ContainsAlphanumericValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{

336

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

if (!preg_match(/^[a-zA-Za0-9]+$/, $value, $matches)) {


$this->context->addViolation($constraint->message, array(%string% => $value));
}
}
}

Nota: O mtodo validate no retorna um valor, em vez disso, ele acrescenta violaes propriedade context
do validador com uma chamada do mtodo addViolation se existem falhas de validao. Portanto, um valor pode
ser considerado como sendo vlido, desde que no cause violaes adicionadas ao contexto. O primeiro parmetro da
chamada addViolation a mensagem de erro para usar para aquela violao.
Novo na verso 2.1: O mtodo isValid foi renomeado para validate no Symfony 2.1. O mtodo setMessage
tambm ficou obsoleto, em favor da chamada addViolation do contexto.
Usando o novo Validador
Usar validadores personalizados muito fcil, assim como os fornecidos pelo Symfony2 em si:
YAML
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\DemoBundle\Entity\AcmeEntity:
properties:
name:
- NotBlank: ~
- Acme\DemoBundle\Validator\Constraints\ContainsAlphanumeric: ~

Annotations
// src/Acme/DemoBundle/Entity/AcmeEntity.php
use Symfony\Component\Validator\Constraints as Assert;
use Acme\DemoBundle\Validator\Constraints as AcmeAssert;
class AcmeEntity
{
// ...
/**
* @Assert\NotBlank
* @AcmeAssert\ContainsAlphanumeric
*/
protected $name;
// ...
}

XML

<!-- src/Acme/DemoBundle/Resources/config/validation.xml -->


<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/sche
<class name="Acme\DemoBundle\Entity\AcmeEntity">
<property name="name">
<constraint name="NotBlank" />

3.1. Cookbook

337

Symfony Docs pt-BR Documentation, Verso 2.4

<constraint name="Acme\DemoBundle\Validator\Constraints\ContainsAlphanumeric" />


</property>
</class>
</constraint-mapping>

PHP
// src/Acme/DemoBundle/Entity/AcmeEntity.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use Acme\DemoBundle\Validator\Constraints\ContainsAlphanumeric;
class AcmeEntity
{
public $name;
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint(name, new NotBlank());
$metadata->addPropertyConstraint(name, new ContainsAlphanumeric());
}
}

Se a sua constraint contm opes, ento elas devem ser propriedades pblicas na classe Constraint personalizada que
voc criou anteriormente. Essas opes podem ser configuradas como opes nas constraints do ncleo do Symfony.
Validadores de Constraints com Dependncias

Se o seu validador de restrio possui dependncias, como uma conexo de banco de dados, ela ter que
ser configurada como um servio no container de injeo de dependncia. Este servio deve incluir a tag
validator.constraint_validator e um atributo alias:
YAML
services:
validator.unique.your_validator_name:
class: Fully\Qualified\Validator\Class\Name
tags:
- { name: validator.constraint_validator, alias: alias_name }

XML

<service id="validator.unique.your_validator_name" class="Fully\Qualified\Validator\Class\Name">


<argument type="service" id="doctrine.orm.default_entity_manager" />
<tag name="validator.constraint_validator" alias="alias_name" />
</service>

PHP
$container
->register(validator.unique.your_validator_name, Fully\Qualified\Validator\Class\Name)
->addTag(validator.constraint_validator, array(alias => alias_name));

Sua classe de constraint pode agora usar este alias para referenciar o validador apropriado:
public function validatedBy()
{
return alias_name;
}

338

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Como mencionado acima, o Symfony2 ir procurar automaticamente por uma classe chamada aps a constraint, com
Validator acrescentado. Se o seu validador constraint est definido como um servio, importante que voc
sobrescreva o mtodo validatedBy() para retornar o alias utilizado na definio de seu servio, caso contrrio,
o Symfony2 no vai usar o servio do validador de constraint, e, em vez disso, ir instanciar a classe, sem quaisquer
dependncias injetadas.
Classe Constraint Validadora

Junto da validao de uma propriedade de classe, uma constraint pode ter um escopo de classe, fornecendo um alvo:
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}

Com isso, o mtodo validador validate() obtm um objeto como seu primeiro argumento:
class ProtocolClassValidator extends ConstraintValidator
{
public function validate($protocol, Constraint $constraint)
{
if ($protocol->getFoo() != $protocol->getBar()) {
$this->context->addViolationAtSubPath(foo, $constraint->message, array(), null);
}
}
}

Note que a classe constraint validadora aplicada na classe em si, e no propriedade:


YAML
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\DemoBundle\Entity\AcmeEntity:
constraints:
- ContainsAlphanumeric

Annotations
/**
* @AcmeAssert\ContainsAlphanumeric
*/
class AcmeEntity
{
// ...
}

XML
<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\DemoBundle\Entity\AcmeEntity">
<constraint name="ContainsAlphanumeric" />
</class>

3.1. Cookbook

339

Symfony Docs pt-BR Documentation, Verso 2.4

3.1.8 Configurao
Como Dominar e Criar novos Ambientes
Cada aplicao a combinao de cdigo e um conjunto de configuraes que dita como o cdigo deve funcionar. A
configurao pode: definir o banco de dados a ser utilizado, se algo deve ou no ser armazenado em cache, ou como
deve ser a verbosidade do log. No Symfony2, a idia de ambientes a idia de que a mesma base de cdigo pode ser
executada usando vrias configuraes diferentes. Por exemplo, o ambiente dev deve usar uma configurao que faa
com que o desenvolvimento seja fcil e amigvel, enquanto o ambiente prod deve usar um conjunto de configuraes
otimizada para a velocidade.
Ambientes Diferentes, Diferentes Arquivos de Configurao

Uma aplicao tpica do Symfony2 comea com trs ambientes: dev, prod e test. Como discutido, cada ambiente simplesmente representa uma forma de executar o mesmo cdigo com configuraes diferentes. No deve ser
nenhuma surpresa, ento, que cada ambiente carrega seu arquivo de configurao individual. Se voc estiver usando o
formato de configurao YAML, os seguintes arquivos so usados:
para o ambiente dev: app/config/config_dev.yml
para o ambiente prod: app/config/config_prod.yml
para o ambiente test: app/config/config_test.yml
Isso funciona por meio de uma conveno simples que usada, por padro, dentro da classe AppKernel:
// app/AppKernel.php
// ...
class AppKernel extends Kernel
{
// ...
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(__DIR__./config/config_.$this->getEnvironment()..yml);
}
}

Como voc pode ver, quando o Symfony2 carregado, ele usa um dado ambiente para determinar qual arquivo de configurao deve carregar. Isto alcana o objetivo de vrios ambientes de uma forma elegante, poderosa e transparente.
claro que, na realidade, cada ambiente s difere um pouco dos outros. Geralmente, todos os ambientes iro compartilhar uma grande base de configurao comum. Abrindo o arquivo de configurao dev, voc pode ver como isso
feito fcil e transparentemente:
YAML
imports:
- { resource: config.yml }
# ...

XML
<imports>
<import resource="config.xml" />
</imports>
<!-- ... -->

340

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

PHP
$loader->import(config.php);
// ...

Para compartilhar as configuraes comuns, cada arquivo de configurao de ambiente simplesmente primeiro importa
de um arquivo de configurao central (config.yml). O restante do arquivo pode ento desviar-se da configurao
padro sobrescrevendo os parmetros individuais. Por exemplo, por padro, a barra de ferramentas web_profiler
est desativada. No entanto, no ambiente dev, a barra de ferramentas ativada, modificando o valor padro no arquivo
de configurao dev:
YAML
# app/config/config_dev.yml
imports:
- { resource: config.yml }
web_profiler:
toolbar: true
# ...

XML
<!-- app/config/config_dev.xml -->
<imports>
<import resource="config.xml" />
</imports>
<webprofiler:config
toolbar="true"
... />

PHP
// app/config/config_dev.php
$loader->import(config.php);
$container->loadFromExtension(web_profiler, array(
toolbar => true,
...,
));

A execuo de uma Aplicao em Ambientes Diferentes

Para executar a aplicao em cada ambiente, carregue a aplicao usando o front controller app.php (para o ambiente
prod) ou o app_dev.php (para o ambiente dev):
http://localhost/app.php
http://localhost/app_dev.php

-> *prod* environment


-> *dev* environment

Nota: As URLs fornecidas assumem que o seu servidor web est configurado para usar o diretrio web/ da aplicao
como sua raiz. Leia mais em Installing Symfony2.
Se voc abrir um desses arquivos, ver rapidamente que o ambiente utilizado por cada um definido explicitamente:
1

<?php

2
3

require_once __DIR__./../app/bootstrap_cache.php;

3.1. Cookbook

341

Symfony Docs pt-BR Documentation, Verso 2.4

require_once __DIR__./../app/AppCache.php;

5
6

use Symfony\Component\HttpFoundation\Request;

7
8
9

$kernel = new AppCache(new AppKernel(prod, false));


$kernel->handle(Request::createFromGlobals())->send();

Como voc pode ver, a chave prod especifica que esse ambiente ser executado no ambiente prod. Uma aplicao
Symfony2 pode ser executada em qualquer ambiente usando este cdigo e alterando a string de ambiente.
Nota: O ambiente test utilizado quando escrevemos testes funcionais e no acessvel diretamente no navegador
atravs de um front controller. Em outras palavras, ao contrrio dos outros ambientes, no h um arquivo front
controller app_test.php.

Modo Depurao
Importante, mas sem relao com o tema de ambientes a chave false na linha 8 do front controller acima.
Esta especifica se a aplicao deve ou no ser executada em modo de depurao. Independentemente do
ambiente, uma aplicao Symfony2 pode ser executada com o modo de depurao definido como true ou
false. Isso afeta muitas coisas na aplicao, como se os erros devem ou no ser apresentados ou se os arquivos
de cache so dinamicamente reconstrudos em cada pedido. Apesar de no ser uma exigncia, o modo de
depurao geralmente definido para true nos ambientes dev e test e false para o ambiente prod.
Internamente, o valor do modo de depurao torna-se o parmetro kernel.debug utilizado dentro do service container. Se voc olhar dentro do arquivo de configurao da aplicao, ver o parmetro utilizado, por
exemplo, para ligar ou desligar o log ao usar o Doctrine DBAL:
YAML
doctrine:
dbal:
logging:
# ...

"%kernel.debug%"

XML
<doctrine:dbal logging="%kernel.debug%" ... />

PHP
$container->loadFromExtension(doctrine, array(
dbal => array(
logging => %kernel.debug%,
...,
),
...
));

Criao de um Novo Ambiente

Por padro, uma aplicao Symfony2 tem que lidar com trs ambientes na maioria dos casos. Claro, desde que um
ambiente nada mais do que uma string que corresponde a um conjunto de configurao, a criao de um novo
ambiente bastante fcil.
Suponha, por exemplo, que antes da implantao, voc precisa realizar um benchmark da sua aplicao. Uma forma
de realizar o benchmark da aplicao usar configuraes prximas da produo, mas com o web_profiler do
Symfony2 habilitado. Isto permite ao Symfony2 registrar as informaes sobre sua aplicao enquanto o realiza o

342

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

benchmark.
A melhor forma de realizar isto atravs de um novo ambiente chamado, por exemplo, benchmark. Comece por
criar um novo arquivo de configurao:
YAML
# app/config/config_benchmark.yml
imports:
- { resource: config_prod.yml }
framework:
profiler: { only_exceptions: false }

XML
<!-- app/config/config_benchmark.xml -->
<imports>
<import resource="config_prod.xml" />
</imports>
<framework:config>
<framework:profiler only-exceptions="false" />
</framework:config>

PHP
// app/config/config_benchmark.php
$loader->import(config_prod.php)
$container->loadFromExtension(framework, array(
profiler => array(only-exceptions => false),
));

E com esta simples adio, a aplicao agora suporta um novo ambiente chamado benchmark.
Este novo arquivo de configurao importa a configurao do ambiente prod e modifica ela. Isso garante que o novo
ambiente idntico ao ao ambiente prod, exceto por quaisquer alteraes explicitamente feitas aqui.
J que voc deseja que este ambiente seja acessvel atravs de um navegador, voc tambm deve criar um front
controller para ele. Copie o arquivo web/app.php para web/app_benchmark.php e edite o ambiente para
benchmark:
<?php
require_once __DIR__./../app/bootstrap.php;
require_once __DIR__./../app/AppKernel.php;
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel(benchmark, false);
$kernel->handle(Request::createFromGlobals())->send();

O novo ambiente est agora acessvel atravs:


http://localhost/app_benchmark.php

Nota: Alguns ambientes, como o ambiente dev, nunca so destinados ao acesso em qualquer servidor implantado
para o pblico em geral. Isto porque certos ambientes, para fins de depurao, podem fornecer muita informao
sobre a aplicao ou a infra-estrutura subjacente. Para ter certeza que estes ambientes no so acessveis, o front
controller normalmente protegido de endereos IP externos atravs do seguinte cdigo no topo do controlador:
3.1. Cookbook

343

Symfony Docs pt-BR Documentation, Verso 2.4

if (!in_array(@$_SERVER[REMOTE_ADDR], array(127.0.0.1, ::1))) {


die(You are not allowed to access this file. Check .basename(__FILE__). for more informat
}

Ambientes e o Diretrio de Cache

O Symfony2 aproveita o cache de muitas maneiras: para a configurao da aplicao, configurao de roteamento,
templates Twig e mais realizado o cache para objetos PHP armazenados em arquivos no sistema de arquivos.
Por padro, esses arquivos em cache so armazenados, em grande parte, no diretrio app/cache. No entanto, cada
ambiente armazena seu prprio conjunto de arquivos:
app/cache/dev
app/cache/prod

- diretrio cache para o ambiente *dev*


- diretrio cache para o ambiente *prod*

s vezes, quando depurando, pode ser til inspecionar um arquivo em cache para compreender como algo est funcionando. Ao faz-lo, lembre-se de olhar no diretrio do ambiente que voc est usando (mais comumente dev enquanto
estiver desenvolvendo e depurando). Embora possa variar, o diretrio app/cache/dev inclui o seguinte:
appDevDebugProjectContainer.php - o container de servio em cache que representa a configurao da aplicao em cache;
appdevUrlGenerator.php - a classe PHP gerada a partir da configurao de roteamento e usada ao gerar
URLs;
appdevUrlMatcher.php - a classe PHP usada para correspondncia de rota - olhe aqui para ver a lgica
de expresses regulares compiladas usadas para correspondncia das URLs de entrada para diferentes rotas;
twig/ - Este diretrio contm todos templates Twig em cache.
Nota: Voc pode facilmente mudar a localizao do diretrio e o seu nome. Para mais informaes leia o artigo
Como Substituir a Estrutura de Diretrio Padro do Symfony.

Investigando mais Detalhadamente

Leia o artigo em Como definir Parmetros Externos no Container de Servios.


Como Substituir a Estrutura de Diretrio Padro do Symfony
O Symfony vem automaticamente com uma estrutura de diretrios padro. Voc pode facilmente substituir essa
estrutura de diretrio criando a seu prpria. A estrutura de diretrio padro :
app/
cache/
config/
logs/
...
src/
...
vendor/
...
web/
app.php
...

344

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Substituir o diretrio cache

Voc pode substituir o diretrio cache, sobrescrevendo o mtodo getCacheDir na classe AppKernel de sua
aplicao:
// app/AppKernel.php
// ...
class AppKernel extends Kernel
{
// ...
public function getCacheDir()
{
return $this->rootDir./.$this->environment./cache/;
}
}

$this->rootDir o caminho absoluto para o diretrio app e $this->environment o ambiente atual (ou
seja, dev). Neste caso voc alterou a localizao do diretrio cache para app/{environment}/cache.
Cuidado: Voc deve manter o diretrio cache diferente para cada ambiente, caso contrrio, algum comportamento inesperado pode acontecer. Cada ambiente gera seus prprios arquivos de configurao em cache, e assim,
cada um precisa de seu prprio diretrio para armazenar os arquivos de cache.

Substituir o diretrio logs

O processo para substituir o diretrio logs o mesmo do diretrio cache, a nica diferena que voc precisa
sobrescrever o mtodo getLogDir:
// app/AppKernel.php
// ...
class AppKernel extends Kernel
{
// ...
public function getLogDir()
{
return $this->rootDir./.$this->environment./logs/;
}
}

Aqui voc alterou o local do diretrio para app/{environment}/logs.


Substituir o diretrio web

Se voc precisa renomear ou mover o seu diretrio web, a nica coisa que voc precisa garantir que o caminho
para o diretrio app ainda est correto em seus front controllers app.php e app_dev.php. Se voc simplesmente
renomear o diretrio, ento est tudo ok. Mas se voc moveu de alguma forma, pode precisar modificar os caminhos
dentro desses arquivos:
require_once __DIR__./../Symfony/app/bootstrap.php.cache;
require_once __DIR__./../Symfony/app/AppKernel.php;

3.1. Cookbook

345

Symfony Docs pt-BR Documentation, Verso 2.4

Dica: Alguns hosts compartilhados tem um diretrio raiz web public_html. Renomeando seu diretrio web de
web para public_html uma maneira de fazer funcionar o seu projeto Symfony em seu servidor compartilhado.
Outra forma implantar sua aplicao em um diretrio fora do raiz web, excluir seu diretrio public_html e,
ento, substitu-lo por um link simblico para o web em seu projeto.
Nota: Se voc utiliza o AsseticBundle precisar configurar o seguinte, para que ele possa usar o diretrio web correto:
# app/config/config.yml
# ...
assetic:
# ...
read_from: "%kernel.root_dir%/../../public_html"

Agora voc s precisa realizar o dump dos assets novamente e sua aplicao deve funcionar:
$ php app/console assetic:dump --env=prod --no-debug

Como definir Parmetros Externos no Container de Servios


No captulo Como Dominar e Criar novos Ambientes, voc aprendeu como gerenciar a configurao da sua aplicao.
s vezes, armazenar certas credenciais fora do cdigo do seu projeto pode beneficiar a sua aplicao. A configurao
do banco de dados um exemplo. A flexibilidade do container de servio do Symfony permite voc fazer isso
facilmente.
Variveis de Ambiente

O Symfony vai pegar qualquer varivel de ambiente com o prefixo SYMFONY__ e set-la como um parmetro no
container de servio. Sublinhados duplos so substitudos por um ponto, pois o ponto no um caracter vlido no
nome de uma varivel de ambiente.
Por exemplo, se voc est usando o Apache, as variveis de ambiente podem ser definidas utilizando a seguinte
configurao VirtualHost:
<VirtualHost *:80>
ServerName
DocumentRoot
DirectoryIndex
SetEnv
SetEnv

Symfony2
"/path/to/symfony_2_app/web"
index.php index.html
SYMFONY__DATABASE__USER user
SYMFONY__DATABASE__PASSWORD secret

<Directory "/path/to/symfony_2_app/web">
AllowOverride All
Allow from All
</Directory>
</VirtualHost>

Nota: O exemplo acima para uma configurao Apache, usando a diretiva SetEnv. No entanto, isso vai funcionar
para qualquer servidor web que suporte a definio de variveis de ambiente.
Alm disso, para que o seu console funcione (que no usa Apache), voc deve exportar estas como variveis shell. Em
um sistema Unix, voc pode executar o seguinte:

346

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

$ export SYMFONY__DATABASE__USER=user
$ export SYMFONY__DATABASE__PASSWORD=secret

Agora que voc declarou uma varivel de ambiente, ela estar presente na varivel global $_SERVER do PHP. O Symfony, ento, automaticamente define todas as variveis $_SERVER prefixadas com SYMFONY__ como parmetros
no container de servios.
Agora, voc pode referenciar estes parmetros em qualquer local onde precisar deles.
YAML
doctrine:
dbal:
driver
dbname:
user:
password:

pdo_mysql
symfony2_project
"%database.user%"
"%database.password%"

XML

<!-- xmlns:doctrine="http://symfony.com/schema/dic/doctrine" -->


<!-- xsi:schemaLocation="http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/do
<doctrine:config>
<doctrine:dbal
driver="pdo_mysql"
dbname="symfony2_project"
user="%database.user%"
password="%database.password%"
/>
</doctrine:config>

PHP
$container->loadFromExtension(doctrine, array(dbal => array(
driver
=> pdo_mysql,
dbname
=> symfony2_project,
user
=> %database.user%,
password => %database.password%,
));

Constantes

O container tambm possui suporte para definir constantes do PHP como parmetros. Para aproveitar esse recurso,
mapeie o nome da sua constante para uma chave de parmetro , e defina o tipo como constant.
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<parameters>
<parameter key="global.constant.value" type="constant">GLOBAL_CONSTANT</parameter>
<parameter key="my_class.constant.value" type="constant">My_Class::CONSTANT_NAME</parame
</parameters>
</container>

3.1. Cookbook

347

Symfony Docs pt-BR Documentation, Verso 2.4

Nota: Isso funciona somente para a configurao XML. Se voc no est usando XML, simplesmente importe um
arquivo XML para aproveitar essa funcionalidade:
# app/config/config.yml
imports:
- { resource: parameters.xml }

Configuraes Diversas

A diretiva imports pode ser usada para puxar os parmetros armazenados em outro lugar. Importando um arquivo
PHP lhe d a flexibilidade para adicionar o que for necessrio no container. O seguinte importa um arquivo chamado
parameters.php.
YAML
# app/config/config.yml
imports:
- { resource: parameters.php }

XML
<!-- app/config/config.xml -->
<imports>
<import resource="parameters.php" />
</imports>

PHP
// app/config/config.php
$loader->import(parameters.php);

Nota: Um arquivo de recursos pode ser um de muitos tipos. PHP, XML, YAML, INI e recursos de closure so todos
suportados pela directiva imports.
No parameters.php, diga ao container de servio os parmetros que voc deseja definir. Isto til quando alguma
configurao importante est em um formato fora do padro. O exemplo abaixo inclui a configurao de um banco de
dados do Drupal no container de servio do Symfony.
// app/config/parameters.php
include_once(/path/to/drupal/sites/default/settings.php);
$container->setParameter(drupal.database.url, $db_url);

Como usar o PdoSessionStorage para armazenar as Sesses no Banco de Dados


O armazenamento de sesso padro do Symfony2 grava as informaes da sesso em arquivo(s). A maioria dos sites
de mdio grande porte utilizam um banco de dados para armazenar os valores da sesso, em vez de arquivos, pois os
bancos de dados so mais fceis de usar e escalam em um ambiente multi-servidor.
O Symfony2 tem uma soluo interna para o armazenamento de sesso em banco de dados chamado
PdoSessionHandler. Para us-lo, voc s precisa alterar alguns parmetros no config.yml (ou o formato
de configurao de sua escolha):
Novo na verso 2.1:
No Symfony2.1 a classe e o namespace foram ligeiramente modificados.
Voc pode agora encontrar as classes de armazenamento de sesso no namespace Session\Storage:

348

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Symfony\Component\HttpFoundation\Session\Storage. Observe tambm que no Symfony2.1


voc deve configurar o handler_id e no o storage_id como no Symfony2.0. Abaixo, voc vai perceber que o
%session.storage.options% no mais usado.
YAML
# app/config/config.yml
framework:
session:
# ...
handler_id:
session.handler.pdo
parameters:
pdo.db_options:
db_table:
db_id_col:
db_data_col:
db_time_col:

session
session_id
session_value
session_time

services:
pdo:
class: PDO
arguments:
dsn:
"mysql:dbname=mydatabase"
user:
myuser
password: mypassword
session.handler.pdo:
class:
Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
arguments: [@pdo, %pdo.db_options%]

XML
<!-- app/config/config.xml -->
<framework:config>
<framework:session handler-id="session.handler.pdo" lifetime="3600" auto-start="true"/>
</framework:config>
<parameters>
<parameter key="pdo.db_options" type="collection">
<parameter key="db_table">session</parameter>
<parameter key="db_id_col">session_id</parameter>
<parameter key="db_data_col">session_value</parameter>
<parameter key="db_time_col">session_time</parameter>
</parameter>
</parameters>
<services>
<service id="pdo" class="PDO">
<argument>mysql:dbname=mydatabase</argument>
<argument>myuser</argument>
<argument>mypassword</argument>
</service>

<service id="session.handler.pdo" class="Symfony\Component\HttpFoundation\Session\Storage\Ha


<argument type="service" id="pdo" />
<argument>%pdo.db_options%</argument>
</service>
</services>

3.1. Cookbook

349

Symfony Docs pt-BR Documentation, Verso 2.4

PHP
// app/config/config.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
$container->loadFromExtension(framework, array(
// ...
session => array(
...,
handler_id => session.handler.pdo,
),
));
$container->setParameter(pdo.db_options, array(
db_table
=> session,
db_id_col
=> session_id,
db_data_col
=> session_value,
db_time_col
=> session_time,
));
$pdoDefinition = new Definition(PDO, array(
mysql:dbname=mydatabase,
myuser,
mypassword,
));
$container->setDefinition(pdo, $pdoDefinition);

$storageDefinition = new Definition(Symfony\Component\HttpFoundation\Session\Storage\Handler\Pd


new Reference(pdo),
%pdo.db_options%,
));
$container->setDefinition(session.handler.pdo, $storageDefinition);

db_table: O nome da tabela de sesso no seu banco de dados


db_id_col: O nome da coluna id na sua tabela de sesso (VARCHAR (255) ou maior)
db_data_col: O nome da coluna value na sua tabela de sesso (TEXT ou CLOB)
db_time_col: O nome da coluna time em sua tabela de sesso (INTEGER)
Compartilhando suas Informaes de Conexo do Banco de Dados

Com a configurao fornecida, as configuraes de conexo do banco de dados so definidas somente para a conexo
de armazenamento de sesso. Isto est OK quando voc usa um banco de dados separado para os dados da sesso.
Mas, se voc gostaria de armazenar os dados da sesso no mesmo banco de dados que o resto dos dados do seu projeto,
voc pode usar as definies de conexo do parameter.ini referenciando os parmetros relacionados ao banco de dados
definidos l:
YAML
pdo:
class: PDO
arguments:
- "mysql:dbname=%database_name%"
- %database_user%
- %database_password%

350

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

XML
<service id="pdo" class="PDO">
<argument>mysql:dbname=%database_name%</argument>
<argument>%database_user%</argument>
<argument>%database_password%</argument>
</service>

PHP
$pdoDefinition = new Definition(PDO, array(
mysql:dbname=%database_name%,
%database_user%,
%database_password%,
));

Exemplo de Instrues SQL

MySQL A instruo SQL para criar a tabela de banco de dados necessria pode ser semelhante a seguinte (MySQL):
CREATE TABLE session (
session_id varchar(255) NOT NULL,
session_value text NOT NULL,
session_time int(11) NOT NULL,
PRIMARY KEY (session_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

PostgreSQL Para o PostgreSQL, a declarao deve ficar assim:


CREATE TABLE session (
session_id character varying(255) NOT NULL,
session_value text NOT NULL,
session_time integer NOT NULL,
CONSTRAINT session_pkey PRIMARY KEY (session_id)
);

Como usar o Apache Router


O Symfony2, mesmo sendo rpido logo aps a sua instalao, ainda fornece vrias formas de aumentar essa velocidade
com poucos ajustes. Uma dessas formas deixar o apache lidar com as rotas diretamente, em vez de usar o Symfony2
para esta tarefa.
Alterando os Parmetros de Configurao do Router

Para fazer o dump das rotas do Apache precisamos primeiro ajustar alguns parmetros de configurao e dizer ao
Symfony2 para usar o ApacheUrlMatcher em vez do padro:
# app/config/config_prod.yml
parameters:
router.options.matcher.cache_class: ~ # disable router cache
router.options.matcher_class: Symfony\Component\Routing\Matcher\ApacheUrlMatcher

Dica:

Note que o ApacheUrlMatcher estende UrlMatcher assim, mesmo se voc no regerar as regras

3.1. Cookbook

351

Symfony Docs pt-BR Documentation, Verso 2.4

url_rewrite, tudo vai funcionar (porque no final do ApacheUrlMatcher::match() realizada uma chamada
para o parent::match()).

Gerando regras do mod_rewrite

Para testar se est funcionando, vamos criar uma rota bem bsica para o bundle demo:
# app/config/routing.yml
hello:
pattern: /hello/{name}
defaults: { _controller: AcmeDemoBundle:Demo:hello }

Agora vamos gerar regras url_rewrite:


$ php app/console router:dump-apache -e=prod --no-debug

Que deve produzir aproximadamente o seguinte:


# skip "real" requests
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .* - [QSA,L]

# hello
RewriteCond %{REQUEST_URI} ^/hello/([^/]+?)$
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:hello,E=_ROUTING_name:%1,E=_ROUTING__controller:AcmeD

Agora voc pode reescrever o web/.htaccess para usar as novas regras, portanto, com o nosso exemplo, ele deve parecer
com o seguinte:
<IfModule mod_rewrite.c>
RewriteEngine On
# skip "real" requests
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .* - [QSA,L]

# hello
RewriteCond %{REQUEST_URI} ^/hello/([^/]+?)$
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:hello,E=_ROUTING_name:%1,E=_ROUTING__controller:A
</IfModule>

Nota: O procedimento acima deve ser feito cada vez que voc adicionar/alterar uma rota se deseja aproveitar o
mximo desta configurao.
isso! Voc agora est pronto para usar as regras do Apache Route.
Ajustes adicionais

Para economizar um pouco de tempo de processamento, altere as ocorrncias de Request para ApacheRequest
no web/app.php:
// web/app.php
require_once __DIR__./../app/bootstrap.php.cache;
require_once __DIR__./../app/AppKernel.php;

352

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

//require_once __DIR__./../app/AppCache.php;
use Symfony\Component\HttpFoundation\ApacheRequest;
$kernel = new AppKernel(prod, false);
$kernel->loadClassCache();
//$kernel = new AppCache($kernel);
$kernel->handle(ApacheRequest::createFromGlobals())->send();

3.1.9 Bundles
Como usar Melhores Prticas para a Estruturao dos Bundles
Um Bundle um diretrio que tem uma estrutura bem definida e pode hospedar qualquer coisa desde classes at
controladores e recursos web. Mesmo os bundles sendo muito flexveis, voc deve seguir algumas das melhores
prticas se deseja distribu-los.
Nome do Bundle

Um bundle tambm um namespace PHP. O namespace deve seguir os padres tcnicos de interoperabilidade para
namespaces do PHP 5.3 e nomes de classes: ele inicia com o segmento do vendor, seguido por zero ou mais segmentos
de categoria, e termina com o nome curto do namespace, que deve terminar com o sufixo Bundle.
Um namespace torna-se um bundle assim que voc adiciona uma classe bundle ele. O nome da classe do bundle
deve seguir estas regras simples:
Usar apenas caracteres alfanumricos e sublinhados;
Usar o nome em CamelCase;
Usar um nome descritivo e curto (no mais que 2 palavras);
Prefixar o nome com a concatenao do vendor (e, opcionalmente, os namespaces da categoria);
Adicionar o sufixo Bundle ao nome.
Aqui esto alguns namespaces de bundle e nomes de classes vlidos:
Namespace
Acme\Bundle\BlogBundle
Acme\Bundle\Social\BlogBundle
Acme\BlogBundle

Nome da Classe do Bundle


AcmeBlogBundle
AcmeSocialBlogBundle
AcmeBlogBundle

Por conveno, o mtodo getName() da classe bundle deve retornar o nome da classe.
Nota: Se voc compartilhar publicamente seu bundle, voc deve usar o nome da classe do bundle como o nome do
repositrio (AcmeBlogBundle e no BlogBundle por exemplo).
Nota: Os Bundles do ncleo do Symfony2 no prefixam a classe Bundle com Symfony e sempre adicionam um
subnamespace Bundle, por exemplo: FrameworkBundle.
Cada bundle tem um alias, que a verso curta e em letras minsculas do nome do bundle
usando sublinhados (por exemplo, acme_hello para AcmeHelloBundle, ou acme_social_blog para
Acme\Social\BlogBundle). Estes alias so usados para definir exclusividade dentro de um bundle (veja abaixo
alguns exemplos de uso).

3.1. Cookbook

353

Symfony Docs pt-BR Documentation, Verso 2.4

Estrutura de Diretrios

A estrutura bsica de diretrio de um bundle HelloBundle deve parecer com a seguinte:


XXX/...
HelloBundle/
HelloBundle.php
Controller/
Resources/
meta/
LICENSE
config/
doc/
index.rst
translations/
views/
public/
Tests/

O(s) diretrio(s) XXX refletem a estrutura do namespace do bundle.


Os seguintes arquivos so obrigatrios:
HelloBundle.php;
Resources/meta/LICENSE: A licena completa para o cdigo;
Resources/doc/index.rst: O arquivo raiz para a documentao do Bundle.
Nota: Estas convenes garantem que ferramentas automatizadas possam contar com esta estrutura padro para
trabalhar.
A profundidade dos sub-diretrios deve ser mantida ao mnimo para as classes e arquivos mais utilizados (2 nveis, no
mximo). Mais nveis podem ser definidos para arquivos no-estratgicos ou menos utilizados.
O diretrio bundle somente leitura. Se voc precisa gravar arquivos temporrios, armazene-os sob o diretrio
cache/ ou log/ da aplicao host. Ferramentas podem gerar arquivos na estrutura de diretrio do bundle, mas
apenas se os arquivos gerados sero parte do repositrio.
As classes e arquivos seguintes possuem um local especfico:
Tipo
Comandos
Controladores
Extenses do Service Container
Listeners de Evento
Configurao
Recursos Web
Arquivos de Traduo
Templates
Testes Unitrios e Funcionais

Diretrio
Command/
Controller/
DependencyInjection/
EventListener/
Resources/config/
Resources/public/
Resources/translations/
Resources/views/
Tests/

Classes

A estrutura de diretrios do bundle usada como hierarquia de namespace. Por exemplo, um controlador
HelloController armazenado em Bundle/HelloBundle/Controller/HelloController.php e
o nome totalmente qualificado da classe Bundle\HelloBundle\Controller\HelloController.

354

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Todas as classes e arquivos devem seguir os padres de codificao do Symfony2.


Algumas classes deve ser vistas como facades e devem ser o mais curtas possvel, como Commands, Helpers, Listeners
e Controllers.
As classes que se conectam ao Dispatcher de Eventos devem ter o sufixo Listener.
Classes de excees (Exceptions) devem ser armazenadas em um sub-namespace Exception.
Vendors

Um bundle no deve incorporar bibliotecas PHP de terceiros. Em vez disso, ele deve contar com o autoloading padro
do Symfony2.
Um bundle no deve incorporar bibliotecas de terceiros escritas em JavaScript, CSS ou qualquer outra linguagem.
Testes

Um bundle deve vir com um conjunto de testes escritos com o PHPUnit e armazenados sob o diretrio Tests/. Os
testes devem seguir os seguintes princpios:
O conjunto de testes deve ser executvel com um simples comando phpunit, executado a partir de uma
aplicao de exemplo;
Os testes funcionais s devem ser usados para testar a resposta de sada e algumas informaes de perfis, caso
voc tiver;
A cobertura de cdigo deve cobrir pelo menos 95% da base de cdigo.
Nota: Um conjunto de testes no deve conter scripts AllTests.php, mas deve contar com a existncia de um
arquivo phpunit.xml.dist.

Documentao

Todas as classes e funes devem vir com PHPDoc completo.


Documentao extensa tambm deve ser fornecida no formato reStructuredText, sob o diretrio
Resources/doc/; o arquivo Resources/doc/index.rst o nico arquivo obrigatrio e deve ser o ponto de
entrada para a documentao.
Controladores

Como melhor prtica, os controladores em um bundle que se destina a ser distribudo no deve estender a classe
base Controller. Ao invs disso, eles podem implementar ContainerAwareInterface ou estender
ContainerAware .
Nota: Se voc verificar os mtodos do Controller, vai ver que eles so apenas atalhos para facilitar a curva de
aprendizado.

3.1. Cookbook

355

Symfony Docs pt-BR Documentation, Verso 2.4

Roteamento

Se o bundle oferece rotas, elas devem ser prefixadas com o alias do bundle. Por exemplo, para o AcmeBlogBundle,
todas as rotas devem ser prefixadas com acme_blog_.
Templates

Se um bundle fornece templates, eles devem usar o Twig. Um bundle no deve fornecer um layout principal, exceto
se ele fornece uma aplicao completa.
Arquivos de Traduo

Se um bundle fornece mensagens de traduo, elas devem ser definidas no formato XLIFF; o domnio deve ser nomeado aps o nome do bundle (bundle.hello).
Um bundle no deve sobrescrever as mensagens existentes de outro bundle.
Configurao

Para proporcionar maior flexibilidade, um bundle pode fornecer definies configurveis usando os mecanismos
embutidos do Symfony2.
Definies de configurao simples contam com a entrada padro parameters da configurao do Symfony2. Os
parmetros do Symfony2 so simples pares chave/valor, um valor pode ser qualquer valor vlido em PHP. Cada nome
de parmetro deve comear com o alias do bundle, embora esta seja apenas uma sugesto de melhor prtica. O resto do
nome do parmetro usar um ponto (.) para separar partes diferentes (por exemplo, acme_hello.email.from).
O usurio final pode fornecer valores em qualquer arquivo de configurao:
YAML
# app/config/config.yml
parameters:
acme_hello.email.from: fabien@example.com

XML
<!-- app/config/config.xml -->
<parameters>
<parameter key="acme_hello.email.from">fabien@example.com</parameter>
</parameters>

PHP
// app/config/config.php
$container->setParameter(acme_hello.email.from, fabien@example.com);

INI
; app/config/config.ini
[parameters]
acme_hello.email.from = fabien@example.com

Recupere os parmetros de configurao no seu cdigo a partir do container:

356

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

$container->getParameter(acme_hello.email.from);

Mesmo esse mecanismo sendo bastante simples, voc altamente encorajado a usar a configurao semntica descrita
no cookbook.
Nota: Se voc estiver definindo servios, eles tambm devem ser prefixados com o alias do bundle.

Aprenda mais no Cookbook

Como expor uma Configurao Semntica para um Bundle


Como usar herana para substituir partes de um Bundle
Ao trabalhar com bundles de terceiros, voc frequentemente precisar substituir um arquivo dele por um prprio seu
para personalizar seu comportarmento ou aparncia. Symfony possui uma maneira bem conveniente de personalizar
controllers, templates, Tradues e outros arquivos do diretrio Resources/ de um bundle.
Por exemplo, suponha que voc est instalando FOSUserBundle, mas voc quer que a template
layout.html.twig o um dos seus controllers seja aqueles que voc personalizou e colocou no seu bundle. No exemplo seguinte, estamos assumindo que voc j tenha o bundle AcmeUserBundle e coloque os arquivos
personalizados nele. O primeiro passo registrar o bundle FOSUserBundle como pai do seu bundle:
// src/Acme/UserBundle/AcmeUserBundle.php
namespace Acme\UserBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AcmeUserBundle extends Bundle
{
public function getParent()
{
return FOSUserBundle;
}
}

Esta simples alterao permitir que substitua vrios partes de FOSUserBundle simplesmente criando um arquivo
com o mesmo nome.
Substituindo controladores

Suponha que voc queira adicionar alguma funcionalidade a ao registerAction do controlador


RegistrationController que est dentro do bundle FOSUserBundle. Para faz-lo, basta criar o seu prprio
RegistrationController.php, crie um mtodo que substitua o do bundle original e mude sua funcionalidade
como mostrado a seguir.
// src/Acme/UserBundle/Controller/RegistrationController.php
namespace Acme\UserBundle\Controller;
use FOS\UserBundle\Controller\RegistrationController as BaseController;
class RegistrationController extends BaseController
{
public function registerAction()
{

3.1. Cookbook

357

Symfony Docs pt-BR Documentation, Verso 2.4

$response = parent::registerAction();
// do custom stuff
return $response;
}
}

Dica: Dependendo do tipo de personalizao que precisa fazer no controlador, voc pode substituir completamente o
mtodo com lgica prpria sem nem mesmo chamar parent::registerAction().
Nota: Substituir controladores desta maneira somente funciona se o bundle referencia o controlador utilizando sintaxe
padro FOSUserBundle:Registration:register nas rotas e nos templates. Esta a sintaxe recomendada.

Substituindo recursos: Templates, Rotas, Tradues, Validao, etc

A maioria dos recursos tambm podem ser substitudos, simplesmente criando um arquivo no mesmo caminho relativo
que estiver no bundle pai.
Por exemplo, muito comum precisar substituir o arquivo de template layout.html.twig do bundle
FOSUserBundle para utilizar o layout base de sua prpria aplicao. Uma vez que o arquivo fica no caminho
Resources/views/layout.html.twig dentro do bundle FOSUserBundle voc consegue criar seu prprio
arquivo no mesmo lugar relativo dentro do seu bundler (por exemplo, Resources/views/layout.html.twig
do bundle FOSUserBundle). O Symfony vai ignorar o arquivo dentro do FOSUserBundle e utilizar o seu no
lugar.
O mesmo vale para arquivos de rotas, configurao de Validao e outros recursos.
Nota: A substituio de recursos s funciona quando voc se refere a recursos utilizando a sintaxe recomendada
@FosUserBundle/Resources/config/routing/security.xml. Se voc se referir a recursos sem o
atalho @FosUserBundle, eles no sero substitudos.
Nota: Arquivos de tradues no funcionam da maneira descrita acima. Todos os ficheiros traduzidos sero adicionados em um conjunto de pools organizados por dominios. Symfony abrir os ficheiros de traduo dos bundles
primeiro na ordem que eles so inicializados e ento do seu diretorio app/Resource. Se houver dois ficheiros da
mesma traduo estiver especificados por diferentes Resources, o ficheiro de traduo que for aberto por ultimo ser
o utilizado.

Como Sobrescrever qualquer parte de um Bundle


Este documento uma referncia rpida sobre como sobrescrever diferentes partes de bundles de terceiros.
Templates

Para obter informaes sobre como sobrescrever templates, consulte * Sobrepondo Templates de Pacote. * Como usar
herana para substituir partes de um Bundle

358

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Roteamento

O roteamento nunca automaticamente importado no Symfony2. Se voc quiser incluir as rotas de qualquer bundle,
elas devem ser manualmente importadas em algum lugar na sua aplicao (ex.: app/config/routing.yml).
A maneira mais fcil para sobrescrever o roteamento de um bundle nunca import-lo . Em vez de importar o
roteamento de um bundle de terceiros, simplesmente copie o arquivo de roteamento em sua aplicao, modifique-o e
importe-o no lugar.
Controladores

Assumindo que o bundle de terceiro envolvido usa controladores no-servios (que quase sempre o caso), voc pode
facilmente sobrescrever os controladores atravs de herana do bundle: Para mais informaes, consulte Como usar
herana para substituir partes de um Bundle.
Servios e Configurao

Existem duas opes para sobrescrever/estender um servio. Primeiro, voc pode definir o parmetro que contm
o nome do servio da classe para a sua prpria classe, definindo ele em app/config/config.yml. Isto, naturalmente, s possvel se o nome da classe est definido como um parmetro na configurao de servio do
bundle que contm o servio. Por exemplo, para sobrescrever a classe usada pelo servio translator do Symfony, voc poderia sobrescrever o parmetro translator.class. Para saber exatamente qual parmetro deve-se
sobrescrever, poder ser necessria alguma pesquisa. Para o tradutor, o parmetro definido e usado no arquivo
Resources/config/translation.xml do FrameworkBundle:
YAML
# app/config/config.yml
parameters:
translator.class:

Acme\HelloBundle\Translation\Translator

XML
<!-- app/config/config.xml -->
<parameters>
<parameter key="translator.class">Acme\HelloBundle\Translation\Translator</parameter>
</parameters>

PHP
// app/config/config.php
$container->setParameter(translator.class, Acme\HelloBundle\Translation\Translator);

Em segundo lugar, se a classe no est disponvel como um parmetro, voc quer ter a certeza que a classe ser sempre
sobrescrita quando seu bundle for utilizado, ou quando voc precisa modificar algo alm do nome da classe, voc deve
usar um compiler pass:
// src/Acme/FooBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php
namespace Acme\DemoBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class OverrideServiceCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)

3.1. Cookbook

359

Symfony Docs pt-BR Documentation, Verso 2.4

{
$definition = $container->getDefinition(original-service-id);
$definition->setClass(Acme\DemoBundle\YourService);
}
}

Neste exemplo, buscamos a definio de servio do servio original, e definimos seu nome de classe para a nossa
prpria classe.
Veja /cookbook/service_container/compiler_passes para obter informaes sobre como usar compiler passes. Se voc quer fazer algo alm de apenas sobrescrever a classe - como adicionar uma chamada de mtodo voc s pode usar o mtodo compiler pass.
Entidades e Mapeamento de Entidade

Em andamento...
Formulrios

A fim de sobrescrever um tipo de formulrio (form type), ele tem que ser registrado como um servio (o que significa
que tem a tag definida como form.type). Voc pode, ento, sobrescrev-lo como faria com qualquer servio, como
foi explicado em Servios e Configurao. Isto, claro, somente funcionar se o tipo referido por seu alias, em vez
de ser instanciado, ex.:
$builder->add(name, custom_type);

em vez de:
$builder->add(name, new CustomType());

Validao de Metadados

Em andamento..
Tradues

Em andamento...
Como expor uma Configurao Semntica para um Bundle
Se voc abrir o arquivo de configurao da sua aplicao (geralmente app/config/config.yml), ver um nmero de diferentes namespaces de configuraes, como framework, twig e doctrine. Cada um deles configura um bundle especfico, permitindo configurar as coisas a um alto nvel e deixar o bundle fazer todas as modificaes
complexas, de baixo nvel resultantes.
Por exemplo, o cdigo a seguir diz ao FrameworkBundle para habilitar a integrao do formulrio, a qual envolve
a definio de alguns servios, bem como a integrao de outros componentes relacionados:
YAML
framework:
# ...
form:

360

true

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

XML
<framework:config>
<framework:form />
</framework:config>

PHP
$container->loadFromExtension(framework, array(
// ...
form
=> true,
// ...
));

Quando voc cria um bundle, voc tem duas opes para lidar com a configurao:
1. Configurao de Servio Normal (fcil):
Voc pode especificar seus servios em um arquivo de configurao (por exemplo services.yml)
que reside em seu bundle e, ento, import-lo a partir da configurao principal da sua aplicao.
Isto realmente fcil, rpido e totalmente eficaz. Se voc fazer uso de parmetros, ento, voc
ainda tem a flexibilidade para personalizar seu bundle a partir da configurao da sua aplicao. Veja
Importando configurao com imports para mais detalhes.
2. Expondo Configurao Semntica (avanado):
Esta a maneira como a configurao feita com os bundles do ncleo (como descrito acima). A
idia bsica que, em vez de o usurio sobrescrever parmetros individuais, voc deixa ele configurar apenas algumas opes criadas . Como desenvolvedor do bundle, voc ento faz o parse desta
configurao e carrega os servios dentro de uma classe Extension. Com este mtodo, voc no
vai precisar importar quaisquer recursos de configurao a partir da configurao principal da sua
aplicao: a classe de Extenso (Extension) pode lidar com tudo isso.
A segunda opo - que voc vai aprender neste artigo - muito mais flexvel, mas tambm requer mais tempo para
configurar. Se voc est se perguntando qual mtodo deve usar, provavelmente uma boa idia comear com o mtodo
1, e depois mudar para o 2 mais tarde, se voc precisar.
O segundo mtodo tem vrias vantagens especficas:
Muito mais poderoso do que simplesmente definir parmetros: um valor de opo especfico pode acionar a
criao de muitas definies de servio;
Possibilidade de ter hierarquia de configurao
Smart merging quando possuir vrios arquivos de configurao (por exemplo config_dev.yml e
config.yml) sobrescrevendo a configurao um do outro;
Validao de configurao (se voc usar uma Classe de Configurao);
Auto-completar da IDE quando voc criar um XSD e os desenvolvedores usarem XML.
Sobresecrevendo parmetros do bundle
Se um Bundle fornecer uma classe Extension, ento, voc geralmente no deve sobrescrever quaisquer parmetros do container de servio daquele bundle. A idia que, se uma classe Extension estiver presente, cada
definio que deve ser configurvel deve estar presente na configurao disponibilizada por esta classe. Em
outras palavras, a classe Extension as definies de configurao pblicas suportadas para as quais a compatibilidade com verses anteriores ser mantida.

3.1. Cookbook

361

Symfony Docs pt-BR Documentation, Verso 2.4

Criando uma Classe Extension

Se voc optar por expor uma configurao semntica para seu bundle, voc vai precisar primeiro criar uma nova classe
Extension, que ir lidar com o processo. Esta classe deve residir no diretrio DependencyInjection de seu
bundle e o seu nome deve ser construdo substituindo o sufixo Bundle do nome da classe Bundle por Extension.
Por exemplo, a classe Extension do AcmeHelloBundle seria chamada AcmeHelloExtension:
// Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class AcmeHelloExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
// ... where all of the heavy logic is done
}
public function getXsdValidationBasePath()
{
return __DIR__./../Resources/config/;
}
public function getNamespace()
{
return http://www.example.com/symfony/schema/;
}
}

Nota: Os mtodos getXsdValidationBasePath e getNamespace so necessrios apenas se o bundle fornece XSDs opcionais para a configurao.
A presena da classe anterior significa que agora voc pode definir um namespace de configurao acme_hello
em qualquer arquivo de configurao. O namespace acme_hello construdo a partir do nome da classe de extenso, removendo a palavra Extension e, em seguida, deixando o resto do nome todo em letras minsculas e com
underscores. Em outras palavras, AcmeHelloExtension torna-se acme_hello.
Voc pode comear a especificar a configurao sob este namespace imediatamente:
YAML
# app/config/config.yml
acme_hello: ~

XML
<!-- app/config/config.xml -->
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:acme_hello="http://www.example.com/symfony/schema/"
xsi:schemaLocation="http://www.example.com/symfony/schema/ http://www.example.com/symfony/sc
<acme_hello:config />

362

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

<!-- ... -->


</container>

PHP
// app/config/config.php
$container->loadFromExtension(acme_hello, array());

Dica: Se voc seguir as convenes de nomenclatura estabelecidas acima, ento, o mtodo load() de seu cdigo
de extenso sempre chamado, uma vez que seu bundle est registrado no Kernel. Em outras palavras, mesmo se
o usurio no fornecer qualquer configurao (ou seja, a entrada acme_hello nem mesmo aparecer), o mtodo
load() ser chamado e passado um array $configs vazio. Voc ainda pode fornecer alguns padres para seu
bundle se desejar.

Fazendo o parse do array $configs

Sempre que um usurio inclui o namespace acme_hello em um arquivo de configurao, a configurao abaixo
dele adicionada um array de configuraes e passado para o mtodo load() de sua extenso (o Symfony2
automaticamente converte XML e YAML para um array).
Assuma a seguinte configurao:
YAML
# app/config/config.yml
acme_hello:
foo: fooValue
bar: barValue

XML
<!-- app/config/config.xml -->
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:acme_hello="http://www.example.com/symfony/schema/"
xsi:schemaLocation="http://www.example.com/symfony/schema/ http://www.example.com/symfony/sc
<acme_hello:config foo="fooValue">
<acme_hello:bar>barValue</acme_hello:bar>
</acme_hello:config>
</container>

PHP
// app/config/config.php
$container->loadFromExtension(acme_hello, array(
foo => fooValue,
bar => barValue,
));

O array passado para seu mtodo load() ficar parecido com o seguinte:
array(
array(
foo => fooValue,

3.1. Cookbook

363

Symfony Docs pt-BR Documentation, Verso 2.4

bar => barValue,


)
)

Observe que este um array de arrays, e no apenas um nico array simples de valores de configurao. Isso
intencional. Por exemplo, se acme_hello aparece em outro arquivo de configurao - digamos config_dev.yml
- com valores diferentes abaixo dele, ento, o array de entrada poderia ser assim:
array(
array(
foo
bar
),
array(
foo
baz
),
)

=> fooValue,
=> barValue,

=> fooDevValue,
=> newConfigEntry,

A ordem dos dois arrays depende de qual definido primeiro.


o seu trabalho, ento, decidir como deve ser o merge dessas configuraes . Voc pode, por exemplo, ter valores
posteriores sobrescrevendo os valores anteriores ou de alguma forma fazer o merge deles.
Mais tarde, na seo sobre a Classe de Configurao , voc vai aprender uma forma verdadeiramente robusta para
lidar com isso. Mas, por ora, voc pode apenas fazer o merge manualmente:
public function load(array $configs, ContainerBuilder $container)
{
$config = array();
foreach ($configs as $subConfig) {
$config = array_merge($config, $subConfig);
}
// ... now use the flat $config array
}

Cuidado: Certifique-se que a tcnica de merge acima faz sentido para o seu bundle. Este apenas um exemplo, e
voc deve ter cuidado para no us-lo cegamente.

Usando o Mtodo load()

Dentro do load() a varivel $container refere-se a um container que apenas sabe sobre essa configurao de
namespace (ou seja, no contm informao de servio carregada a partir de outros bundles). O objetivo do mtodo
load() manipular o container, adicionando e configurando quaisquer mtodos ou servios necessrios ao seu
bundle.
Carregando Recursos de Configurao Externos Algo comum de se fazer carregar um arquivo de configurao
externo que pode conter a maioria dos servios necessrios para o seu bundle. Por exemplo, suponha que voc tem um
arquivo services.xml que contm muitas das configuraes de servios do seu bundle:
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;
public function load(array $configs, ContainerBuilder $container)
{

364

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

// ... prepare your $config variable


$loader = new XmlFileLoader($container, new FileLocator(__DIR__./../Resources/config));
$loader->load(services.xml);
}

Voc pode at fazer isso condicionalmente, com base em um dos valores de configurao. Por exemplo, supondo que
voc quer carregar um conjunto de servios somente se a opo enabled for passada e definida com true:
public function load(array $configs, ContainerBuilder $container)
{
// ... prepare your $config variable
$loader = new XmlFileLoader($container, new FileLocator(__DIR__./../Resources/config));
if (isset($config[enabled]) && $config[enabled]) {
$loader->load(services.xml);
}
}

Configurando Servios e Definindo Parmetros Uma vez que voc j carregou alguma configurao de servio,
voc pode precisar modificar a configurao com base em alguns dos valores de entrada. Por exemplo, supondo
que existe um servio cujo primeiro argumento alguma string type que ele ir usar internamente. Voc gostaria
que isto fosse facilmente configurado pelo usurio do bundle, ento, em seu arquivo de configurao do servio (ex.
services.xml), voc define este servio e usa um parmetro em branco - acme_hello.my_service_type como seu primeiro argumento:

<!-- src/Acme/HelloBundle/Resources/config/services.xml -->


<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
<parameters>
<parameter key="acme_hello.my_service_type" />
</parameters>
<services>
<service id="acme_hello.my_service" class="Acme\HelloBundle\MyService">
<argument>%acme_hello.my_service_type%</argument>
</service>
</services>
</container>

Mas por que definir um parmetro vazio e ento pass-lo ao seu servio? A resposta que voc vai definir este
parmetro em sua classe de extenso, com base nos valores de configurao de entrada. Suponha, por exemplo, que
voc quer permitir ao usurio definir esta opo type sob uma chave chamada my_type. Para fazer isso, adicione o
seguinte ao mtodo load():
public function load(array $configs, ContainerBuilder $container)
{
// ... prepare your $config variable
$loader = new XmlFileLoader($container, new FileLocator(__DIR__./../Resources/config));
$loader->load(services.xml);
if (!isset($config[my_type])) {
throw new \InvalidArgumentException(The "my_type" option must be set);

3.1. Cookbook

365

Symfony Docs pt-BR Documentation, Verso 2.4

}
$container->setParameter(acme_hello.my_service_type, $config[my_type]);
}

Agora, o usurio pode efetivamente configurar o servio especificando o valor de configurao my_type:
YAML
# app/config/config.yml
acme_hello:
my_type: foo
# ...

XML
<!-- app/config/config.xml -->
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:acme_hello="http://www.example.com/symfony/schema/"
xsi:schemaLocation="http://www.example.com/symfony/schema/ http://www.example.com/symfony/sc
<acme_hello:config my_type="foo">
<!-- ... -->
</acme_hello:config>
</container>

PHP
// app/config/config.php
$container->loadFromExtension(acme_hello, array(
my_type => foo,
// ...
));

Parmetros Globais Quando estiver configurando o container, esteja ciente de que voc tem os seguintes parmetros
globais disponveis para uso:
kernel.name
kernel.environment
kernel.debug
kernel.root_dir
kernel.cache_dir
kernel.logs_dir
kernel.bundle_dirs
kernel.bundles
kernel.charset
Cuidado: Todos os nomes de parmetros e servios, comeando com um _ so reservados para o framework, e
os novos no devem ser definidos por bundles.

366

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Validao e Merging com uma Classe de Configurao

At agora, voc j fez o merge de seus arrays de configurao manualmente e est verificando a presena de valores de
configurao manualmente usando a funo isset() do PHP. Um sistema opcional de Configurao est tambm
disponvel que pode ajudar com merge, validao, valores padro e normalizao de formato.
Nota: Normalizao de formato refere-se ao fato de que certos formatos - em grande parte XML - resultam em arrays
de configurao ligeiramente diferentes e que estes arrays precisam ser normalizados para corresponder com todo o
resto.
Para tirar vantagem deste sistema, voc vai criar uma classe Configuration e construir uma rvore que define a
sua configurao nesta classe:
// src/Acme/HelloBundle/DependencyInjection/Configuration.php
namespace Acme\HelloBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root(acme_hello);
$rootNode
->children()
->scalarNode(my_type)->defaultValue(bar)->end()
->end();
return $treeBuilder;
}

Este um exemplo muito simples, mas agora voc pode usar essa classe em seu mtodo load() para o merge da sua
configurao e forar a validao. Se outras opes que no sejam my_type forem passadas, o usurio ser notificado
com uma exceo de que uma opo no suportada foi passada:
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// ...
}

O mtodo processConfiguration() usa a rvore de configurao que voc definiu na classe


Configuration para validar, normalizar e fazer o merge de todos os arrays de configurao em conjunto.
A classe Configuration pode ser muito mais complicada do que mostramos aqui, suportando array nodes, prototype nodes, validao avanada, normalizao especfica de XML e merge avanado. Voc pode ler mais sobre
isso na documentao do Componente de Configurao. Voc tambm pode v-lo em ao verificando
algumas das classes de Configurao do ncleo, tais como a Configurao do FrameworkBundle ou a Configurao
do TwigBundle.

3.1. Cookbook

367

Symfony Docs pt-BR Documentation, Verso 2.4

Dump de Configurao Padro Novo na verso 2.1: O comando config:dump-reference foi adicionado
no Symfony 2.1
O comando config:dump-reference permite que a configurao padro de um bundle seja impressa no console
em YAML.
Enquanto a configurao do bundle est localizada no local padro (YourBundle\DependencyInjection\Configuration)
e no tem um __constructor() ele vai funcionar automaticamente. Se voc tem algo diferente a sua classe
Extension ter que sobrescrever o mtodo Extension::getConfiguration(). Fazendo ele retornar uma
instncia de sua Configuration.
Comentrios e exemplos podem ser adicionados aos ns de configurao utilizando os mtodos ->info() e
->example():
// src/Acme/HelloBundle/DependencyExtension/Configuration.php
namespace Acme\HelloBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root(acme_hello);
$rootNode
->children()
->scalarNode(my_type)
->defaultValue(bar)
->info(what my_type configures)
->example(example setting)
->end()
->end()
;
return $treeBuilder;
}

Este texto aparece como comentrios yaml na sada do comando config:dump-reference.


Convenes de Extenso

Ao criar uma extenso, siga estas convenes simples:


A extenso deve ser armazenada no sub-namespace DependencyInjection;
A extenso deve ser nomeada aps o nome do bundle e com o sufixo Extension (AcmeHelloExtension
para AcmeHelloBundle);
A extenso deve fornecer um esquema XSD.
Se voc seguir estas convenes simples, suas extenses sero registradas automaticamente pelo Symfony2. Se no,
sobrescreva o mtodo Bundle build() em seu bundle:
// ...
use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass;
class AcmeHelloBundle extends Bundle

368

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

{
public function build(ContainerBuilder $container)
{
parent::build($container);
// register extensions that do not follow the conventions manually
$container->registerExtension(new UnconventionalExtensionClass());
}
}

Neste caso, a classe de extenso tambm deve implementar um mtodo getAlias() e retornar um alias exclusivo
nomeado aps o bundle (por exemplo, acme_hello). Isto necessrio porque o nome da classe no segue os padres
terminando em Extension.
Alm disso, o mtodo load() de sua extenso ser apenas chamado se o usurio especificar o alias acme_hello
em pelo menos um arquivo de configurao . Mais uma vez, isso porque a classe de extenso no segue os padres
acima referidos, de modo que, nada acontece automaticamente.

3.1.10 Email
Como enviar um e-mail
Enviar e-mails uma tarefa clssica para qualquer aplicao web e, possui complicaes especiais e potenciais armadilhas. Em vez de recriar a roda, uma soluo para enviar e-mails usar o SwiftmailerBundle, que aproveita o
poder da biblioteca Swiftmailer.
Nota: No esquea de ativar o bundle em seu kernel antes de us-lo:
public function registerBundles()
{
$bundles = array(
// ...
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
);
// ...
}

Configurao

Antes de usar o Swiftmailer, no esquea de incluir a sua configurao. O nico parmetro de configurao obrigatrio
o transport:
YAML
# app/config/config.yml
swiftmailer:
transport: smtp
encryption: ssl
auth_mode: login
host:
smtp.gmail.com
username:
your_username
password:
your_password

XML
3.1. Cookbook

369

Symfony Docs pt-BR Documentation, Verso 2.4

<!-- app/config/config.xml -->

<!-xmlns:swiftmailer="http://symfony.com/schema/dic/swiftmailer"
http://symfony.com/schema/dic/swiftmailer http://symfony.com/schema/dic/swiftmailer/swiftmailer-->
<swiftmailer:config
transport="smtp"
encryption="ssl"
auth-mode="login"
host="smtp.gmail.com"
username="your_username"
password="your_password" />

PHP
// app/config/config.php
$container->loadFromExtension(swiftmailer, array(
transport => "smtp",
encryption => "ssl",
auth_mode => "login",
host
=> "smtp.gmail.com",
username
=> "your_username",
password
=> "your_password",
));

A maioria das configuraes do Swiftmailer lidam com a forma como as mensagens devem ser entregues.
Os seguintes atributos de configurao esto disponveis:
transport (smtp, mail, sendmail, ou gmail)
username
password
host
port
encryption (tls, ou ssl)
auth_mode (plain, login, ou cram-md5)
spool
type (como ser o queue de mensagens, so suportados file ou memory, veja Como fazer Spool de
E-mail)
path (onde armazenar as mensagens)
delivery_address (um endereo de email para onde sero enviados TODOS os e-mails)
disable_delivery (defina como true para desabilitar completamente a entrega)
Enviando e-mails

A biblioteca Swiftmailer funciona atravs da criao, configurao e, ento, o envio de objetos Swift_Message. O
mailer responsvel pela entrega da mensagem e acessvel atravs do servio mailer. No geral, o envio de um
e-mail bastante simples:

370

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

public function indexAction($name)


{
$message = \Swift_Message::newInstance()
->setSubject(Hello Email)
->setFrom(send@example.com)
->setTo(recipient@example.com)
->setBody($this->renderView(HelloBundle:Hello:email.txt.twig, array(name => $name)))
;
$this->get(mailer)->send($message);
return $this->render(...);
}

Para manter as coisas desacopladas, o corpo do e-mail foi armazenado em um template e renderizado atravs do
mtodo renderView().
O objeto $message suporta mais opes, como, a incluso de anexos, a adio de contedo HTML, e muito mais.
Felizmente, o Swiftmailer cobre o tpico Criao de Mensagens em grande detalhe na sua documentao.
Dica: Vrios outros artigos cookbook relacionados ao envio de e-mails esto disponveis no Symfony2:
Como usar o Gmail para enviar E-mails
Como Trabalhar com E-mails Durante o Desenvolvimento
Como fazer Spool de E-mail

Como usar o Gmail para enviar E-mails


Durante o desenvolvimento, em vez de usar um servidor SMTP regular para enviar e-mails, voc pode descobrir que
usar o Gmail mais fcil e prtico. O bundle Swiftmailer torna esta tarefa realmente fcil.
Dica: Em vez de usar a sua conta do Gmail normal, , com certeza, recomendado que voc crie uma conta especial
para este propsito.
No arquivo de configurao de desenvolvimento, altere a definio transport para gmail e defina o username
e password com as credenciais do Google:
YAML
# app/config/config_dev.yml
swiftmailer:
transport: gmail
username: your_gmail_username
password: your_gmail_password

XML
<!-- app/config/config_dev.xml -->

<!-xmlns:swiftmailer="http://symfony.com/schema/dic/swiftmailer"
http://symfony.com/schema/dic/swiftmailer http://symfony.com/schema/dic/swiftmailer/swiftmai
-->
<swiftmailer:config
transport="gmail"

3.1. Cookbook

371

Symfony Docs pt-BR Documentation, Verso 2.4

username="your_gmail_username"
password="your_gmail_password" />

PHP
// app/config/config_dev.php
$container->loadFromExtension(swiftmailer, array(
transport => "gmail",
username => "your_gmail_username",
password => "your_gmail_password",
));

Est pronto!
Nota: O transporte gmail simplesmente um atalho que usa o transporte smtp e seta as definies encryption,
auth_mode e host para funcionar com o Gmail.

Como Trabalhar com E-mails Durante o Desenvolvimento


Quando voc estiver criando uma aplicao que envia e-mails, muitas vezes no vai desejar enviar os e-mails ao
destinatrio especificado, durante o desenvolvimento. Se voc estiver usando o SwiftmailerBundle com o Symfony2, poder facilmente conseguir isso atravs de definies de configurao sem ter que fazer quaisquer alteraes
no cdigo da sua aplicao. Existem duas opes principais quando se trata de manipulao de e-mails durante o
desenvolvimento: (a) desativao do envio de e-mails totalmente ou (b) o envio de todos os e-mails para um endereo
especificado.
Desativando o Envio

Voc pode desativar o envio de e-mails, definindo a opo disable_delivery para true. Este o padro para o
ambiente test na distribuio Standard. Se voc fizer isso especificamente na configurao test, ento os emails
no ser enviados quando voc executar testes, mas continuaro a ser enviados nos ambientes prod e dev:
YAML
# app/config/config_test.yml
swiftmailer:
disable_delivery: true

XML
<!-- app/config/config_test.xml -->

<!-xmlns:swiftmailer="http://symfony.com/schema/dic/swiftmailer"
http://symfony.com/schema/dic/swiftmailer http://symfony.com/schema/dic/swiftmailer/swiftmailer-->
<swiftmailer:config
disable-delivery="true" />

PHP
// app/config/config_test.php
$container->loadFromExtension(swiftmailer, array(
disable_delivery => "true",
));

372

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Se voc tambm gostaria de desativar a entrega no ambiente dev, simplesmente adicione esta configurao ao arquivo
config_dev.yml.
Enviando para um Endereo Especificado

Voc tambm pode optar por enviar todos os emails para um endereo especfico, em vez do endereo atualmente
especificado, ao enviar a mensagem. Isto pode ser feito atravs da opo delivery_address:
YAML
# app/config/config_dev.yml
swiftmailer:
delivery_address: dev@example.com

XML
<!-- app/config/config_dev.xml -->

<!-xmlns:swiftmailer="http://symfony.com/schema/dic/swiftmailer"
http://symfony.com/schema/dic/swiftmailer http://symfony.com/schema/dic/swiftmailer/swiftmailer-->
<swiftmailer:config
delivery-address="dev@example.com" />

PHP
// app/config/config_dev.php
$container->loadFromExtension(swiftmailer, array(
delivery_address => "dev@example.com",
));

Agora, suponha que voc est enviando um email para recipient@example.com.


public function indexAction($name)
{
$message = \Swift_Message::newInstance()
->setSubject(Hello Email)
->setFrom(send@example.com)
->setTo(recipient@example.com)
->setBody($this->renderView(HelloBundle:Hello:email.txt.twig, array(name => $name)))
;
$this->get(mailer)->send($message);
return $this->render(...);
}

No ambiente dev, o e-mail ser enviado para dev@example.com. O Swiftmailer ir adicionar um cabealho extra
para o e-mail, X-Swift-To contendo o endereo substitudo, assim voc ainda poder visualizar para quem ele teria
sido enviado.
Nota: Alm do endereo to, ele tambm ir parar os e-mails sendo enviados para quaisquer endereos CC e BCC
definidos. O SwiftMailer ir adicionar cabealhos adicionais para o e-mail com os endereos substitudos neles. Eles
so X-Swift-Cc e X-Swift-Bcc para os endereos CC e BCC, respectivamente.

3.1. Cookbook

373

Symfony Docs pt-BR Documentation, Verso 2.4

Visualizao na Barra de Ferramentas de Debug Web

Voc pode visualizar quaisquer e-mails enviados por uma pgina quando estiver no ambiente dev usando a Barra de
Ferramentas para Debug Web. O cone de e-mail na barra de ferramentas ir mostrar quantos e-mails foram enviados.
Se voc clicar nele, um relatrio mostrando os detalhes dos e-mails ser aberto.
Se voc estiver enviando um e-mail e imediatamente executar um redirecionamento, voc precisar definir a opo
intercept_redirects para true no arquivo config_dev.yml para que possa ver o e-mail na barra de
ferramentas de debug web antes de ser redirecionado.
Como fazer Spool de E-mail
Quando voc estiver usando o SwiftmailerBundle para enviar um email de uma aplicao Symfony2, ele ir,
por padro, enviar o e-mail imediatamente. Voc pode, entretanto, desejar evitar um impacto no desempenho da
comunicao entre o Swiftmailer e o transporte do e-mail, o que poderia fazer com que o usurio tenha que
aguardar a prxima pgina carregar, enquanto est enviando o e-mail. Isto pode ser evitado escolhendo pelo spool
dos e-mails em vez de envi-los diretamente. Isto significa que o Swiftmailer no tentar enviar o email, mas,
ao invs, salvar a mensagem em algum lugar, como um arquivo. Outro processo poder ento ler a partir do spool
e cuidar de enviar os e-mails no spool. Atualmente, apenas o spool para arquivo ou memria so suportados pelo
Swiftmailer.
Spool usando memria

Quando voc usa o spool para armazenar os e-mails em memria, eles so enviados mesmo antes do kernel terminar.
Isto significa que o e-mail s enviado se o pedido foi todo executado sem qualquer exceo no tratada ou quaisquer
erros. Para configurar o SwiftMailer com a opo de memria, utilize a seguinte configurao:
YAML
# app/config/config.yml
swiftmailer:
# ...
spool: { type: memory }

XML
<!-- app/config/config.xml -->

<!-xmlns:swiftmailer="http://symfony.com/schema/dic/swiftmailer"
http://symfony.com/schema/dic/swiftmailer http://symfony.com/schema/dic/swiftmailer/swiftmai
-->
<swiftmailer:config>
<swiftmailer:spool type="memory" />
</swiftmailer:config>

PHP
// app/config/config.php
$container->loadFromExtension(swiftmailer, array(
...,
spool => array(type => memory)
));

374

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Spool usando um arquivo

Para utilizar o spool com um arquivo, use a seguinte configurao:


YAML
# app/config/config.yml
swiftmailer:
# ...
spool:
type: file
path: /path/to/spool

XML
<!-- app/config/config.xml -->

<!-xmlns:swiftmailer="http://symfony.com/schema/dic/swiftmailer"
http://symfony.com/schema/dic/swiftmailer http://symfony.com/schema/dic/swiftmailer/swiftmailer-->
<swiftmailer:config>
<swiftmailer:spool
type="file"
path="/path/to/spool" />
</swiftmailer:config>

PHP
// app/config/config.php
$container->loadFromExtension(swiftmailer, array(
// ...
spool => array(
type => file,
path => /path/to/spool,
)
));

Dica: Se voc deseja armazenar o spool em algum lugar no diretrio do seu projeto, lembre-se que voc pode usar o
parmetro %kernel.root_dir% para referenciar o raiz do seu projeto:
path: %kernel.root_dir%/spool

Agora, quando a sua aplicao enviar um e-mail, ele no ser realmente enviado, ao invs, ser adicionado ao spool.
O envio de mensagens do spool feito separadamente. Existe um comando do console para enviar as mensagens que
encontram-se no spool:
php app/console swiftmailer:spool:send

Ele tem uma opo para limitar o nmero de mensagens a serem enviadas:
php app/console swiftmailer:spool:send --message-limit=10

Voc tambm pode definir o limite de tempo em segundos:


php app/console swiftmailer:spool:send --time-limit=10

Claro que, na realidade, voc no vai querer executar ele manualmente. Em vez disso, o comando do console deve ser
disparado por um cron job ou tarefa agendada e executar em um intervalo regular.
3.1. Cookbook

375

Symfony Docs pt-BR Documentation, Verso 2.4

3.1.11 Segurana
Listas de controle de acesso (ACLs)
Em aplicativos complexos, comumente existem o problema que as decises de permitir ou negar acesso no podem ser
tomadas somente baseada no usurio (Token) solicitando acesso, mas tambm deve levar em considerao o objeto
de domnio que est tendo o acesso solicitado. a que o sistema ACL entra em ao.
Imagine que est projetando um sistema de blog onde seus usurio podem comentar os textos (posts) publicados.
Agora, voc deseja que um usurio possa editar seus prprios comentrios, mas no os comentrios dos outros usurios. Alm disso, voc como administrador deseja pode editar todos os comentrios. Neste cenrio, Comment seria
seu objeto de domnio ao qual voc quer restringir acesso. Voc poderia usar vrias abordagens para conseguir o
mesmo resultado. Duas dessas seriam:
Impor segurana em seus mtodos: Basicamente, isso significa que dever manter referncias em cada
Comment de todos os usurios que tm acesso e depois comparar com o usurio Token solicitando acesso.
Impor segurana com perfis: Nesta abordagem, voc adicionaria um perfil para cada objeto Comment, isto ,
ROLE_COMMENT_1, ROLE_COMMENT_2, etc.
Ambas abordagens so perfeitamnete vlidas. Elas, porm, amarram sua lgica de autorizao de acesso com seu
cdigo, deixando-o mais difcil de reusar em outros contextos. Tambm aumenta a dificuldade de criar testes unitrios.
Alm disso, pode-se ter problemas de performance caso muitos usurios tenham acesso a um nico objeto de domnio.
Felizmente, h uma maneira melhor que veremos a seguir.
Configurao

Agora, antes de realmente comearmos, precisamos fazer algumas configuraes. Primeiramente, precisamos configurar a conexo de banco de dados queo sistema ACL utilizar.
YAML
# app/config/security.yml
security:
acl:
connection: default

XML
<!-- app/config/security.xml -->
<acl>
<connection>default</connection>
</acl>

PHP
// app/config/security.php
$container->loadFromExtension(security, acl, array(
connection => default,
));

Nota: O sistema ACL requer que ao menos uma conexo Doctrine DBAL esteja configurada. Isto, porm, no
significa que voc tem que utilizar o Doctrine para mapear seus objetos de domnio. Voc pode utilizar qualquer
mapeamento que quiser para seus objetos, seja ele Doctrine ORM, Mongo ODM, Propel, ou SQL puro. A escolha
sua.

376

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Depois de configurar a conexo, temos que importar a estrutura do banco de dados. Felizmente, temos um comando
para isto. Rode o seguinte comando.
php app/console init:acl

Comeando

Voltando ao nosso pequeno exemplo do incio, vamos implementar o sistema ACL dele.
Criando uma ACL, e adicionando uma entrada (ACE)
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
use Symfony\Component\Security\Acl\Permission\MaskBuilder;
// ...
// BlogController.php
public function addCommentAction(Post $post)
{
$comment = new Comment();
// setup $form, and bind data
// ...
if ($form->isValid()) {
$entityManager = $this->get(doctrine.orm.default_entity_manager);
$entityManager->persist($comment);
$entityManager->flush();
// creating the ACL
$aclProvider = $this->get(security.acl.provider);
$objectIdentity = ObjectIdentity::fromDomainObject($comment);
$acl = $aclProvider->createAcl($objectIdentity);
// retrieving the security identity of the currently logged-in user
$securityContext = $this->get(security.context);
$user = $securityContext->getToken()->getUser();
$securityIdentity = UserSecurityIdentity::fromAccount($user);
// grant owner access
$acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_OWNER);
$aclProvider->updateAcl($acl);
}
}

H algumas importantes decises de implementao neste trecho de cdigo. Por enquanto, gostaria de destacar duas.
Primeiro, note que o mtodo ->createAcl() no aceita objetos de domnio diretamente, mas somente implementaes de ObjectIdentityInterface. Este passo adicional permite que trabalhe com ACLs mesmo quando no
tiver uma instncia do objeto de domnio disponvel. Isto ser extremamente til se voc quiser verificar permisses
para um grande nmero de objetos sem realmente criar os objetos.
Outra parte interessante a chamada ->insertObjectAce(). Em nosso exemplo, estamos concedendo ao usurio
que est autenticado permisso de proprietrio do objeto Comment. MaskBuilder::MASK_OWNER uma mscara
(integer bitmask) pr-definida. No se preocupe que MaskBuilder abstrai a maior parte dos detalhes tcnicos, mas
saiba que utilizando esta tcnica possvel armazenar muitas permisses diferentes em apenas uma linha do banco de
dados, o que significa uma considervel melhora na performance.
3.1. Cookbook

377

Symfony Docs pt-BR Documentation, Verso 2.4

Dica: A ordem em que as entradas de controle (ACE) so checadas importante. Como regra geral, voc deve
colocar as entradas mais especficas no incio.

Verificando o acesso
// BlogController.php
public function editCommentAction(Comment $comment)
{
$securityContext = $this->get(security.context);
// check for edit access
if (false === $securityContext->isGranted(EDIT, $comment))
{
throw new AccessDeniedException();
}
// retrieve actual comment object, and do your editing here
// ...
}

Neste exemplo, verificamos se o usurio tem permisso de edio (EDIT). Internamente, Symfony2 mapea a permisso
para vrias mscaras (integer bitmasks) e verifica se o usurio tem alguma delas.
Nota: Voc pode definir at 32 permisses base (dependendo do seu SO, pode variar entre 30 e 32). Voc ainda pode
definir permises cumulativas.

Permisses Cumulativas

No nosso primeiro exemplo acima, ns concedemos somente a permisso base OWNER. Apesar disso significar que
o usurio pode executar qualquer operao no objeto de domnio tais como exibir, editar, etc, em alguns casos voc
pode querer conceder essas permisses explicitamente.
O MaskBuilder pode ser usado para criar mscaras (bit masks) facilmente atravs da combinao de vrias permisses base.
$builder = new MaskBuilder();
$builder
->add(view)
->add(edit)
->add(delete)
->add(undelete)
;
$mask = $builder->get(); // int(15)

Este inteiro (integer bitmask) pode ento ser usado para conceder a um usurio todas as permisses base que voc
adicionou acima.
$acl->insertObjectAce(new UserSecurityIdentity(johannes), $mask);

O usurio agora poder exibir, editar, deletar e desfazer a deleo dos objetos.

378

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Como usar Conceitos Avanados de ACL


O objetivo deste captulo fornecer uma viso mais aprofundada do sistema de ACL, e tambm explicar algumas das
decises de projeto por trs dele.
Conceitos de Projeto

Os recursos de uma instncia do objeto de segurana do Symfony2 tem base no conceito de uma Lista de Controle
de Acesso (ACL). Cada instncia do objeto de domnio tem a sua prpria ACL. A instncia ACL contm uma lista
detalhada de Entradas de Controle de Acesso (ACEs), que so usadas para tomar decises de acesso. O sistema ACL
do Symfony2 concentra-se em dois objetivos principais:
fornecer uma maneira de recuperar uma grande quantidade de ACLs/ACEs de forma eficiente para os seus
objetos de domnio e para modific-los;
fornecer uma maneira de tomar decises facilmente para saber se uma pessoa est autorizada a executar uma
ao em um objeto de domnio ou no.
Conforme indicado no primeiro ponto, uma das principais capacidades do sistema ACL do Symfony2 uma forma de
recuperar ACLs/ACEs com alto desempenho. Isto extremamente importante, pois cada ACL pode ter vrias ACEs, e
herdam de outra ACL em forma de rvore. Portanto, nenhum ORM utilizado, em vez disso, a implementao padro
interage com a sua conexo diretamente usando o DBAL do Doctrine.
Identidades de Objeto O sistema de ACL completamente desacoplado de seus objetos de domnio. Eles no tem
que ser armazenados na mesma base de dados, ou no mesmo servidor. De forma a atingir esse desacoplamento, os
seus objetos, no sistema ACL, so representados atravs de objetos de identidade de objeto. Toda vez que voc deseja
recuperar a ACL para um objeto de domnio, o sistema de ACL vai primeiro criar uma identidade de objeto do seu
objeto de domnio e, em seguida, passar essa identidade de objeto ao provedor ACL para processamento adicional.
Identidades de Segurana Isto anlogo identidade de objeto, mas representa um usurio ou um papel em sua
aplicao. Cada papel ou usurio tem a sua prpria identidade de segurana.
Estrutura da Tabela de Banco de Dados

A implementao padro usa cinco tabelas de banco de dados, conforme listado abaixo. Em uma aplicao tpica, as
tabelas so ordenadas da com menos linhas para a com mais linhas:
acl_security_identities: Esta tabela registra todas as identidades de segurana (SID) que detm ACEs.
A implementao padro vem com duas identidades de segurana: RoleSecurityIdentity e
UserSecurityIdentity
acl_classes: Esta classe mapeia nomes de classes para um ID nico, que pode ser referenciado a partir de outras
tabelas.
acl_object_identities: Cada linha nessa tabela representa um nico domnio de instncia de objeto.
acl_object_identity_ancestors: Esta tabela permite que todos os ancestrais de uma ACL, possam ser determinados de uma maneira muito eficiente.
acl_entries: Esta tabela contm todas as ACEs. Essa tipicamente a tabela com o maior nmero de linhas. Ela
pode conter dezenas de milhes sem afetar significativamente o desempenho.

3.1. Cookbook

379

Symfony Docs pt-BR Documentation, Verso 2.4

Escopo das Entradas de Controle de Acesso

Entradas de controle de acesso podem ter escopos diferentes nos quais se aplicam. No Symfony2, existem basicamente
dois escopos diferentes:
Class-Scope: Essas entradas se aplicam a todos os objetos com a mesma classe.
Object-Scope: Este foi o escopo utilizado unicamente no captulo anterior, e ele s se aplica a um objeto
especfico.
s vezes, voc vai encontrar a necessidade de aplicar uma ACE apenas a um campo especfico do objeto. Vamos
dizer que voc quer que o ID somente seja visualizado por um administrador, mas no por seu servio do cliente. Para
resolver este problema comum, mais dois sub-escopos foram adicionados:
Classe-Field-Scope: Essas entradas se aplicam a todos os objetos com a mesma classe, mas apenas a um campo
especfico dos objetos.
Object-Field-Scope: Essas entradas se aplicam a um objeto especfico, e apenas a um campo especfico do
objeto.
Decises de Pr-Autorizao

Para as decises de pr-autorizao, que so as decises tomadas antes de qualquer mtodo seguro (ou ao segura)
ser invocado, o servio AccessDecisionManager usado. O AccessDecisionManager tambm usado para tomar
decises de autorizao com base em papis. Assim como os papis, o sistema de ACL adiciona vrios atributos
novos que podem ser utilizados para verificar se h permisses diferentes.

Mapa de Permisso Integrado

Atributo
VIEW

Significado Pretendido

Bitmasks In

Se algum tem permisso para ver o objeto de domnio.

EDIT

Se algum tem permisso para fazer alteraes no objeto de domnio.

CREATE
DELETE
UNDELETE
OPERATOR
MASTER
OWNER

Se algum tem permisso para criar o objeto de domnio.

VIEW, EDI
OPERATOR
ou OWNER
EDIT, OPER
MASTER o
CREATE, O
MASTER o
DELETE, O
MASTER o
UNDELETE
OPERATOR
ou OWNER
OPERATOR
ou OWNER

Se algum tem permisso excluir o objeto de domnio.


Se algum tem permisso para restaurar o objeto de domnio anteriormente
excludo.
Se algum tem permisso para executar todas as aes acima.

Se algum tem permisso para executar todas as aes acima, e alm disso
autorizada a conceder qualuqer uma das permisses acima para outros.
Se algum dono do objeto de domnio. Um dono pode executar qualquer
uma das aes acima e conceder permisses master e owner

Atributos de Permisso vs Bitmasks de Permisso Os atributos so usados pelo AccessDecisionManager, assim


como papis. Muitas vezes, estes atributos representam, de fato, um agregado de bitmasks inteiros. Bitmasks inteiros
por outro lado, so utilizados internamente pelo sistema de ACL para armazenar de forma eficiente suas permisses
de usurio no banco de dados e executar verificaes de acesso usando operaes bitmask extremamente rpidas.

380

Captulo 3. Cookbook

MASTER o
OWNER

Symfony Docs pt-BR Documentation, Verso 2.4

Extensibilidade O mapa de permisso acima no de forma alguma esttico, e teoricamente poderia ser completamente substitudo. No entanto, ele deve cobrir a maioria dos problemas que voc encontra e para a interoperabilidade
com outros bundles, voc incentivado a manter o significado previsto por eles.
Decises Ps-Autorizao

Decises ps-autorizao so feitas depois de um mtodo seguro ter sido invocado, e normalmente envolvem o objeto
de domnio que retornado por esse mtodo. Aps a invocao de providers, tambm permitido modificar ou filtrar
o objeto de domnio antes de ser devolvido.
Devido s limitaes atuais da linguagem PHP, no existem recursos de ps-autorizao integrados no componente
de segurana. No entanto, existe um JMSSecurityExtraBundle experimental que adiciona esses recursos. Consulte a
documentao para obter mais informaes sobre a forma como isso feito.
Processo para Tomar Decises de Autorizao

A classe ACL fornece dois mtodos para determinar se uma identidade de segurana tem as bitmasks necessrias,
isGranted e isFieldGranted. Quando a ACL recebe um pedido de autorizao atravs de um desses mtodos,
ela delega esse pedido para uma implementao de PermissionGrantingStrategy. Isso permite a substituio da forma
como as decises de acesso so tomadas sem modificar a prpria classe ACL.
O PermissionGrantingStrategy primeiro verifica todos os seus ACEs object-scope, se nenhum for aplicvel, as ACEs
class-scope sero verificadas, se nenhuma for aplicvel, em seguida, o processo vai ser repetido com as ACEs da ACL
pai. Se no existe nenhuma ACL pai, uma exceo ser lanada.
Como forar HTTPS ou HTTP para URLs Diferentes
Atravs da configurao de segurana, voc pode forar que reas do seu site usem o protocolo HTTPS. Isso feito
atravs das regras access_control usando a opo requires_channel. Por exemplo, se voc quiser forar
que todas as URLs que comeam com /secure utilizem HTTPS ento voc poderia usar a seguinte configurao:
YAML
access_control:
- path: ^/secure
roles: ROLE_ADMIN
requires_channel: https

XML
<access-control>
<rule path="^/secure" role="ROLE_ADMIN" requires_channel="https" />
</access-control>

PHP
access_control => array(
array(
path
=> ^/secure,
role
=> ROLE_ADMIN,
requires_channel => https,
),
),

3.1. Cookbook

381

Symfony Docs pt-BR Documentation, Verso 2.4

O prprio formulrio de login precisa permitir acesso annimo, caso contrrio, os usurios no seriam capazes
de se autenticar. Para for-lo a usar HTTPS voc pode ainda usar regras access_control com o papel
IS_AUTHENTICATED_ANONYMOUSLY:
YAML
access_control:
- path: ^/login
roles: IS_AUTHENTICATED_ANONYMOUSLY
requires_channel: https

XML
<access-control>
<rule path="^/login"
role="IS_AUTHENTICATED_ANONYMOUSLY"
requires_channel="https" />
</access-control>

PHP
access_control => array(
array(
path
=> ^/login,
role
=> IS_AUTHENTICATED_ANONYMOUSLY,
requires_channel => https,
),
),

Tambm possvel especificar o uso de HTTPS na configurao de roteamento. Veja Como forar as rotas a usar
sempre HTTPS ou HTTP para mais detalhes.
Como personalizar o seu Formulrio de Login
Usar um formulrio de login para autenticao um mtodo comum e flexvel para lidar com a autenticao no
Symfony2. Praticamente todos os aspectos do formulrio de login podem ser personalizados. A configurao padro
completa mostrada na prxima seo.
Configurao de Referncia do Formulrio de Login

YAML
# app/config/security.yml
security:
firewalls:
main:
form_login:
# the user is redirected here when he/she needs to login
login_path:
/login
# if true, forward the user to the login form instead of redirecting
use_forward:
false
# submit the login form here
check_path:

/login_check

# by default, the login form *must* be a POST, not a GET

382

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

post_only:

true

# login success redirecting options (read further below)


always_use_default_target_path: false
default_target_path:
/
target_path_parameter:
_target_path
use_referer:
false
# login failure redirecting options (read further below)
failure_path:
null
failure_forward:
false
# field names for the username and password fields
username_parameter:
_username
password_parameter:
_password
# csrf token options
csrf_parameter:
intention:

_csrf_token
authenticate

XML
<!-- app/config/security.xml -->
<config>
<firewall>
<form-login
check_path="/login_check"
login_path="/login"
use_forward="false"
always_use_default_target_path="false"
default_target_path="/"
target_path_parameter="_target_path"
use_referer="false"
failure_path="null"
failure_forward="false"
username_parameter="_username"
password_parameter="_password"
csrf_parameter="_csrf_token"
intention="authenticate"
post_only="true"
/>
</firewall>
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
firewalls => array(
main => array(form_login => array(
check_path
=> /login_check,
login_path
=> /login,
user_forward
=> false,
always_use_default_target_path => false,
default_target_path
=> /,
target_path_parameter
=> _target_path,
use_referer
=> false,
failure_path
=> null,
failure_forward
=> false,

3.1. Cookbook

383

Symfony Docs pt-BR Documentation, Verso 2.4

username_parameter
password_parameter
csrf_parameter
intention
post_only

=>
=>
=>
=>
=>

_username,
_password,
_csrf_token,
authenticate,
true,

)),
),
));

Redirecionando aps Sucesso

Voc pode alterar o local para onde o formulrio de login redireciona aps um login bem-sucedido usando opes de configurao diferentes. Por padro, o formulrio ir redirecionar para a URL solicitada pelo usurio
(ou seja, a URL que acionou o formulrio de login que est sendo exibido). Por exemplo, se o usurio solicitou
http://www.example.com/admin/post/18/edit, ento, aps o login bem-sucedido, ele ser enviado de
volta para http://www.example.com/admin/post/18/edit . Isto feito atravs do armazenamento da
URL solicitada em sesso. Se nenhuma URL estiver presente na sesso (talvez o usurio acessou diretamente a pgina
de login), ento, o usurio redirecionado para a pgina padro, que / (ou seja, a homepage). Voc pode alterar esse
comportamento de vrias formas.
Nota: Como mencionado, por padro, o usurio redirecionado para a pgina que ele solicitou originalmente. s
vezes, isso pode causar problemas, como, por exemplo, uma requisio AJAX em background aparece sendo a
ltima URL visitada, fazendo com que o usurio seja redirecionado para l. Para informaes sobre como controlar
esse comportamento, consulte /cookbook/security/target_path.

Alterando a Pgina Padro Primeiro, a pgina padro pode ser definida (ou seja, a pgina para a qual o usurio ser
redirecionado se nenhuma pgina anterior foi armazenada em sesso). Para configur-la para /admin use a seguinte
configurao:
YAML
# app/config/security.yml
security:
firewalls:
main:
form_login:
# ...
default_target_path: /admin

XML
<!-- app/config/security.xml -->
<config>
<firewall>
<form-login
default_target_path="/admin"
/>
</firewall>
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
firewalls => array(

384

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

main => array(form_login => array(


...,
default_target_path => /admin,
)),
),
));

Agora, quando no houver uma URL definida na sesso, os usurios sero enviados para /admin.
Sempre Redirecionar para a Pgina Padro Voc pode fazer com que os usurios sejam sempre redirecionados
para a pgina padro, independentemente da URL que eles tenham solicitado anteriormente, definindo a a opo
always_use_default_target_path para true:
YAML
# app/config/security.yml
security:
firewalls:
main:
form_login:
# ...
always_use_default_target_path: true

XML
<!-- app/config/security.xml -->
<config>
<firewall>
<form-login
always_use_default_target_path="true"
/>
</firewall>
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
firewalls => array(
main => array(form_login => array(
...,
always_use_default_target_path => true,
)),
),
));

Usando a URL de referncia (Referring URL) No caso de nenhuma URL anterior estar armazenada em sesso,
voc pode desejar usar o HTTP_REFERER no lugar, pois este, muitas vezes, o mesmo. Voc pode fazer isso
configurando o use_referer para true (o padro false):
YAML
# app/config/security.yml
security:
firewalls:
main:
form_login:

3.1. Cookbook

385

Symfony Docs pt-BR Documentation, Verso 2.4

# ...
use_referer:

true

XML
<!-- app/config/security.xml -->
<config>
<firewall>
<form-login
use_referer="true"
/>
</firewall>
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
firewalls => array(
main => array(form_login => array(
...,
use_referer => true,
)),
),
));

Novo na verso 2.1: A partir da verso 2.1, se o referer for igual opo login_path, o usurio ser redirecionado
para default_target_path.
Controlando a URL de redirecionamento dentro do Formulrio Voc tambm pode sobrescrever para onde o
usurio ser redirecionado, atravs do prprio formulrio incluindo um campo oculto com o nome _target_path.
Por exemplo, para redirecionar para a URL definida por rota account, use o seguinte:
Twig
{# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig #}
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form action="{{ path(login_check) }}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />
<input type="hidden" name="_target_path" value="account" />
<input type="submit" name="login" />
</form>

PHP
<!-- src/Acme/SecurityBundle/Resources/views/Security/login.html.php -->
<?php if ($error): ?>
<div><?php echo $error->getMessage() ?></div>
<?php endif; ?>

386

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

<form action="<?php echo $view[router]->generate(login_check) ?>" method="post">


<label for="username">Username:</label>
<input type="text" id="username" name="_username" value="<?php echo $last_username ?>" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />
<input type="hidden" name="_target_path" value="account" />
<input type="submit" name="login" />
</form>

Agora, o usurio ser redirecionado para o valor do campo oculto do formulrio. O valor do atributo pode ser um
caminho relativo, uma URL absoluta, ou um nome de rota. Voc pode at mesmo alterar o nome do campo oculto do
formulrio, alterando a opo target_path_parameter para um outro valor.
YAML
# app/config/security.yml
security:
firewalls:
main:
form_login:
target_path_parameter: redirect_url

XML
<!-- app/config/security.xml -->
<config>
<firewall>
<form-login
target_path_parameter="redirect_url"
/>
</firewall>
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
firewalls => array(
main => array(form_login => array(
target_path_parameter => redirect_url,
)),
),
));

Redirecionando quando o Login Falhar Alm de redirecionar o usurio aps um login bem-sucedido, voc tambm pode definir a URL que o usurio deve ser redirecionado aps uma falha de login (por exemplo, quando um nome
de usurio ou senha invlida foi submetida). Por padro, o usurio redirecionado de volta ao formulrio de login.
Voc pode definir isso para uma URL diferente com a seguinte configurao:
YAML
# app/config/security.yml
security:
firewalls:
main:

3.1. Cookbook

387

Symfony Docs pt-BR Documentation, Verso 2.4

form_login:
# ...
failure_path: /login_failure

XML
<!-- app/config/security.xml -->
<config>
<firewall>
<form-login
failure_path="login_failure"
/>
</firewall>
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
firewalls => array(
main => array(form_login => array(
...,
failure_path => login_failure,
)),
),
));

Como criar um Provider de Usurio Personalizado


Parte do processo de autenticao padro do Symfony depende de providers de usurio. Quando um usurio submete
um nome de usurio e senha, a camada de autenticao solicita ao provider de usurio configurado para retornar um
objeto de usurio para um determinado nome de usurio. Em seguida, o Symfony verifica se a senha deste usurio est
correta e gera um token de segurana para que o usurio permanea autenticado durante a sesso atual. O Symfony
vem com os providers de usurio in_memory e entity prontos para uso. Neste artigo, voc vai aprender como
criar o seu prprio provider de usurio, que pode ser til se os usurios so acessados atravs de um banco de dados
personalizado, um arquivo ou - como mostrado neste exemplo - um servio web.
Crie uma Classe de Usurio

Em primeiro lugar, independentemente de onde os seus dados de usurio esto vindo, voc vai precisar criar uma classe
User que representa esses dados. No entando, a classe User pode parecer com o que voc quiser e conter quaisquer
dados. O nico requisito que a classe implemente UserInterface. Os mtodos dessa interface devem ser
definidos na classe de usurio personalizada: getRoles(), getPassword(), getSalt(), getUsername(),
eraseCredentials(). Tambm pode ser til implementar a interface EquatableInterface, a qual define
um mtodo para verificar se o usurio igual ao usurio atual. Essa interface requer um mtodo isEqualTo() .
Vamos ver isso na prtica:
// src/Acme/WebserviceUserBundle/Security/User/WebserviceUser.php
namespace Acme\WebserviceUserBundle\Security\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
class WebserviceUser implements UserInterface, EquatableInterface
{

388

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

private
private
private
private

$username;
$password;
$salt;
$roles;

public function __construct($username, $password, $salt, array $roles)


{
$this->username = $username;
$this->password = $password;
$this->salt = $salt;
$this->roles = $roles;
}
public function getRoles()
{
return $this->roles;
}
public function getPassword()
{
return $this->password;
}
public function getSalt()
{
return $this->salt;
}
public function getUsername()
{
return $this->username;
}
public function eraseCredentials()
{
}
public function isEqualTo(UserInterface $user)
{
if (!$user instanceof WebserviceUser) {
return false;
}
if ($this->password !== $user->getPassword()) {
return false;
}
if ($this->getSalt() !== $user->getSalt()) {
return false;
}
if ($this->username !== $user->getUsername()) {
return false;
}
return true;
}
}

3.1. Cookbook

389

Symfony Docs pt-BR Documentation, Verso 2.4

Se voc tiver mais informaes sobre seus usurios - como um primeiro nome - ento voc pode adicionar um campo
firstName para guardar esse dado.
Criar um Provider de Usurio

Agora que voc tem uma classe User, voc vai criar um provider de usurio, que ir pegar informaes de usurio de
algum servio web, criar um objeto WebserviceUser e popular ele com os dados.
O provider de usurio apenas uma classe PHP que deve implementar a UserProviderInterface, que requer a definio de trs mtodos: loadUserByUsername($username), refreshUser(UserInterface
$user) e supportsClass($class). Para mais detalhes, consulte UserProviderInterface.
Aqui est um exemplo de como isso pode parecer:
// src/Acme/WebserviceUserBundle/Security/User/WebserviceUserProvider.php
namespace Acme\WebserviceUserBundle\Security\User;
use
use
use
use

Symfony\Component\Security\Core\User\UserProviderInterface;
Symfony\Component\Security\Core\User\UserInterface;
Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
Symfony\Component\Security\Core\Exception\UnsupportedUserException;

class WebserviceUserProvider implements UserProviderInterface


{
public function loadUserByUsername($username)
{
// make a call to your webservice here
$userData = ...
// pretend it returns an array on success, false if there is no user
if ($userData) {
$password = ...;
// ...
return new WebserviceUser($username, $password, $salt, $roles);
}
throw new UsernameNotFoundException(sprintf(Username "%s" does not exist., $username));
}

public function refreshUser(UserInterface $user)


{
if (!$user instanceof WebserviceUser) {
throw new UnsupportedUserException(sprintf(Instances of "%s" are not supported., get_cl
}
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class)
{
return $class === Acme\WebserviceUserBundle\Security\User\WebserviceUser;
}
}

390

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Crie um Servio para o Provider de Usurio

Agora voc tornar o provider de usurio disponvel como um servio:


YAML

# src/Acme/WebserviceUserBundle/Resources/config/services.yml
parameters:
webservice_user_provider.class: Acme\WebserviceUserBundle\Security\User\WebserviceUserProvid
services:
webservice_user_provider:
class: "%webservice_user_provider.class%"

XML

<!-- src/Acme/WebserviceUserBundle/Resources/config/services.xml -->


<parameters>
<parameter key="webservice_user_provider.class">Acme\WebserviceUserBundle\Security\User\Webs
</parameters>
<services>
<service id="webservice_user_provider" class="%webservice_user_provider.class%"></service>
</services>

PHP
// src/Acme/WebserviceUserBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;

$container->setParameter(webservice_user_provider.class, Acme\WebserviceUserBundle\Security\U

$container->setDefinition(webservice_user_provider, new Definition(%webservice_user_provider.

Dica: A verdadeira implementao do provider de usurio provavelmente ter algumas dependncias, opes de
configurao ou outros servios. Adicione eles como argumentos na definio de servio.
Nota: Certifique-se que o arquivo de servios est sendo importado. Veja Importando configurao com imports para
mais detalhes.

Modifique o security.yml

Tudo ser combinado em sua configurao de segurana. Adicione o provider de usurio na lista de providers na seo
security. Escolha um nome para o provider de usurio (por exemplo, webservice) e mencione o id do servio que
voc acabou de definir.
YAML
// app/config/security.yml
security:
providers:
webservice:
id: webservice_user_provider

XML

3.1. Cookbook

391

Symfony Docs pt-BR Documentation, Verso 2.4

<!-- app/config/security.xml -->


<config>
<provider name="webservice" id="webservice_user_provider" />
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
providers => array(
webservice => array(
id => webservice_user_provider,
),
),
));

O Symfony tambm precisa saber como codificar as senhas que so fornecidas no site pelos usurios, por exemplo,
atravs do preenchimento de um formulrio de login. Voc pode fazer isso adicionando uma linha na seo encoders
da sua configurao de segurana:
YAML
# app/config/security.yml
security:
encoders:
Acme\WebserviceUserBundle\Security\User\WebserviceUser: sha512

XML
<!-- app/config/security.xml -->
<config>
<encoder class="Acme\WebserviceUserBundle\Security\User\WebserviceUser">sha512</encoder>
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
encoders => array(
Acme\WebserviceUserBundle\Security\User\WebserviceUser => sha512,
),
));

O valor aqui deve corresponder porm com as senhas que foram originalmente codificadas ao criar os seus usurios
(no entanto os usurios foram criados). Quando um usurio submete a sua senha, o salt acrescentado ao valor da
senha e ento so codificados usando este algoritmo antes de ser comparada com o hash da senha retornado pelo seu
mtodo getPassword(). Alm disso, dependendo das suas opes, a senha pode ser codificada vrias vezes e
codificada para base64.

392

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Detalhes sobre como as senhas so codificadas


O Symfony utiliza um mtodo especfico para combinar o salt e codificar a senha antes de compar-la com a
senha codificada. Se o getSalt() no retornar nada, ento a senha submetida simplesmente codificada
utilizando o algoritmo que voc especificou no security.yml. Se um salt especificado, ento o valor
seguinte criado e ento feito o hash atravs do algoritmo:
$password.{.$salt.};,
Caso os usurios externos tenham nas suas senhas um salt atravs de um mtodo diferente, ento voc ter
um pouco mais de trabalho para que o Symfony codifique corretamente a senha. Isso est alm do escopo
deste artigo, mas incluiria estender a classe MessageDigestPasswordEncoder e sobrescrever o mtodo
mergePasswordAndSalt.
Alm disso, o hash, por padro, codificado vrias vezes e codificado para base64. Para obter detalhes especficos, consulte MessageDigestPasswordEncoder. Para evitar isso, configure ele no seu arquivo de configurao:
YAML
# app/config/security.yml
security:
encoders:
Acme\WebserviceUserBundle\Security\User\WebserviceUser:
algorithm: sha512
encode_as_base64: false
iterations: 1

XML
<!-- app/config/security.xml -->
<config>
<encoder class="Acme\WebserviceUserBundle\Security\User\WebserviceUser"
algorithm="sha512"
encode-as-base64="false"
iterations="1"
/>
</config>

PHP
// app/config/security.php
$container->loadFromExtension(security, array(
encoders => array(
Acme\WebserviceUserBundle\Security\User\WebserviceUser => array(
algorithm
=> sha512,
encode_as_base64 => false,
iterations
=> 1,
),
),
));

3.1.12 Cache
Como usar Varnish para aumentar a velocidade do meu Website
Pelo cache do Symfony2 usar os cache headers padres do HTTP, o Proxy Reverso do Symfony2 pode facilmente se
substituido por qualquer por qualquer outro proxy reverso. Varnish um poderoso, open-source, HTTP accelerator
capaz de servir contedo em cache de forma rpida em incluindo suporte Edge Side Includes (ESI).

3.1. Cookbook

393

Symfony Docs pt-BR Documentation, Verso 2.4

Configurao

Como visto anteriormente, Symfony2 esperto o bastente para detectar se fala com um proxy reverso que compreende
ESI ou no. Ele trabalha fora da caixa quando voc usa o proxy reverso do Symfony2, mas voc precisa de um
configurao especial para poder trabalhar com Varnish. Felizmente, Symfony2 depende ainda de outro padro escrito
por Akama (Edge Architecture), ento as dicas de configurao desde captulo podem ser teis mesmo se voc no
usar Symfony2.
Nota: Varnish suporta apenas o atributo src para tags ESI (atributos onerror e alt so ignorados).
Primeiro, configure o Varnish para que ele informe o suporte ESI adicionando um Surrogate-Capability ao
cabealho nas requisies enviadas ao servidor de aplicao:
sub vcl_recv {
set req.http.Surrogate-Capability = "abc=ESI/1.0";
}

Aps isso, otimize o Varnish para que ele apenas analise o contedo da resposta quando existir ao menos uma tag ESI,
verificando o cabealho Surrogate-Control que adicionado automaticamente pelo Symfony2.
sub vcl_fetch {
if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
unset beresp.http.Surrogate-Control;
// para Varnish >= 3.0
set beresp.do_esi = true;
// para Varnish < 3.0
// esi;
}
}

Cuidado: Compresso com ESI no suportada pelo Varnish at a verso 3.0 (leia GZIP and Varnish). Se voc
no est utilizando Varnish 3.0, coloque um servidor web a frente do Varnish para executar a compresso.

Invalidao do Cache

Voc nunca dever precisar invalidar os dados em cache porque a invalidao colocada nativamente nos modelos de
cache HTTP (veja Invalidao do Cache).
Ainda assim, o Varnish pode ser configurado para aceitar um mtodo HTTP PURGE especial que invalidar o cache
para derterminado recurso:
sub vcl_hit {
if (req.request == "PURGE") {
set obj.ttl = 0s;
error 200 "Purgado";
}
}
sub vcl_miss {
if (req.request == "PURGE") {
error 404 "No Purgado";
}
}

394

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

Cuidado: Voc deve proteger o mtodo HTTPPURGE para evitar que qualquer pessoa possa purgar os dados
em cache.

3.1.13 Templating
Utilizando variveis em todas templates (Variveis globais)
Algumas vezes voc quer que uma varivel esteja disponvel em todas as templates que utiliza. Isto possvel configurando o twig dentro do arquivo app/config/config.yml :
# app/config/config.yml
twig:
# ...
globals:
ga_tracking: UA-xxxxx-x

Agora a varivel ga_tracking est disponvel em todas templates Twig e pode ser acessada da seguinte forma.
<p>Our google tracking code is: {{ ga_tracking }} </p>

fcil! Voc tambm pode utilizar do sistema de parmetros (Parmetros do Servio), que permite voc isolar e
reutilizar o valor como a seguir.
; app/config/parameters.yml
[parameters]
ga_tracking: UA-xxxxx-x
# app/config/config.yml
twig:
globals:
ga_tracking: %ga_tracking%

A mesma varivel est disponvel exatamente como antes.


Variveis globais mais complexas

Se a varivel global que deseja definir mais complexa, como um objeto por exemplo, ento voc no poder utilizar
o mtodo acima. Ao invs disso, precisa criar uma extenso Twig (Twig Extension) e retornar a varivel global como
uma das entradas no mtodo getGlobals.

3.1.14 Log
Como usar o Monolog para escrever Logs
O Monolog uma biblioteca de log para o PHP 5.3 usada pelo Symfony2. inspirado pela biblioteca LogBook do
Python.
Utilizao

Para fazer o log de uma mensagem, basta obter o servio logger a partir do container em seu controlador:

3.1. Cookbook

395

Symfony Docs pt-BR Documentation, Verso 2.4

public function indexAction()


{
$logger = $this->get(logger);
$logger->info(I just got the logger);
$logger->err(An error occurred);
// ...
}

O servio logger possui mtodos diferentes para os nveis de log. Para mais detalhes sobre os mtodos que esto
disponveis, veja LoggerInterface.
Manipuladores (handlers) e Canais (channels): Escrevendo logs em locais diferentes

No Monolog, cada logger define um canal de log, que organiza as suas mensagens de log em diferentes categorias.
Ento, cada canal tem uma pilha de manipuladores para escrever os logs (os manipuladores podem ser compartilhados).
Dica: Ao injetar o logger em um servio voc pode usar um canal personalizado para controlar que canal o logger
ir realizar o log.
O manipulador bsico o StreamHandler, que grava logs em um stream (por padro em app/logs/prod.log
no ambiente prod e app/logs/dev.log no ambiente dev).
O Monolog tambm vem com um poderoso manipulador integrado para realizar log no ambiente prod:
FingersCrossedHandler. Ele permite que voc armazene as mensagens em um buffer e registre o log somente se a mensagem alcana o nvel de ao (ERROR na configurao fornecida na edio standard), enviando as
mensagens para outro manipulador.
Usando vrios manipuladores O logger usa uma pilha de manipuladores que so chamados sucessivamente. Isto
permite registrar facilmente as mensagens de vrias maneiras.
YAML
# app/config/config.yml
monolog:
handlers:
applog:
type: stream
path: /var/log/symfony.log
level: error
main:
type: fingers_crossed
action_level: warning
handler: file
file:
type: stream
level: debug
syslog:
type: syslog
level: error

XML
<!-- app/config/config.xml -->
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

396

Captulo 3. Cookbook

Symfony Docs pt-BR Documentation, Verso 2.4

xmlns:monolog="http://symfony.com/schema/dic/monolog"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/ser
http://symfony.com/schema/dic/monolog http://symfony.com/schema/dic/mono
<monolog:config>
<monolog:handler
name="applog"
type="stream"
path="/var/log/symfony.log"
level="error"
/>
<monolog:handler
name="main"
type="fingers_crossed"
action-level="warning"
handler="file"
/>
<monolog:handler
name="file"
type="stream"
level="debug"
/>
<monolog:handler
name="syslog"
type="syslog"
level="error"
/>
</monolog:confi