Constraint validation not show message in form - symfony

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

Related

How to show Type name in Twig

I have 2 tables: Product and Type. Relationship is One Type has Many Product
Type: id, name
Product: id, name, type_id
I dont know how to show type name in Twig, I'm new in Symfony and here is my code:
<div>
{% for pro in product %}
<div>Product name: {{ pro.name }}</div>
<div>
Type name:
</div>
<div>Price: {{ pro.price }}</div>
<div>
{% for img in pro.images %}
<img src="{{ "/uploads/product/" ~ img.path }}" style="height: 100px;">
{% endfor %}
</div>
{% endfor %}
</div>
You can access the properties of an object in Twig. You should not look at your database, but the way your entities are made. Your Product has a $id, $name and $type. Type has $id and $name.
In Twig you can do {{product.name}}
To get the type, you want {{product.type}}
To get the name of the type, you want {{product.type.name}}
So, in your code, that would be:
<div>
Type name: {{ pro.type.name }}
</div>
Internally, the Twig parser will get $product and check if it has the method getName() when you do {{product.name}}. That same logic applies to {{product.type.name}}. It checks if $product has getType(), then if that has getName(). The PHP equivalant is $product->getType()->getName().
You almost never should type an actual method in your code. If you have to, you might want to take a step back and re-evaluate, because it often is a code smell.
You can just add {{ pro.type.name }} to your code:
<div>
{% for pro in product %}
<div>Product name: {{ pro.name }}</div>
<div>
Type name: {{ pro.type.name }}
</div>
<div>Price: {{ pro.price }}</div>
<div>
{% for img in pro.images %}
<img src="{{ "/uploads/product/" ~ img.path }}" style="height: 100px;">
{% endfor %}
</div>
{% endfor %}
</div>
And be sure that type attribute refer to Type entity. Your product entity should have type attribute like below:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Type", inversedBy="products")
* #ORM\JoinColumn(nullable=false)
*/
private $type;
// getter
public function getType()
{
return $this->type;
}
And your Type entity should have products attribute like below:
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Type", mappedBy="type")
* #ORM\JoinColumn(nullable=true)
*/
private $products;
public function getProducts()
{
return $this->products;
}

How to Customize an individual Field in Symfony2

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>

Symfony twig how to add class to a form row

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>

uniqueEntity message

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]]);

How to render form_rest() as hidden fields in Symfony2/Twig?

I have a Form class that contains many fields. I would like to render few of them and pass the left ones as hidden. How is this possible ?
I would like to do something like {{ form_rest(form, {'display': 'hidden'}) }} or <div display="hidden">{{ form_rest(form) }}</div>.
Example :
<form action="{{ path('fiche_intervention', {'rreid': rre.rreid}) }}" method="post" {{ form_enctype(form) }}>
{{ form_errors(form) }}
<div class="bloc-input">{{ form_label(form.rredatecommencement, "Date de retrait :") }}
{{ form_widget(form.rredatecommencement) }}
</div>
{# Some other fields... #}
{# ... #}
{# /Some other fields... #}
<div display="hidden">{{ form_rest(form) }}</div>
<input type="submit" />
</form>
You have to do it in you buildForm function, inside the "FormController". Just adding 'hidden' when you add the field is enough.
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name');
$builder->add('email', 'email');
$builder->add('subject');
$builder->add('anyone', 'hidden');
}
Also you may set all your unneeded fields as rendered in your twig template:
<form action="{{ path('fiche_intervention', {'rreid': rre.rreid}) }}" method="post" {{ form_enctype(form) }}>
{{ form_errors(form) }}
<div class="bloc-input">{{ form_label(form.rredatecommencement, "Date de retrait :") }}
{{ form_widget(form.rredatecommencement) }}
</div>
{% do form.unneededfield1.setRendered %}
{% do form.unneededfield2.setRendered %}
{% do form.unneededfield3.setRendered %}
<div display="hidden">{{ form_rest(form) }}</div>
<input type="submit" />
</form>
form_rest() renders all non-rendered fields from your form. It just renders them as they are, so if you want to render remaining fields as 'hidden', you just have to define them as 'hidden' in your Form !
{{ form_end(form, {'render_rest': false}) }}
It's from the official documentation (v3.0) so it's pretty much best practise i guess.

Resources