@@ -167,7 +167,8 @@ After creating this class, use it in your form:
167
167
168
168
Avoid passing any options to the 3rd argument of the ``->add() `` method as
169
169
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 >`.
171
172
172
173
Congratulations! Your ``EntityType `` is now Ajax-powered!
173
174
@@ -274,6 +275,114 @@ to the options above, you can also pass:
274
275
Set to ``focus `` to call the ``load `` function when control receives focus.
275
276
Set to ``true `` to call the ``load `` upon control initialization (with an empty search).
276
277
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
+
277
386
Using with a TextType Field
278
387
---------------------------
279
388
@@ -481,6 +590,62 @@ the ``ux_entity_autocomplete`` route and ``alias`` route wildcard:
481
590
Usually, you'll pass this URL to the Stimulus controller, which is
482
591
discussed in the next section.
483
592
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
+
484
649
.. _manual-stimulus-controller :
485
650
486
651
Manually using the Stimulus Controller
0 commit comments