Reputation: 4038
in symfony 1.4 it was possible to parameterize a form class definition, via the options of the form. Is there any way to pass custom options to my custom form type??? i've tried to use the options parameter of the buildForm
method, but i'm not very sure what this array is, and apparently it is not for what i want... Thanks!
Upvotes: 27
Views: 66189
Reputation: 524
basing on @pulzarraider answer I created code with changes for Symfony 3.
You need to change
OptionsResolverInterface
for OptionsResolver
FormBuilder
for FormBuilderInterface
In my case:
namespace MediaBundle\Form;
use Symfony\Component\Form\AbstractType as FormAbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
class ImageType extends FormAbstractType {
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'max_images' => ''
));
}
public function buildView(FormView $view, FormInterface $form, array $options) {
$view->vars['max_images'] = $options['max_images'];
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->setAttribute('max_images', $options['max_images'])
;
}
public function getName() {
return 'image_preview';
}
public function getParent() {
return TextareaType::class;
}
}
Upvotes: 5
Reputation: 2317
The solution is simple, if you want your custom option to be available also in Twig template, you must use
$builder->setAttribute()
in buildForm
method
and
$view->set()
method in buildView()
method, too.
<?php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType as FormAbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
// For Symfony 2.1 and higher:
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* ImagePreviewType
*
*/
class ImagePreviewType extends FormAbstractType
{
/**
* {@inheritDoc}
* For Symfony 2.0
*/
//public function getDefaultOptions(array $options)
//{
// $options = parent::getDefaultOptions($options);
// $options['base_path'] = 'path/to/default/dir/';
//
// return $options;
//}
/**
* {@inheritDoc}
* For Symfony 2.1 and higher
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'base_path' => '',
));
}
/**
* {@inheritDoc}
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
// For Symfony 2.0:
// $view->set('base_path', $form->getAttribute('base_path'));
// For Symfony 2.1 and higher:
$view->vars['base_path'] = $options['base_path'];
}
/**
* {@inheritDoc}
*/
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->setAttribute('base_path', $options['base_path'])
;
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'image_preview';
}
public function getParent(array $options)
{
// for Symfony 2.0:
// return 'field';
// for Symfony 2.1 and higher:
return 'form';
}
}
Template for custom form type (file ...Acme/DemoBundle/Resources/views/Form/fields.html.twig):
{% block image_preview_widget %}
{% spaceless %}
<img src="{{ base_path ~ value }}" alt="" {{ block('widget_container_attributes') }} />
{% endspaceless %}
{% endblock %}
Register your template for custom form types in app/config/config.yml
twig:
debug: %kernel.debug%
strict_variables: %kernel.debug%
form:
resources:
- 'AcmeDemoAdminBundle:Form:fields.html.twig'
Usage: Display preview of user's image while editing his profile:
// src/Acme/DemoBundle/Form/Type/UserType.php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class UserType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('user_profile_image_file_name', new ImagePreviewType(), array(
'base_path' => 'some/other/dir',
));
}
}
2014-08-18: Updated for Symfony 2.1 or higher
Upvotes: 45
Reputation: 2568
Using symfony 2.8, I succeeded by using the proposed solution extending the configureOptions()
method.
class ElementType extends AbstractType
{
// ...
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'my_custom_option_parameter' => null,
));
}
}
I needed to use the ElementType
, as a collection and embedded form. I recognized that it was not possible to pass the my_custom_option_parameter
to the CollectionType
, because I did not customize configureOptions()
of CollectionType
, but of my ElementType
. If you need to pass the my_custom_option_parameter
through a CollectionType
, you may succeed by defining my_custom_option_parameter
in entry_options
(see Documentation CollectionType Field) array of CollectionType
.
Example passing my_custom_option_parameter
through a CollectionType
:
class OuterFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
//...
$builder->add('elements', CollectionType::class, array(
'entry_type' => ElementType::class,
// ...
'entry_options' => array(
'my_custom_option_parameter' => 'value is now set!'
)
));
//...
}
}
Upvotes: 10
Reputation: 1140
Using Symfony 3, I was able to pass custom options to the form by setting a default option in the OptionsResolver injected into configureOptions method of my form type class:
In the Controller:
//Compile whatever your options are here. Assume an array is returned
$customOptions = $this->getMyCustomOptions($args);
//Build the form:
$form = $this->createForm(MyCustomFormType::class, array('my_custom_options' => $customOptions));
MyCustomFormType.php:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => DataModel::class,
'my_custom_options' => []
]);
}
//custom options is now set in "$options" array:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('my_custom_fields', Type\ChoiceType::class, [
'choices' => $options['my_custom_options'],
'mapped' => false //if not part of the data model.
. . .
So, it looks like you can define a contract with your form and the data provider to set arbitrary data on the form.
I've just successfully implemented this procedure. Note that on the return trip, since you've set 'mapped' => false,
in the formBuilder, $form->getData()
will not return the selection. To get the selected value:
$mySelectedValue = $form->get('my_custom_options')->getViewData();
in your controller. Why this is is beyond me . . .
Upvotes: 3
Reputation: 1227
UPDATE: Please note that this solution only works in Symfony 2.0.x, which is obsolete, use setDefaultOptions
instead of getDefaultOptions
.
Justly, Symfony 2 form types accept options that you can use for anything you want inside the form type. You need to override getDefaultOptions
method to specify your type options.
For example, I have a type MyCustomType
that accept my_option
, this option has a default value of false
, the implementation of MyCustomType
can be something like this.
class MyCustomType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
if($options['my_option']){
//do something
} else {
//do another thing
}
...
}
public function getDefaultOptions(array $options)
{
return array(
'my_option' => false
);
}
public function getName()
{
return 'mycustomtype';
}
}
Later, you will need to specify the option when you create the form in the controller, using the third parameter of buildForm
:
$form = $this->buildForm(new MyCustomType(), null, array(
'my_option' => true
));
If you not specify the my_option
option, it takes the default value (false
).
Upvotes: 18
Reputation: 20193
I have tried using that options array
with no success as it seemed that it could carry only small, predefined subset of keys. This was, by all means, unacceptable for me...
However, you can pass all the options via forms __construct
method and store it in class properties for later use. Then, from buildForm
you can access it using $this->"propertyName"
...
It's up to you to decide whether you want to pass single array
or just few variables to __construct
...
This is just a rough example:
class Foobar{
private $update = false;
public function __construct($update = false){
$this->update = $update;
}
public function buildForm(FormBuilder builder, array options){
if ( $update ){
// something
}else{
// well, this is not an update - do something else
}
}
}
Upvotes: 2