From 87456fdc61afb6a5bc9eefe6bdc587503518f25e Mon Sep 17 00:00:00 2001 From: Julien 'ruian' Galenski Date: Wed, 4 Dec 2013 18:15:12 +0100 Subject: [PATCH 1/5] [Form] Adding attributes to ChoiceView Description: Because some times is usefull to add some attributes to select's options. Exemple when using, select2, chosen, or juste by adding some prototypes. | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #5286 | License | MIT --- .../views/Form/form_div_layout.html.twig | 15 +++- .../Form/Extension/Core/Type/ChoiceType.php | 77 ++++++++++++++++--- .../Form/Extension/Core/View/ChoiceView.php | 14 ++++ 3 files changed, 95 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig index 7f5e323975478..c647a3d5436b2 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig @@ -96,7 +96,7 @@ {{ block('choice_widget_options') }} {% else %} - + {% endif %} {% endfor %} {% endspaceless %} @@ -423,3 +423,16 @@ {%- endfor -%} {% endspaceless %} {% endblock button_attributes %} + +{% block option_data_attributes %} +{% spaceless %} + {%- for attrname, attrvalue in choice.attr -%} + {{- " " -}} + data-{{attrname}}="{{attrvalue}}" + {%- endfor -%} + + {% if choice.prototype %} + data-prototype="{{ form_row(choice.prototype)|e }}" + {% endif %} +{% endspaceless %} +{% endblock option_data_attributes %} diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index cdaee86b25b03..3a77bfd32fa3c 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -12,21 +12,23 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\View\ChoiceView; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormInterface; -use Symfony\Component\Form\FormView; use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; -use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener; -use Symfony\Component\Form\Extension\Core\EventListener\FixCheckboxInputListener; -use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener; -use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToBooleanArrayTransformer; -use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToBooleanArrayTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; +use Symfony\Component\Form\Extension\Core\EventListener\FixCheckboxInputListener; +use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener; +use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener; +use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; class ChoiceType extends AbstractType { @@ -36,6 +38,11 @@ class ChoiceType extends AbstractType */ private $choiceListCache = array(); + /** + * @var array + */ + private $choicesPrototypeForm = array(); + /** * {@inheritdoc} */ @@ -88,6 +95,23 @@ public function buildForm(FormBuilderInterface $builder, array $options) // transformation is merged back into the original collection $builder->addEventSubscriber(new MergeCollectionListener(true, true)); } + + if ($options['choices_prototypes']) { + foreach ($options['choices_prototypes'] as $prototype) { + $resoler = new OptionsResolver(); + $resoler + ->setDefaults(array('options' => array())) + ->setRequired(array('name', 'type')) + ->setAllowedTypes(array( + 'name' => array('string'), + 'type' => array('string', 'Symfony\Component\Form\FormTypeInterface'), + 'options' => array('array'), + )) + ; + $prototype = $resoler->resolve($prototype); + $this->choicesPrototypeForm[] = $builder->create($prototype['name'], $prototype['type'], $prototype['options'])->getForm(); + } + } } /** @@ -132,6 +156,20 @@ public function buildView(FormView $view, FormInterface $form, array $options) // POST request. $view->vars['full_name'] = $view->vars['full_name'].'[]'; } + + $choicesAttributes = $options['choices_attributes']; + $choicesPrototypes = $options['choices_prototypes']; + if (count($choicesAttributes) || count($choicesPrototypes)) { + foreach ($view->vars['choices'] as $key => $choiceView) { + if (array_key_exists($key, $choicesAttributes)) { + $choiceView->attr = $choicesAttributes[$key]; + } + + if (array_key_exists($key, $this->choicesPrototypeForm) && $this->choicesPrototypeForm[$key] instanceof FormInterface) { + $choiceView->prototype = $this->choicesPrototypeForm[$key]->createView($options['choices_prototypes_view']($view)); + } + } + } } /** @@ -203,6 +241,14 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) return $emptyValue; }; + $choicesClosureNormalizer = function (Options $options, $values) { + if ($values instanceof \Closure && false === is_array($values = $values($options['choice_list']))) { + throw new UnexpectedTypeException($values, 'array'); + } + + return $values; + }; + $compound = function (Options $options) { return $options['expanded']; }; @@ -221,14 +267,25 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) // is manually set to an object. // See https://github.com/symfony/symfony/pull/5582 'data_class' => null, + // All options related to choices extra data + 'choices_attributes' => array(), + 'choices_prototypes' => array(), + 'choices_prototypes_view' => function ($view) { + return $view->parent; + } )); $resolver->setNormalizers(array( 'empty_value' => $emptyValueNormalizer, + 'choices_attributes' => $choicesClosureNormalizer, + 'choices_prototypes' => $choicesClosureNormalizer )); $resolver->setAllowedTypes(array( - 'choice_list' => array('null', 'Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface'), + 'choice_list' => array('null', 'Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface'), + 'choices_attributes' => array('array', '\Closure'), + 'choices_prototypes' => array('array', '\Closure'), + 'choices_prototypes_view' => array('\Closure'), )); } diff --git a/src/Symfony/Component/Form/Extension/Core/View/ChoiceView.php b/src/Symfony/Component/Form/Extension/Core/View/ChoiceView.php index 97cdd214c28f8..e58d45b233893 100644 --- a/src/Symfony/Component/Form/Extension/Core/View/ChoiceView.php +++ b/src/Symfony/Component/Form/Extension/Core/View/ChoiceView.php @@ -18,6 +18,20 @@ */ class ChoiceView { + /** + * Attributes which can be used in view. + * + * @var array + */ + public $attr = array(); + + /** + * A prototype who can be used in view. + * + * @var FormView + */ + public $prototype; + /** * The original choice value. * From 3ba3f75d43e0c1038053af7eb0127dc2c30bed35 Mon Sep 17 00:00:00 2001 From: julien Galenski Date: Mon, 6 Jan 2014 16:33:23 +0100 Subject: [PATCH 2/5] re sort use --- .../Form/Extension/Core/Type/ChoiceType.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 3a77bfd32fa3c..9e080de117723 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -12,23 +12,23 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Exception\LogicException; -use Symfony\Component\Form\Exception\UnexpectedTypeException; -use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; -use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToBooleanArrayTransformer; -use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; -use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToBooleanArrayTransformer; -use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; -use Symfony\Component\Form\Extension\Core\EventListener\FixCheckboxInputListener; -use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener; -use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener; use Symfony\Component\Form\Extension\Core\View\ChoiceView; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; +use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener; +use Symfony\Component\Form\Extension\Core\EventListener\FixCheckboxInputListener; +use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToBooleanArrayTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToBooleanArrayTransformer; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolverInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Form\Exception\UnexpectedTypeException; class ChoiceType extends AbstractType { From 084ddc010290980d0f8f97d35f0331586320763b Mon Sep 17 00:00:00 2001 From: julien Galenski Date: Mon, 6 Jan 2014 17:49:15 +0100 Subject: [PATCH 3/5] Adding php template --- .../Resources/views/Form/choice_widget_options.html.php | 2 +- .../Resources/views/Form/option_data_attributes.html.php | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/option_data_attributes.html.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php index a7a9311d51326..1f518fabbfd74 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php @@ -6,6 +6,6 @@ block($form, 'choice_widget_options', array('choices' => $choice)) ?> - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/option_data_attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/option_data_attributes.html.php new file mode 100644 index 0000000000000..51310db0e2e03 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/option_data_attributes.html.php @@ -0,0 +1,6 @@ +attr as $k => $v): ?> +data-="" + +prototype): ?> + data-prototype="escape($formHelper->block($choice->prototype, 'form_row')) ?>" + From 5a415555bdf5584e59ca09ef1f34d5a10d5d24f8 Mon Sep 17 00:00:00 2001 From: julien Galenski Date: Tue, 7 Jan 2014 10:17:46 +0100 Subject: [PATCH 4/5] fix CS --- .../Component/Form/Extension/Core/Type/ChoiceType.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 9e080de117723..0adf24e61d034 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -17,6 +17,7 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener; use Symfony\Component\Form\Extension\Core\EventListener\FixCheckboxInputListener; @@ -26,9 +27,8 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToBooleanArrayTransformer; use Symfony\Component\OptionsResolver\Options; -use Symfony\Component\OptionsResolver\OptionsResolverInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; class ChoiceType extends AbstractType { @@ -157,9 +157,8 @@ public function buildView(FormView $view, FormInterface $form, array $options) $view->vars['full_name'] = $view->vars['full_name'].'[]'; } - $choicesAttributes = $options['choices_attributes']; - $choicesPrototypes = $options['choices_prototypes']; - if (count($choicesAttributes) || count($choicesPrototypes)) { + if (!empty($options['choices_attributes']) || !empty($options['choices_prototypes'])) { + $choicesAttributes = $options['choices_attributes']; foreach ($view->vars['choices'] as $key => $choiceView) { if (array_key_exists($key, $choicesAttributes)) { $choiceView->attr = $choicesAttributes[$key]; @@ -242,7 +241,7 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) }; $choicesClosureNormalizer = function (Options $options, $values) { - if ($values instanceof \Closure && false === is_array($values = $values($options['choice_list']))) { + if ($values instanceof \Closure && !is_array($values = $values($options['choice_list']))) { throw new UnexpectedTypeException($values, 'array'); } From da4d20b01d2939f1de8ea9ddf08a1bb0bdbff959 Mon Sep 17 00:00:00 2001 From: julien Galenski Date: Tue, 7 Jan 2014 10:29:39 +0100 Subject: [PATCH 5/5] - --- .../Component/Form/Extension/Core/Type/ChoiceType.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 0adf24e61d034..9010d74492257 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -158,10 +158,9 @@ public function buildView(FormView $view, FormInterface $form, array $options) } if (!empty($options['choices_attributes']) || !empty($options['choices_prototypes'])) { - $choicesAttributes = $options['choices_attributes']; foreach ($view->vars['choices'] as $key => $choiceView) { - if (array_key_exists($key, $choicesAttributes)) { - $choiceView->attr = $choicesAttributes[$key]; + if (array_key_exists($key, $options['choices_attributes'])) { + $choiceView->attr = $options['choices_attributes'][$key]; } if (array_key_exists($key, $this->choicesPrototypeForm) && $this->choicesPrototypeForm[$key] instanceof FormInterface) {