symfony2: null date rendering - forms

everyone
I’am having trouble with empty dates and forms in Symfony2.
When I create an entity with an empty date, it works fine, a NULL value is inserted in the database. But when I want to edit it, it renders as today, I found no way of rendering the empy_values
As expected, “preferred_choices” does not work because “date” is not a “choice”.
Seems that a new \DateTime() is called somewhere.
Index and show actions have no problem:
[index/show.html.twig]
{% if entity.dueDate %}
{{ entity.dueDate|date('Y-m-d') }}
{% endif %}
If I ask in the controller, the behaviour is the expected one
[controller]
if (!$entity->getDueDate()) {
// enters here when there is NULL in the database
}
Here is the entity and form definitions:
[entity]
/**
* #var date $dueDate
*
* #ORM\Column(name="dueDate", type="date", nullable="true")
*/
private $dueDate;
[form]
$builder->add('dueDate', 'date', array('label'=>'Due date', 'empty_value' => array('year' => '----', 'month' => '----', 'day' => '----'),'required'=>false))
Please give me a hint, thank you in advance.
There is a related question from 2011-06-26 with no answer in google groups
https://groups.google.com/forum/#!msg/symfony2/nLUmjKzMRVk/9NlOB1Xl5RwJ
http://groups.google.com/group/symfony2/browse_thread/thread/9cb5268caccc4559/1ce5e555074ed9f4?lnk=gst&q=empty+date+#1ce5e555074ed9f4

With modern version of Symfony you seem to need:
$builder->add('dueDate', DateType::class, array(
'placeholder' => ['year' => '--', 'month' => '--', 'day' => '--']
)
empty_value has been replaced by placeholder and you need to pass an array with each "empty" value.

You can solve this way:
$builder->add('dueDate', 'date', array(
'label'=>'Due date',
'empty_value' => array('----'),
'required'=>false
))
You were close to the solution.

I did not want to render the form by myself, but as I was already doing that due to an unrelated issue, I developed some kind of fix:
[edit.html.twig]
<div class="entry {% if not entity.dueDate %}nullabledate{% endif %}">
{{ form_label(form.dueDate) }}
{{ form_errors(form.dueDate) }}
{{ form_widget(form.dueDate) }}
</div>
[add to some javascript file]
jQuery(document).ready(function() {
var nullDate = function(id) {
$(".nullabledate select").each(function(key,elem){
$(elem).val('');
})
}
nullDate();
}

Related

Render field form type twig symfony

I would like to duplicate the same field several times with different values ​​in the drop-down list in twig. I add a simple form with a TextType, but in twig in a for loop, the rendering of the field is done only once. How can I make this system under symfony ? ( In a for loop )
When you try to create a form in your controller and then you render it to you'r view , it gonna be one and only one form , you can't duplicated with a loop because at the end, it gonna give you 2 forms with the same form_id , so if you need 2 forms you need to instantiate them with your builder the same thing with you'r fileds.
Take a look:
$task1 = new Task();
$task2 = new Task();
$form1 = $this->createFormBuilder($task1)
->add('task', TextType::class)->add('task2', TextType::class);
$form2 = $this->createFormBuilder($task2)
->add('task', TextType::class);
And about the drop down , you need to create a form with ChoiceType Field :
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
$builder->add('Tasks', ChoiceType::class, array(
'choices' => array('task1','task2','task3));
CollectionType field type is used to render a "collection" of some field or form. In the easiest sense, it could be an array of TextType fields that populate an array values.
Example:
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
// ...
$builder->add('emails', CollectionType::class, [
// each entry in the array will be an "email" field
'entry_type' => EmailType::class,
// these options are passed to each "email" type
'entry_options' => [
'attr' => ['class' => 'email-box'],
],
]);
The simplest way to render this is all at once:
{{ form_row(form.emails) }}
A much more flexible method would look like this:
{{ form_label(form.emails) }}
{{ form_errors(form.emails) }}
<ul>
{% for emailField in form.emails %}
<li>
{{ form_errors(emailField) }}
{{ form_widget(emailField) }}
</li>
{% endfor %}
</ul>
Please refer this documents for further details : https://symfony.com/doc/current/reference/forms/types/collection.html

Symfony3 Render multiple time same form

I would like to render the same form multiple times to handle the same action for two different tabs.
The problem is that when I try, only the form of the first tab is shown, event if I change the id and name of the form.
I found out it's the expected behavior of symfony, but I still need it to work.
I found that it may works with a collection but don't get how it would work.
twig:
{{ form(contactForm, {'attr': {'id': 'contactFormId' ~ Client.Id}, 'name': "contactFormName" ~ Client.Id})}}
Form:
$this->contactForm = $this->createFormBuilder($contact, array('allow_extra_fields' =>true))
->add('Nom', TextType::class, array('mapped'=>false))
->add('Prenom', TextType::class, array('mapped'=>false))
->add('Telephone', TextType::class, array(
'label' => 'Téléphone'))
->add('Email', TextType::class)
->add('Ajouter', SubmitType::class)
->getForm();
It is an older question, but I just came across it facing a similar situation. I wanted to have multiple versions of one form object in a list view. For me the solution was to move the createView() call on the form object to the view instead of calling it in the controller. This is kind of a dirty solution regarding separation of concerns, but I thought to post it so it may help others anyway.
My controller action looks like this:
/**
* #Route("", name="cart_show")
* #Method("GET")
*/
public function showAction(Request $request)
{
/** #var CartInterface $cart */
$cart = $this->get('rodacker.cart');
$deleteForm = $this->createDeleteForm();
return $this->render(
'AppBundle:Cart:show.html.twig',
['cart' => $cart, 'deleteForm' => $deleteForm]
);
// ...
private function createDeleteForm()
{
return $this->createForm(
OrderItemDeleteType::class,
null,
[
'action' => $this->generateUrl('cart_remove_item'),
'method' => 'DELETE',
]
);
}
}
and in the view I set the form variable by calling the createView function on the form variable (deleteForm) passed from the controller:
{% for item in items %}
{% set form = deleteForm.createView %}
{{ form_start(form) }}
{{ form_widget(form.item, {'value': item.image.filename}) }}
<button type="submit" class="btn btn-xs btn-danger" title="Artikel entfernen">
<i class="fa fa-trash-o"></i> entfernen
</button>
{{ form_end(form) }}
{% endfor %}
Once you render a Symfony form, the same form will not render again.
I would suggest creating a form class and calling Controller::createForm() multiple times to create the desired amount of Form instances; you can call isSubmitted etc. on all forms independently.
http://symfony.com/doc/current/book/forms.html#creating-form-classes

Symfony form builder only works when fields are in a given order

Here is a working code:
$formBuilder = $this->get('form.factory')->createBuilder('form', $scene);
$formBuilder->add('text', 'textarea', array('required' => false));
$formBuilder->add('duration', 'time', array('with_seconds' => true, 'input' => 'string'));
$formBuilder->add('submit', 'submit');
$form = $formBuilder->getForm();
$form->handleRequest($request);
When I switch the lines like this:
$formBuilder = $this->get('form.factory')->createBuilder('form', $scene);
$formBuilder->add('duration', 'time', array('with_seconds' => true, 'input' => 'string'));
$formBuilder->add('text', 'textarea', array('required' => false));
$formBuilder->add('submit', 'submit');
$form = $formBuilder->getForm();
$form->handleRequest($request);
it doesn't work anymore: the value of duration becomes 0 instead of something in the form of 00:00:00.
I have no template to display them. In my twig page, I simple use {{ form(form) }}.
What's going on, there?
I copied your code and find anything wrong with it, you should be able to change the order of lines. But indirect solution would be leaving your code from the first example and manipulate order in the twig template:
{{ form_errors(form) }}
{{ form_row(form.duration) }}
{{ form_row(form.text) }}
{{ form_rest(form) }}

Symfony2 Accessing each entity field elements value in Twig

I have one form that contains entity type field parameters:
->add('parameters', 'entity', array(
'class' => 'SPlaceBundle:Parameter',
'query_builder' => function(ParameterRepository $er)
{
return $er
->createQueryBuilder('s')
->where('s.type = :type1 or s.type = :type2')
->setParameter('type1', 1)
->setParameter('type2', 2)
->orderBy('s.name', 'ASC');
},
'property' => 'name',
'multiple' => true,
'expanded' => true,
))
As you can see I only display parameters with type=1 or type=2.
While rendering template I would like to place hr (or something else) between checkboxes representing different parameter types.
I was trying to use {{ field.get('value').type }} trick to get parameter type:
{% for p in form.parameters %}
{{ form_widget(p) }}
{{ form_label(p) }}
{{ p.get('value').type }}
<br>
{% endfor %}
The problem is that above {{ p.get('value') }} returns parameter id (int) instead of parameter object.
Is there a way to return object?
It's not really elegant, but you could concatenate the type and name in your select, and use that as label. Then, when displaying the labels, split on the delimiter and you have both type and name.

Symfony2 form collection: Index of the current object is shown

I've got a problem with displaying collection in my form.
When displaying my entity collection I've got something like this :
0
Name: myInputName
Address: myInputAddress
1
Name: myInputName
Address: myInputAddress
My question is why Symfony2 display the index...
And this for all saved entities into my collection...
Here the code I use:
$builder
->add('person', 'collection', array(
'label' => ' ',
'type' => new PersonType(),
'prototype' => true,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
))
;
In my twig file:
<div>
{{ form_widget(edit_form) }}
</div>
Help please
Sam
Removing indexes (labels) for collection items:
$builder
->add('person', 'collection', array(
...
'options' => array('label' => false)
))
;
Use key entry_options instead of options for Symfony 3 and 4
If you want to add custom labels per row you can produce the form yourself:
{{ form_start(edit_form) }}
{% for person in form.persons %}
{{ form_row(person, {'label': 'custom label per item' }) }}
{% endfor %}
{{ form_end(edit_form) }}
Note: tested on Symfony 2.3 & 2.4
This one is some days ago but because I was facing same question for Symfony 3 the answer of sectus is the correct one.
Use the
'entry_options' => ['label'=>false],
option within your builder to hide he object item.
Best Regards
You can custom the rendering of your collection for don't display the index with, by example:
{% block _FORMNAME_person_widget %}
{% spaceless %}
{% for child in form %}
{{ form_widget(child.Name) }}
{{ form_widget(child.Address) }}
{% endfor %}
{% endspaceless %}
{% endblock %}
I know this has been closed for a while. And not sure if this has been solved elsewhere. This issue is actually pretty simple to fix and I am surprised there is no documentation about this anywhere. In the PersonType or any type that is used in a collections just modify the vars['name'] in the buildView to be what you want displayed as the label.
public function buildView(FormView $view, FormInterface $form, array $options)
{
// Adjust the view based on data passed
$this->vars['name'] = $form->getData();
// Or...
$this->vars['name'] = 'Some random string';
}
If you want it dynamic, you would use the object by form->getData(). Since, in my problem, I am using a form theme, overriding the twig is not really an option for me.
Hope this helps someone.
Using #MrBandersnatch's solution below, I had to use $view->vars['name'] instead of $this->vars['name'] (Symfony 2.3).
(apologies for not adding this as a comment on #MrBandersnatch's answer, I've not got enough reputation yet).