How to set form name in Symfony2? - symfony

I create my custom form in this way:
$messageForm = $this->createFormBuilder( new MessageEmailForm() )
->add('name', 'text')
->getForm();
I would like to change form name but I don't know how to do it and simultaneously hold MessageEmailForm class in FormBuilder. I tried something like that:
$this->createFormBuilder( new MessageEmailForm(), "myCustomFormName" )
etc. but it doesn't work. I need exactly this result:
<form name="myCustomFormName" ... >
#EDIT//
I found information about my problem, it looks like I should use following code but I have no idea where I should use it...
$this->get('form.factory')->createNamedBuilder

If you want to generate this:
<form name="myCustomFormName" ... >
Just add this in the twig template:
{{ form_start(form, {'attr': { 'name':'myCustomFormName' }}) }}

Could you add the attribute to your twig template.
{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
<form action="{{ path('task_new') }}" name="myCustomFormName" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<input type="submit" />
</form>

Related

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.

How to assign class to invalid Symfony 2 form field, rendered with Twig?

Lets assume that few fields values of my form, build on Symfony 2 and rendered with Twig are not valid and I received validation errors. I want not only to see this errors, but also assign special class to each invalid field.
For example:
<input type="text" class="error">
How can I do that? As I understand, there is need to redeclare my form template. Is there any working example how to assign attributes in case of concrete field validation failure.
All I found now, is that I need to set this class in form template:
{% set attr = attr|merge({'class': attr.class|default('') ~ (errors|length > 0 ? ' error' : '') }) %}
But what I don't understand is how to specify exact field? Any help appreciated.
This works for me:
<div class="input{{ form_errors(form.expiry) == '' ? '' : 'error' }}">
{{ form_widget(form.expiry) }}
</label>
You could also do
{{ form_widget(form.expiry, {'attr': {'class': form_errors(form.expiry) == '' ? '' : 'error'}}) }}
If you use
{{ form(form) }}
for showing your form, I am quite sure you can not accomplish what you want, or at least I am not aware of the possiblity.
If you use something like this
{{ form_row(form.task) }}
{{ form_row(form.dueDate) }}
I am still quite sure you can not get what you want.
My solution for what you need would be to make something like this:
{{ form_start(form) }}
{{ form_errors(form) }}
<div>
{{ form_label(form.task) }}
{{ form_errors(form.task) }}
{{ form_widget(form.task) }}
</div>
<div>
{{ form_label(form.dueDate) }}
{{ form_errors(form.dueDate) }}
{{ form_widget(form.dueDate) }}
</div>
<input type="submit" />
{{ form_end(form) }}
and to simply get information about validation errors from form object and then to replace {{ form_widget(form.task) }} with something like this
{{ form_widget(form.task, {'attr': {'class': 'error'}}) }}
in case that field task failed the validation.
Even more slow and time consuming solution would be to make small twig files that each and every one would actualy represent "your" design for view of each form field and then to call those "little twigs" with an argument which would again come from form object which contains those data about bad validation.
You can read more about form rendering where you actualy make your own form field designs here
http://symfony.com/doc/current/cookbook/form/form_customization.html

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.

Printing a Form more than once. Symfony2. Twig Templates

I'm using symfony2. I have a form, and I want to print it a lot of times on a twig template.
This is what I have in the Controller:
$em=$this->getDoctrine()->getEntityManager();
$pruebas = $em->getRepository('UnetPBundle:prueba')->findAll();
$form = $this->createForm(new PruebaType(), $pruebas);
return $this->render('UnetPBundle:Nomina:prueba.html.twig', array(
'form' => $form->createView(),
'pruebas' => $pruebas
));
And this is the content of twig template.
<form action="{{ path('UnetPBundle_prueba') }}" method="post" {{ form_enctype(form) }} class="sofla">
{{ form_errors(form) }}
{% for prueba in pruebas %}
{{ form_label(form.nombre, 'Nombre')}}
{{ form_errors(form.nombre)}}
{{ form_widget(form.nombre, {'attr':{'value':prueba.nombre}})}}
{% endfor %}
<input type="submit" value="Submit" />
{{ form_rest(form) }}
</form>
It's printing the field just once.
I think you'll need to create a form from a CollectionType() initialized with $puebras. There will be no need for a loop in your template.
Try to define $puebras as entity field type.

FOSUserBundle: embedding the login form and choosing its template

i want to insert the login form of FOSUserBundle in my template like
this:
<div id="sidebar">
{% render "FOSUserBundle::Security::login" %}
</div>
but not to render the template that is been calling in the code of
loginAction() originally.
I have thought I'd find useful the possibility of passing the template
I want to render as a parameter as 'max' in this example:
<div id="sidebar">
{% render "AcmeArticleBundle:Article:recentArticles" with {'max':
3} %}
</div>
Is that possible in symfony2? If not..
should I create another action for my bundle with the same code inside
of loginAction?
or
should I modify the original loginAction code and write control
structures?
if(currentPage == 'home')
renderResponse('template1')
else
renderResponse('template2')
You can create a partial to store your plain HTML form, for example this simple one:
<form action="{{ path("fos_user_security_check") }}" method="post">
<label>Email</label>
<input type="text" name="_username" required="required" />
<label>Password</label>
<input type="password" name="_password" required="required" />
<button type="submit">Submit</button>
</form>
Then in your layout just include your brand new partial :)
If you need the csrf token (which is highly recommended), just define the provider as a Twig global variable:
twig:
globals:
fos_csrf_provider: "#form.csrf_provider"
Then just add the hidden input field in your form:
<input type="hidden" name="_csrf_token" value="{{ fos_csrf_provider.generateCsrfToken('authenticate') }}" />
You have to specify that you are calling a controller
{{ render(controller('FOSUserBundle:Security:login')) }}
Since the part to include the error was not answered here and I had to dig into the FOSUserBundle Code, let me also add to this post how to add the error messages to the loginPartial.html.twig form :
Have a look at the file /vendor/friendsofsymfony/user-bundle/Controller/SecurityController.php
This controller has an loginAction which builds the error variable. All you need to do in the controller you use to render the loginPartial twig is to add to this controller the following code.
$session = $request->getSession();
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContextInterface::AUTHENTICATION_ERROR);
} elseif (null !== $session && $session->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
$error = $session->get(SecurityContextInterface::AUTHENTICATION_ERROR);
$session->remove(SecurityContextInterface::AUTHENTICATION_ERROR);
} else {
$error = null;
}
return $this->render('YourDemoBundle:Default:index.html.twig', ['error' => $error]);
In this case you are just using the same code in your controller to generate any login error messages i.e password incorrect etc.
Now in the twig which shows the form just add the following :
{% include 'YourDemoBundle:Security:loginPartial.html.twig' %}
And in the loginPartial.html.twig file put in this :
{% block fos_user_content %}
{% if error %}
<div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}`Let the rest of the form be as is....and then endblock ofcourse..{% endblock fos_user_content %}`
That should do it
is a single "colon" between each part
{% render "FOSUserBundle:Security:login" %}

Resources