### Install Symfony UX Autocomplete Source: https://context7.com/symfony/ux-autocomplete/llms.txt Use Composer to install the bundle and npm to manage the required JavaScript assets. ```bash composer require symfony/ux-autocomplete # If using WebpackEncore, install assets and restart Encore npm install --force npm run watch ``` -------------------------------- ### Install Assets for WebpackEncore Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Install necessary JavaScript assets and restart the build process when using WebpackEncore. ```terminal $ npm install --force $ npm run watch ``` -------------------------------- ### Install Symfony UX Autocomplete Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Use Composer to add the bundle to your Symfony project. ```terminal $ composer require symfony/ux-autocomplete ``` -------------------------------- ### Install Symfony UX Autocomplete dependencies Source: https://github.com/symfony/ux-autocomplete/blob/3.x/assets/README.md Commands to install the required PHP package and the corresponding npm package version. ```shell composer require symfony/ux-autocomplete:2.23.0 npm add @symfony/ux-autocomplete@2.23.0 ``` -------------------------------- ### Configure TextType Field with Autocomplete and Tom Select Options Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst This example shows how to configure a `TextType` field with autocomplete enabled and specific `tom_select_options` for creating new items. ```php $builder // ... ->add('tags', TextType::class, [ 'autocomplete' => true, 'tom_select_options' => [ 'create' => true, 'createOnBlur' => true, 'delimiter' => ',', ], // 'autocomplete_url' => '... optional: custom endpoint, see below', ]) ; ``` -------------------------------- ### Pass Extra Options to FoodAutocompleteField Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst This example demonstrates passing `excluded_foods` as an extra option to the `FoodAutocompleteField` to dynamically filter results. ```php // src/Form/FoodForm.php // ... class FoodForm extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $currentFoodId = $builder->getData()->getId(); $builder ->add('food', FoodAutocompleteField::class, [ 'extra_options' => [ 'excluded_foods' => [$currentFoodId], ], ) ; } } ``` -------------------------------- ### Implement EntityAutocompleterInterface Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Create a class that implements EntityAutocompleterInterface and tag it with 'ux.entity_autocompleter' to expose an autocompleter endpoint. The 'alias' option is used in the route. ```php namespace App\Autocompleter; use App\Entity\Food; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; use Symfony\UX\Autocomplete\EntityAutocompleterInterface; #[AutoconfigureTag('ux.entity_autocompleter', ['alias' => 'food'])] class FoodAutocompleter implements EntityAutocompleterInterface { public function getEntityClass(): string { return Food::class; } public function createFilteredQueryBuilder(EntityRepository $repository, string $query): QueryBuilder { return $repository // the alias "food" can be anything ->createQueryBuilder('food') ->andWhere('food.name LIKE :search OR food.description LIKE :search') ->setParameter('search', '%'.$query.'%') // maybe do some custom filtering in all cases //->andWhere('food.isHealthy = :isHealthy') //->setParameter('isHealthy', true) ; } public function getLabel(object $entity): string { return $entity->getName(); } public function getValue(object $entity): string { return $entity->getId(); } public function isGranted(Security $security): bool { // see the "security" option for details return true; } } ``` -------------------------------- ### Configure Tom Select CSS in assets/controllers.json Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Set CSS files for Tom Select styling. Use 'tom-select.bootstrap5.css' for Bootstrap 5 compatibility. ```json "autoimport": { "tom-select/dist/css/tom-select.default.css": false, "tom-select/dist/css/tom-select.bootstrap5.css": true } ``` -------------------------------- ### Create a Custom Autocompleter (Without Form) Source: https://context7.com/symfony/ux-autocomplete/llms.txt Implement `EntityAutocompleterInterface` to create a standalone autocomplete API endpoint. This is useful for manual Stimulus controller initialization. ```php 'product'])] class ProductAutocompleter implements EntityAutocompleterInterface { public function getEntityClass(): string { return Product::class; } public function createFilteredQueryBuilder(EntityRepository $repository, string $query): QueryBuilder { $qb = $repository->createQueryBuilder('p') ->andWhere('p.isEnabled = :enabled') ->setParameter('enabled', true); if ($query) { $qb->andWhere('p.name LIKE :search OR p.sku LIKE :search') ->setParameter('search', '%' . $query . '%'); } return $qb->setMaxResults(20); } public function getLabel(object $entity): string { return sprintf('%s (SKU: %s)', $entity->getName(), $entity->getSku()); } public function getValue(object $entity): mixed { return $entity->getId(); } public function getAttributes(object $entity): array { return [ 'data-price' => $entity->getPrice(), 'data-category' => $entity->getCategory()?->getName(), ]; } public function isGranted(Security $security): bool { return $security->isGranted('ROLE_USER'); } public function getGroupBy(): mixed { // Group by category return 'category.name'; } } // Usage in Twig template: // ``` -------------------------------- ### Extend Tom Select with Stimulus Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Create a custom Stimulus controller to listen for pre-connect and connect events, allowing modification of Tom Select options or access to the instance. ```javascript // assets/controllers/custom-autocomplete_controller.js import { Controller } from '@hotwired/stimulus'; export default class extends Controller { initialize() { this._onPreConnect = this._onPreConnect.bind(this); this._onConnect = this._onConnect.bind(this); } connect() { this.element.addEventListener('autocomplete:pre-connect', this._onPreConnect); this.element.addEventListener('autocomplete:connect', this._onConnect); } disconnect() { // You should always remove listeners when the controller is disconnected to avoid side-effects this.element.removeEventListener('autocomplete:connect', this._onConnect); this.element.removeEventListener('autocomplete:pre-connect', this._onPreConnect); } _onPreConnect(event) { // TomSelect has not been initialized - options can be changed console.log(event.detail.options); // Options that will be used to initialize TomSelect event.detail.options.onChange = (value) => { // ... }; } _onConnect(event) { // TomSelect has just been initialized and you can access details from the event console.log(event.detail.tomSelect); // TomSelect instance console.log(event.detail.options); // Options used to initialize TomSelect } } ``` -------------------------------- ### Configure Tom Select Plugins Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Use the tom_select_options array to enable or disable specific Tom Select plugins within the OptionsResolver. ```php $resolver->setDefaults([ 'class' => Ingredient::class, 'tom_select_options' => [ 'plugins' => [ // Enable Input Autogrow plugin 'input_autogrow', // Enable Dropdown Header plugin, with custom configuration 'dropdown_header' => [ 'title' => 'Select an ingredient', ], // Force natively-enabled plugins (by UX Autocomplete) to be disabled 'clear_button' => false, 'remove_button' => false, ], ], ]); ``` -------------------------------- ### Implement Options-Aware Autocompleter Source: https://context7.com/symfony/ux-autocomplete/llms.txt Implement `OptionsAwareEntityAutocompleterInterface` to receive extra options from the frontend. This enables dynamic filtering based on form context or other passed data. ```php 'dynamic_product'])] class DynamicProductAutocompleter implements OptionsAwareEntityAutocompleterInterface { /** @var array */ private array $options = []; public function getEntityClass(): string { return Product::class; } public function setOptions(array $options): void { $this->options = $options; } public function createFilteredQueryBuilder(EntityRepository $repository, string $query): QueryBuilder { $qb = $repository->createQueryBuilder('p'); // Access extra_options passed from the frontend $categoryId = $this->options['extra_options']['category_id'] ?? null; $excludeIds = $this->options['extra_options']['exclude_ids'] ?? []; if ($categoryId) { $qb->andWhere('p.category = :category') ->setParameter('category', $categoryId); } if (!empty($excludeIds)) { $qb->andWhere($qb->expr()->notIn('p.id', $excludeIds)); } if ($query) { $qb->andWhere('p.name LIKE :search') ->setParameter('search', '%' . $query . '%'); } return $qb; } public function getLabel(object $entity): string { return $entity->getName(); } public function getValue(object $entity): mixed { return $entity->getId(); } public function getAttributes(object $entity): array { return []; } public function isGranted(Security $security): bool { return true; } public function getGroupBy(): mixed { return null; } } ``` -------------------------------- ### Configure Tom Select Options in EntityType Source: https://context7.com/symfony/ux-autocomplete/llms.txt Customize Tom Select behavior by passing options through `tom_select_options` in the EntityType configuration. This allows for enabling plugins, setting limits, and controlling selection behavior. ```php add('ingredients', EntityType::class, [ 'class' => Ingredient::class, 'multiple' => true, 'autocomplete' => true, 'tom_select_options' => [ // Enable plugins 'plugins' => [ 'input_autogrow', 'dropdown_header' => [ 'title' => 'Select ingredients', ], // Disable default plugins 'clear_button' => false, 'remove_button' => false, ], // Other Tom Select options 'maxItems' => 5, 'hideSelected' => true, 'closeAfterSelect' => false, ], ]) // TextType with tag-like input ->add('tags', TextType::class, [ 'autocomplete' => true, 'tom_select_options' => [ 'create' => true, 'createOnBlur' => true, 'delimiter' => ',', ], ]); } } ``` -------------------------------- ### Configure Stimulus Controller with URL Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Pass a 'url' value to the Stimulus controller to enable Ajax autocompletion. This works well with custom autocompleters. ```html+twig {# AJAX-powered autocomplete using a custom autocompleter #} {# Tag input with create option #} ``` -------------------------------- ### Extend Tom Select with Stimulus Source: https://context7.com/symfony/ux-autocomplete/llms.txt Create a custom Stimulus controller to hook into autocomplete lifecycle events and modify Tom Select behavior. ```javascript // assets/controllers/custom-autocomplete_controller.js import { Controller } from '@hotwired/stimulus'; export default class extends Controller { initialize() { this._onPreConnect = this._onPreConnect.bind(this); this._onConnect = this._onConnect.bind(this); } connect() { this.element.addEventListener('autocomplete:pre-connect', this._onPreConnect); this.element.addEventListener('autocomplete:connect', this._onConnect); } disconnect() { this.element.removeEventListener('autocomplete:connect', this._onConnect); this.element.removeEventListener('autocomplete:pre-connect', this._onPreConnect); } _onPreConnect(event) { // Modify options before Tom Select initializes event.detail.options.onChange = (value) => { console.log('Selected value:', value); // Dispatch custom event, update other fields, etc. this.dispatch('selection-changed', { detail: { value } }); }; event.detail.options.onDropdownOpen = () => { console.log('Dropdown opened'); }; // Add custom rendering event.detail.options.render = { ...event.detail.options.render, option: (data, escape) => { return `
${escape(data.text)} ${data['data-price'] ? `$${escape(data['data-price'])}` : ''}
`; }, }; } _onConnect(event) { // Access the initialized Tom Select instance const tomSelect = event.detail.tomSelect; // Programmatically control Tom Select console.log('Tom Select initialized:', tomSelect); // Example: Focus on connect // tomSelect.focus(); } } ``` -------------------------------- ### Secure Autocomplete Endpoint with Callback Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Secure the Ajax endpoint using a callback function that checks user permissions. Return true to grant access, false to deny. ```php use Symfony\Bundle\SecurityBundle\Security; [ 'security' => function(Security $security): bool { return $security->isGranted('ROLE_FOO'); }, ] ``` -------------------------------- ### Customize AJAX Route Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Define a custom route for autocomplete requests and apply it to the entity field using the AsEntityAutocompleteField attribute. ```yaml # config/routes/attributes.yaml ux_entity_autocomplete_admin: controller: ux.autocomplete.entity_autocomplete_controller path: '/admin/autocomplete/{alias}' ``` ```php // src/Form/FoodAutocompleteField.php #[AsEntityAutocompleteField(route: 'ux_entity_autocomplete_admin')] class FoodAutocompleteField { // ... } ``` -------------------------------- ### Define Initial Query Builder in FoodForm Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst This code defines the initial query builder for a food autocomplete field, excluding the current food item. ```php // src/Form/FoodForm.php // ... class FoodForm extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $currentFoodId = $builder->getData()->getId(); $builder ->add('food', FoodAutocompleteField::class, [ 'query_builder' => function (EntityRepository $er) { $qb = $er->createQueryBuilder('o'); return $qb->andWhere($qb->expr()->notIn('o.id', [$currentFoodId])); } ]) ; } } ``` -------------------------------- ### Configure Query Builder with Extra Options in FoodAutocompleteField Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst This code configures the `FoodAutocompleteField` to use `extra_options` to dynamically adjust the query builder, excluding specified foods. ```php // src/Form/FoodAutocompleteField.php // ... use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField; use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType; #[AsEntityAutocompleteField] class FoodAutocompleteField extends AbstractType { public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ // ... 'query_builder' => function (Options $options) { return function (EntityRepository $er) use ($options) { $qb = $er->createQueryBuilder('o'); $excludedFoods = $options['extra_options']['excluded_foods'] ?? []; if ([] !== $excludedFoods) { $qb->andWhere($qb->expr()->notIn('o.id', $excludedFoods)); } return $qb; }; } ]); } public function getParent(): string { return BaseEntityAutocompleteType::class; } } ``` -------------------------------- ### Generate Ajax Autocomplete Field Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Use the MakerBundle console command to scaffold an Ajax-powered autocomplete field. ```terminal $ php bin/console make:autocomplete-field ``` -------------------------------- ### AJAX Endpoint JSON Response with Grouping Source: https://context7.com/symfony/ux-autocomplete/llms.txt This JSON response format includes grouped options and optgroups, suitable for categorizing autocomplete results. 'next_page' can be null if there are no more results. ```json { "results": { "options": [ { "value": "1", "text": "Pizza Margherita", "group_by": ["Italian"] }, { "value": "2", "text": "Sushi", "group_by": ["Japanese"] }, { "value": "3", "text": "Pad Thai", "group_by": ["Thai"] } ], "optgroups": [ { "value": "Italian", "label": "Italian Cuisine" }, { "value": "Japanese", "label": "Japanese Cuisine" }, { "value": "Thai", "label": "Thai Cuisine" } ] }, "next_page": null } ``` -------------------------------- ### Disable Flex package.json synchronization Source: https://github.com/symfony/ux-autocomplete/blob/3.x/assets/README.md Configuration command to prevent Symfony Flex from automatically modifying the package.json file. ```shell composer config --json "extra.symfony.flex.synchronize_package_json" false ``` -------------------------------- ### Register AutocompleteChoiceTypeExtension for Unit Tests Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst When writing unit tests for forms that use autocomplete, register the AutocompleteChoiceTypeExtension in your TypeTestCase to ensure proper functionality. ```php // tests/Form/Type/TestedTypeTest.php namespace App\Tests\Form\Type; use Symfony\Component\Form\Test\TypeTestCase; use Symfony\UX\Autocomplete\Form\AutocompleteChoiceTypeExtension; class TestedTypeTest extends TypeTestCase { protected function getTypeExtensions(): array { return [ new AutocompleteChoiceTypeExtension(), ]; } // ... your tests } ``` -------------------------------- ### Create AJAX-Powered Autocomplete Fields Source: https://context7.com/symfony/ux-autocomplete/llms.txt Define a custom form type with the #[AsEntityAutocompleteField] attribute to handle server-side searching, security, and custom query logic. ```php setDefaults([ 'class' => Food::class, 'placeholder' => 'What should we eat?', // Fields to search on (null = all fields) 'searchable_fields' => ['name', 'description'], // Secure the endpoint with a role 'security' => 'ROLE_USER', // Or use a callback for custom security logic // 'security' => function (Security $security): bool { // return $security->isGranted('ROLE_FOOD_ADMIN'); // }, // Maximum results per page 'max_results' => 10, // Minimum characters before search triggers 'min_characters' => 2, // Custom query builder for filtering 'query_builder' => function (EntityRepository $er) { return $er->createQueryBuilder('f') ->andWhere('f.isAvailable = :available') ->setParameter('available', true) ->orderBy('f.name', 'ASC'); }, ]); } public function getParent(): string { return BaseEntityAutocompleteType::class; } } // Usage in a form: // src/Form/OrderType.php class OrderType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->add('food', FoodAutocompleteField::class); } } ``` -------------------------------- ### Registering AutocompleteChoiceTypeExtension for Unit Tests Source: https://context7.com/symfony/ux-autocomplete/llms.txt Register the AutocompleteChoiceTypeExtension when testing forms that use autocomplete. This extension is necessary for the 'autocomplete' option to be recognized in form type tests. ```php 'm', ]; $form = $this->factory->create(MealType::class); $form->submit($formData); $this->assertTrue($form->isSynchronized()); $this->assertEquals('m', $form->get('portionSize')->getData()); } } ``` -------------------------------- ### Enable Local Autocomplete in Forms Source: https://context7.com/symfony/ux-autocomplete/llms.txt Add the 'autocomplete' option to EntityType or ChoiceType fields to enable client-side filtering of options. ```php add('food', EntityType::class, [ 'class' => Food::class, 'placeholder' => 'What should we eat?', 'autocomplete' => true, // Enables Tom Select autocomplete ]) ->add('portionSize', ChoiceType::class, [ 'choices' => [ 'Choose a portion size' => '', 'small' => 's', 'medium' => 'm', 'large' => 'l', 'extra large' => 'xl', 'all you can eat' => '∞', ], 'autocomplete' => true, // Enables Tom Select autocomplete ]); } } ``` -------------------------------- ### Secure Autocomplete Endpoint with Role Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Secure the Ajax endpoint by requiring a specific role. Access is granted only if the user has the specified role. ```php [ 'security' => 'ROLE_FOOD_ADMIN' ] ``` -------------------------------- ### JSON Result Format with Option Groups Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Use this JSON format when you need to group autocomplete options by category. It includes 'options' and 'optgroups' keys. ```json { "results": { "options": [ { "value": "1", "text": "Pizza", "group_by": ["food"] }, { "value": "2", "text": "Banana", "group_by": ["food"] } ], "optgroups": [{ "value": "food", "label": "food" }] } } ``` -------------------------------- ### Enable Autocomplete in Forms Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Add the 'autocomplete' option to EntityType or ChoiceType fields to enable local filtering. ```diff // src/Form/AnyForm.php // ... class AnyForm extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('food', EntityType::class, [ 'class' => Food::class, 'placeholder' => 'What should we eat?', + 'autocomplete' => true, ]) ->add('portionSize', ChoiceType::class, [ 'choices' => [ 'Choose a portion size' => '', 'small' => 's', 'medium' => 'm', 'large' => 'l', 'extra large' => 'xl', 'all you can eat' => '∞', ], + 'autocomplete' => true, ]) ; } } ``` -------------------------------- ### Create Custom Entity Autocomplete Field Source: https://context7.com/symfony/ux-autocomplete/llms.txt Implement a custom form field type using the AsEntityAutocompleteField attribute to apply specific security roles. ```php setDefaults([ 'class' => Food::class, 'security' => 'ROLE_ADMIN', ]); } public function getParent(): string { return BaseEntityAutocompleteType::class; } } ``` -------------------------------- ### Generate Autocomplete URL Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Use the ux_entity_autocomplete route with the defined alias to generate the URL for the autocompleter endpoint. ```twig {{ path('ux_entity_autocomplete', { alias: 'food' }) }} ``` -------------------------------- ### Define Custom Ajax Autocomplete Field Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Create a custom form type class with the AsEntityAutocompleteField attribute for server-side search. ```php // src/Form/FoodAutocompleteField.php // ... use Symfony\Component\Form\AbstractType; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField; use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType; #[AsEntityAutocompleteField] class FoodAutocompleteField extends AbstractType { public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'class' => Food::class, 'placeholder' => 'What should we eat?', // choose which fields to use in the search // if not passed, *all* fields are used //'searchable_fields' => ['name'], // if the autocomplete endpoint needs to be secured //'security' => 'ROLE_FOOD_ADMIN', // ... any other normal EntityType options // e.g. query_builder, choice_label ]); } public function getParent(): string { return BaseEntityAutocompleteType::class; } } ``` -------------------------------- ### Manually Use Stimulus Controller Source: https://github.com/symfony/ux-autocomplete/blob/3.x/doc/index.rst Activate Tom Select on a select or input element using the Stimulus controller. This can be used outside of the Form component. ```html+twig