Getting empty_data to work in a Symfony 2 application - php

I am adding fields to a project based on a project based on Symfony 2 and Sonata. I am trying to follow the instructions from this answer. In one of my admin classes, I have inserted the following code:
$default = 'Germany';
if (!$this->getUser()->hasRole(User::CONTENT_SUPPLIER)) {
$formMapper
->tab('Distribution')
->with('Distribution')
->add(
'module',
null,
[
'empty_data' => $default,
]
)
->add(
'distributions',
'distribution_list',
[
'label' => false,
'required' => 'false',
'disabled' => true
]
)
->add('plannedDistributions')
->end()
->end()
;
}
... and while I expect to see a reference to the "Germany" object by default in my form, I instead see an empty field. Should I be passing in an object rather than a string? Is what I'm trying to do even possible? What am I doing incorrectly here?

I think you missed a crucial bit in the documentation regarding empty_data:
This option determines what value the field will return when the submitted value is empty (or missing). It does not set an initial value if none is provided when the form is rendered in a view.
This means it helps you handling form submission with blank fields.
That means empty_data will populate your model with the data when the form was submitted without a default value.
I'm not familiar with the $formMapper used in your snippet, but in a typical Symfony-Controller you could create your form like this:
$form = $this->createForm(MyForm::class, $initialData);
In this case $initialData contains a property Distribution with the value Germany. Alternatively you could try providing the value in your frontend.

To set default data use option 'data'.
Sample:
//Use block
use Symfony\Component\Form\Extension\Core\Type\TextType;
//...
$formMapper
->add('module', TextType::class,[
'data' => 'Gearmany',
]);

Related

Input validating forms Symfony

I've got an Integer field in Symfony form, which represents quantity - ofcourse it should be unable to pass the value equal or less than 0, so I've validated form in Entity and Form.
But - It's validated only when form is already sent. Is there any built-in method to validate form on input? I know that propably i would need to write own JS function to provide this, but I don't want to repeat someone others job :)
Found the solution.
To validate Symfony form on HTML side, you can just add an attribute to input field, reference: https://www.w3schools.com/htmL/html_form_attributes.asp
So, you form field should look like:
->add('quantity', IntegerType::class, ['label' => false,
'constraints' => new GreaterThan(array('value' => 0)),
'attr' => array(
'min' => '0',
)
])
Use pattern and min on your input. HTML should prevent form submit as long as the pattern isn't matched. (Careful, isn't doens't prevent submit if you're usin Javascript/Ajax to submit)
$builder->add('input_name', TextType::class, array(
'attr'=>array(
'pattern'=>'^[1-9][0-9]*' // 1 and above
'min'=>'1'
)
));
I'm using a TextType to remove the arrows in the input.
If you want to keep them, use NumberType.
A little addition, if you want to make sure a user can only enter a number you can add this bit of js to your code.
->add('quantity', NumberType::class, [ 'attr' =>
[
'oninput' => "this.value = this.value.replace(/[^0-9]/g, '').replace(/(\..*)\./g, '$1');"
] ])

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.

Form in Symfony 2 whose fields change in number and purpose according to external data

I am building an application using Symfony 2, and one of the pages contains a large form, which has a number of fixed fields but also a number of fields whose presence and number depends entirely on related data taken from the DB. More specifically, the form poses a number of questions, and the questions themselves come from the DB, having different text and IDs. The answers need to be stored in the DB, linked to the questions.
I've read up on how Symfony handles forms (Basic Symfony Forms), including the relationship between a Form object and the data object backing it, creating Fields from the Form object, and validating input according to rules applied to the Form object.
Since I don't want a fixed Form object I looked at how you're meant to dynamically modify Forms (Dynamic Symfony Forms), but the examples there seem to be about choosing whether to show certain pre-defined fields, or controlling the options in the fields, not completely generating new fields from scratch, which is what I want.
I had a look at the Symfony page on Data Transformers, and it seemed relevant, but I couldn't quite get my head around how it would help me. They seem more about mapping a Form object to an Entity than mapping complex form submissions to a Form object.
I did see this SO answer where the object backing the Form object is just an array, which also seemed relevant: I thought that maybe the answer was to generate some anonymous object with the dynamic fields in, and then make a form from that, but I don't see how I'd set up Symfony validation on it, and my form is not completely different each time, so doesn't need to be entirely anonymous.
What is the best way to achieve this using Symfony 2, ideally without giving up such framework benefits as easy validation etc?
EDIT I hadn't heard of Symfony Form Collections before, and it looks like this might do what I want - each Question in my case would map to an individual subform object. I'll update again if I try it out successfully.
Your question is pretty broad but this might help focus.
I have what I call a DynamicFormType which can add elements to a form based on a configuration items array.
class DynamicFormType extends AbstractType
{
protected $name;
protected $items;
public function getName() { return $this->name; }
public function __construct($name, $items)
{
$this->name = $name;
$this->items = $items;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
foreach($this->items as $name => $item)
{
switch($item['type'])
{
case 'radio':
$attr = array('class' => 'radio-medium');
$builder->add($name,'choice',array(
'label' => $item['label'],
'required' => false,
'attr' => $attr,
'empty_value' => false,
'expanded' => true,
'multiple' => false,
'choices' => $item['choices'],
));
break;
case 'select':
$attr = array();
$builder->add($name,'choice',array(
'label' => $item['label'],
'required' => false,
'attr' => $attr,
'empty_value' => false,
'expanded' => false,
'multiple' => false,
'choices' => $item['choices'],
));
break;
case 'text':
$attr = array();
if (isset($item['size'])) $attr['size'] = $item['size'];
$builder->add($name,'text',array(
'label' => $item['label'],
'required' => false,
'attr' => $attr,
));
break;
case 'textarea':
$attr = array();
if (isset($item['rows'])) $attr['rows'] = $item['rows'];
if (isset($item['cols'])) $attr['cols'] = $item['cols'];
$builder->add($name,'textarea',array(
'label' => $item['label'],
'required' => false,
'attr' => $attr,
));
break;
case 'collection':
$dynamicType = new DynamicFormType($name,$item['items']);
$builder->add($name,$dynamicType,array(
'label' => false,
'required' => false,
));
break;
}
}
return; if ($options);
}
}
I trust you can see how the items array is used to create form elements. In my application I use this with:
$basicType = new DynamicFormType('basic',$project->getPlan());
$builder->add('basic',$basicType, array('label' => false));
$project->getPlan() returns the items array which describes the various elements I want to add.
Hopefully, you can start with something like this and figure out what elements need to be added from your data structure. I didn't need validation here but it should be easy enough to add constraint information to you elements once you get things working.
OK, in the end what I wanted wasn't a truly dynamic form (but thanks to #Cerad for that code), but one using Collections where the number of certain child form items was variable.
So I essentially followed the Symfony Docs about Collections

Symfony2 Form validation dependency between form_types in one form

In my symfony2 project I created a new FormType which is called "ChoiceAndOrTextType" which is a list of choices (checkboxes) but the user can also check the "others" option and write his answer into a textfield.
The code is like this one here in the answer of bschussek:
Use a conditional statement when creating a form
$builder
->add('choice', 'choice', array(
'choices' => $options['choices'] + array('Other' => 'Other'),
'required' => false,
))
->add('text', 'text', array(
'required' => false,
))
->addModelTransformer(new ValueToChoiceOrTextTransformer($options['choices']))
;
Now i want to validate this correctly, so when the user checks "Others" then the textfield needs to be filled out, if "Others" isn't checked it can be blank. (Kind of dependent validation).
How do I do this?
You need to use two validation constraints, for example in validation.yml yaml file

Symfony2 and FormBuilder: How to get the elemetns number added in the builder

I have a formbuilder where I am adding some values from an entity:
$builder->add('affiliation', 'entity', array(
'class' => 'SciForumVersion2Bundle:UserAffiliation',
'multiple' => true,
'expanded' => true,
'query_builder' => function(EntityRepository $er) use ($author,$user) {
return $er->createQueryBuilder('ua')
->where("ua.user_id = {$user->getId()}")
->andWhere("ua.affiliation_id not in ( select pa.affiliation_id FROM SciForumVersion2Bundle:PersonAffiliation pa where pa.person_id = {$author->getPersonId()} )");
},
'required' => true,
));
In my controller, I would like to check if there is something in my form. If there is something, I will display one view, if there is nothing, I will display another view.
Is this possible and if so, how?
Thank you.
Simply try it:
$data = $form->getData()
function getData() documentation Book
If you want to get the current data (just after rendering the form) in your form type you can use the builder supplied in each form type by standard.
It works exactly as a normal form response, so you can use:
$builder->getData();
and use if clauses to add different fields depending on what you want to generate.

Categories