Symfony2 Embed form values - php

I have my own utility CalculatorType for just calculating values for my document(I am using ODM), not reference for another document or sub arrays|object.
It has simple 2 inputs:
$builder
->add('price', 'text', array(
'label' => false,
'data' => isset($options['data']) ? $options['data']->getPrice() : '0.00'
))
->add('count', 'integer', array(
'label' => false,
'data' => isset($options['data']) ? $options['data']->getCount() : '10000'
));
In parent form I have:
$builder->
... // multiple fields
->add('calculator', 'calculator');
So when I am trying to save my form, I have an error:
Neither the property "calculator" nor one of the methods
To skip setting calculator field, I've added mapped => false to options
->add('calculator', 'calculator', array('mapped' => false));
and added eventlistener to transform calculator data
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$data = $event->getData();
$data["price"] = $data["calculator"]["price"];
$data["count"] = $data["calculator"]["count"];
unset($data["calculator"]);
$event->setData($data);
});
Now form submits values but calculator fields not passing to embed form, because of unsetting $data['calculator']
If I comment unset($data["calculator"]); then I have an error
This form should not contain extra fields
So I can't find any way to make this form work. Any ideas?

Found mistake in my form, so there is option for such type of forms: http://symfony.com/doc/current/cookbook/form/inherit_data_option.html

Related

How to use unmapped field in Symfony form to adjust data post submit?

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.

How can I edit a field of a form in my controller?

I'm having trouble formulating a solution for 'editing' a field of my form in my controller.
Here's what I have:
I have a symfony2 form registered as a service that I call in a function in my controller. I am removing a bunch of fields that aren't necessary for this other form I am directing my users to and then adding a few others.
(I realize I could create another form and create another service and such but for my purpose this would be a bit overkill. I'm doing it this way because the form functions the same, however some fields are not needed and a few new specific ones are.)
I would now like to essentially 'edit' one field in this form... The 'occupation' field. This field is a choice field acting as radio buttons populated by an array of choices. It's required and has no empty_value requirement in its original state.
I would like to edit it in my controller function to have the same exact values however with a required value of false and an empty_value of null.
With the commented out code below the result is a dissapearance of the occupation field in my 'new' form and it is replaced by an empty drop down. I realize it's because I'm overriding the whole field below, but I cannot figure out how to simply edit it.
Code:
/**
* Explanation of addAndRemoveFieldsInRegisterForm function:
* The function gets the 'registration' form and removes any
* fields not needed for the 'in_registration' form
* and then adds the necessary fields to the form.
*/
private function addAndRemoveFieldsInRegisterForm($user)
{
$form = $this->createForm('user_registration', $user);
// http://stackoverflow.com/questions/10920006/pass-custom-options-to-a-symfony2-form ---
// --- use that to help. Look at changing the value of array.
$form->remove('title');
$form->remove('company');
$form->remove('username');
$form->remove('city');
$form->remove('state');
$form->remove('country');
$form->remove('gender');
$form->remove('age');
$form->remove('roles');
// $form->remove('occupation');
// $pr = $form->get('occupation');
// $pr->set('required' => false);
// $form->get('occupation')->add('required'=>false, 'empty_value'=>null);
// $form->add('occupation','choice', array(
// 'required' => false,
// 'empty_value' => null,
// ));
// echo "<pre>";
// var_dump(get_class_methods($form));die;
$form->add('occupation','choice', array(
'required' => false,
'empty_value' => null,
));
$form->add('canEmail', 'checkbox', array(
'label' => 'Can Email?',
'required' => false,
));
$form->add('sendEmail', 'choice', array(
'label' => 'Send Welcome Email? ',
'required' => true,
'mapped' => false,
'expanded' => true,
'choices' => array(
"yes" => "Yes",
"no" => "No"
),
));
return $form;
}
Original Form (the one that's used as a service)
private $requireOccupation;
$this->requireOccupation = true;
->add('occupation','choice', $options['occupation'])
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$occupation = array(
"label" => "Which of these currently describes you best? (Occupation):",
"expanded" => true,
'required'=> $this->requireOccupation,
"choices" => array(
"X" => "X",
"B" => "B",
"C" => "C",
"J" => "J",
),
'constraints' => array(
new NotBlank()
));
$resolver->setDefaults(array(
'occupation' => $occupation,
));
}
I think it is better to create another form. It can herit from your already defined form to change only the field you want
class SomeFormType extends OriginalFormType {
public function buildForm(FormBuilderInterface $builder, array $options) {
parent::buildForm($builder, $options);
$builder
->remove('someField')
->add('someField', 'choice', [
"expanded" => true,
"choices" => $yourArray
]);
}
It has the advantage to be mapped on different object
Firstly, I realize the way I wanted to solve this issue is odd when considering I could have created another form with either the fields I wanted to use or just the one field that needed to change and register the form as a service to use it elsewhere, but I was tasked to complete it this way.
Second, my solution is quite simple. I pass values into my form with a default value set in the form.
In the form:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$requireOccupation = true;
$emptyValue = null;
//whatever other values you want to set here
$resolver->setDefaults(array(
'requireOccupation' => $requireOccupation,
'emptyValue' => $emptyValue,
));
}
and then on the field's properties:
$builder->add('occupation', 'choice', array(
"label" => "Some sort of label",
"required" => $options['requireOccupation'],
"empty_value" => $options['emptyValue'],
...
));
Now in the controller:
$form = $this->createForm('registration', $user, array(
'requireOccupation' => false, 'emptyValue' => null
));
call that where you want to generate your form while passing in the values you want to use for that form.
I am by no means an expert on Symfony and this solution would probably generate some issue with those who are. But it works for me.

Symfony Sonata project: How to add multiple input texts to block?

I would like to add a collection of input text with same name (i.e. name="blabla[]") filed to admin block with add/delete buttons.
I'm using collection form field type but can't see add/delete buttons
public function buildEditForm(FormMapper $formMapper, BlockInterface $block)
{
$formMapper->add('settings', 'sonata_type_immutable_array', array(
'keys' => array(
array('title', 'collection',
array('type' => 'text' ,
'required' => true,
'allow_add' => true,
'data' => array('First' => 'One')
)
)
)
));
}
I get below result without add/delete buttons!
Any idea how to get it working ?
I think you should use sonata_type_collection or sonata_type_native_collection instead of collection.
Here is an extract of the field doc :
14.1.7. SONATA_TYPE_NATIVE_COLLECTION (PREVIOUSLY COLLECTION)
This bundle handle the native Symfony collection form type by adding:
an add button if you set the allow_add option to true. a delete button
if you set the allow_delete option to true.

other in entity choice list form

I'm new to symfony. I have a drop down in a form with data fetched from DB.
$builder->add('category', 'entity', array(
'label' => 'category',
'class' => 'MyBundle:category',
'expanded' => false,
'multiple' => false,
'mapped' => false,
'empty_value' => 'category'
));
$builder->add('other_category', 'text', array(
'label' => 'category',
'required' => false,
'invalid_message' => 'Please enter a valid category',
'mapped' => false,
));
the user can also add new category to the table. when other is selected from drop down, the 'other_category' input field is shown, else its hidden.
'Other' was added to drop down with the help of this code.
public function finishView(FormView $view, FormInterface $form, array $options)
{
$new_choice = new ChoiceView(array(), 'other', 'Other');
$view->children['category']->vars['choices'][] = $new_choice;
}
If a option is selected from drop down the form works fine. Data gets stored without any error. But if user selects 'other' and enters a new category the page reloads with 'This value is no valid' under the category options and there is no form validation for the 'other_category' entered by user.
Can someone help me with the form validation and also entering of a new category or suggest a better way to implement the above functionality.
The validation error is happening because the form field type is Entity, but there is no "MyBundle:category" entity with the identifying value "other".
You've not specified the "choice_label" property in your Entity form type so I'll assume your "MyBundle:category" entity has a __toString() function. This would mean none of the "MyBundle:category" entities return "other" in their __toString() function.
I can think of two options to work around this right now:
1) Add a "MyBundle:category" entity with value "other". This is the easiest way, but it's fairly assumed you don't want such a category to exist in your database.
2) Load the list of "MyBundle:category" entities in advance from your controller, build them into an associative array, append your "other" option to the array, then pass that array to the form. You'd need to swap the Entity form type for a Choice type and use the categories array as the choices.
If your form is a FormType class you'll need to pass the array in with the class constructor.
If you don't mind having a new category with an "other" value in your category table, just add it. Otherwise go for option 2, which won't make much difference to what you do after receiving the submitted form as this form field isn't mapped to an entity property anyway.

preferred_choices entity field form never run

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)

Categories