I'm trying to add the selected attribute to my <option> tags
{% for product in form.products %}
{# of course this should only done inside a if,
but for testing purpose, it's okay #}
{{ form_widget(product, { 'attr': {'selected': 'selected'} }) }}
{% endfor %}
However this does not work. Even exactly the same copy&paste from the symfony2 docs here does not work: http://symfony.com/doc/current/book/forms.html#rendering-each-field-by-hand
I'm adding the form element inside a FormType à la:
public function buildForm(FormBuilderInterface $builder, array $options) {
parent::buildForm($builder, $options);
$builder
->add('products', 'entity', array('attr' => array('class' => 'choseable input-xlarge'),
'property_path' => false, 'label' => 'form.products.title', 'class' => 'Test\Bundle\Entity\Product', 'choices' => $products, 'multiple' => true, 'empty_value' => 'form.products.placeholder'));
}
All variables ($products) are ok in the example above.
Is there a problem?
I'm using Symfony 2.1.9-dev.
Can you update the question with form creation code? I'm pretty sure that Symfony is able to make item selected only if it's present in your model. But since you field is marked with property_path => false I don't think you can override this behavior...
An idea:
Fetch all the data used in your entity field type and pass it to form object (either via constructor or options array)
Add field as you're currently doing it
Invoke setData right afterwards to set field data (using data fetched in step #1)
Btw, I am not big fan of setData but situation like this just calls for it :)
Hope that this helps...
There are several issues with your code. I reformatted the code below for better clarity.
public function buildForm(FormBuilderInterface $builder, array $options) {
parent::buildForm($builder, $options);
$builder->add('products', 'entity', array(
'attr' => array('class' => 'choseable input-xlarge'),
'property_path' => false,
'label' => 'form.products.title',
'class' => 'Test\Bundle\Entity\Product',
'choices' => $products,
'multiple' => true,
'empty_value' => 'form.products.placeholder'
));
}
Don't use class inheritance for form types (except of course extending AbstractType). Use getParent() for extending other types instead.
You can't set attributes of <option> tags manually. You would have to render the tags by hand.
For marking choices as selected, you need to pass default data to the field. Usually getProducts() would be called here to get the defaults, but you disabled that by setting property_path to false. So you should set the defaults manually by using the data option. Note that the defaults need to be an array/a collection, since multiple is true.
You are manually passing choices, which is a bad practice. If you just remove this option, the entity field can itself query the choices from the DB and optimize the needed statements for better performance.
Format your code. Really. :P
Related
I have an older project which still uses Symfony 2. In it there is a form for editing a client profile. In the controller we have this:
$form = $this->createForm(new ClientProfile($remindTimes), $client);
$form->handleRequest($request);
And in the ClientProfile class we have
class ClientProfile extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('client_name', 'text', array('label' => 'Full name'))
->add('client_address', 'text', array('label' => 'Address', 'required' => false))
->add('client_city', 'text', array('label' => 'City', 'required' => false))
->add('client_post_code', 'text', array('label' => 'Postal index', 'required' => false))
->add('client_email', 'email', array('label' => 'E-mail', 'required' => false));
}
}
... and some other fields, but you get the gist. Then there's also a Twig view which renders the HTML. Standard stuff, as far as I can tell.
Now for my requirement. The client object has two special properties. Let's call them FroobleEnabled and FroobleType. If Frooble is disabled, then the type value has no meaning (can be set to 0). In the UI I want a dropdown with the values:
Disabled
Type 1
Type 2
Type 3
If the user selects Disabled, then FroobleEnabled gets set to false and FroobleType gets set to 0. Otherwise FroobleEnabled gets set to true and FroobleType to 1, 2 or 3 respectively.
How do I do this? The thing which makes this special is that it's not a 1:1 mapping anymore. There are two fields in the model object but just one UI control. I think I could achieve this with the a DataMapper, but I also don't want to manually map all the other fields (though I can, if there's no other option). I also cannot find any decent documentation about DataMapper or any other Symfony Forms features that could help me.
One way to accomplish this is to create a field, let's say, frooble.
In the form class create a frooble Choice field with mapped => false and values 0, 1, 2, 3. Set its choices to strings appropriate to the application.
In the controller, after form submission & validation, include code something like:
...
$frooble = $form->get('frooble')->getData();
if (0 === $frooble) {
$client->setFroobleEnabled(false);
$client->setFroobleType(0);
} else {
$client->setFroobleEnabled(true);
$client->setFroobleType($frooble);
}
I have two fields in my entity contractLength and contractEndDate. The aim is to be able to specify EITHER a contractLength OR a contractEndDate.
To accomplish this I have added an unmapped field to the form useBespokeEndDate, of checkbox type.
I want to be able in the form class ContractType to listen for the submit event and then check if the checkbox is ticked, if it is then proceed as normal but if it is not ticked (so we are using contractLength) then set the contractEndDate field to null.
I believe I should be using Event Listeners as detailed here http://symfony.com/doc/master/form/dynamic_form_modification.html#customizing-your-form-based-on-the-underlying-data (under the Dynamic Generation for Submitted Forms heading) to listen for the POST_SUBMIT event and then check the value of useBespokeEndDate and then modify the data as appropriate. However, I'm confused as to how exactly to get the form data for the unmapped field and then to also modify the actual data of the form.
My buildForm method in ContractType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('useBespokeEndDate', CheckboxType::class, array(
'label' => 'Use Bespoke Contract End Date?',
'empty_data' => false,
'mapped' => false
))
->add('contractLength', ChoiceType::class, array(
'choices' => array(12 => 12, 24 => 24, 36 => 36, 48 => 48, 60 => 60),
'label' => 'Contract Length (months)'
))
->add('contractEndDate', DateType::class, array('label' => 'Contract End Date'))
->add('save', SubmitType::class, array('label' => 'Save'));
$builder->addEventListener(FormEvents::POST_SUBMIT,
// Do something here to modify the data
);
}
Had the same problem, and I could access the unmapped values this way :
// in your POST_SUBMIT callback :
$form = $event->getForm();
$contract = $event->getData();
// get the unmapped value :
$useBespokeEndDate = $form->get('useBespokeEndDate')->getData();
// .. continue
I've got a similar situation where I have an option field, it's use (and therefore value) depending on a radio button group.
Rather than making a dynamic form, I instead separated the form from the entity, and actually ended up created a sub-form (Type) that had its own data_class Form configuration option and Data Mappers to convert the value objects to the entities.
There are useful blog posts with more information about Value Objects in forms and Creating a Custom Data Mapper for Symfony Forms.
For me, this alos gives the advantage of moving some quite complex checks into their own smaller class, and making the code more reusable.
I have a simple formType with an single checkBoxType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('domain_choose', CheckboxType::class, [
'label' => false,
]);
}
Then I render it inside a loop
{% for domain, availability in availabilities %}
<tr>
<td>{{ form_widget(form.domain_choose) }}</td>
</tr>
{% endfor %}
The problem is that, only the first checkbox of the loop is rendered.
I tried to manually change the form name without succes
{'full_name': 'domain_choose[domain_choose_' ~ loop.index ~ ']'},
Is there a way to render this checkbox multiple time inside my loop ? Or should I use a single checkbox input without formType ?
#Jérôme As per symfony form you do not repeat form element. If you need multiple checkbox then you can use something like this
$builder->add('domain_choose', ChoiceType::class, array(
'choices' => array(
'Select' => null,
'Domain1' => 'Domain1',
'Domain2' => 'Domain2',
),
'expanded' => true,
'multiple' => true
));
expanded => true and multiple => true is used for (multiple)checkbox, For more detail you can visit http://symfony.com/doc/current/reference/forms/types/choice.html
I have a Symfony2 unmapped form which has a field of type 'Entity'. Here is the form type definition:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('RegistrationForgotEmailTemplate', 'entity', array(
'class' => 'EmailBundle:EmailTemplate',
'placeholder' => '-- Default ---',
));
}
Here is where I build the form instance:
$data = array('RegistrationForgotEmailTemplate' => 4);
$form = $this->createForm($formType, $data, array(
'action' => $url,
'method' => 'POST'
));
My problem is the form is not setting the RegistrationForgotEmailTemplate field to the correct entity (id 4). I assume this is because I am providing the id of the entity rather than an instance of it. Is there a way of providing the just the id of the entity to set this field or is the only way to pass an instance of the entity I want to set the field to?
You could either use a transformer or you could use the "choice" field rather than "entity" and inject an object to provide the choices list (likely a repository).
Personally I think you'll find the transformer method easier. The resulting code will be nicer too
I want to set a form with a "preferred_choices" on top of my select field HTML corresponding to previous submitted datas selected by the user. I want to construct an entity field with a constant list AND a preferred_choices top element if the form is previously submitted.
I never ran correctly this function in symfony2.
Can you help me to construct correctly my field form.
Why my preferred_choices options select nothing when the form is construct ?
I setting this with correct object setted previously in the code.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$defaultCQSsearch = new CqsProSansMarque();
$defaultCQSsearch->setRayLibelle((!array_key_exists('ray_libelle', $options['attr'])) ? null : $options['attr']['ray_libelle']);
$defaultCQSsearch->setFamLibelle((!array_key_exists('fam_libelle', $options['attr'])) ? null : $options['attr']['fam_libelle']);
$defaultCQSsearch->setCaeLibelle((!array_key_exists('cae_libelle', $options['attr'])) ? null : $options['attr']['cae_libelle']);
$builder
->add('ray_libelle', 'entity', array(
'class' => 'ApplicationDriveBundle:CqsProSansMarque',
'data_class' => 'Application\DriveBundle\Entity\CqsProSansMarque',
'property' => 'ray_libelle',
'query_builder' => function(CqsProSansMarqueRepository $er){
return $er->createQueryBuilder('a')
->select('a')
->groupBy('a.ray_libelle');
},
'preferred_choices' => array($defaultCQSsearch),
'label' => 'rayon',
'required' => false,
))
preferred_choices option expects an array of values but you are passing an array of object (i.e. $defaultCQSsearch)