Set default values using form classes in Symfony 2 [duplicate] - php

Is there an easy way to set a default value for text form field?

you can set the default value with empty_data
$builder->add('myField', 'number', ['empty_data' => 'Default value'])

Can be use during the creation easily with :
->add('myfield', 'text', array(
'label' => 'Field',
'empty_data' => 'Default value'
))

I've contemplated this a few times in the past so thought I'd jot down the different ideas I've had / used. Something might be of use, but none are "perfect" Symfony2 solutions.
Constructor
In the Entity you can do $this->setBar('default value'); but this is called every time you load the entity (db or not) and is a bit messy. It does however work for every field type as you can create dates or whatever else you need.
If statements within get's
I wouldn't, but you could.
return ( ! $this->hasFoo() ) ? 'default' : $this->foo;
Factory / instance.
Call a static function / secondary class which provides you a default Entity pre-populated with data. E.g.
function getFactory() {
$obj = new static();
$obj->setBar('foo');
$obj->setFoo('bar');
return $obj;
}
Not really ideal given you'll have to maintain this function if you add extra fields, but it does mean you're separating the data setters / default and that which is generated from the db. Similarly you can have multiple getFactories should you want different defaulted data.
Extended / Reflection entities
Create a extending Entity (e.g. FooCreate extends Foo) which gives you the defaulted data at create time (through the constructor). Similar to the Factory / instance idea just a different approach - I prefer static methods personally.
Set Data before build form
In the constructors / service, you know if you have a new entity or if it was populated from the db. It's plausible therefore to call set data on the different fields when you grab a new entity. E.g.
if( ! $entity->isFromDB() ) {
$entity->setBar('default');
$entity->setDate( date('Y-m-d');
...
}
$form = $this->createForm(...)
Form Events
When you create the form you set default data when creating the fields. You override this use PreSetData event listener. The problem with this is that you're duplicating the form workload / duplicating code and making it harder to maintain / understand.
Extended forms
Similar to Form events, but you call the different type depending on if it's a db / new entity. By this I mean you have FooType which defines your edit form, BarType extends FooType this and sets all the data to the fields. In your controller you then simply choose which form type to instigate. This sucks if you have a custom theme though and like events, creates too much maintenance for my liking.
Twig
You can create your own theme and default the data using the value option too when you do it on a per-field basis. There is nothing stopping you wrapping this into a form theme either should you wish to keep your templates clean and the form reusable. e.g.
form_widget(form.foo, {attr: { value : default } });
JS
It'd be trivial to populate the form with a JS function if the fields are empty. You could do something with placeholders for example. This is a bad, bad idea though.
Forms as a service
For one of the big form based projects I did, I created a service which generated all the forms, did all the processing etc. This was because the forms were to be used across multiple controllers in multiple environments and whilst the forms were generated / handled in the same way, they were displayed / interacted with differently (e.g. error handling, redirections etc). The beauty of this approach was that you can default data, do everything you need, handle errors generically etc and it's all encapsulated in one place.
Conclusion
As I see it, you'll run into the same issue time and time again - where is the defaulted data to live?
If you store it at db/doctrine level what happens if you don't want to store the default every time?
If you store it at Entity level what happens if you want to re-use that entity elsewhere without any data in it?
If you store it at Entity Level and add a new field, do you want the previous versions to have that default value upon editing? Same goes for the default in the DB...
If you store it at the form level, is that obvious when you come to maintain the code later?
If it's in the constructor what happens if you use the form in multiple places?
If you push it to JS level then you've gone too far - the data shouldn't be in the view never mind JS (and we're ignoring compatibility, rendering errors etc)
The service is great if like me you're using it in multiple places, but it's overkill for a simple add / edit form on one site...
To that end, I've approached the problem differently each time. For example, a signup form "newsletter" option is easily (and logically) set in the constructor just before creating the form. When I was building forms collections which were linked together (e.g. which radio buttons in different form types linked together) then I've used Event Listeners. When I've built a more complicated entity (e.g. one which required children or lots of defaulted data) I've used a function (e.g. 'getFactory') to create it element as I need it.
I don't think there is one "right" approach as every time I've had this requirement it's been slightly different.
Good luck! I hope I've given you some food for thought at any rate and didn't ramble too much ;)

If you need to set default value and your form relates to the entity, then you should use following approach:
// buildForm() method
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
...
->add(
'myField',
'text',
array(
'data' => isset($options['data']) ? $options['data']->getMyField() : 'my default value'
)
);
}
Otherwise, myField always will be set to default value, instead of getting value from entity.

You can set the default for related field in your model class (in mapping definition or set the value yourself).
Furthermore, FormBuilder gives you a chance to set initial values with setData() method. Form builder is passed to the createForm() method of your form class.
Also, check this link: http://symfony.com/doc/current/book/forms.html#using-a-form-without-a-class

If your form is bound to an entity, just set the default value on the entity itself using the construct method:
public function __construct()
{
$this->field = 'default value';
}

Approach 1 (from http://www.cranespud.com/blog/dead-simple-default-values-on-symfony2-forms/)
Simply set the default value in your entity, either in the variable declaration or the constructor:
class Entity {
private $color = '#0000FF';
...
}
or
class Entity {
private $color;
public function __construct(){
$this->color = '#0000FF';
...
}
...
}
Approach 2 from a comment in the above link, and also Dmitriy's answer (not the accepted one) from How to set default value for form field in Symfony2?
Add the default value to the data attribute when adding the field with the FormBuilder, adapted from Dmitriy's answer.
Note that this assumes that the property will and will only have the value null when it's a new, and not an existing, entity.
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('color', 'text', array(
'label' => 'Color:',
'data' => (isset($options['data']) && $options['data']->getColor() !== null) ? $options['data']->getColor() : '#0000FF'
)
);
}

A general solution for any case/approach, mainly by using a form without a class or when we need access to any services to set the default value:
// src/Form/Extension/DefaultFormTypeExtension.php
class DefaultFormTypeExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
if (null !== $options['default']) {
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($options) {
if (null === $event->getData()) {
$event->setData($options['default']);
}
}
);
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('default', null);
}
public function getExtendedType()
{
return FormType::class;
}
}
and register the form extension:
app.form_type_extension:
class: App\Form\Extension\DefaultFormTypeExtension
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }
After that, we can use default option in any form field:
$formBuilder->add('user', null, array('default' => $this->getUser()));
$formBuilder->add('foo', null, array('default' => 'bar'));

Don't use:
'data' => 'Default value'
Read here: https://symfony.com/doc/current/reference/forms/types/form.html#data
"The data option always overrides the value taken from the domain data (object) when rendering. This means the object value is also overriden when the form edits an already persisted object, causing it to lose its persisted value when the form is submitted."
Use the following:
Lets say, for this example, you have an Entity Foo, and there is a field "active" (in this example is CheckBoxType, but process is the same to every other type), which you want to be checked by default
In your FooFormType class add:
...
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
...
public function buildForm( FormBuilderInterface $builder, array $options )
{
...
$builder->add('active', CheckboxType::class, array(
'label' => 'Active',
));
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event){
$foo = $event->getData();
// Set Active to true (checked) if form is "create new" ($foo->active = null)
if(is_null($foo->getActive())) $foo->setActive(true);
}
);
}
public function configureOptions( OptionsResolver $resolver )
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle:Foo',
));
}

You can set a default value, e.g. for the form message, like this:
$defaultData = array('message' => 'Type your message here');
$form = $this->createFormBuilder($defaultData)
->add('name', 'text')
->add('email', 'email')
->add('message', 'textarea')
->add('send', 'submit')
->getForm();
In case your form is mapped to an Entity, you can go like this (e.g. default username):
$user = new User();
$user->setUsername('John Doe');
$form = $this->createFormBuilder($user)
->add('username')
->getForm();

->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
if ($data == null) {
$form->add('position', IntegerType::class, array('data' => 0));
}
});

My solution:
$defaultvalue = $options['data']->getMyField();
$builder->add('myField', 'number', array(
'data' => !empty($defaultvalue) ? $options['data']->getMyField() : 0
)) ;

Just so I understand the problem.
You want to adjust the way the form is built based on data in your entity. If the entity is being created then use some default value. If the entity is existing use the database value.
Personally, I think #MolecularMans's solution is the way to go. I would actually set the default values in the constructor or in the property statement. But you don't seem to like that approach.
Instead you can follow this: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html
You hang a listener on your form type and you can then examine your entity and adjust the builder->add statements accordingly based on havine a new or existing entity. You still need to specify your default values somewhere though you could just code them in your listener. Or pass them into the form type.
Seems like a lot of work though. Better to just pass the entity to the form with it's default values already set.

If you're using a FormBuilder in symfony 2.7 to generate the form, you can also pass the initial data to the createFormBuilder method of the Controler
$values = array(
'name' => "Bob"
);
$formBuilder = $this->createFormBuilder($values);
$formBuilder->add('name', 'text');

If you set 'data' in your creation form, this value will be not modified when edit your entity.
My solution is :
public function buildForm(FormBuilderInterface $builder, array $options) {
// In my example, data is an associated array
$data = $builder->getData();
$builder->add('myfield', 'text', array(
'label' => 'Field',
'data' => array_key_exits('myfield', $data) ? $data['myfield'] : 'Default value',
));
}
Bye.

Often, for init default values of form i use fixtures. Of cause this way is not easiest, but very comfortable.
Example:
class LoadSurgeonPlanData implements FixtureInterface
{
public function load(ObjectManager $manager)
{
$surgeonPlan = new SurgeonPlan();
$surgeonPlan->setName('Free trial');
$surgeonPlan->setPrice(0);
$surgeonPlan->setDelayWorkHours(0);
$surgeonPlan->setSlug('free');
$manager->persist($surgeonPlan);
$manager->flush();
}
}
Yet, symfony type field have the option data.
Example
$builder->add('token', 'hidden', array(
'data' => 'abcdef',
));

There is a very simple way, you can set defaults as here :
$defaults = array('sortby' => $sortby,'category' => $category,'page' => 1);
$form = $this->formfactory->createBuilder('form', $defaults)
->add('sortby','choice')
->add('category','choice')
->add('page','hidden')
->getForm();

Default values are set by configuring corresponding entity. Before binding the entity to form set its color field to "#0000FF":
// controller action
$project = new Project();
$project->setColor('#0000FF');
$form = $this->createForm(new ProjectType(), $project);

If that field is bound to an entity (is a property of that entity) you can just set a default value for it.
An example:
public function getMyField() {
if (is_null($this->MyField)) {
$this->setMyField('my default value');
}
return $this->MyField;
}

I usually just set the default value for specific field in my entity:
/**
* #var int
* #ORM\Column(type="integer", nullable=true)
*/
protected $development_time = 0;
This will work for new records or if just updating existing ones.

As Brian asked:
empty_data appears to only set the field to 1 when it is submitted with no value.
What about when you want the form to default to displaying 1 in the
input when no value is present?
you can set the default value with empty_value
$builder->add('myField', 'number', ['empty_value' => 'Default value'])

I solved this problem, by adding value in attr:
->add('projectDeliveringInDays', null, [
'attr' => [
'min'=>'1',
'value'=>'1'
]
])

Related

How do I use a Symfony data mapper on form fields I don't want to directly persist?

I am building a Symfony data mapper class called EventDataMapper that takes two TextType form fields and converts them into an EventDetails object stored by Doctrine. That EventDetails object is, in turn, referred to in an Event object. (The DataMapper also does some other things.) A slightly simplified version of my MapFormsToData() method looks like this:
public function mapFormsToData($forms, &$data): void
{
...
foreach ($forms as $element => $form) {
switch ($form->getConfig()->getName()) {
case 'title':
$this->mapTitleFormToData($form, $element, $data);
$form->getParent()->remove('title');
break;
case 'description':
$this->mapDescriptionFormToData($form, $element, $data);
$form->getParent()->remove('description');
break;
}
}
$this->mapTitleAndDescriptionFieldsToEventDetailsData($data, $forms);
}
... and a slightly simplified version of my EventFormType's buildForm() method looks like this:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
...
$builder
->add('title', TextType::class)
->add('description', TextareaType::class)
->setDataMapper(new EventDataMapper)
;
}
When I submit my form, however, I get a NoSuchPropertyException stating the following:
Could not determine access type for property "title" in class
"AppBundle\Entity\Event": Neither the property "title" nor one of the
methods "addTitle()"/"removeTitle()", "setTitle()", "title()",
"__set()" or "__call()" exist and have public access in class
"AppBundle\Entity\Event"..
As you can see, I have tried using the remove() method to take the form fields out of the form in order to avoid this exception. That, however, does nothing, which is not surprising, since the list of forms is being passed by value and not by reference. (Changing the function to pass by reference results in a FatalError as the method signature then no longer matches Symfony's DataMapperInterface.)
For reasons I won't get into here, I can't use a new form type to handle EventDetails, so an EventDetailsFormType is not an option. I must do this with my EventFormType class and my EventDataMapper class, and with those classes alone. Similarly, I must keep these two TextType fields in place with the names they currently have, in order not to disrupt work by other members of my team.
Is there a simple way to fix this within these constraints?
Found it!
I just had to add ['mapped' => false] to the form field definitions:
->add('title', TextType::class, ['mapped' => false])
->add('description', TextareaType::class, ['mapped' => false])
Nice and easy.

symfony FormType: setData in EventListener

I've written a symfony FormType-Class which has a POST_SUBMIT EventListener, in which I wanted to set a default value for an
existing field dependant on another, filled form element.
In other words: dependant on a selected, existing user (entity select) I want to set the default data of another entity-select field (country and state) to the correspondant value.
The code looks like this:
$formModifier = function (FormInterface $form, Person $person = null)
{
if ($person) {
$form->get('country')->setData(
$person->getCountry()
);
}
};
By selecting the user the form is submitted but not successfully validated, so the Country should be selected. The Person-Object exists, the value is correct and the statement is reached.
But the value isn't selected. Isn't this possible by using a POST_SUBMIT-FormEvent?
Additional info: symfony 2.7, PHP 5.6, implementation done like the second example under http://symfony.com/doc/2.7/cookbook/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms
Thanks in advance!
Martin
Edit:
The formModifier is called like this:
$builder->get('field')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$person = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $person)
}
};

How to unit-test the presence of form fields in a Symfony2 form with eventlisteners

This is on Symfony2.4:
I am trying to test (in a unit test) the presence of some fields in a form which are supposed to be added dynamically by an event listener. Based on the data passed to the form, certain fields should be present and others not in one case and a different combination in another case.
The form itself is correct, it does work (seemingly) as intended.
My form class' MyFormType::buildForm() method roughly being:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', null, array(
'label' => 'name'
));
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event)
$form = $event->getForm();
$data = $event->getData();
if ($data->getSomeField() === 123) {
$form->add('field1');
} else {
$form->add('field2');
}
});
}
And in the unit test I get a form and set some data (passing the data to the create() method does not make any difference):
$this->form = $this->getService('form.factory')->create(new MyFormType(), null, array('csrf_protection' => false));
$this->form->setData($data);
And right after this I try to dump $this->form->all() to see if the dynamically added fields are present, but they are not.
$this->assertTrue($this->form->has('field1')); also fails.
Any ideas what have I missed, or should have done differently in order to have a fully initialized form (with all the relevant event listeners executed and all fields present, dynamically added or not)?

How to autoselect a passed in date in PHP

Im new to PHP so apologies if this is a simple question. Please could you tell me how to auto select a date drop down in PHP (uses 3 drop downs - day/month/year). This is what my PHP code looks like:
$s = '01/08/2014 11:00:02';
$date = strtotime($s);
$objectToParse->setDateReceived(date('d/M/Y H:i:s', $date));
$form = $this->createForm(new Type(), $objectToParse);
And then the form looks like:
class Type extends AbstractType {
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('dateReceived', 'date', array(
'required' => false
))
}
}
I expected a \DateTime. But at the moment I just receive a
500 Internal Server Error - TransformationFailedException error
On your class Type, add something like :
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\AcmeBundle\Entity\MyObject',
));
}
It makes the binding between the given entity (MyObject) and the form.
From Forms (The Symfony Book) : :
Every form needs to know the name of the class that holds the
underlying data (e.g. Acme\TaskBundle\Entity\Task). Usually, this is
just guessed based off of the object passed to the second argument to
createForm (i.e. $task). Later, when you begin embedding forms, this
will no longer be sufficient. So, while not always necessary, it's
generally a good idea to explicitly specify the data_class option
Also, you need to give a DateTime to the setter setDateReceived. What you gives with the datefunction is a string.
What you need to do is :
$s = '01/08/2014 11:00:02';
$date = \DateTime::createFromFormat('d/M/Y H:i:s', $s);
$objectToParse->setDateReceived($date);

Symfony2 Form Event PreSetData Subscriber

In my Application the user can create Custom Fields for some entities and then set the values for this custom fields for each entity object when i display a form.
The implementation is like this:
1º) I created a Interface for the forms, and the forms that i want implement this Interface.
2º) I created a form extension for all forms:
app_core_form_builder.form_extension:
class: App\Core\Bundle\FormBuilderBundle\Form\FormExtension
arguments: ["#service_container", "#doctrine.orm.entity_manager"]
tags:
- { name: form.type_extension, alias: form }
3º) In this extension if the form implements the interface referenced in the step 1 i add a EventSubscriber:
if($formType instanceof \App\Core\Bundle\FormBuilderBundle\Model\IAllowCustomFieldsdInterface){
$builder->addEventSubscriber(new FormSubscriber($this->container, $this->em));
}
4º) This Form Subscriber subscribes the preSetData FormEvent. In this method i get the Entity associated with the form and i get all custom fields created for it.
Then i add this fields to the form with the help of Symfony2 Form Type.
Everything goes well, and when i display my form the custom fields are rendered correct. Just for the record, when i save the form the values inserted in the custom fields also are store well.
public function preSetData(FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
// During form creation setData() is called with null as an argument
// by the FormBuilder constructor. You're only concerned with when
// setData is called with an actual Entity object in it (whether new
// or fetched with Doctrine). This if statement lets you skip right
// over the null condition.
if (null === $data) {
return;
}
$formEntity = $form->getConfig()->getType()->getInnerType()->getEntity();
$DbEntity = $this->em->getRepository('AppCoreSchemaBundle:DbEntity')->findOneBy(array('id' => $formEntity));
if ($DbEntity && $DbEntity->getAllowCustomFields()) {
$organization = $this->container->get('app_user.user_manager')->getCurrentOrganization();
if (!$organization) {
throw $this->createNotFoundException('Unable to find Organization entity.');
}
$params = array(
'organization' => $organization,
'entity' => $DbEntity,
);
$entities = $this->em->getRepository('AppCoreSchemaBundle:DbCustomField')->getAll($params);
# RUN BY ALL CUSTOM FIELDS AND ADD APPROPRIATE FIELD TYPES AND VALIDATORS
foreach ($entities as $customField) {
# configurate customfield
FieldConfiguration::configurate($customField, $form);
# THE PROBLEM IS HERE
# IF OBJECT IS NOT NULL THEN MAKE SET DATA FOR APPROPRIATED FIELD
if ($data->getId()) {
$filters = array(
'custom_field' => $customField,
'object' => $data->getId(),
);
$DbCustomFieldValue = $this->em->getRepository('UebCoreSchemaBundle:DbCustomFieldValue')->getFieldValue($filters);
if ($DbCustomFieldValue) {
$form[$customField->getFieldAlias()]->setData($DbCustomFieldValue->getValue());
} else {
$form[$customField->getFieldAlias()]->setData(array());
}
}
}
}
}
The problem is when i try to edit a form. if you look at the part in the code above where says "THE PROBLEM IS HERE" you can understand.
If the object of the form has an ID, then i will get the values stored for the custom fields of that object, and i call $form[field_alias']->setData(value returned from database that is mapped as type Array).
But this dont work, and the Data is not set for the fields. But if in my controller i do the same, the data is set properly.
Does anybody have an idea where the problem can be? Can't i set the data in preSetData Event?
EDITED
The value field from the Entity DbCustomField is mapped in this way:
/**
* #var string
*
* #ORM\Column(name="value", type="array", nullable=true)
*/
protected $value;
`
var_dump($DbCustomFieldValue) -> object(Ueb\Core\Bundle\SchemaBundle\Entity\DbCustomFieldValue)
var_dump(DbCustomFieldValue->getValue())
-> string(11) "bruno valor"
But even if i try something like:
var_dump($customField->getFieldAlias()); = string(21) "testebruno-1383147874"
$form[$customField->getFieldAlias()]->setData('example1'); it doesnt work.
But in my controller if i do the following for the fieldAlias above:
$form['testebruno-1383147874']->setData('example2');
-> it does work
Any idea?
As metalvarez suggested in his/her comment and working as expected, use the postSetData event instead of the preSetData one:
public function postSetData(FormEvent $event) {
// ...
}
The preSetData event method is called before populating the form with default values, then Symfony2 will set the data and it may change from what you set before, thus the use of postSetData instead.
Figure from the doc

Categories