How to Customize an individual Field in Symfony2 - symfony

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>

Related

Symfony splitted embedded form

I work with a symfony embedded form to manage my translated fields for a given entity. My principal entity has a boolean field and I have multiple text fields for each translation.
I do not use the translatable doctrine extension and I do not want to use it.
In my FormType, I use a CollectionType to embed the translated fields in my form, and in the template, I use the form theme to customize the HTML.
This is my problem : I would like to group fields in my form to optimize UX but once I call the form_widget on my field, I cannot use it a second time. I would like to show 2 fields for a given language and then another field for another language further in the form. How can I solve my problem ?
This is a twig example that illustrate my problem.
{% form_theme form _self %}
{% block _service_translations_entry_widget %}
<div>
{% if name == 0 %}
<div class="s12 m6 l6">
{{ form_label(form.title) }}
{{ form_errors(form.title) }}
{{ form_widget(form.title) }}
</div>
<div class="s12 m6 l6">
{{ form_label(form.subtitle) }}
{{ form_errors(form.subtitle) }}
{{ form_widget(form.subtitle) }}
</div>
{% endif %}
{% if name == 1 %}
<div class="s12 m12 l12">
{{ form_label(form.desc) }}
{{ form_errors(form.desc) }}
{{ form_widget(form.desc) }}
</div>
{% endif %}
</div>
{% endblock %}
{% block body %}
{{ form_start() }}
{{ form_errors(form) }}
<div>
<div class="s12 m12 l12">
{{ form_label(form.doubleBlock) }}
{{ form_errors(form.doubleBlock) }}
{{ form_widget(form.doubleBlock) }}
</div>
</div>
<div id="block-1">
{{ form_widget(form.translations) }}
</div>
<div>
<div class="s12 m6 l6">
{{ form_label(form.activedStyle) }}
{{ form_errors(form.activedStyle) }}
{{ form_widget(form.activedStyle) }}
</div>
<div class="s12 m6 l6">
{{ form_label(form.checkoutOption) }}
{{ form_errors(form.checkoutOption) }}
{{ form_widget(form.checkoutOption) }}
</div>
</div>
<div id="block-2">
{{ form_widget(form.translations) }}
</div>
{{ form_widget(form.save) }}
{{ form_rest(form) }}
{{ form_end(form) }}
{% endblock body %}
As you say, you can’t reuse a field.
My suggested approach would be to add an additional field to the form, translations_extra, which either isn’t mapped to or persisted by the entity. Extract the data in the controller on form submission and then add it to the translations collection before persisting.

Symfony: trying to customize a collection form prototype

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.

Symfony: How to avoid custom form-types getting wrapped in a div automatically?

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) }}

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