i've created a form with symfony 2, and i check if the fields are unique with the UniqueEntity constraint. But i want the fields "firstname", "name" to appear in the message, like:
"Mark Blaze already exists!" rather than "this person already exists!"
can someone help?
part of the form builder
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('firstname')
->add('name')
Entity
/**
* #ORM\Entity
* #ORM\Entity(repositoryClass="InviteRepository")
* #UniqueEntity(fields={"firstname","name"}, message="this person already exists!")
*/
class Invite {
/**
* #ORM\Column(type="string",length=50)
*/
private $firstname;
/**
* #ORM\Column(type="string",length=50)
*/
private $name;
twig file
{{ form_start(form) }}
{{ form_errors(form) }}
{% spaceless %}
<div class="control-group">
{{ form_label(form.firstname, 'firstname *', { 'label_attr': { 'class':'control-label'} }) }}
{{ form_errors(form.firstname) }}
<div class="controls">
{{ form_widget(form.firstname) }}
{% if form.vars.help is defined %}
<span class="help-block">{{ form.vars.help }}</span>
{% endif %}
</div>
</div>
{% endspaceless %}
{% spaceless %}
<div class="control-group">
{{ form_label(form.name, 'name *', { 'label_attr': { 'class':'control-label'} }) }}
{{ form_errors(form.name) }}
<div class="controls">
{{ form_widget(form.name) }}
{% if form.vars.help is defined %}
<span class="help-block">{{ form.vars.help }}</span>
{% endif %}
</div>
</div>
{% endspaceless %}
Much thanks
In theory you can use this in the error message {{ value }} and this will represent the sent value. I use this in the emailconstraint so, 'The {{ value }} email address already in use'.
I think this would work for you too.
I rechecked your constraint and sadly, there you don't have the option to set a dynamic value. What I posted earlier was a solution for a really symfony validation and this constraint is part of doctrine. But any time, you can create your own constraint, if you want to fulfil your special needs:
$this->context->addViolationAt($errorPath, $constraint->message, array(), $criteria[$fields[0]]);
Related
I have this form:
class BillType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user')
->add('numberPlate')
->add('servicesPerformed', CollectionType::class, array(
'label' => false,
'entry_type' => ServicePerformedType::class,
'allow_add' => true,
))
->add('Save', SubmitType::class)
;
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'DefaultBundle\Entity\Bill'
));
}
being ServicePerformedType class this:
class ServicePerformedType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description', TextareaType::class, array('label' => false))
->add('price', TextType::class, array('label' => false))
->add('quantity', TextType::class, array('label' => false));
}
}
And this template to render the form:
{{ form(form) }}
Add service
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script type="text/javascript">
var index = 0;
$('a').on('click', function() {
var prototype = $('#bill_servicesPerformed').data('prototype');
prototype = prototype.replace(/_name_/g, index.toString());
$('#bill_servicesPerformed').html(prototype);
index++;
})
</script>
As it is said in the docs, to get a custom prototype I should add the lines below at the top of my template:
{% form_theme form _self %}
{% block _servicesPerformed_entry_widget %}
I WILL WRITE MY CUSTOM PROTOTYPE HERE
{% endblock %}
But when I press Add service I dont get the text I WILL WRITE MY CUSTOME PROTOTYPE HERE, but the description, fields and quantity related to the ServicePerformedType class as before..
NOTE: maybe there are other ways to custom a form prototype, but I'm interested on this, so will be very thankful to someone who give a solution related to this way to custom form prototypes, thanks.
I must warn you that customizing the prototype could be a bit tricky. If you change your FormType fields you'll need to go through the template and make the same changes as well or your form will fail to render.
What I like to do is to create a custom template for that specific field and then customize it appropriately. So, looking at your code I would do something like this:
Create a page template - the one you'll use to render the entire page, including the form.
{# MyBundle/Resources/views/myPage.html.twig #}
{% extends "::base.html.twig" %}
{# This will tell the form_theme to apply the
`MyBundle:form/fields/servicePerformed.html.twig` template to your "form" #}
{% form_theme form with [
'MyBundle:form/fields/servicePerformed.html.twig'
] %}
{% block body %}
<div>
{{ form_start(form) }}
{{ form_rest(form) }}
{{ form_end(form) }}
</div>
{% endblock %}
Now you'll need to create the template MyBundle/Resources/views/form/fields/servicePerformed.html.twig. It will be used to customize only the servicePerformed field. The template should look something like this
{% macro service_template(fields) %}
<tr>
<td>I WILL WRITE MY CUSTOM PROTOTYPE HERE</td>
</tr>
{% endmacro %}
{#
The block name must match your field name!
You can find it by going into the debug toolbar -> Forms ->
click on your form field and then search for "unique_block_prefix".
Copy that and add "_widget" at the end.
#}
{% block _service_performed_widget %}
<table data-prototype="{{ _self.service_template(form.vars.prototype)|e }}">
{% for fields in form.children %}
{{ _self.service_template(fields) }}
{% endfor %}
</table>
{% endblock %}
I want to note that in the field template I'm passing the original prototype _self.service_template(form.vars.prototype). By doing this, you can use the original fields and render them in your customized prototype. For example this code:
{% macro service_template(fields) %}
<tr>
<td>{{ form_widget(fields.description) }}</td>
</tr>
{% endmacro %}
Will result in something like the following rendered prorotype:
<tr>
<td>
<input type="text" id="service_performed___name___description" name="service[__name__][description]"/>
</td>
</tr>
You can then manipulate it using your javascript.
I hope this helps you.
Actually form theme blocks that start with an underscore _ relate to a field with a specific name.
What I mean is that, if your master form BillType is called my_form, you will need to do this:
{% form_theme form _self %}
{% block _my_form_servicesPerformed_entry_widget %}
I WILL WRITE MY CUSTOM PROTOTYPE HERE
{% endblock %}
The problem with this approach is that it concerns a specific iteration of BillType. If you use this form type elsewhere and provide it with a different name my_form_2, you would have to add an identical block named _my_form_2_servicesPerformed_entry_widget.
You can use macro, have a look at below example it's working fine for me even in Symfony3. Using this example you will be able to format your collection prototype as well.
View
{% macro widget_prototype(widget, remove_text) %}
{% if widget.vars.prototype %}
{% set form = widget.vars.prototype %}
{% set name = widget.vars.name %}
{% else %}
{% set form = widget %}
{% set name = widget.vars.full_name %}
{% endif %}
<div data-content="{{ name }}" class="panel panel-default">
<div class="section row">
<div class="col-md-12">
<label class="field-label">Skill <span class="text-danger">*</span></label>
<label class="field select">
{{ form_widget(form.skill) }}
<i class="arrow double"></i>
</label>
</div>
</div>
<div data-content="{{ name }}">
<a class="btn-remove" data-related="{{ name }}">{{ remove_text }}</a>
{{ form_widget(form) }}
</div>
</div>
{% endmacro %}
Your template to render the form has some problems. The first is this line:
prototype = prototype.replace(/_name_/g, index.toString());
The regex should be __name__.
Next, you are retrieving the prototype, but then immediately overwriting it and replacing the HTML of the prototype. There is nothing there I can see that actually appends the new form to your existing form anywhere. Plus since you just have a string of text, that replace isn't going to find any text __name__ to replace.
You should post the full extent of your Twig/Javascript so we can actually see the #bill_servicesPerformed as well as everything else you are trying to do. Before you write a custom prototype you should get the form working with the standard prototype just to make sure you don't have any bugs there first.
As an example, this is the way I keep going it. I do not know if there are some reasons not to, so be careful.
Form to include prototype:
<div class="modal-body" id="contactMehtods" data-prototype="
{% filter escape %}
{{ include(':organization/partials:prototype_contact_method.html.twig', { 'contact_method': contact_form.contactMethods.vars.prototype }) }}
{% endfilter %}">
<div class="form-group">
{{ form_label(contact_form.name, null, { 'label_attr': { 'class': 'control-label' }}) }}
{{ form_widget(contact_form.name, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
Prototype template:
<div class="form-group">
{{ form_label(contact_method.name, null, { 'label_attr': { 'class': 'col-sm-3 control-label' }}) }}
<div class="col-sm-9">
{{ form_widget(contact_method.name, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
<div class="form-group">
{{ form_label(contact_method.value, null, { 'label_attr': { 'class': 'col-sm-3 control-label' }}) }}
<div class="col-sm-9">
{{ form_widget(contact_method.value, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
But a note to consider, the javascript needs to accommodate for these changes I guess.
This is my ArticleType.php
class ArticleType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('category')
->add('tags')
->add('cover')
->add('is_recommend', null, array('attr'=>array('require' => true)))
->add('description')
;
}
how to customize filed "cover" in twig? this is my code in twig
<div class="col-xs-12">
<!-- PAGE CONTENT BEGINS -->
{% form_theme edit_form with 'AppsAdminBundle:Form:fields.html.twig'%}
{{ form_start(edit_form) }}
{{ form_errors(edit_form) }}
{% block _article_cover_widget %}
<div class="text_widget">
{{ block('form_widget_simple') }}
上传文件
</div>
{% endblock %}
{{ form_end(edit_form) }}
</div><!-- /.col -->
I want customize field cover in this twig, but, I don't know, why it's not work, I hope get help, thanks a lot!
Check docs to customize form rendering.
If you want to manage each field rendering separately, you can do it this way too:
<div class="col-xs-12">
<!-- PAGE CONTENT BEGINS -->
{% form_theme edit_form with 'AppsAdminBundle:Form:fields.html.twig'%}
{{ form_start(edit_form) }}
{{ form_errors(edit_form) }}
{{ form_row(edit_form.title) }}
{{ form_row(edit_form.category) }}
{{ form_row(edit_form.tags) }}
<div class="text_widget">
{{ form_row(edit_form.cover) }}
上传文件
</div>
{{ form_row(edit_form.is_recommend) }}
{{ form_row(edit_form.description) }}
{{ form_end(edit_form) }}
</div>
or, simply:
<div class="col-xs-12">
<!-- PAGE CONTENT BEGINS -->
{% form_theme edit_form with 'AppsAdminBundle:Form:fields.html.twig'%}
<div class="text_widget">
{{ form_row(edit_form.cover) }}
上传文件
</div>
{{ form_rest(edit_form) }}
{{ form_end(edit_form) }}
</div>
I am building a project in Symfony 2.3 using Twig. I want to add a class to the form row block. I am using a form theme file which contains:
{% block form_row %}
<div class="form-row">
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
{% endblock %}
Now some of my form rows I want to add an extra class form-row-split. I can't figure out how to do this properly. The way I have it almost-working is:
{% block form_row %}
{% set attr = attr|merge({'class': 'form-row' ~ (attr.class is defined ? ' ' ~ attr.class : '') ~ (errors|length > 0 ? ' error' : '')} ) %}
<div {{ block('widget_container_attributes') }}>
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
{% endblock %}
(Note, I've left the error class logic in there too as that needs to stay).
Then in the form builder:
$builder
->add('first_name', 'text', array(
'attr' => array(
'class' => 'form-row-split'
)
));
This almost works but it adds this class everywhere and also adds the widget id to the row!
<div id="myform_first_name" class="form-row form-row-split">
<label for="myform_first_name">First name</label>
<input id="myform_first_name" class="form-row-split" type="text" name="myform[first_name]">
</div>
I can think of a few potential solutions but none of them are pretty or straight forward. Surely there must be a simple way of doing this?
There is a fairly simple solution to this problem actually. I just needed a form type extension to extend the base form type to allow an extra available option: http://symfony.com/doc/2.3/cookbook/form/create_form_type_extension.html
Following through the example in the docs, I created a new form type extension:
// src/Acme/FrontendBundle/Form/Extension/FormTypeExtension.php
namespace Acme\FrontendBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Class FormTypeExtension
* #package Acme\FrontendBundle\Form\Extension
*/
class FormTypeExtension extends AbstractTypeExtension
{
/**
* Extends the form type which all other types extend
*
* #return string The name of the type being extended
*/
public function getExtendedType()
{
return 'form';
}
/**
* Add the extra row_attr option
*
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'row_attr' => array()
));
}
/**
* Pass the set row_attr options to the view
*
* #param FormView $view
* #param FormInterface $form
* #param array $options
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['row_attr'] = $options['row_attr'];
}
}
Then I registered the service in my bundle...
<!-- Form row attributes form extension -->
<service id="acme.form_type_extension" class="Acme\FrontendBundle\Form\Extension\FormTypeExtension">
<tag name="form.type_extension" alias="form" />
</service>
Since every widget extends the base form type this then allows me to pass this new row_attr option through on any field, eg:
$builder
->add('first_name', 'text', array(
'row_attr' => array(
'class' => 'form-row-split'
)
));
Then the twig overrides to make use of the new row_attr option:
{% block form_row %}
<div {{ block('form_row_attributes') }}>
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
{% endblock form_row %}
{% block form_row_attributes %}
{% spaceless %}
{% for attrname, attrvalue in row_attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %}
{% endspaceless %}
{% endblock form_row_attributes %}
And it's done!
(For completeness, my full twig override still merges in the form-row and error classes in like so:
{% set row_attr = row_attr|merge({'class': 'form-row' ~ (row_attr.class is defined ? ' ' ~ row_attr.class : '') ~ (errors|length > 0 ? ' error' : '')} ) %}
.. but thats not really necessary for answering my own question :P )
Docs say: you always able to pass attr to rendered element:
{{ form_start(form, {'attr': {'class': 'your-class'}} ) }}
{{ form_label(form, {'attr': {'class': 'your-class'}}) }}
{{ form_widget(form, {'attr': {'class': 'your-class'}}) }}
{{ form_errors(form, {'attr': {'class': 'your-class'}}) }}
{{ form_end(form) }}
Below is a clone of answer by #lopsided but with changes reflecting latest Symfony structure changes (v. 2.7+):
There is a fairly simple solution to this problem actually. I just needed a form type extension to extend the base form type to allow an extra available option: http://symfony.com/doc/master/form/create_form_type_extension.html
Following through the example in the docs, I created a new form type extension:
// src/Acme/FrontendBundle/Form/Extension/FormTypeExtension.php
namespace Acme\FrontendBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class FormTypeExtension
* #package Acme\FrontendBundle\Form\Extension
*/
class FormTypeExtension extends AbstractTypeExtension
{
/**
* Extends the form type which all other types extend
*
* #return string The name of the type being extended
*/
public function getExtendedType()
{
return FormType::class;
}
/**
* Add the extra row_attr option
*
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'row_attr' => []
));
}
/**
* Pass the set row_attr options to the view
*
* #param FormView $view
* #param FormInterface $form
* #param array $options
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['row_attr'] = $options['row_attr'];
}
}
Then I registered the service in my bundle...
<!-- Form row attributes form extension -->
<service id="acme.form_type_extension" class="Acme\FrontendBundle\Form\Extension\FormTypeExtension">
<tag name="form.type_extension" alias="form" extended_type="Symfony\Component\Form\Extension\Core\Type\FormType" />
</service>
Since every widget extends the base form type this then allows me to pass this new row_attr option through on any field, eg:
$builder
->add('first_name', TextType:class, [
'row_attr' => [
'class' => 'form-row-split'
]
]);
Then the twig overrides to make use of the new row_attr option:
{% block form_row %}
<div {{ block('form_row_attributes') }}>
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
{% endblock form_row %}
{% block form_row_attributes %}
{% spaceless %}
{% for attrname, attrvalue in row_attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %}
{% endspaceless %}
{% endblock form_row_attributes %}
And it's done!
(For completeness, my full twig override still merges in the form-row and error classes in like so:
{% set row_attr = row_attr|merge({'class': 'form-row' ~ (row_attr.class is defined ? ' ' ~ row_attr.class : '') ~ (errors|length > 0 ? ' error' : '')} ) %}
.. but thats not really necessary for answering my own question :P )
What I did was simpler (though maybe a bit less clean?).
Pass the class for the form row via a field's "data" attribute :
// template.html.twig
{{ form_start(form) }}
{{ form_row(form.field, {'attr': {'data-row-class': 'my-row-class'} }) }}
{{ form_end(form) }}
And then handle it in the form theme template this way :
// form-theme.html.twig
{% block form_row -%}
{% set row_class = attr['data-row-class'] | default('') %}
<div class="{{ row_class }}">
{{- form_label(form) -}}
{{- form_widget(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock form_row %}
Which gives this :
<form name="formName" method="post">
<div class="my-row-class">
<label for="formName_field">Field label</label>
<input type="text" id="formName_field" name="formName[field]" data-row-class="my-row-class">
</div>
</form>
UserType Form:
class UserType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('email', 'email', ['label' => 'EMail']);
// various other fields....
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => array('registration'),
'data_class' => 'Vendor\Model\Entity\User',
));
}
public function getName()
{
return 'form_user';
}
}
TutorType Form:
class TutorType extends Translate
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('user', new UserType(), ['label' => false]);
$builder->add('school', 'entity', [
'class' => 'Model:School',
'property' => 'name',
'label' => 'Label'
]);
// Various other fields
$builder->add('save', 'Submit');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
//'validation_groups' => array('registration'),
'data_class' => 'Vendor\Model\Entity\Tutor',
'cascade_validation' => true,
));
}
public function getName()
{
return 'form_tutor';
}
}
When rendering, the UserType is rendered inside a div, i cant find a way to overcome this.
The Form is rendered as
<form name="form_tutor"
method="post"
action=""
novalidate="novalidate"
class="form-horizontal form-horizontal"
id="form_tutor">
<div id="form_tutor"
novalidate="novalidate"
class="form-horizontal">
<div class="form-group">
<div class="col-lg-10">
<div id="form_tutor_user">
<div class="form-group">
<label class="col-lg-2 control-label aaaa required"
for="form_tutor_user_email">EMail</label>
<div class="col-lg-10">
<input type="email"
id="form_tutor_user_email"
name="form_tutor[user][email]"
required="required"
class="form-control" />
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-lg-2 control-label aaaa required"
for="form_tutor_tutorType">Type</label>
<div class="col-lg-10">
<select id="form_tutor_tutorType"
name="form_tutor[tutorType]"
class="form-control">
</select>
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<button type="submit"
id="form_tutor_save"
name="form_tutor[save]"
class="btn btn-default">Speichern</button>
</div>
</div><input type="hidden"
id="form_tutor__token"
name="form_tutor[_token]"
class="form-control"
value="s6i6zPxJs7KU5CiEe8i6Ahg_ca8rc2t5CnSk5yAsUhk" />
</div>
</form>
The form_tutor_user is wrapped in a own form-group div.
I tried to overwrite the form_tutor_user_widget but this is one level to deep. (And only a quick fix, it should be globally applied to all form type - Classes)
How can i change the theme so all custom types are not wrapped with the default form_row template?
Or how do i know in twig when a "subform" is rendered?
so i can decide to print the <div class="form-group"> when the child-node is not a subform, or skip it, if this is the case.
TIA
By default, in the base form theme:
{% block form_row %}
{% spaceless %}
<div>
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
And, for custom compound forms:
{% block form_widget_compound %}
{% spaceless %}
<div {{ block('widget_container_attributes') }}>
{% if form.parent is empty %}
{{ form_errors(form) }}
{% endif %}
{{ block('form_rows') }}
{{ form_rest(form) }}
</div>
{% endspaceless %}
{% endblock form_widget_compound %}
Unless you changed something here, the DIV you see should come from either one or the other bit of template.
However, in your specfic example, if form_tutor_user_row is defined, the first bit is never used, and if form_tutor_user_widget is defined, the last bit is never used.
Back to your question. Your question is :
"How can i change the theme so all custom types are not wrapped with the default form_row template?"
Here is the problem the way I see it: you want that your TOP forms (the form in which all sub-forms are included) all have a common way of rendering, in sections. Each section will be included in a DIV with class="form-group". You may want to throw in some additional rendering operations but I will limit myself to this to keep things simple.
What you need to do then is to create a specfic form type and make all your TOP forms inherit from this new form type. For instance:
class TopType extends AbstractType
{
public function getName()
{
return 'top_form';
}
}
... and an inherited form:
class MyFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
...
}
public function getName()
{
return 'my_form';
}
public function getParent()
{
return 'top_form';
}
}
As you can see, there is no need to make PHP inheritance for form theming inheritance to work.
Template-theming-wise (can I even say that?), if no specific form theming is set for my_form, Symfony will understand that the default form theme to use here is the form theme of top_form, that you can define as such:
{% block top_form_widget %}
{% spaceless %}
{% for child in form %}
<div class="form-group">
{{ form_widget(child) }}
</div>
{% endfor %}
{{ form_rest(form) }}
{% endspaceless %}
{% endblock top_form_widget %}
I should add that this is a problem I already encountered and solved. Tell me how that works for you.
Edit:
To sum it up, what you have to do is:
Create the TopType form type,
Add the top_form_widget block in your form theme,
For all your root forms (i.e. top-level forms, forms that have no parent), add a getParent() method that will return the name of your TopType form ("top_form")
In theory, if you override the form_widget_compound block in a global form theme this way, it should work as you want:
// app/Resources/views/form.html.twig
{% block form_widget_compound %}
{% if form.parent is empty %}
<div {{ block('widget_container_attributes') }}>
{{ form_errors(form) }}
{% endif %}
{{ block('form_rows') }}
{{ form_rest(form) }}
{% if form.parent is empty %}
</div>
{% endif %}
{% endblock %}
And register your form theme:
// app/config/config.yml
twig:
form:
resources:
- "::form.html.twig"
I usually solve this problem by rendering the nested form's fields manually:
{{ form_row(form.tutor.school) }}
{{ form_row(form.tutor.user.email) }}
Probably that's not the most elegant solution, but it works for me and I haven't looked for an elegant one yet.
Bootstrap 3.3.4 I ended up doing this.
They key part of this:
<div class="{% if form.parent.parent is empty %}form-group{% endif %}{% if (not compound or force_error|default(false)) and not valid %} has-error{% endif %}">
Full template.
{% block form_row -%}
<div class="{% if form.parent.parent is empty %}form-group{% endif %}{% if (not compound or force_error|default(false)) and not valid %} has-error{% endif %}">
{% if form.parent.parent is null and label is not empty %}
{{- form_label(form) -}}
{% elseif label is empty %}
{{- form_label(form) -}}
{% endif %}
{% if compound is empty %}<div class="{{ block('form_group_class') }}">{% endif %}
{{- form_widget(form) -}}
{{- form_errors(form) -}}
{% if compound is empty %}</div>{% endif %}
</div>
{%- endblock form_row %}
Maybe it is not an elegant solution, however works for me, because I was also trying to find the solution.
As an example:
{% for custom_field in form.custom_fields %}
<div class="edit_custom">
{{ form_row(custom_field.name) }}
{{ form_row(custom_field.value) }}
</div>
{% endfor %}
<script>
$('.edit_custom').find('input').unwrap();
</script>
Try using form_themes.
First, in you parent template define the form theme:
{% form_theme form with ['BundleName:ControlerName:somesubform_form.html.twig'] %}
btw replace BundleName, ControllerName and somesubform with the proper names.
then render it with:
{{ form_row(form.somesubform) }}
I have a form that contains a user entity with an $email property:
{{ form_start(form, {'attr': {'class':'form-horizontal'} }) }}
{{ form_errors(form) }}
...
<div class="control-group formSep">
<label class="control-label">
{{ form_label(form.email, 'E-mail:') }}
</label>
<div class="controls text_line">
{{ form_widget(form.email) }}
</div>
</div>
...
{{ form_rest(form) }}
{{ form_end(form) }}
The user entity has constraints on $email:
/**
* #ORM\Column(type="string", length=64)
* #Assert\Email
* #Assert\NotBlank
*/
protected $email;
When I put an invalid e-mail value the entity does not persist in the database (good), but twig doesn't display the error either (bad).
Any ideas?
like said in the documentation form_errors(form) render global errors (like class constraint errors, etc...), to display the error for the email field you should use form_errors(form.email).
http://symfony.com/doc/current/reference/forms/twig_reference.html#form-errors-view