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
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 got a table in data base ID, json, status and everything works in another table and in this one I can save, read but can not take the data from repository and pass to form. Look:
that's my SliderType Form
class SliderType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('status', ChoiceType::class, array(
'data' => $options['status'],
'choices' => array(
"Aktywna" => 1,
"Nieaktywna" => 0
),
'attr' => array(
'class' => 'form-control'
),
))
And here I need to pass from options resolver a status variable which is boolean, so in controller I done it look:
//tworzymy formularz
$form = $this->createForm(SliderType::class, $request, array(
.
.
'status' => $slider->getStatus,
te problem is that another variables works perfect if i add only this i thrown a error:
Cannot read index "status" from object of type "Symfony\Component\HttpFoundation\Request" because it doesn't implement \ArrayAccess.
Any Idea what is wrong here? and the funny thing is that I got another controller with another table (structure the same) and all works fine.
You are giving the HttpRequest of symfony to the data of your form.
This is causing your issue
You should pass the entity which will be hydrated by the form. Give your slider entity to createForm method second argument
$form = $this->createForm(SliderType::class, $slider, array(
'status' => $slider->getStatus));
Than you should tell the form to handle the request and extract data from request to hydrate your entity with your form.
$form->handleRequest($request);
You should read the form documentation
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 form class with several ChoiceType fields that contain an array of options with a key:value pair. When the form is submitted the value is saved. However when I'm rendering the object I would like to show the Key value instead.
Example: 'Monthly' => '1month'. 1month is stored, prefer output to be Monthly.
I'm trying to avoid conditionals to check the value and changing the output to the key value.
I wasn't able to find any documentation about best practices for this sort of thing. I'm thinking about creating a service that stores all the choice options arrays and build a twig filter for changing the rendered output based on the array from the service.
Am I on the right track or is there an easier way?
I tried the service solution and got it working. I'm not sure if it is the most elegant or efficient way but it did the job. The form was a form class type and I injected a service that contained the choice arrays.
I created a choices.php class file inside my Form folder next to the formType file. It acts as a service where it returns the choices to the formType and a custom twig extension filter I created. The formType I had to set up as a service in order to inject the choices service.
/*choices.php*/
public function getChoices($choice)
{
$choices = array('paymentFrequency' => array('Monthly' => '1month',
'Bi-weekly' => '2weeks'),
'compounding' => array('Monthly' => 'monthly',
'Daily' => 'daily')
);
return $choices[$choice];
}
/*formType.php*/
->add('paymentFrequency', ChoiceType::class, array(
'label' => 'Payment Frequency:',
'choices' => $this->choicesService->getChoices('paymentFrequency'),
))
->add('compounding', ChoiceType::class, array(
'label' => 'Compounding:',
'choices' => $this->choicesService->getChoices('compounding'),
))
I then created a custom twig filter function where the choices service is injected into it.
/*twigExtension.php*/
public function renderChoicesFilter($value, $type)
{
$choices = $this->choicesService->getChoices($type);
return array_search($value, $choices);
}
/*twig template*/
{{ object.paymentFrequency|renderChoices('paymentFrequency') }}
You can create enumerator class and use it in your template, like so:
class MyChoicesEnum {
private static $choices = array(
'Monthly' => '1month',
'Quarterly' => '4month',
// etc...
);
public static function choices() {
return self::$choices;
}
}
Then you pass the class method result to template, in the returned array:
...
'form' => $form->createView()
'my_choices' => MyChoicesEnum::choices()
And in twig:
{{ my_choices.key }}
I'm trying to dynamically generate a form based on user-provided field definitions. This is fairly straightforward for the basic form:
$builder = $app['form.factory']->createBuilder('form', $my_data);
foreach ($my_user_provided_field_definitions as $field) {
$builder->add($field->handle, $field->type, $field->options);
}
$form = $builder->getForm();
But I also want to have sub-forms using the collection field type, and while it is so easy to dynamically generate the top-level form as I've done above, it does not seem possible to dynamically generate a sub-form for the collection field because all of the docs and sample code I've come across utilize either a single field like so:
$builder->add('my field', 'collection', array(
'type' => 'text',
'allow_add' => true,
));
...or a custom "Type" object, like so:
$builer->add('my field', 'collection', array(
'type' => new TagType(),
'allow_add' => true,
));
The TagType class in the above example must be defined in code (see http://symfony.com/doc/master/cookbook/form/form_collections.html )... but my problem is that I cannot have the class defined in code because these sub-forms are dynamically generated based on user data -- so I do not know what fields the sub-form will contain until run-time!
Is there a way to dynamically generate the sub-forms that can then be passed in to the top-level form, or is this a use case that is not handled by the Symfony FormBuilder?