diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index d90173eb2e6..3b4f6bbe7b5 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -181,6 +181,11 @@ To get fancier, use the `group_by`_ option instead. Field Options ------------- +.. versionadded:: 5.1 + + The :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class has + been introduced in Symfony 5.1, to help configuring choices options. + choices ~~~~~~~ @@ -211,31 +216,9 @@ correct types will be assigned to the model. .. include:: /reference/forms/types/options/choice_label.rst.inc -choice_loader -~~~~~~~~~~~~~ - -**type**: :class:`Symfony\\Component\\Form\\ChoiceList\\Loader\\ChoiceLoaderInterface` - -The ``choice_loader`` can be used to only partially load the choices in cases where -a fully-loaded list is not necessary. This is only needed in advanced cases and -would replace the ``choices`` option. - -You can use an instance of :class:`Symfony\\Component\\Form\\ChoiceList\\Loader\\CallbackChoiceLoader` -if you want to take advantage of lazy loading:: - - use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader; - use Symfony\Component\Form\Extension\Core\Type\ChoiceType; - // ... - - $builder->add('constants', ChoiceType::class, [ - 'choice_loader' => new CallbackChoiceLoader(function() { - return StaticClass::getConstants(); - }), - ]); +.. _reference-form-choice-loader: -This will cause the call of ``StaticClass::getConstants()`` to not happen if the -request is redirected and if there is no pre set or submitted data. Otherwise -the choice options would need to be resolved thus triggering the callback. +.. include:: /reference/forms/types/options/choice_loader.rst.inc .. include:: /reference/forms/types/options/choice_name.rst.inc diff --git a/reference/forms/types/options/choice_attr.rst.inc b/reference/forms/types/options/choice_attr.rst.inc index ac149f3999d..1c9f5138d66 100644 --- a/reference/forms/types/options/choice_attr.rst.inc +++ b/reference/forms/types/options/choice_attr.rst.inc @@ -24,3 +24,20 @@ If an array, the keys of the ``choices`` array must be used as keys:: return ['class' => 'attending_'.strtolower($key)]; }, ]); + +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use App\Entity\Category; + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'choice_label' => ChoiceList::attr($this, function (?Category $category) { + return $category ? ['data-uuid' => $category->getUuid()] : []; + }), + ]); + + See the :ref:`"choice_loader" option documentation `. diff --git a/reference/forms/types/options/choice_label.rst.inc b/reference/forms/types/options/choice_label.rst.inc index 53cd469b916..6cfac9323ae 100644 --- a/reference/forms/types/options/choice_label.rst.inc +++ b/reference/forms/types/options/choice_label.rst.inc @@ -53,3 +53,17 @@ If your choice values are objects, then ``choice_label`` can also be a If set to ``false``, all the tag labels will be discarded for radio or checkbox inputs. You can also return ``false`` from the callable to discard certain labels. + +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'choice_label' => ChoiceList::label($this, 'displayName'), + ]); + + See the :ref:`"choice_loader" option documentation `. diff --git a/reference/forms/types/options/choice_loader.rst.inc b/reference/forms/types/options/choice_loader.rst.inc new file mode 100644 index 00000000000..35a9e86cd81 --- /dev/null +++ b/reference/forms/types/options/choice_loader.rst.inc @@ -0,0 +1,75 @@ +choice_loader +~~~~~~~~~~~~~ + +**type**: :class:`Symfony\\Component\\Form\\ChoiceList\\Loader\\ChoiceLoaderInterface` + +The ``choice_loader`` can be used instead of ``choices`` option. It allows to +create a list lazily or partially when fetching only the choices for a set of +submitted values (i.e. querying a search engine like ``ElasticSearch`` can be +a heavy process). + +You can use an instance of :class:`Symfony\\Component\\Form\\ChoiceList\\Loader\\CallbackChoiceLoader` +if you want to take advantage of lazy loading:: + + use App\StaticClass; + use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader; + use Symfony\Component\Form\Extension\Core\Type\ChoiceType; + // ... + + $builder->add('loaded_choices', ChoiceType::class, [ + 'choice_loader' => new CallbackChoiceLoader(function() { + return StaticClass::getConstants(); + }), + ]); + +This will cause the call of ``StaticClass::getConstants()`` to not happen if the +request is redirected and if there is no pre set or submitted data. Otherwise +the choice options would need to be resolved thus triggering the callback. + +When you're defining a custom choice type that may be reused in many fields +(like entries of a collection) or reused in multiple forms at once, you +should use the :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` +static methods to wrap the loader and make the choice list cacheable for +better performance:: + + use App\Form\ChoiceList\CustomChoiceLoader; + use App\StaticClass; + use Symfony\Component\Form\AbstractType; + use Symfony\Component\Form\ChoiceList\ChoiceList; + use Symfony\Component\Form\Extension\Core\Type\ChoiceType; + use Symfony\Component\OptionsResolver\Options; + use Symfony\Component\OptionsResolver\OptionsResolver; + + class ConstantsType extends AbstractType + { + public static function getExtendedTypes(): iterable + { + return [ChoiceType::class]; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + // the example below will create a CallbackChoiceLoader from the callable + 'choice_loader' => ChoiceList::lazy($this, function() { + return StaticClass::getConstants(); + }), + + // you can pass your own loader as well, depending on other options + 'some_key' => null, + 'choice_loader' => function (Options $options) { + return ChoiceList::loader( + // pass the instance of the type or type extension which is + // currently configuring the choice list as first argument + $this, + // pass the other option to the loader + new CustomChoiceLoader($options['some_key']), + // ensure the type stores a loader per key + // by using the special third argument "$vary" + // an array containing anything that "changes" the loader + [$options['some_key']] + ); + }, + ]); + } + } diff --git a/reference/forms/types/options/choice_name.rst.inc b/reference/forms/types/options/choice_name.rst.inc index a01341b5418..4ec8abb6ffe 100644 --- a/reference/forms/types/options/choice_name.rst.inc +++ b/reference/forms/types/options/choice_name.rst.inc @@ -5,12 +5,26 @@ Controls the internal field name of the choice. You normally don't care about this, but in some advanced cases, you might. For example, this "name" becomes the index -of the choice views in the template and is used as part o the field name +of the choice views in the template and is used as part of the field name attribute. This can be a callable or a property path. See `choice_label`_ for similar usage. By default, the choice key or an incrementing integer may be used (starting at ``0``). +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'choice_name' => ChoiceList::fieldName($this, 'name'), + ]); + + See the :ref:`"choice_loader" option documentation `. + .. caution:: The configured value must be a valid form name. Make sure to only return diff --git a/reference/forms/types/options/choice_value.rst.inc b/reference/forms/types/options/choice_value.rst.inc index a37a36cf299..13bc324cd2a 100644 --- a/reference/forms/types/options/choice_value.rst.inc +++ b/reference/forms/types/options/choice_value.rst.inc @@ -18,3 +18,17 @@ for each choice or ``null`` in a placeholder is used, which you need to handle:: 'choice_value' => function (?MyOptionEntity $entity) { return $entity ? $entity->getId() : ''; }, + +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'choice_value' => ChoiceList::value($this, 'uuid'), + ]); + + See the :ref:`"choice_loader" option documentation `. diff --git a/reference/forms/types/options/group_by.rst.inc b/reference/forms/types/options/group_by.rst.inc index b649793e9ff..ca747683662 100644 --- a/reference/forms/types/options/group_by.rst.inc +++ b/reference/forms/types/options/group_by.rst.inc @@ -40,3 +40,17 @@ a "Later" ````: If you return ``null``, the option won't be grouped. You can also pass a string "property path" that will be called to get the group. See the `choice_label`_ for details about using a property path. + +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'group_by' => ChoiceList::groupBy($this, 'category'), + ]); + + See the :ref:`"choice_loader" option documentation `. diff --git a/reference/forms/types/options/preferred_choices.rst.inc b/reference/forms/types/options/preferred_choices.rst.inc index 36a800c1c9b..bffb021f864 100644 --- a/reference/forms/types/options/preferred_choices.rst.inc +++ b/reference/forms/types/options/preferred_choices.rst.inc @@ -63,3 +63,17 @@ when rendering the field: widget($form['publishAt'], [ 'separator' => '=====', ]) ?> + +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'preferred_choices' => ChoiceList::preferred($this, 'taggedAsFavorite'), + ]); + + See the :ref:`"choice_loader" option documentation `.