Documentação do Symfony - versão 3.1
Renderizada do repositório symfony-docs-pt-BR no Github
O Symfony dispõe de uma grande variedade de maneiras para personalizar como um formulário é renderizado. Neste guia, você vai aprender a personalizar cada parte possível do seu formulário com o menor esforço, se você usa o Twig ou PHP como sua templating engine.
Lembre-se que o widget HTML, a label e o erro de um campo do formulário podem ser facilmente
renderizados usando a função Twig form_row
ou o método helper PHP
row
:
1 | {{ form_row(form.age) }}
|
1 | <?php echo $view['form']->row($form['age']) }} ?>
|
Você também pode renderizar cada uma das três partes do campo individualmente:
1 2 3 4 5 | <div>
{{ form_label(form.age) }}
{{ form_errors(form.age) }}
{{ form_widget(form.age) }}
</div>
|
1 2 3 4 5 | <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 formulário, os erros e o widget HTML são renderizados usando um conjunto de marcação que vem por padrão com o symfony. Por exemplo, ambos os templates acima seriam renderizados da seguinte forma:
1 2 3 4 5 6 7 | <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 protótipos rápidos e para testar um formulário, você pode renderizar todo o formulário com apenas uma linha:
1 | {{ form_widget(form) }}
|
1 | <?php echo $view['form']->widget($form) }} ?>
|
O restante desta receita irá explicar como cada parte de marcação do formulário pode ser modificada em vários níveis diferentes. Para mais informações sobre a renderização do formulário em geral, consulte Renderizando um formulário em um Template.
O Symfony utiliza fragmentos de formulário - um pequeno pedaço de um template que renderiza apenas
uma parte de um formulário - para renderizar cada parte de um formulário - labels de campo, os erros,
campos de texto input
, tags select
, etc
Os fragmentos são definidos como blocos no Twig e como arquivos de template no PHP.
Um tema é nada mais do que um conjunto de fragmentos que você deseja usar quando está renderizando um formulário. Em outras palavras, se você quiser personalizar uma parte de como um formulário é processado, você vai importar um tema que contém uma personalização dos fragmentos apropriados do formulário.
O Symfony vem com um tema padrão (form_div_layout.html.twig no Twig e
FrameworkBundle:Form
no PHP) que define cada fragmento necessário
para renderizar cada parte de um formulário.
Na seção 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
1 | {{ form_widget(form.age) }}
|
1 | <?php echo $view['form']->widget($form['age']) ?>
|
renderiza:
1 | <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 oposição a sua label
ou errors
).
No Twig ele seria por padrão o bloco integer_widget
do template
form_div_layout.html.twig.
No PHP ele seria o arquivo integer_widget.html.php
localizado no diretório
FrameworkBundle/Resources/views/Form
.
A implementação padrão do fragmento integer_widget
seria parecida com esta:
1 2 3 4 5 | {# form_div_layout.html.twig #}
{% block integer_widget %}
{% set type = type|default('number') %}
{{ block('field_widget') }}
{% endblock integer_widget %}
|
1 2 | <!-- integer_widget.html.php -->
<?php echo $view['form']->renderBlock('field_widget', array('type' => isset($type) ? $type : "number")) ?>
|
Como você pode ver, este próprio fragmento renderiza outro fragmento - field_widget
:
1 2 3 4 5 | {# form_div_layout.html.twig #}
{% block field_widget %}
{% set type = type|default('text') %}
<input type="{{ type }}" {{ block('widget_attributes') }} value="{{ value }}" />
{% endblock field_widget %}
|
1 2 3 4 5 6 | <!-- 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') ?>
/>
|
O ponto é, os fragmentos ditam a saída HTML de cada parte de um formulário. Para personalizar a saída do formulário, você só precisa identificar e substituir o fragmento apropriado. O conjunto dessas personalizações de fragmentos de formulário é conhecida como “tema” de formulário. Ao renderizar um formulário, você pode escolher qual(ais) tema(s) de formulário deseja aplicar.
No Twig, um tema é um único arquivo de template e os fragmentos são os blocos definidos neste arquivo.
No PHP um tema é um diretório e os fragmentos são os arquivos de template individuais neste diretório.
Para ver o poder da tematização de formulários, suponha que você queira envolver cada campo input number
com uma tag div
. A chave para fazer isso é personalizar o
fragmento integer_widget
.
Ao personalizar o bloco de campo de formulário no Twig, você tem duas opções de onde o bloco de formulário personalizado pode residir:
Método | Prós | Contras |
---|---|---|
Dentro do mesmo template que o formulário | Rápido e fácil | Não pode ser reutilizado em outros templates |
Dentro de um template separado | Pode ser reutilizado por muitos templates | Requer que um template extra seja criado |
Ambos os métodos têm o mesmo efeito, mas são melhores em situações diferentes.
A maneira mais fácil de personalizar o bloco integer_widget
é personalizá-lo
diretamente no mesmo template que está renderizando o formulário.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | {% extends '::base.html.twig' %}
{% form_theme form _self %}
{% block integer_widget %}
<div class="integer_widget">
{% set type = type|default('number') %}
{{ block('field_widget') }}
</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 formulários 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 método é que o bloco de formulário personalizado não pode ser reutilizado ao renderizar outros formulários em outros templates. Em outras palavras, este método é mais útil ao fazer personalizações de formulários que são específicas para um único formulário em sua aplicação. Se você quiser reutilizar uma personalização em vários (ou todos) os formulários de sua aplicação, leia a próxima seção.
Você também pode optar por colocar o bloco de formulário integer_widget
personalizado em um
template totalmente separado. O código e o resultado final é o mesmo, mas
agora você pode reutilizar a personalização de formulário em muitos templates:
1 2 3 4 5 6 7 | {# 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 formulário personalizado, você precisa dizer ao Symfony
para usá-lo. Dentro do template onde você está renderizando o seu formulário,
diga ao Symfony para usar o template através da tag form_theme
:
1 2 3 | {% 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.
Ao usar o PHP como templating engine, o único método de personalizar um fragmento é criar um novo arquivo template - é semelhante ao segundo método utilizado pelo Twig.
O arquivo de template deve ser nomeado após o fragmento. Você deve criar um arquivo integer_widget.html.php
para personalizar o fragmento integer_widget
.
1 2 3 4 | <!-- 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 : "number")) ?>
</div>
|
Agora que você criou o template de formulário personalizado, você precisa dizer ao Symfony
para usá-lo. Dentro do template onde você está renderizando o seu formulário,
diga ao Symfony para usar o tema através do método helper setTheme
:
1 2 3 | <?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
.
Até agora, para sobrescrever um bloco de formulário em particular, o melhor método é copiar o bloco padrão 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 através da referência ao bloco base quando for personalizá-lo.
Isso é fácil de fazer, mas varia um pouco dependendo se as personalizações de seu bloco de formulário estão no mesmo template que o formulário ou em um template separado.
Importe os blocos adicionando uma tag use
no template onde você está renderizando
o formulário:
1 | {% use 'form_div_layout.html.twig' with integer_widget as base_integer_widget %}
|
Agora, quando os blocos do form_div_layout.html.twig são importados, o
bloco integer_widget
é chamado base_integer_widget
. Isto significa que quando
você redefinir o bloco integer_widget
, você pode fazer referência a marcação padrão
através do base_integer_widget
:
1 2 3 4 5 | {% block integer_widget %}
<div class="integer_widget">
{{ block('base_integer_widget') }}
</div>
{% endblock %}
|
Se as personalizações do formulário residirem dentro de um template externo, você pode fazer referência
ao bloco base usando a função parent()
do Twig:
1 2 3 4 5 6 7 8 | {# 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 %}
|
Note
Não é possível fazer referência ao bloco base quando se utiliza o PHP como template engine. Você tem que copiar manualmente o conteúdo do bloco base para o seu novo arquivo de template.
Se você deseja que uma certa personalização de formulário seja global para a sua aplicação, você pode conseguir isso fazendo as personalizações de formulário em um template externo e depois importá-lo dentro da sua configuração da aplicação:
Usando a seguinte configuração, quaisquer blocos de formulários personalizados dentro do
template AcmeDemoBundle:Form:fields.html.twig
serão usados globalmente quando um
formulário é renderizado.
1 2 3 4 5 6 | # app/config/config.yml
twig:
form:
resources:
- 'AcmeDemoBundle:Form:fields.html.twig'
# ...
|
1 2 3 4 5 6 7 | <!-- app/config/config.xml -->
<twig:config ...>
<twig:form>
<resource>AcmeDemoBundle:Form:fields.html.twig</resource>
</twig:form>
<!-- ... -->
</twig:config>
|
1 2 3 4 5 6 7 8 9 10 | // app/config/config.php
$container->loadFromExtension('twig', array(
'form' => array(
'resources' => array(
'AcmeDemoBundle:Form:fields.html.twig',
),
),
// ...
));
|
Por padrão, o Twig usa um layout div ao renderizar os formulários. Algumas pessoas, no entanto,
podem preferir renderizar formulários em um layout de tabela. Para isso use o recurso
form_table_layout.html.twig
como layout:
1 2 3 4 5 | # app/config/config.yml
twig:
form:
resources: ['form_table_layout.html.twig']
# ...
|
1 2 3 4 5 6 7 | <!-- app/config/config.xml -->
<twig:config ...>
<twig:form>
<resource>form_table_layout.html.twig</resource>
</twig:form>
<!-- ... -->
</twig:config>
|
1 2 3 4 5 6 7 8 9 10 | // app/config/config.php
$container->loadFromExtension('twig', array(
'form' => array(
'resources' => array(
'form_table_layout.html.twig',
),
),
// ...
));
|
Se você quer fazer a alteração somente em um template, adicione a seguinte linha em seu arquivo de template em vez de adicionar o template como um recurso:
1 | {% form_theme form 'form_table_layout.html.twig' %}
|
Note que a variável form
no código acima é a variável de visão do formulário
que você passou para o seu template.
Usando a seguinte configuração, quaisquer fragmentos de formulários personalizados no interior do
diretório src/Acme/DemoBundle/Resources/views/Form
serão usados globalmente quando um
formulário é renderizado.
1 2 3 4 5 6 7 | # app/config/config.yml
framework:
templating:
form:
resources:
- 'AcmeDemoBundle:Form'
# ...
|
1 2 3 4 5 6 7 8 9 | <!-- app/config/config.xml -->
<framework:config ...>
<framework:templating>
<framework:form>
<resource>AcmeDemoBundle:Form</resource>
</framework:form>
</framework:templating>
<!-- ... -->
</framework:config>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 | // app/config/config.php
// PHP
$container->loadFromExtension('framework', array(
'templating' => array(
'form' => array(
'resources' => array(
'AcmeDemoBundle:Form',
),
),
),
// ...
));
|
Por padrão, a engine PHP usa um layout div ao renderizar formulários. Algumas pessoas,
no entanto, podem preferir renderizar formulários em um layout de tabela. Para isso use o recurso FrameworkBundle:FormTable
como layout:
1 2 3 4 5 6 | # app/config/config.yml
framework:
templating:
form:
resources:
- 'FrameworkBundle:FormTable'
|
1 2 3 4 5 6 7 8 9 | <!-- app/config/config.xml -->
<framework:config ...>
<framework:templating>
<framework:form>
<resource>FrameworkBundle:FormTable</resource>
</framework:form>
</framework:templating>
<!-- ... -->
</framework:config>
|
1 2 3 4 5 6 7 8 9 10 11 12 | // app/config/config.php
$container->loadFromExtension('framework', array(
'templating' => array(
'form' => array(
'resources' => array(
'FrameworkBundle:FormTable',
),
),
),
// ...
));
|
Se você quer fazer a alteração somente em um template, adicione a seguinte linha em seu arquivo de template em vez de adicionar o template como um recurso:
1 | <?php $view['form']->setTheme($form, array('FrameworkBundle:FormTable')); ?>
|
Note que a variável $form
no código acima é a variável de visão do formulário
que você passou para o seu template.
Até agora, você viu as diferentes maneiras que pode personalizar a saída dos widgets
de todos os tipos de campo texto. Você também 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
personalização de um fragmento cujo nome é uma combinação do atributo id do campo e
qual parte do campo está sendo personalizada. Por exemplo:
1 2 3 4 5 6 7 8 9 | {% form_theme form _self %}
{% block _product_name_widget %}
<div class="text_widget">
{{ block('field_widget') }}
</div>
{% endblock %}
{{ form_widget(form.name) }}
|
1 2 3 4 5 6 7 8 9 10 | <!-- 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]
).
Tip
A parte product
do campo é o nome do formulário, que pode ser definido
manualmente ou gerado automaticamente com base no nome do tipo de formulário (por exemplo,
ProductType
equivale ao product
). Se você não tem certeza de qual é o nome de seu
formulário, apenas visualize o código fonte do formulário gerado.
Você também pode sobrescrever a marcação de uma linha inteira de campo utilizando o mesmo método:
1 2 3 4 5 6 7 8 9 10 | {# _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 %}
|
1 2 3 4 5 6 7 | <!-- _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>
|
Até agora, esta receita tem demonstrado diversas maneiras de personalizar um único pedaço de como um formulário é renderizado. A chave é personalizar um fragmento específico que corresponde à parte do formulário que você deseja controlar (veja naming form blocks).
Nas próximas seções, você vai ver como é possível fazer várias personalizações comuns de formulário. Para aplicar essas personalizações, utilize um dos métodos descritos na seção Tematizando os Formulários.
Note
O componente de formulário só lida com como os erros de validação são renderizados, e não com as mensagens de erro de validação. As mensagens de erro em si são determinadas pelas restrições de validação que você aplicou aos seus objetos. Para mais informações, consulte o capítulo sobre validation.
Há muitas formas diferentes para personalizar como os erros são renderizados quando um
formulário é enviado com erros. As mensagens de erro para um campo são renderizadas
quando você usa o helper form_errors
:
1 | {{ form_errors(form.age) }}
|
1 | <?php echo $view['form']->errors($form['age']); ?>
|
Por padrão, os erros são renderizados dentro de uma lista não ordenada:
1 2 3 | <ul>
<li>This field is required</li>
</ul>
|
Para sobrecrever como os erros são renderizados para todos os campos, basta copiar, colar
e personalizar o fragmento field_errors
.
1 2 3 4 5 6 7 8 9 10 11 12 | {# 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 %}
|
1 2 3 4 5 6 7 8 9 10 11 12 | <!-- 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 ?>
|
Tip
Veja Tematizando os Formulários para saber como aplicar essa personalização.
Você também pode personalizar a saída de erro de apenas um tipo de campo específico. Por exemplo, certos erros que são mais globais para o seu formulário (ou seja, não específico para apenas um campo) são renderizados separadamente, geralmente no topo do seu formulário:
1 | {{ form_errors(form) }}
|
1 | <?php echo $view['form']->render($form); ?>
|
Para personalizar apenas a marcação usada para esses erros, siga as mesmas instruções
acima, mas agora chame o bloco form_errors
(Twig) / o arquivo form_errors.html.php
(PHP). Agora, quando os erros para o tipo form
são renderizados, o seu fragmento
personalizado será usado em vez do padrão field_errors
.
Quando você pode gerenciá-lo, a maneira mais fácil de renderizar um campo de formulário é através da
função form_row
, que renderiza a label, os erros e o widget HTML de
um campo. Para personalizar a marcação usada para renderizar todas as linhas de campo do formulário,
sobrescreva o fragmento field_row
. Por exemplo, suponha que você deseja adicionar uma
classe para o elemento div
que envolve cada linha:
1 2 3 4 5 6 7 8 | {# field_row.html.twig #}
{% block field_row %}
<div class="form_row">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endblock field_row %}
|
1 2 3 4 5 6 | <!-- 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>
|
Tip
Veja Tematizando os Formulários para saber como aplicar essa personalização.
Se você quiser indicar todos os seus campos obrigatórios com um asterisco (*
),
você pode fazer isso personalizando o fragmento field_label
.
No Twig, se você estiver fazendo a personalização de formulário dentro do mesmo template que o seu
formulário, modifique a tag use
e adicione o seguinte:
1 2 3 4 5 6 7 8 9 | {% use 'form_div_layout.html.twig' with field_label as base_field_label %}
{% block field_label %}
{{ block('base_field_label') }}
{% if required %}
<span class="required" title="This field is required">*</span>
{% endif %}
{% endblock %}
|
No Twig, se você estiver fazendo a personalização do formulário dentro de um template separado, use o seguinte:
1 2 3 4 5 6 7 8 9 | {% 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 conteúdo do template original:
1 2 3 4 5 6 7 8 9 | <!-- field_label.html.php -->
<!-- original content -->
<label for="<?php echo $view->escape($id) ?>" <?php foreach($attr as $k => $v) { printf('%s="%s" ', $view->escape($k), $view->escape($v)); } ?>><?php echo $view->escape($view['translator']->trans($label)) ?></label>
<!-- customization -->
<?php if ($required) : ?>
<span class="required" title="This field is required">*</span>
<?php endif ?>
|
Tip
Veja Tematizando os Formulários para saber como aplicar essa personalização.
Você também pode personalizar os widgets do formulário para ter uma mensagem opcional de “ajuda”.
No Twig, se você está fazendo a personalização do formulário dentro do mesmo template que o seu
formulário, modifique a tag use
e adicione o seguinte:
1 2 3 4 5 6 7 8 9 | {% 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 personalização do formulário dentro de um template separado, use o seguinte:
1 2 3 4 5 6 7 8 9 | {% extends 'form_div_layout.html.twig' %}
{% block field_widget %}
{{ parent() }}
{% if help is defined %}
<span class="help">{{ help }}</span>
{% endif %}
{% endblock %}
|
Ao usar o PHP como template engine você tem que copiar o conteúdo do template original:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <!-- 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 variável help
:
1 | {{ form_widget(form.title, {'help': 'foobar'}) }}
|
1 | <?php echo $view['form']->widget($form['title'], array('help' => 'foobar')) ?>
|
Tip
Veja:ref:cookbook-form-theming-methods para saber como aplicar essa personalização.
A maioria das funções disponíveis para renderizar diferentes partes de um formulário (por exemplo, o widget de formulário, a label do formulário, os erros de formulário, etc) também permitem que você faça certas personalizações diretamente. Veja o exemplo a seguir:
1 2 | {# render a widget, but add a "foo" class to it #}
{{ form_widget(form.name, { 'attr': {'class': 'foo'} }) }}
|
1 2 3 4 5 6 | <!-- 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 contém “variáveis” de formulário. Para mais detalhes sobre este conceito no Twig, veja twig-reference-form-variables.