Symfony2 form layout - variables source - forms

Here's the main form layout twig file:
https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
An example:
{% block form_widget_simple %}
{% spaceless %}
{% set type = type|default('text') %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endspaceless %}
{% endblock form_widget_simple %}
I wonder where varaibles like "type" or "value" come from?
The goal I'm trying to achieve is to set form row's label as a placeholder in the widget. How can I accomplish this?

Details how to override Template for Form field you will find here.
If you trying to change labels to placeholders all you need is to change way of rendering your forms. Remove form_widget(form) and switch to render every separate form field:
{# ... #}
<div class="form-group">
{{ form_errors(form.email) }}
{{ form_widget(form.email, {'attr': {'class': 'form-control', 'placeholder': 'E-mail address'|trans }}) }}
</div>
{# ... #}
This example generate input for email field and html/css classes for bootstrap.
And shows you how {{ type }} and {{ value }} are passed - by attr array.
Good Luck!

Related

symfony2 fosUserbundle input type with bootstrap

I'm quite new to symfony2, i have managed to implement fosUserBundle and Braincrafted bootstrap bundle.
I am trying to style the registration form to use boot strap and the inputs with input-sm class
the username and email are displaying as input-sm but the 2 password fields refuse to resize to the class i have applied.
is there somewhere in the fosUserbundle where the password field widget is configured
{% trans_default_domain 'FOSUserBundle' %}
{{ form_start(form, { 'style': 'horizontal', 'col_size': 'xs', 'label_col': 5, 'widget_col': 7, attr: {class: 'pull-left'}}) }}
{{ form_row(form.username, { attr: {class: 'input-sm'}}) }}
{{ form_row(form.email, { attr: {class: 'input-sm'}}) }}
{{ form_row(form.plainPassword, { attr: {class: 'input-sm'}}) }}
{{ form_row(form.plainPassword.second, { attr: {class: 'input-sm'}}) }}
<div class="col-sm-5"></div>
<div class="col-sm-7">
<input class="btn-primary btn-sm" type="submit" value="{{ 'registration.submit'|trans }}" />
</div>
{{ form_rest(form) }}
{{ form_end(form) }}
FosUserBundle or not, you can overwrite Twig form rendering directly to achieve this, have a look at How to customize form rendering in SF cookbook.
Basically you can either get more precise in your template by splitting form_row into labels, widgets and errors to target the input ("form_widget" below) more specifically and apply the correct classes :
{{ form_errors(form.plainPassword) }}
{{ form_label(form.plainPassword) }}
{{ form_widget(form.plainPassword) }}
You can also generate your own form style for desired type of input by decalring it directly in your template :
{% form_theme form _self %}
{% block integer_widget %}
<div class="integer_widget">
// Your custom layout
</div>
{% endblock %}
{% Block body %}
// Rendering your form, your custom layout will be used.
{% endblock %}
You can also declare it in a separate template if you want to use it somewhere else, refer to the above cookbook page to see how.

How is symfony form rendered in the view

I was sure I understand this process, but when I dug deeper I saw I am wrong ;(
Let's take simple form, please notice that this form contains 3 fields
$form = $this->createFormBuilder($defaultData, ['csrf_protection' => false])
->add('email', 'email')
->add('name', 'text')
->add('message', 'textarea')
->getForm()
->createView();
which is rendered as
{{ form(form, {'attr': {'novalidate': 'novalidate'}}) }}
into
Built in "form" block from vendor\symfony\symfony\src\Symfony\Bridge\Twig\Resources\views\Form\form_div_layout.html.twig looks like:
{% block form %}
{% spaceless %}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
{% endspaceless %}
{% endblock form %}
and form_widget(form)
{% block form_widget %}
{% spaceless %}
{% if compound %}
{{ block('form_widget_compound') }}
{% else %}
{{ block('form_widget_simple') }}
{% endif %}
{% endspaceless %}
{% endblock form_widget %}
{% block form_widget_simple %}
{% spaceless %}
{% set type = type|default('text') %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endspaceless %}
{% endblock form_widget_simple %}
Let's modify form and form_widget a bit:
{% block form %}
{% spaceless %}
{{ form_start(form) }}
-form_widget before-
{{ form_widget(form) }}
-form_widget after-
{{ form_end(form) }}
{% endspaceless %}
{% endblock form %}
{% block form_widget %}
{% spaceless %}
- form_widget call -
{% if compound %}
- form_widget compound-
{{ block('form_widget_compound') }}
{% else %}
- form_widget simple-
{{ block('form_widget_simple') }}
{% endif %}
{% endspaceless %}
{% endblock form_widget %}
then as an output I got (listing 5):
<form name="form" method="post" action="" novalidate="novalidate">
-form_widget before-
- form_widget call -
- form_widget compound-
<div id="form" novalidate="novalidate">
<div>
<label for="form_email" class="required">Email</label>
<input type="email" id="form_email" name="form[email]" required="required" />
</div>
<div>
<label for="form_name" class="required">Name</label>
- form_widget call -
- form_widget simple-
<input type="text" id="form_name" name="form[name]" required="required" />
</div>
<div>
<label for="form_message" class="required">Message</label>
<textarea id="form_message" name="form[message]" required="required">Type your message here</textarea>
</div>
</div>
-form_widget after-
</form>
we can easly notcie the flow
form -> form_widget (input parameter is the whole form) -> form_widget_compound -> form_rows (iterates form elements into next function) -> form_row -> form_widget (this call form element is passed as parameter)
so here is the time for question, if form_widget is called 4 times (or more), once for form, 3 times for fields then why in listing 5 'form_widget call' text appears only 2 times?
or other words how email and message were rendered?
The form theming in SF2 uses inheritance. That means that if blocks named email_widget and textarea_widget are defined, neither your e-mail nor your textarea fields will use form_widget. They will rather respectively use their own widget blocks: email_widget and textarea_widget.
Well, in form_div_layout.twig, these two widget blocks are defined. Thus form_widget is not called for 2 of your fields. Thus your message is displayed 2 times instead of 4.
If you want to customize the rendering of these fields, you will have to create your own block definitions email_widget and textarea_widget in your custom form theme file.
Edit:
The default form theme files are defined under Symfony\Bridge\Twig\Resources\views\Form. The default file used is form_div_layout.html.twig.
Although the inheritance logic itself is indeed in the PHP code of the TWIG bridge, it does not define which block inherit from which other block.
Inheritance is actually defined in your FormType classes. Each form type class sports a getParent() method that return the name of the form from which it inherits. The name of a form type is the result of the method getName() of its associated form type class. For instance, with a built-in example, Symfony\Component\Form\Extension\Core\Type\TextareaType:
Textarea >> Text >> Field >> Form
All you have to do is to look for the methods getParent() and getName(). Thus, by default, when rendering the textarea row, Twig will search the block textarea_row, text_row, field_row and finally form_row (the base default row). The first of these blocks that is defined in your form theme is rendered.
The definition of the blocks themselves happen in the form theme files.

Symfony 2 Forms with Twig: add form variable to existing types

I need my symfony2/twig forms to adhere to a certain condition: All form rows must look similar to this:
{% block form_row %}
<div class="CONSTANT_CLASS class_based_on_field_type class_based_on_error">
{{ form_label(form) }}
{{ form_widget(form) }}
...
</div>
{% endblock form_row %}
Notice that I need to get the field type within the form_row block. Alas, the field type is only defined at the widget level.
I definitely need a way to let my form_row know what type of field it is dealing with.
So i suppose it would be best to somehow override the form_row twig function.
Where can the default twig functions be overridden? And how could this be done?
Remember, this is not about customizing a form. I need to know how to add to existing form variables for existing field types.
#nifr:
The key answer for you seems to be the {% set typeClass ... %}. But there is not a defined variable text anywhere for the template. Look at form_div_layout.html.twig at line 158ff, I think the type really gets set only at the form_widget level and is thus capsulated to be there. That means using the type at the form_row level will aways result in the given default (So it occurred to me while testing). If you can prove this wrong I will happily accept your answer.
How to override the form_row block in Twig adding attributes by field-type?
Though you say this is not about form customization it can be achieved with it ... Quick Introduction for others reading this now.
The default symfony twig form extensions can be found here.
The default twig form theme can be found at Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig.
General information on how to override forms can be found in the How to Customize Form Rendering chapter of the Book but i will sum this up shortly.
form_row Default
{% block form_row %}
{% spaceless %}
<div>
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
Overriding Form Level
Add this to the form template you want to customize:
{% form_theme form _self %}
If you want to put the {% block form_row %} into another bundle/template use this:
{% form_theme form 'AcmeDemoBundle:Form:fields.html.twig' %}
Now insert your custom form_row block after the form_theme declaration or put it into the specified template (in our case this would be AcmeDemoBundle:Form:fields.html.twig).
In my example we will add the class 'error' if there is an error in the form row and a another classname nameely the type name of the current field-type.
{% block form_row %}
{% spaceless %}
{# set class to 'error' if errors exist #}
{% set attr = attr|merge({'class': attr.class|default('') ~ (errors|length > 0 ? ' error' : '') }) %}
{% set typeClass = ' ' ~ type|default('text') %}
{#
you could also implement a logic matching input types with an array of their
desired classname-representations here.
#}
{% set attr = attr|merge({'class': attr.class|default('') ~ type) }) %}
<div class="{% for class in attr.class %}{{ class }}{% endfor %}{{ typeClass }}">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
if you want to apply your form_row block system-wide add your AcmeDemoBundle:Form:fields.html.twig to your twig.templating.form.resources !
# app/config/config.yml
framework:
templating:
form:
resources:
- 'AcmeDemoBundle:Form'
In the form_row block you can use :
{{ form.vars.block_prefixes[2] }}
form.vars.block_prefixes gives you an array with more information and it might change with versions. But from what I have seen so far it is always index 2

symfony 2 form_row with 2 fields

I need something like below:
{% block form_row %}
<div class="form_row">
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_widget(form.field_name + '_previous') }}
{{ form_errors(form) }}
</div>
{% endblock form_row %}
i.e, two form fields in one row; second field name is equal to first_field_name + _previous.
For example, if field name is 'total_cost', then second field will be 'total_cost_previous'.
How can I do that?
i think you can do something like this:
{% set field = field_name ~ '_previous' %}
{{ form_widget(attribute(form, field)) }}

symfony2 form twig input value

I am doing a form with symfony2 and twig, form who get infos from BDD.
I want to customize render in function of some informations.
If my data chanson is empty, I want to show input to set it.
If my data is not empty I want to show a paragraphe who shows data, and a link for modify the value and show the input.
I try something like that :
{% if form_widget(session.chanson).attrvalue!='' %}
<p>{{form_widget(session.chanson).attrvalue}} <a>modify</a></p>
{% else %}
<p>{{ form_label(session.chanson,"Chanson : ") }}
{{ form_errors(session.chanson) }}
{{ form_widget(session.chanson) }}</p>
{% endif %}
It's not working. I try with value instead of attrvalue, it's not working neither.
Here is what symfony say : Item "attrvalue" for "<input type="text" id="form_chanson" name="form[chanson]" required="required" value="La Rage" />" does not exist in CDUserBundle:Prof:edit_session.html.twig at line 19
Did someone know the issue ?
Thank you,
You could check if the app.session.chanson variable is empty instead using:
{% if app.session.chanson %}
<p>{{ app.session.chanson }} modify</p>
{% else %}
<p>{{ form_label(app.session.chanson,"Chanson : ") }}
{{ form_errors(app.session.chanson) }}
{{ form_widget(app.session.chanson) }}</p>
{% endif %}
You then need to plug the action you want on the modify link.
Also note that if your object chanson is stored in a session, the right way to access it in your twig template is by using the app.session object.