Display Many-To-One fields in Symfony2 forms? - symfony

I am having two entity files one as Activite.php and another as Mesurage.php.
Now i want to display an Activite form with 3 fields typeActivite, emplacement and mesurage. the mesurage will be a selection that will fetch data from mesurage table. here is the code that i wrote inside Activite.php to create a many_to_one field for mesurage_id
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="GestionEnvironnementale\ISO14001Bundle\Entity\Mesurage")
*/
private $mesurage;
Below is my Form generation Code :
class ActiviteType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('typeActivite'),
->add('emplacement'),
->add('mesurage', 'entity', array('class' => 'ISO14001Bundle:Mesurage'));
}
}
here is my form code :
<div class="well">
<form method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<br/> <input type="submit" value="Envoyer" class="btn btn-primary" />
</form>
<script src="{{ asset('js/jquery-2.1.1.min.js') }}"></script>
<script type="text/javascript">
$(document).ready(function()
{
var $container3 = $('div#gestionenvironnementale_iso14001bundle_activitetype_activiteMesurage');
var $lienAjout3 = $('Ajouter un mesurage');
$container3.append($lienAjout3);
$lienAjout3.click(function(h) {
ajouterMesurage($container3);
h.preventDefault();
return false;
});
var index3 = $container3.find(':input').length;
if (index3 == 0) {
ajouterMesuragePolluant($container3);
} else {
$container3.children('div').each(function() {
ajouterLienSuppression3($(this));
});
}
function ajouterMesurage($container3) {
var $prototype3 = $($container3.attr('data-prototype').replace(/__name__label__/g, 'Mesurage n°' + (index3+1))
.replace(/__name__/g, index3));
ajouterLienSuppression3($prototype3);
$container3.append($prototype3);
index3++;
}
function ajouterLienSuppression3($prototype3) {
$lienSuppression3 = $('Supprimer');
$prototype3.append($lienSuppression3);
$lienSuppression3.click(function(h) {
$prototype3.remove();
h.preventDefault();
return false;
});
}
});
the code works very well but I dont want to display the list of Mesurage, I want to display the form of Mesurage to add a new !!

If you want to display a form even for mesurage, you have to take a look at embed form
So, basically, you have to create a FormType for mesurage (call it MesurageFormType) and modify your ActiviteType as follows
class ActiviteType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('typeActivite'),
->add('emplacement'),
->add('mesurage', 'collection', array(
'type' => new MesurageFormType(),
'allow_add' => true,));
}
}
This should be fine but if you want to render in a different way your form you should use prototype and jquery

Related

Symfony2 registration form - extra created fields with jQuery are not persisted in DB only value from last input

I want to persist into database email field all dynamically created emails input values, after submitting the form. Now the problem is that - only last value is saved, from emails field. I don't have idea how i can fix it.
Registration controller:
class RegistrationFormType extends BaseType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('first_name')
->add('last_name')
->add('is_company', CheckboxType::class, array(
'label' => 'Is company?',
'required' => true,
))
->add('emails', TextType::class)
->add('add' , ButtonType::class, array('attr'=>array('class'=>'add-email')))
->add('department', EntityType::class, array(
'class' => 'UserBundle:Department',
'choice_label' => 'name',
));
}
public function getParent()
{
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
}
public function getBlockPrefix()
{
return 'custom_user_registration';
}
public function getName()
{
return $this->getBlockPrefix();
}
}
jQuery funcion to add / remove form field:
$(document).ready(function() {
var max_fields = 10; //maximum input boxes allowed
var wrapper = $("div"); //Fields wrapper
var add_button = $(".add-email"); //Add button ID
var x = 1; //initlal text box count
$(add_button).click(function(e){ //on add input button click
e.preventDefault();
console.log('Clicking Add Button');
if(x < max_fields){ //max input box allowed
x++; //text box increment
$('.add-email').parent("div").prev().append('<div><input type="text" id="fos_user_registration_form_emails" name="fos_user_registration_form[emails]" required="required">Remove</div>');
}
});
$(wrapper).on("click",".remove_field", function(e){ //user click on remove text
e.preventDefault(); $(this).parent('div').remove(); x--;
})
});
User Entity:
/**
* #ORM\Column(type="text",nullable=true)
*/
protected $emails;
/**
* #return mixed
*/
public function getEmails()
{
return $this->emails;
}
/**
* #param mixed $emails
*/
public function setEmails($emails)
{
$this->emails = $emails;
}
Registration controller:
class RegistrationController extends BaseController
{
// public function registerAction(Request $request)
// {
//
// $response = parent::registerAction( $request );
//
// return $response;
// }
public function registerAction(Request $request)
{
$form = $this->container->get('fos_user.registration.form');
$formHandler = $this->container->get('fos_user.registration.form.handler');
$confirmationEnabled = $this->container->getParameter('fos_user.registration.confirmation.enabled');
$process = $formHandler->process($confirmationEnabled);
if ($process) {
$user = $form->getData();
$this->container->get('logger')->info(
sprintf('New user registration: %s', $user)
);
if ($confirmationEnabled) {
$this->container->get('session')->set('fos_user_send_confirmation_email/email', $user->getEmail());
$route = 'fos_user_registration_check_email';
} else {
$this->authenticateUser($user);
$route = 'fos_user_registration_confirmed';
}
$this->setFlash('fos_user_success', 'registration.flash.user_created');
$url = $this->container->get('router')->generate($route);
return new RedirectResponse($url);
}
return $this->container->get('templating')->renderResponse('FOSUserBundle:Registration:register.html.'.$this->getEngine(), array(
'form' => $form->createView(),
));
}
}
register.html.twig (overriden from FOS User Bundle):
{% extends "FOSUserBundle::layout.html.twig" %}
{% block fos_user_content %}
{% include "FOSUserBundle:Registration:register_content.html.twig" %}
{% endblock fos_user_content %}
It sounds like the fields being rendered/cloned by the js aren't having their names updated, and those that come later are overriding earlier fields with the same names.
For example, if I have a form rendered something like:
<!-- pseudo markup; yours will likely differ -->
<div class="form_row">
<input type="email" name="fos_user[email]" />
</div>
<button class="add_email" onclick="javascript:[...];">Add email</button>
And I click that .add-email button, giving me:
<div class="form_row">
<input type="email" name="fos_user[email]" />
</div>
<div class="form_row">
<input type="email" name="fos_user[email]" />
<button onclick="javascript:[...];">Remove</button>
</div>
<button class="add_email" onclick="javascript:[...];">Add email</button>
If I were to fill in both <input> fields, and submit the form, I'd essentially have have two values for the fos_user[email] input field. The browser takes the last one as the canonical value, ignoring all others with the same name. When the request goes out, it sends only one value field name.
To verify this hypothesis, you should examine the request data from the "network" panel in your browser's developer tools. Here's how to do that in Google Chrome: link.
Pic of google chrome dev tools examining an HTTP request. Where the user has selected remotedebugging.png, you might look for the main POST request to /register, or whatever your FOSUserBundle's register action is.
If this turns out to be the case - then the error lies with the way that your javascript is cloning the fields. You'll need to be certain that the fields have a tailing [n] on each of their name attributes. (Where n is a number.) The best way to to that is to use a collection.
But hold up... a collection!?. It looks like you've not primed your user model to hold a collection of emails.
<?php
namespace AppBundle\Entity\User;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
class User extends BaseUser
{
...
/**
* #ORM\Column(type="text",nullable=true)
*/
protected $emails;
You've named it in the plural, but it appears just like a normal string as far as Doctrine or your ORM are concerned.
The Fix
Change your User's $emails field to a simple_array type, which stores comma-delimited values in the db column. The example below uses a doctrine annotation, to do this. (Here's the doctrine reference for simple_array.) Other array-like options: "array" (stored w/ php serialization) "json" (stored as a json object)
Don't discard the $email field, (note the singular name) as I believe FOSUserBundle requires it when extending their User model class. Be sure to update your getters and setters (as in example below), and ensure the $emails property of your user is an empty ArrayCollection object on instantiation. Run doctrine:schema:update from the symfony console after doing this.
<?php
// src/AppBundle/Entities/User.php
namespace AppBundle\Entity\User;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
class User extends BaseUser
{
...
/**
* #ORM\Column(type="text", nullable=true)
*/
protected $email;
/**
* #ORM\Column(type="simple_array")
*/
protected $emails;
...
/**
* Instantiate the User
*/
public function __construct()
{
parent::__construct();
$this->emails = new \Doctrine\Common\Collections\ArrayCollection();
...
}
/**
* Add an email to the collection of emails
*
* #param string $email The email to add.
*
* #return User
*/
public function addEmail($email)
{
$this->emails[] = $email;
return $this;
}
/**
* Remove an email from the collection of emails
*
* #param string $email The email to disassociate from this user.
*
* #return User
*/
public function removeEmail($email)
{
$this->email->removeElement($email);
return $this;
}
/**
* Get all emails in colletion
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEmails()
{
return $this->emails;
}
Extend your registration form further so it includes the collection field.
<?php
// src/AppBundle/Form/UserRegistrationType.php
namespace AppBundle\Form;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
class RegistrationFormType extends BaseType
{
public function buildForm(FormBuilderInterface $builder, array $options)
$builder->add(
...
'emails',
CollectionType::class,
[
'entry_type' => EmailType::class,
'allow_add' => true,
'allow_delete' => true
],
...
)
...
}
In my example, I'm extending the form outside the controller. When I extend the reg. form in FOSUserBundle, I let the bundle know in app/config.yml
// app/config.yml
...
fos_user:
registration:
form:
type: AppBundle\Form\RegistrationFormType
...
The allow_add key on the options array tells twig to render the emails field's container with a data-prototype attribute, which will include a __name__ placeholder string. That placeholder will help you number your form fields more accurately, and avoid the duplicate name trap we fell into above.
Render the form a little differently. Pull from Symfony's recommended javascript for CollectionType. From http://symfony.com/doc/current/reference/forms/types/collection.html:
{# '/project/base/app/Resources/FOSUserBundle/views/Registration/register_content.html.twig' #}
{{ form_start(form) }}
{# ... #}
{# store the prototype on the data-prototype attribute #}
<ul id="email-fields-list"
data-prototype="{{ form_widget(form.emails.vars.prototype)|e }}">
{% for emailField in form.emails %}
<li>
{{ form_errors(emailField) }}
{{ form_widget(emailField) }}
</li>
{% endfor %}
</ul>
Add another email
{# ... #}
{{ form_end(form) }}
<script type="text/javascript">
// keep track of how many email fields have been rendered
var emailCount = '{{ form.emails|length }}';
jQuery(document).ready(function() {
jQuery('#add-another-email').click(function(e) {
e.preventDefault();
var emailList = jQuery('#email-fields-list');
// grab the prototype template
var newWidget = emailList.attr('data-prototype');
// replace the "__name__" used in the id and name of the prototype
// with a number that's unique to your emails
// end name attribute looks like name="contact[emails][2]"
newWidget = newWidget.replace(/__name__/g, emailCount);
emailCount++;
// create a new list element and add it to the list
var newLi = jQuery('<li></li>').html(newWidget);
newLi.appendTo(emailList);
});
})
</script>
Notice how the script replaces a prototype's __name__ placeholder with that email's position in the form? That will render as something like:
<div class="form_row">
<input type="email" name="fos_user[emails][0]" />
</div>
<div class="form_row">
<input type="email" name="fos_user[emails][1]" />
<button onclick="javascript:[...];">Remove</button>
</div>
<button class="add_email" onclick="javascript:[...];">Add email</button>
... which will force the browser to treat each field individually.
If you have 10 inputs with name fos_user_registration_form[emails], the last one overrides the first nines. But if you change the name of e-mail field, Symfony does not recognize the field and send an error.
Solution for you: CollectionType Field. It allows you to create multiple fields for one attribute or delete existing ones. Also by default it generates prototype - a HTML template to insert new input into DOM.

Symfony2 Conditional Validation

I have a FormType method.
<?php
class PostType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('text')
->add('paymentMethod', 'choice', array(
'required' => true,
'choices' => array(
'cach' => 'Cach',
'check'=> 'Check'
)
))
->add('checkId', 'integer', array(
'required' => true
))
->add('submit', 'submit');
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Post',
]);
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_post';
}
}
?>
The field checkId is displaying only when the choice 'check' is selected with the field paymentMethod.
Here is the javascript:
PaymentMethod = {
hideCheckId: function(){
$('.check-id').hide();
},
showCheckId: function(){
$('.check-id').fadeIn();
},
whenPaymentMethodChange: function(){
$('#appbundle_post_paymentMethod').on('change', function(){
var method = this.value ;
if(method == 'check'){
PaymentMethod.showCheckId();
}else{
PaymentMethod.hideCheckId();
}
})
}
};
PaymentMethod.hideCheckId();
PaymentMethod.whenPaymentMethodChange();
I would like the field 'checkId' to be required to TRUE only when the option check is selected.
How can I do that?
Ok thanks, Here is the solution for everyone:
Add a callback method function in your entity class.
/**
* #param ExecutionContextInterface $context
* #Assert\Callback()
*/
public function isPaymentIsCheck(ExecutionContextInterface $context)
{
if ($this->getPaymentMethod() == 'check' and $this->getCheckId() == '') {
$context->buildViolation('A check ID as to be defined')
->atPath('checkID')
->addViolation();
}
}
Don't forget to add the Assert and ExecutionContextInterface component in you entity class:
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
And finally don't forget to display errors in the twig template:
{% if form.vars.errors|length %}
<div class="alert alert-danger">{{ form_errors(form) }}</div>
{% endif %}
Hope it will help you.
More info here: http://symfony.com/doc/current/reference/constraints/Callback.html
What KevinR wrote would definitely solve your problem, but in my opinion there's even cleaner solution:
http://symfony.com/doc/current/book/validation.html#group-sequence-providers
Imagine a User entity which can be a normal
user or a premium user. When it's a premium user, some extra
constraints should be added to the user entity (e.g. the credit card
details). To dynamically determine which groups should be activated,
you can create a Group Sequence Provider. First, create the entity and
a new constraint group called Premium:
This is exactly your how you would solve this kinda of problem
public function getGroupSequence()
{
$groups = array('Post'); //Or array('Default') whichever you prefer
if ($this->getPaymentMethod() === 'Check') {
$groups[] = 'Check';
}
return $groups;
}
Of course you'll have to add validation group paymentMethod property in your entity mapping. Here's annotation example, for more examples check above link.
/**
* #Assert\IsTrue(groups={"Check"})
*/
private $checkId;

Symfony 2 - rearrange form fields

In our Symfony2 project we have a very complex structure for forms with embedded forms.... Now we got the requirement to bring the output of the form in an specific order.
And here is the problem: We use the form_widget(form) and now we are looking for a solution in the object (e.g. via annotations) or the formbuilder to move a specific field to the end of the form. in symfony 1.4 it was the widget-movefield() function, i guess...
Thx...
You can re-order the fields using this bundle: https://github.com/egeloen/IvoryOrderedFormBundle
This allows you to do things like this:
$builder
->add('g', 'text', array('position' => 'last'))
->add('a', 'text', array('position' => 'first'))
->add('c', 'text')
->add('f', 'text')
->add('e', 'text', array('position' => array('before' => 'f')))
->add('d', 'text', array('position' => array('after' => 'c')))
->add('b', 'text', array('position' => 'first'));
This was going to be in core, but was rejected and pulled out into a bundle.
Had same issue today with the form elements ordering.
Ended up with a trait that will override finishView method and reorder items in children property of a FormView:
trait OrderedTrait
{
abstract function getFieldsOrder();
public function finishView(FormView $view, FormInterface $form, array $options)
{
/** #var FormView[] $fields */
$fields = [];
foreach ($this->getFieldsOrder() as $field) {
if ($view->offsetExists($field)) {
$fields[$field] = $view->offsetGet($field);
$view->offsetUnset($field);
}
}
$view->children = $fields + $view->children;
parent::finishView($view, $form, $options);
}
}
Then in type implement getFieldsOrder method:
use OrderedTrait;
function getFieldsOrder()
{
return [
'first',
'second',
'next',
'etc.',
];
}
There's no need to "reorder" fields. All you need to do is to call form_label and/or form_widget for each field individually. Assuming you use Twig you could, for example, do:
<div>{{ form_label(form.firstName) }}</div>
<div>{{ form_widget(form.firstName) }}</div>
<div>{{ form_label(form.lastName) }}</div>
<div>{{ form_widget(form.lastName) }}</div>
here is the solution I came up with.
I created a class that my types extend.
namespace WF\CORE\CoreBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class WfBaseForm extends AbstractType
{
protected function useFields(FormBuilderInterface $builder, $fields)
{
foreach ($builder->all() as $field) {
if (!in_array($field->getName(), $fields))
$builder->remove($field->getName());
}
}
public function reorder(FormBuilderInterface $builder, $keys = array())
{
$fields = $builder->all();
$ordered_fields = array_merge(array_flip($keys), $fields);
$this->useFields($builder, array());
foreach($ordered_fields as $field)
$builder->add($field);
}
public function getName()
{
return 'base';
}
}
Then you can use it in your inherited classes.
class AddressType extends WfBaseForm
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// I add as many fields as I need
$builder->add( '…');
}
This class extends the one above
class ModifyAddressType extends BaseType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder->add('title', 'text', array('constraints' => array(new NotBlank())));
$this->reorder($builder, array('title', 'firstname', 'lastname'));
}
}
I had the same problem, but solved it in a different way. Here is my solution, as an idea for others who are searching for this problem.
You could add all the form fields in an event listener, because here you have all the objects data to decide on the fields order. You could for example use a method from the data object to decide on the fields order:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Don't use $builder->add(), or just for those fields which are always
// at the beginning of the form.
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$entry = $event->getData();
$form = $event->getForm();
// Now add all the fields in the order you need them to be, e.g by reading
// the needed order from the data entry.
if ($entry->isFieldAFirst()) {
$form->add(fieldA);
$form->add(fieldB);
} else {
$form->add(fieldB);
$form->add(fieldA);
}
}
}
As I understand, you want to use only form_widget(form) in final template.
Let assume we have two inherited models (ModelA, ModelB) and form types for them (ModelAType, ModelBType)
class ModelA {
private $A;
private $B;
// Getters and setters
}
class ModelB extends ModelA {
private $C;
// Getters and setters
}
/**
* #DI\Service(id = "form.type.modelA")
* #DI\Tag("form.type", attributes={ "alias":"model_a_type" })
*/
class FormAType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('A')
->add('B')
;
}
// getName and so on
}
/**
* #DI\Service(id = "form.type.modelA")
* #DI\Tag("form.type", attributes={ "alias":"model_b_type" })
*/
class FormAType extends AbstractType {
public function getParent() {
return "model_a_type";
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('C')
;
}
// getName and so on
}
If you render formB, you will get A,B,C order, but you want A,C,B.
To accomplish that, create form template and add reference to app. config file:
#app/config/config.yml
twig:
....
form:
resources:
- YourAppBundle:Form:fields.html.twig
{# src/YourAppBundle/Resources/views/Form/fields.html.twig #}
{% block model_a_type_widget %}
{{ form_widget(form.A) }}
{{ form_widget(form.B) }}
{% endblock model_a_type_widget %}
{% block model_b_type_widget %}
{{ form_widget(form.A) }}
{{ form_widget(form.C) }}
{{ block('model_a_type_widget') }}
{% endblock model_b_type_widget %}
Now, when you render formB, you will see desired order and keep code structured.
It happens because every widget rendered only once, so if you render it before calling parent block, you will change their order.
You can do it right in your controller before you render the template:
$form = ...
$view = $form->createView();
// Perform standard array operations to reorder: `$view->children`
// Here's a very simple example:
$firstField = $view->children['firstField'];
unset($view->children['firstField']);
$view->children['firstField'] = $firstField;
return array('form' => $view);
uasort works as well, however usort creates a non-associative array which will break the form rendering later on.
{{ form_start(form) }}
<div>{{ form_label(form.title) }}</div>
<div>{{ form_widget(form.title,{'id': 'blog_title'} )}}</div>
<div>{{ form_label(form.tag) }}</div>
<div>{{ form_widget(form.tag,{'id': 'blog_tag'} )}}</div>
<div>{{ form_label(form.category) }}</div>
<div>{{ form_widget(form.category,{'id': 'blog_category'} )}}</div>
<div>{{ form_label(form.image) }}</div>
<div>{{ form_widget(form.image,{'id': 'blog_image'} )}}</div>
<div>{{ form_label(form.body) }}</div>
<div>{{ form_widget(form.body,{'id': 'editor'} )}}</div>
<input type="submit" class="btn btn-success" value="Create" />
{{ form_end(form) }}

Double validation errors in custom form type

I'm implementing a custom form type that provides an autocomplete field to select a location (country,city or spot). The form type creates two fields, one text field for the autocomplete search input and one hidden field to hold the selected id of the selected location.
When typing into the text field, a server call is made and results are displayed via jquery autocomplete. If a location is selected, the id of the selected location is written to the hidden field whereas the name of the location is displayed in the text field. On the server, I use a client transformer to lookup the entity of the id passed by the hidden field. The text field is ignored.
My model class defines a location field with a property to write back the location entity annotated with a NotNull validation constraint.
Everything works perfectly fine so far but if I do not select a location, the validation message "This value should not be null." is displayed two times.
The bundle is public and can be found in my github repo. The relevant classes are the LocationFieldType and the LocationDataTransformer and the form theme.
And now for how I'm integrating the form type into my project. I added the whole code, sorry for the mass;)
In the model, I define the property as following:
class JourneyCreate
{
/**
* #Assert\NotNull()
* #Assert\Choice(choices = {"offer", "request"})
*/
public $type;
/**
* #Assert\NotNull()
* #Assert\Date()
*/
public $date;
/**
* #Assert\NotNull()
* #Assert\Time()
*/
public $time;
/**
* #Assert\NotNull()
*
*/
public $start;
/**
* #Assert\NotNull()
*
*/
public $destination;
public function buildJourney(User $owner)
{
switch($this->type)
{
case 'offer':
$journey = new JourneyOffer();
break;
case 'request':
$journey = new JourneyRequest();
break;
default:
throw new \InvalidArgumentException('Invalid journey type');
}
$journey->setDate($this->date);
$journey->setTime($this->time);
$journey->addStation(new JourneyStation($this->start));
$journey->addStation(new JourneyStation($this->destination));
$journey->setOwner($owner);
return $journey;
}
}
And in the main form I add the field as following:
class JourneyCreateType extends BaseType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('type','choice', array(
'choices' => array(
'offer' => 'Driver',
'request' => 'Passanger',
),
'empty_value'=>'',
'multiple' => false,
'expanded' => true,
))
->add('date','date',array(
'widget' => 'single_text',
'format' => $this->getDateFormat(\IntlDateFormatter::TRADITIONAL),
))
->add('time','time',array(
'widget' => 'single_text',
))
->add('start','room13_geo_location')
->add('destination','room13_geo_location')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\DemoBundle\Form\Model\JourneyCreate',
));
}
public function getName()
{
return 'journey_create';
}
}
And the controller code:
/**
* #Route("/create/{type}", defaults={"type" = null})
* #Template()
*/
public function createAction($type=null)
{
if($type !== null && !in_array($type,array('request','offer')))
{
throw new NotFoundHttpException();
}
$journeyCreate = new JourneyCreate();
$journeyCreate->type = $type;
$form = $this->createForm(new JourneyCreateType(),$journeyCreate);
if($this->isPost())
{
$form->bind($this->getRequest());
if($form->isValid())
{
$journeyCreate = $form->getData();
$journey = $journeyCreate->buildJourney($this->getCurrentUser());
$this->persistAndFlush($journey);
return $this->redirect($this->generateUrl('acme_demo_journey_edit',array('id'=>$journey->getId())));
}
}
return array(
'form' => $form->createView(),
);
}
And finaly the template code to display the form:
{% block page_body %}
<form class="form-horizontal" action="{{ path('acme_demo_journey_create') }}" method="post" novalidate>
{{form_widget(form)}}
<div class="form-actions">
<button class="btn btn-primary" type="submit">{{'form.submit'|trans}}</button>
{{'form.cancel'|trans}}
</div>
</form>
{% endblock %}
I'm having the theory that this could be because I use two form fields but don't know how to fix this. Any suggestions about how to solve this more elegant are welcome.
As complicated as this question might look, the answer is as simple as removing the {{form_errors(form)}} from the widget template block. Because the *form_row* block looks like:
{% block form_row %}
{% spaceless %}
<div class="form_row">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
The error was simply outputted two times.

symfony2 formbuilder

i created a customFormtype in symfony2 and i'm using it in a formbuilder in my controller.
This is the html result when i render the form:
<div id="form">
<input type="hidden" id="form__token" name="form[_token]" value="2e8fe0d777b5c0d7d30d9bfd9d5143811c790b1d">
<div>
<label class=" required">Stars</label>
<!-- some other stuff -->
</div>
</div>
Where does the id form came from and where can i change the name?
Thank you very much.
The id of the form is defined by the getName() function
class TaskType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('task');
$builder->add('dueDate', null, array('widget' => 'single_text'));
}
public function getName()
{
return 'task';
}
}
Ex. 'task' here. (http://symfony.com/doc/current/book/forms.html#creating-form-classes)
You may use a named form builder:
protected function createMyForm()
{
return $this->container->get('form.factory')->createNamedBuilder('my_form_name', 'form')
->add('id', 'hidden')
->getForm();
}

Resources