From b90cbcb974e040866c015ba718d526768ee2a462 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sat, 22 Nov 2025 09:33:11 +0100 Subject: [PATCH] [Autocomplete][Turbo] Remove BC layers for methods and parameters --- src/Autocomplete/CHANGELOG.md | 3 ++- .../src/AutocompleteResultsExecutor.php | 9 ++------ .../src/EntityAutocompleterInterface.php | 17 +++++++-------- .../src/Form/BaseEntityAutocompleteType.php | 3 +++ .../Form/WrappedEntityTypeAutocompleter.php | 21 +++++++++++++++++-- src/Turbo/CHANGELOG.md | 1 + .../Mercure/TurboStreamListenRenderer.php | 12 ++++------- src/Turbo/src/Twig/TurboRuntime.php | 4 +--- .../TurboStreamListenRendererInterface.php | 5 +++-- ...reamListenRendererWithOptionsInterface.php | 19 ----------------- src/Turbo/tests/Twig/TurboRuntimeTest.php | 3 +-- 11 files changed, 43 insertions(+), 54 deletions(-) delete mode 100644 src/Turbo/src/Twig/TurboStreamListenRendererWithOptionsInterface.php diff --git a/src/Autocomplete/CHANGELOG.md b/src/Autocomplete/CHANGELOG.md index c27628fefd7..bd8e61aaeaa 100644 --- a/src/Autocomplete/CHANGELOG.md +++ b/src/Autocomplete/CHANGELOG.md @@ -7,6 +7,7 @@ - Remove `ParentEntityAutocompleteType` in favor of `BaseEntityAutocompleteType` - Remove `ExtraLazyChoiceLoader` in favor of `Symfony\Component\Form\ChoiceList\Loader\LazyChoiceLoader` from Symfony Form >=7.2 - Add parameter `$security` to `AutocompleteResultsExecutor::__construct()` +- Remove BC layer for `EntityAutocompleterInterface::getAttributes()` and `EntityAutocompleterInterface::getGroupBy()` ## 2.30 @@ -52,7 +53,7 @@ class IngredientAutocompleteType extends AbstractType - Deprecate `ExtraLazyChoiceLoader` in favor of `Symfony\Component\Form\ChoiceList\Loader\LazyChoiceLoader` - Reset TomSelect when updating url attribute #1505 -- Add `getAttributes()` method to define additional attributes for autocomplete results #2541 +- Add `EntityAutocompleterInterface::getAttributes()` method to define additional attributes for autocomplete results #2541 ## 2.22.0 diff --git a/src/Autocomplete/src/AutocompleteResultsExecutor.php b/src/Autocomplete/src/AutocompleteResultsExecutor.php index ddbbaf26632..34b9a2d9948 100644 --- a/src/Autocomplete/src/AutocompleteResultsExecutor.php +++ b/src/Autocomplete/src/AutocompleteResultsExecutor.php @@ -59,7 +59,7 @@ public function fetchResults(EntityAutocompleterInterface $autocompleter, string $results = []; - if (!method_exists($autocompleter, 'getGroupBy') || null === $groupBy = $autocompleter->getGroupBy()) { + if (null === $groupBy = $autocompleter->getGroupBy()) { foreach ($paginator as $entity) { $results[] = $this->formatResult($autocompleter, $entity); } @@ -112,13 +112,8 @@ public function fetchResults(EntityAutocompleterInterface $autocompleter, string */ private function formatResult(EntityAutocompleterInterface $autocompleter, object $entity): array { - $attributes = []; - if (method_exists($autocompleter, 'getAttributes')) { - $attributes = $autocompleter->getAttributes($entity); - } - return [ - ...$attributes, + ...$autocompleter->getAttributes($entity), 'value' => $autocompleter->getValue($entity), 'text' => $autocompleter->getLabel($entity), ]; diff --git a/src/Autocomplete/src/EntityAutocompleterInterface.php b/src/Autocomplete/src/EntityAutocompleterInterface.php index 4ad8a855f18..1268c6c1ae2 100644 --- a/src/Autocomplete/src/EntityAutocompleterInterface.php +++ b/src/Autocomplete/src/EntityAutocompleterInterface.php @@ -19,11 +19,6 @@ * Interface for classes that will have an "autocomplete" endpoint exposed. * * @template T of object - * - * TODO Remove next lines for Symfony UX 3 - * - * @method array getAttributes(object $entity) Returns extra attributes to add to the autocomplete result. - * @method mixed getGroupBy() Return group_by option. */ interface EntityAutocompleterInterface { @@ -58,9 +53,11 @@ public function getValue(object $entity): mixed; /** * Returns extra attributes to add to the autocomplete result. * - * TODO Uncomment for Symfony UX 3 + * @param T $entity + * + * @return array */ - /* public function getAttributes(object $entity): array; */ + public function getAttributes(object $entity): array; /** * Return true if access should be granted to the autocomplete results for the current user. @@ -69,10 +66,10 @@ public function getValue(object $entity): mixed; */ public function isGranted(Security $security): bool; - /* + /** * Return group_by option. * - * TODO Uncomment for Symfony UX 3 + * @return string|null */ - /* public function getGroupBy(): mixed; */ + public function getGroupBy(): mixed; } diff --git a/src/Autocomplete/src/Form/BaseEntityAutocompleteType.php b/src/Autocomplete/src/Form/BaseEntityAutocompleteType.php index a7e103eecad..2e9db802bb3 100644 --- a/src/Autocomplete/src/Form/BaseEntityAutocompleteType.php +++ b/src/Autocomplete/src/Form/BaseEntityAutocompleteType.php @@ -65,11 +65,14 @@ public function configureOptions(OptionsResolver $resolver): void 'security' => false, // set the max results number that a query on automatic endpoint return. 'max_results' => 10, + // extra attributes to add to the autocomplete result, either an array or a callable (called with the entity) + 'additional_attributes' => null, ]); $resolver->setAllowedTypes('security', ['boolean', 'string', 'callable']); $resolver->setAllowedTypes('max_results', ['int', 'null']); $resolver->setAllowedTypes('filter_query', ['callable', 'null']); + $resolver->setAllowedTypes('additional_attributes', ['null', 'callable', 'array']); $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.'); diff --git a/src/Autocomplete/src/Form/WrappedEntityTypeAutocompleter.php b/src/Autocomplete/src/Form/WrappedEntityTypeAutocompleter.php index b83c7407ad7..c512265305f 100644 --- a/src/Autocomplete/src/Form/WrappedEntityTypeAutocompleter.php +++ b/src/Autocomplete/src/Form/WrappedEntityTypeAutocompleter.php @@ -138,11 +138,28 @@ public function getGroupBy(): mixed return $this->getFormOption('group_by'); } + public function getAttributes(object $entity): array + { + $attributesOption = $this->getFormOption('additional_attributes'); + + if (null === $attributesOption) { + return []; + } + + if (\is_array($attributesOption)) { + return $attributesOption; + } + + if (\is_callable($attributesOption)) { + return $attributesOption($entity); + } + + throw new \InvalidArgumentException('The "additional_attributes" option must be either an array or a callable.'); + } + private function getFormOption(string $name): mixed { $form = $this->getForm(); - // Remove when dropping support for ParentEntityAutocompleteType - $form = $form->has('autocomplete') ? $form->get('autocomplete') : $form; $formOptions = $form->getConfig()->getOptions(); return $formOptions[$name] ?? null; diff --git a/src/Turbo/CHANGELOG.md b/src/Turbo/CHANGELOG.md index 9cec827f5ee..e3abefcfe62 100644 --- a/src/Turbo/CHANGELOG.md +++ b/src/Turbo/CHANGELOG.md @@ -5,6 +5,7 @@ - Minimum required Symfony version is now 6.4 - Minimum required PHP version is now 8.2 - Remove old compatibility layer with deprecated `StimulusTwigExtension` from WebpackEncoreBundle ^1.0, use StimulusBundle instead +- Remove BC layer for `TurboStreamListenRendererInterface::renderTurboStreamListen()` `$eventSourceOptions` parameter ## 2.32 diff --git a/src/Turbo/src/Bridge/Mercure/TurboStreamListenRenderer.php b/src/Turbo/src/Bridge/Mercure/TurboStreamListenRenderer.php index 29a8e34becb..107aab306d8 100644 --- a/src/Turbo/src/Bridge/Mercure/TurboStreamListenRenderer.php +++ b/src/Turbo/src/Bridge/Mercure/TurboStreamListenRenderer.php @@ -15,7 +15,7 @@ use Symfony\Component\Mercure\Twig\MercureExtension; use Symfony\UX\StimulusBundle\Helper\StimulusHelper; use Symfony\UX\Turbo\Broadcaster\IdAccessor; -use Symfony\UX\Turbo\Twig\TurboStreamListenRendererWithOptionsInterface; +use Symfony\UX\Turbo\Twig\TurboStreamListenRendererInterface; use Twig\Environment; use Twig\Error\RuntimeError; use Twig\Extension\AbstractExtension; @@ -25,7 +25,7 @@ * * @author Kévin Dunglas */ -final class TurboStreamListenRenderer implements TurboStreamListenRendererWithOptionsInterface +final class TurboStreamListenRenderer implements TurboStreamListenRendererInterface { public function __construct( private HubInterface $hub, @@ -35,12 +35,8 @@ public function __construct( ) { } - public function renderTurboStreamListen(Environment $env, $topic /* array $eventSourceOptions = [] */): string + public function renderTurboStreamListen(Environment $env, $topic, array $eventSourceOptions = []): string { - if (\func_num_args() > 2) { - $eventSourceOptions = func_get_arg(2); - } - $topics = $topic instanceof TopicSet ? array_map($this->resolveTopic(...), $topic->getTopics()) : [$this->resolveTopic($topic)]; @@ -52,7 +48,7 @@ public function renderTurboStreamListen(Environment $env, $topic /* array $event $controllerAttributes['topic'] = current($topics); } - if (isset($eventSourceOptions)) { + if ([] !== $eventSourceOptions) { try { // Mercure >= 0.7: https://github.com/symfony/mercure/pull/123 /* @phpstan-ignore-next-line function.alreadyNarrowedType */ diff --git a/src/Turbo/src/Twig/TurboRuntime.php b/src/Turbo/src/Twig/TurboRuntime.php index 78fcba2d3b4..78b1737b65f 100644 --- a/src/Turbo/src/Twig/TurboRuntime.php +++ b/src/Turbo/src/Twig/TurboRuntime.php @@ -52,8 +52,6 @@ public function renderTurboStreamListen(Environment $env, $topic, ?string $trans $renderer = $this->turboStreamListenRenderers->get($transport); - return $renderer instanceof TurboStreamListenRendererWithOptionsInterface - ? $renderer->renderTurboStreamListen($env, $topic, $options) // @phpstan-ignore-line - : $renderer->renderTurboStreamListen($env, $topic); + return $renderer->renderTurboStreamListen($env, $topic, $options); } } diff --git a/src/Turbo/src/Twig/TurboStreamListenRendererInterface.php b/src/Turbo/src/Twig/TurboStreamListenRendererInterface.php index 240721317f1..cb1f84d6f0b 100644 --- a/src/Turbo/src/Twig/TurboStreamListenRendererInterface.php +++ b/src/Turbo/src/Twig/TurboStreamListenRendererInterface.php @@ -21,7 +21,8 @@ interface TurboStreamListenRendererInterface { /** - * @param string|object $topic + * @param string|object $topic + * @param array $eventSourceOptions */ - public function renderTurboStreamListen(Environment $env, $topic /* , array $eventSourceOptions = [] */): string; + public function renderTurboStreamListen(Environment $env, $topic, array $eventSourceOptions = []): string; } diff --git a/src/Turbo/src/Twig/TurboStreamListenRendererWithOptionsInterface.php b/src/Turbo/src/Twig/TurboStreamListenRendererWithOptionsInterface.php deleted file mode 100644 index 6364fe3b97b..00000000000 --- a/src/Turbo/src/Twig/TurboStreamListenRendererWithOptionsInterface.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\UX\Turbo\Twig; - -/** - * @internal - */ -interface TurboStreamListenRendererWithOptionsInterface extends TurboStreamListenRendererInterface -{ -} diff --git a/src/Turbo/tests/Twig/TurboRuntimeTest.php b/src/Turbo/tests/Twig/TurboRuntimeTest.php index 666ab68200f..44d9eaea233 100644 --- a/src/Turbo/tests/Twig/TurboRuntimeTest.php +++ b/src/Turbo/tests/Twig/TurboRuntimeTest.php @@ -16,7 +16,6 @@ use Symfony\Contracts\Service\ServiceProviderInterface; use Symfony\UX\Turbo\Twig\TurboRuntime; use Symfony\UX\Turbo\Twig\TurboStreamListenRendererInterface; -use Symfony\UX\Turbo\Twig\TurboStreamListenRendererWithOptionsInterface; use Twig\Environment; final class TurboRuntimeTest extends TestCase @@ -41,7 +40,7 @@ public function testRenderTurboStreamListenWithMultipleHubs() { $twig = $this->createMock(Environment::class); $renderer1 = $this->createMock(TurboStreamListenRendererInterface::class); - $renderer2 = $this->createMock(TurboStreamListenRendererWithOptionsInterface::class); + $renderer2 = $this->createMock(TurboStreamListenRendererInterface::class); $renderer2->expects($this->once()) ->method('renderTurboStreamListen') ->with($twig, 'a_topic', ['hub' => 'hub2'])