@@ -167,7 +167,8 @@ After creating this class, use it in your form:
167167
168168 Avoid passing any options to the 3rd argument of the ``->add() `` method as
169169 these won't be used during the Ajax call to fetch results. Instead, include
170- all options inside the custom class (``FoodAutocompleteField ``).
170+ all options inside the custom class (``FoodAutocompleteField ``) or pass them as
171+ :ref: `extra options <passing-extra-options-to-the-ajax-powered-autocomplete >`.
171172
172173Congratulations! Your ``EntityType `` is now Ajax-powered!
173174
@@ -274,6 +275,114 @@ to the options above, you can also pass:
274275 Set to ``focus `` to call the ``load `` function when control receives focus.
275276 Set to ``true `` to call the ``load `` upon control initialization (with an empty search).
276277
278+ ``extra_options `` (default ``[] ``)
279+ Allow you to pass extra options for Ajax-based autocomplete fields.
280+
281+ .. _passing-extra-options-to-the-ajax-powered-autocomplete :
282+
283+ Passing Extra Options to the Ajax-powered Autocomplete
284+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
285+
286+ .. versionadded :: 2.14
287+
288+ The ability to pass extra options was added in Autocomplete 2.14.
289+
290+ Autocomplete field options are not preserved when the field is rendered on an Ajax call. So, features like exclude some options
291+ based on the current form data are not possible by default. To partially avoid this limitation, the `extra_options ` option was added.
292+
293+ ::warning
294+
295+ Only scalar values (`string `, `integer `, `float `, `boolean `), `null ` and `arrays ` (consisted from the same types as mentioned before) can be passed as extra options.
296+
297+ Considering the following example, when the form type is rendered for the first time, it will use the `query_builder ` defined
298+ while adding a `food ` field to the `FoodForm `. However, when the Ajax is used to fetch the results, on the consequent renders,
299+ the default `query_builder ` will be used::
300+
301+ // src/Form/FoodForm.php
302+ // ...
303+
304+ class FoodForm extends AbstractType
305+ {
306+ public function buildForm(FormBuilderInterface $builder, array $options): void
307+ {
308+ $currentFoodId = $builder->getData()->getId();
309+
310+ $builder
311+ ->add('food', FoodAutocompleteField::class, [
312+ 'query_builder' => function (EntityRepository $er) {
313+ $qb = $er->createQueryBuilder('o');
314+
315+ $qb->andWhere($qb->expr()->notIn('o.id', [$currentFoodId]));
316+
317+ return $qb;
318+ };
319+ }
320+ ])
321+ ;
322+ }
323+ }
324+
325+ If some food can be consisted of other foods, we might want to exclude the "root" food from the list of available foods.
326+ To achieve this, we can remove the `query_builder ` option from the above example and pass the `excluded_foods ` extra option
327+ to the `FoodAutocompleteField `::
328+
329+ // src/Form/FoodForm.php
330+ // ...
331+
332+ class FoodForm extends AbstractType
333+ {
334+ public function buildForm(FormBuilderInterface $builder, array $options): void
335+ {
336+ $currentFoodId = $builder->getData()->getId();
337+
338+ $builder
339+ ->add('food', FoodAutocompleteField::class, [
340+ 'extra_options' => [
341+ 'excluded_foods' => [$currentFoodId],
342+ ],
343+ )
344+ ;
345+ }
346+ }
347+
348+ The magic of the `extra_options ` is that it will be passed to the `FoodAutocompleteField ` every time an Ajax call is made.
349+ So now, we can just use the `excluded_foods ` extra option in the default `query_builder ` of the `FoodAutocompleteField `::
350+
351+ // src/Form/FoodAutocompleteField.php
352+ // ...
353+
354+ use Symfony\Bundle\SecurityBundle\Security;
355+ use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
356+ use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType;
357+
358+ #[AsEntityAutocompleteField]
359+ class FoodAutocompleteField extends AbstractType
360+ {
361+ public function configureOptions(OptionsResolver $resolver): void
362+ {
363+ $resolver->setDefaults([
364+ // ...
365+ 'query_builder' => function (Options $options) {
366+ return function (EntityRepository $er) use ($options) {
367+ $qb = $er->createQueryBuilder('o');
368+
369+ $excludedFoods = $options['extra_options']['excluded_foods'] ?? [];
370+ if ([] !== $excludedFoods) {
371+ $qb->andWhere($qb->expr()->notIn('o.id', $excludedFoods));
372+ }
373+
374+ return $qb;
375+ };
376+ }
377+ ]);
378+ }
379+
380+ public function getParent(): string
381+ {
382+ return BaseEntityAutocompleteType::class;
383+ }
384+ }
385+
277386Using with a TextType Field
278387---------------------------
279388
@@ -481,6 +590,62 @@ the ``ux_entity_autocomplete`` route and ``alias`` route wildcard:
481590 Usually, you'll pass this URL to the Stimulus controller, which is
482591discussed in the next section.
483592
593+ Passing Extra Options to the Autocompleter
594+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
595+
596+ .. versionadded :: 2.14
597+
598+ The ability to pass extra options was added in Autocomplete 2.14.
599+
600+ If you need to pass extra options to the autocompleter, you can do so by implementing the
601+ ``\Symfony\UX\Autocomplete\OptionsAwareEntityAutocompleterInterface `` interface.
602+
603+ .. tip ::
604+
605+ If you want to know **why ** you might need to use the `extra options ` feature, see :ref: `passing-extra-options-to-the-ajax-powered-autocomplete `.
606+
607+ .. code-block :: diff
608+
609+ use Doctrine\ORM\EntityRepository;
610+ use Doctrine\ORM\QueryBuilder;
611+ use Sylius\Component\Product\Model\ProductAttributeInterface;
612+ use Symfony\Bundle\SecurityBundle\Security;
613+ use Symfony\UX\Autocomplete\OptionsAwareEntityAutocompleterInterface;
614+
615+ #[AutoconfigureTag('ux.entity_autocompleter', ['alias' => 'food'])]
616+ class FoodAutocompleter implements OptionsAwareEntityAutocompleterInterface
617+ {
618+ + /**
619+ + * @var array<string, mixed>
620+ + */
621+ + private array $options = [];
622+
623+ // ...
624+
625+ + public function createFilteredQueryBuilder(EntityRepository $repository, string $query): QueryBuilder
626+ + {
627+ + $excludedFoods = $this->options['extra_options']['excluded_foods'] ?? [];
628+ +
629+ + $qb = $repository->createQueryBuilder('o');
630+ +
631+ + if ($productAttributesToBeExcluded !== []) {
632+ + $qb
633+ + ->andWhere($qb->expr()->notIn('o.id', $excludedFoods));
634+ + ->setParameter('excludedFoods', $excludedFoods)
635+ + ;
636+ + }
637+ +
638+ + return $qb;
639+ + }
640+
641+ +/**
642+ + * @param array<string, mixed> $options
643+ + */
644+ +public function setOptions(array $options): void
645+ +{
646+ + $this->options = $options;
647+ +}
648+
484649 .. _manual-stimulus-controller :
485650
486651Manually using the Stimulus Controller
0 commit comments