From 79e76f3ee870ede63a67ee8984022f4cd9fcb86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Wed, 24 Sep 2025 20:41:42 +0200 Subject: [PATCH 01/10] [Vue] Remove deprecations for 3.0 --- src/Vue/src/Twig/VueComponentExtension.php | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/Vue/src/Twig/VueComponentExtension.php b/src/Vue/src/Twig/VueComponentExtension.php index 322a143340e..6426976a526 100644 --- a/src/Vue/src/Twig/VueComponentExtension.php +++ b/src/Vue/src/Twig/VueComponentExtension.php @@ -12,7 +12,6 @@ namespace Symfony\UX\Vue\Twig; use Symfony\UX\StimulusBundle\Helper\StimulusHelper; -use Symfony\WebpackEncoreBundle\Twig\StimulusTwigExtension; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -24,19 +23,8 @@ */ class VueComponentExtension extends AbstractExtension { - private $stimulusHelper; - - /** - * @param $stimulus StimulusHelper - */ - public function __construct(StimulusHelper|StimulusTwigExtension $stimulus) + public function __construct(private StimulusHelper $stimulusHelper) { - if ($stimulus instanceof StimulusTwigExtension) { - trigger_deprecation('symfony/ux-vue', '2.9', 'Passing an instance of "%s" to "%s" is deprecated, pass an instance of "%s" instead.', StimulusTwigExtension::class, __CLASS__, StimulusHelper::class); - $stimulus = new StimulusHelper(null); - } - - $this->stimulusHelper = $stimulus; } public function getFunctions(): array From c3a2718a510201ae4556acc42ee61f4e6db82622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Wed, 24 Sep 2025 20:47:34 +0200 Subject: [PATCH 02/10] Revert "[Vue] Remove deprecations for 3.0" This reverts commit 79e76f3ee870ede63a67ee8984022f4cd9fcb86c. --- src/Vue/src/Twig/VueComponentExtension.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Vue/src/Twig/VueComponentExtension.php b/src/Vue/src/Twig/VueComponentExtension.php index 6426976a526..322a143340e 100644 --- a/src/Vue/src/Twig/VueComponentExtension.php +++ b/src/Vue/src/Twig/VueComponentExtension.php @@ -12,6 +12,7 @@ namespace Symfony\UX\Vue\Twig; use Symfony\UX\StimulusBundle\Helper\StimulusHelper; +use Symfony\WebpackEncoreBundle\Twig\StimulusTwigExtension; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -23,8 +24,19 @@ */ class VueComponentExtension extends AbstractExtension { - public function __construct(private StimulusHelper $stimulusHelper) + private $stimulusHelper; + + /** + * @param $stimulus StimulusHelper + */ + public function __construct(StimulusHelper|StimulusTwigExtension $stimulus) { + if ($stimulus instanceof StimulusTwigExtension) { + trigger_deprecation('symfony/ux-vue', '2.9', 'Passing an instance of "%s" to "%s" is deprecated, pass an instance of "%s" instead.', StimulusTwigExtension::class, __CLASS__, StimulusHelper::class); + $stimulus = new StimulusHelper(null); + } + + $this->stimulusHelper = $stimulus; } public function getFunctions(): array From db31dec1872ac51f2ed5ed2c4b1a4138e122dbd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Wed, 24 Sep 2025 21:13:15 +0200 Subject: [PATCH 03/10] Remove getPropertyMetadata method --- src/Autocomplete/src/Doctrine/EntityMetadata.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Autocomplete/src/Doctrine/EntityMetadata.php b/src/Autocomplete/src/Doctrine/EntityMetadata.php index 6094d0b45e1..b6fed37bc0a 100644 --- a/src/Autocomplete/src/Doctrine/EntityMetadata.php +++ b/src/Autocomplete/src/Doctrine/EntityMetadata.php @@ -42,17 +42,6 @@ public function isEmbeddedClassProperty(string $propertyName): bool return \array_key_exists($propertyNameParts[0], $this->metadata->embeddedClasses); } - public function getPropertyMetadata(string $propertyName): array - { - trigger_deprecation('symfony/ux-autocomplete', '2.15.0', 'Calling EntityMetadata::getPropertyMetadata() is deprecated. You should stop using it, as it will be removed in the future.'); - - try { - return $this->getFieldMetadata($propertyName); - } catch (\InvalidArgumentException $e) { - return $this->getAssociationMetadata($propertyName); - } - } - /** * @internal * From 0bc004de9f4d8c25c236e25620e5670decf8e174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Wed, 24 Sep 2025 21:14:17 +0200 Subject: [PATCH 04/10] Remove BC layer for constructor --- .../src/AutocompleteResultsExecutor.php | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/Autocomplete/src/AutocompleteResultsExecutor.php b/src/Autocomplete/src/AutocompleteResultsExecutor.php index 03d10e03466..ddbbaf26632 100644 --- a/src/Autocomplete/src/AutocompleteResultsExecutor.php +++ b/src/Autocomplete/src/AutocompleteResultsExecutor.php @@ -14,7 +14,6 @@ use Doctrine\ORM\Tools\Pagination\Paginator; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; -use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\PropertyAccess\PropertyPath; use Symfony\Component\PropertyAccess\PropertyPathInterface; @@ -26,22 +25,11 @@ */ final class AutocompleteResultsExecutor { - private PropertyAccessorInterface $propertyAccessor; - private ?Security $security; - public function __construct( private DoctrineRegistryWrapper $managerRegistry, - $propertyAccessor, - /* Security $security = null */ + private PropertyAccessorInterface $propertyAccessor, + private ?Security $security = null, ) { - if ($propertyAccessor instanceof Security) { - trigger_deprecation('symfony/ux-autocomplete', '2.8.0', 'Passing a "%s" instance as the second argument of "%s()" is deprecated, pass a "%s" instance instead.', Security::class, __METHOD__, PropertyAccessorInterface::class); - $this->security = $propertyAccessor; - $this->propertyAccessor = new PropertyAccessor(); - } else { - $this->propertyAccessor = $propertyAccessor; - $this->security = \func_num_args() >= 3 ? func_get_arg(2) : null; - } } public function fetchResults(EntityAutocompleterInterface $autocompleter, string $query, int $page): AutocompleteResults From 36fa6fbaf748589f21d107bc310b781f322f06cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Wed, 24 Sep 2025 21:15:11 +0200 Subject: [PATCH 05/10] Remove ExtraLazyChoiceLoader --- .../Loader/ExtraLazyChoiceLoader.php | 52 ------------------- 1 file changed, 52 deletions(-) delete mode 100644 src/Autocomplete/src/Form/ChoiceList/Loader/ExtraLazyChoiceLoader.php diff --git a/src/Autocomplete/src/Form/ChoiceList/Loader/ExtraLazyChoiceLoader.php b/src/Autocomplete/src/Form/ChoiceList/Loader/ExtraLazyChoiceLoader.php deleted file mode 100644 index c027ab65aaa..00000000000 --- a/src/Autocomplete/src/Form/ChoiceList/Loader/ExtraLazyChoiceLoader.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\UX\Autocomplete\Form\ChoiceList\Loader; - -use Symfony\Component\Form\ChoiceList\ArrayChoiceList; -use Symfony\Component\Form\ChoiceList\ChoiceListInterface; -use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; - -/** - * Loads choices on demand only. - * - * @deprecated since Symfony UX 2.23 and will be removed in 3.0, use `Symfony\Component\Form\ChoiceList\Loader\LazyChoiceLoader` instead. - */ -class ExtraLazyChoiceLoader implements ChoiceLoaderInterface -{ - private ?ChoiceListInterface $choiceList = null; - - public function __construct( - private readonly ChoiceLoaderInterface $decorated, - ) { - } - - public function loadChoiceList(?callable $value = null): ChoiceListInterface - { - return $this->choiceList ??= new ArrayChoiceList([], $value); - } - - public function loadChoicesForValues(array $values, ?callable $value = null): array - { - $choices = $this->decorated->loadChoicesForValues($values, $value); - $this->choiceList = new ArrayChoiceList($choices, $value); - - return $choices; - } - - public function loadValuesForChoices(array $choices, ?callable $value = null): array - { - $values = $this->decorated->loadValuesForChoices($choices, $value); - $this->loadChoicesForValues($values, $value); - - return $values; - } -} From b9fd149b69f5b8ea3c08b3a80d8f990d7b4d7233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Wed, 24 Sep 2025 21:15:38 +0200 Subject: [PATCH 06/10] Remove ParentEntityAutocompleteType --- .../src/Form/ParentEntityAutocompleteType.php | 118 ------------------ 1 file changed, 118 deletions(-) delete mode 100644 src/Autocomplete/src/Form/ParentEntityAutocompleteType.php diff --git a/src/Autocomplete/src/Form/ParentEntityAutocompleteType.php b/src/Autocomplete/src/Form/ParentEntityAutocompleteType.php deleted file mode 100644 index be088ce5089..00000000000 --- a/src/Autocomplete/src/Form/ParentEntityAutocompleteType.php +++ /dev/null @@ -1,118 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\UX\Autocomplete\Form; - -use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\DataMapperInterface; -use Symfony\Component\Form\Exception\RuntimeException; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormInterface; -use Symfony\Component\Form\FormView; -use Symfony\Component\OptionsResolver\Options; -use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; - -/** - * All form types that want to expose autocomplete functionality should use this for its getParent(). - * - * @deprecated since Symfony UX 2.13, use "Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType" instead - */ -final class ParentEntityAutocompleteType extends AbstractType implements DataMapperInterface -{ - public function __construct( - private UrlGeneratorInterface $urlGenerator, - ) { - } - - public function buildForm(FormBuilderInterface $builder, array $options): void - { - $formType = $builder->getType()->getInnerType(); - $attribute = AsEntityAutocompleteField::getInstance($formType::class); - - if (!$attribute && empty($options['autocomplete_url'])) { - throw new \LogicException(\sprintf('You must either provide your own autocomplete_url, or add #[AsEntityAutocompleteField] attribute to "%s".', $formType::class)); - } - - // Use the provided URL, or auto-generate from the provided alias - $autocompleteUrl = $options['autocomplete_url'] ?? $this->urlGenerator->generate($attribute->getRoute(), [ - 'alias' => $attribute->getAlias() ?: AsEntityAutocompleteField::shortName($formType::class), - ]); - - $builder - ->addEventSubscriber(new AutocompleteEntityTypeSubscriber($autocompleteUrl)) - ->setDataMapper($this); - } - - public function finishView(FormView $view, FormInterface $form, array $options): void - { - // Add a custom block prefix to inner field to ease theming: - array_splice($view['autocomplete']->vars['block_prefixes'], -1, 0, 'ux_entity_autocomplete_inner'); - // this IS A compound (i.e. has children) field - // however, we only render the child "autocomplete" field. So for rendering, fake NOT compound - // This is a hack and we should check into removing it in the future - $view->vars['compound'] = false; - // the above, unfortunately, can also trick other things that might use - // "compound" for other reasons. This, at least, leaves a hint. - $view->vars['compound_data'] = true; - } - - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'multiple' => false, - // force display errors on this form field - 'error_bubbling' => false, - // set to the fields to search on or null to search on all fields - 'searchable_fields' => null, - // override the search logic - set to a callable: - // function(QueryBuilder $qb, string $query, EntityRepository $repository) { - // $qb->andWhere('entity.name LIKE :filter OR entity.description LIKE :filter') - // ->setParameter('filter', '%'.$query.'%'); - // } - 'filter_query' => null, - // set to the string role that's required to view the autocomplete results - // or a callable: function(Symfony\Bundle\SecurityBundle\Security $security): bool - 'security' => false, - // set the max results number that a query on automatic endpoint return. - 'max_results' => 10, - ]); - - $resolver->setRequired(['class']); - $resolver->setAllowedTypes('security', ['boolean', 'string', 'callable']); - $resolver->setAllowedTypes('max_results', ['int', 'null']); - $resolver->setAllowedTypes('filter_query', ['callable', 'null']); - $resolver->setNormalizer('searchable_fields', function (Options $options, ?array $searchableFields) { - if (null !== $searchableFields && null !== $options['filter_query']) { - throw new RuntimeException('Both the searchable_fields and filter_query options cannot be set.'); - } - - return $searchableFields; - }); - } - - public function getBlockPrefix(): string - { - return 'ux_entity_autocomplete'; - } - - public function mapDataToForms($data, $forms): void - { - $form = current(iterator_to_array($forms, false)); - $form->setData($data); - } - - public function mapFormsToData($forms, &$data): void - { - $form = current(iterator_to_array($forms, false)); - $data = $form->getData(); - } -} From 202204b4ebddc98dc78d0ba7e12ad39af4590686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Wed, 24 Sep 2025 21:15:53 +0200 Subject: [PATCH 07/10] Remove AutocompleteEntityTypeSubscriber --- .../Form/AutocompleteEntityTypeSubscriber.php | 110 ------------------ 1 file changed, 110 deletions(-) delete mode 100644 src/Autocomplete/src/Form/AutocompleteEntityTypeSubscriber.php diff --git a/src/Autocomplete/src/Form/AutocompleteEntityTypeSubscriber.php b/src/Autocomplete/src/Form/AutocompleteEntityTypeSubscriber.php deleted file mode 100644 index 24c255c705e..00000000000 --- a/src/Autocomplete/src/Form/AutocompleteEntityTypeSubscriber.php +++ /dev/null @@ -1,110 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\UX\Autocomplete\Form; - -use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Utility\PersisterHelper; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\Form\FormEvent; -use Symfony\Component\Form\FormEvents; - -/** - * Helps transform ParentEntityAutocompleteType into a EntityType that will not load all options. - * - * @internal - * - * @deprecated since Symfony UX 2.13 - */ -final class AutocompleteEntityTypeSubscriber implements EventSubscriberInterface -{ - public function __construct( - private ?string $autocompleteUrl = null, - ) { - } - - public function preSetData(FormEvent $event) - { - $form = $event->getForm(); - $data = $event->getData() ?: []; - - $options = $form->getConfig()->getOptions(); - $options['compound'] = false; - $options['choices'] = is_iterable($data) ? $data : [$data]; - // pass to AutocompleteChoiceTypeExtension - $options['autocomplete'] = true; - $options['autocomplete_url'] = $this->autocompleteUrl; - unset($options['searchable_fields'], $options['security'], $options['filter_query']); - - $form->add('autocomplete', EntityType::class, $options); - } - - public function preSubmit(FormEvent $event) - { - $data = $event->getData(); - $form = $event->getForm(); - $options = $form->get('autocomplete')->getConfig()->getOptions(); - - /** @var EntityManagerInterface $em */ - $em = $options['em']; - $repository = $em->getRepository($options['class']); - $queryBuilder = $options['query_builder'] ?: $repository->createQueryBuilder('o'); - $rootAlias = $queryBuilder->getRootAliases()[0]; - - if (!isset($data['autocomplete']) || '' === $data['autocomplete']) { - $options['choices'] = []; - } else { - $idField = $options['id_reader']->getIdField(); - $idType = PersisterHelper::getTypeOfField($idField, $em->getClassMetadata($options['class']), $em)[0]; - - if ($options['multiple']) { - $params = []; - $idx = 0; - - foreach ($data['autocomplete'] as $id) { - $params[":id_$idx"] = [$id, $idType]; - ++$idx; - } - - if ($params) { - $queryBuilder - ->andWhere(\sprintf("$rootAlias.$idField IN (%s)", implode(', ', array_keys($params)))) - ; - foreach ($params as $key => $param) { - $queryBuilder->setParameter($key, $param[0], $param[1]); - } - } - - $options['choices'] = $queryBuilder->getQuery()->getResult(); - } else { - $options['choices'] = $queryBuilder - ->andWhere("$rootAlias.$idField = :id") - ->setParameter('id', $data['autocomplete'], $idType) - ->getQuery() - ->getResult(); - } - } - - // reset some critical lazy options - unset($options['em'], $options['loader'], $options['empty_data'], $options['choice_list'], $options['choices_as_values']); - - $form->add('autocomplete', EntityType::class, $options); - } - - public static function getSubscribedEvents(): array - { - return [ - FormEvents::PRE_SET_DATA => 'preSetData', - FormEvents::PRE_SUBMIT => 'preSubmit', - ]; - } -} From 87069636ade9a408aae0422b7e88be5aefcce1f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Wed, 24 Sep 2025 21:17:07 +0200 Subject: [PATCH 08/10] Remove ParentEntityAutocompleteType definition --- .../src/DependencyInjection/AutocompleteExtension.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php b/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php index 3dec572a11b..611c6404b47 100644 --- a/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php +++ b/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php @@ -147,14 +147,6 @@ private function registerFormServices(ContainerBuilder $container): void ]) ->addTag('form.type'); - $container - ->register('ux.autocomplete.entity_type', ParentEntityAutocompleteType::class) - ->setDeprecated('symfony/ux-autocomplete', '2.13', 'The "%service_id%" form type is deprecated since 2.13. Use "ux.autocomplete.base_entity_type" instead.') - ->setArguments([ - new Reference('router'), - ]) - ->addTag('form.type'); - $container ->register('ux.autocomplete.choice_type_extension', AutocompleteChoiceTypeExtension::class) ->setArguments([ From 8bfd71efd07c25498e9fe83bf0b2f54370fd0a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Wed, 24 Sep 2025 21:22:59 +0200 Subject: [PATCH 09/10] Remove unused import --- .../src/DependencyInjection/AutocompleteExtension.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php b/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php index 611c6404b47..c4f6f26672a 100644 --- a/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php +++ b/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php @@ -29,7 +29,6 @@ use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField; use Symfony\UX\Autocomplete\Form\AutocompleteChoiceTypeExtension; use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType; -use Symfony\UX\Autocomplete\Form\ParentEntityAutocompleteType; use Symfony\UX\Autocomplete\Form\WrappedEntityTypeAutocompleter; use Symfony\UX\Autocomplete\Maker\MakeAutocompleteField; From 0f97f56ec279e6240aee53664fd6825c834bf1a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Wed, 24 Sep 2025 22:05:58 +0200 Subject: [PATCH 10/10] Remove call to removed ExtraLazyChoiceLoader --- src/Autocomplete/src/Form/BaseEntityAutocompleteType.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Autocomplete/src/Form/BaseEntityAutocompleteType.php b/src/Autocomplete/src/Form/BaseEntityAutocompleteType.php index 18804b7753f..8956a7a2300 100644 --- a/src/Autocomplete/src/Form/BaseEntityAutocompleteType.php +++ b/src/Autocomplete/src/Form/BaseEntityAutocompleteType.php @@ -19,7 +19,6 @@ use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\UX\Autocomplete\Form\ChoiceList\Loader\ExtraLazyChoiceLoader; /** * All form types that want to expose autocomplete functionality should use this for its getParent(). @@ -43,11 +42,7 @@ public function configureOptions(OptionsResolver $resolver): void return null; } - if (class_exists(LazyChoiceLoader::class)) { - return new LazyChoiceLoader($loader); - } - - return new ExtraLazyChoiceLoader($loader); + return new LazyChoiceLoader($loader); }; $resolver->setDefaults([