From ee1d5ec9ce4c8de40f94a950347e595024051777 Mon Sep 17 00:00:00 2001 From: soyuka Date: Mon, 5 Oct 2026 11:06:49 +0200 Subject: [PATCH] feat(symfony): named autowiring aliases for state services Deprecate the FQCN aliases of the final Doctrine processors and Doctrine/Elasticsearch providers in favor of named aliases on ProcessorInterface/ProviderInterface, type-hinting a final class breaks when the service is decorated. --- .../Resources/config/doctrine_mongodb_odm.php | 24 ++++++-- .../Bundle/Resources/config/doctrine_orm.php | 24 ++++++-- .../Bundle/Resources/config/elasticsearch.php | 11 +++- .../ApiPlatformExtensionTest.php | 56 +++++++++++++++++++ 4 files changed, 103 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Bundle/Resources/config/doctrine_mongodb_odm.php b/src/Symfony/Bundle/Resources/config/doctrine_mongodb_odm.php index 8e7625bf4af..2d1ad23de5e 100644 --- a/src/Symfony/Bundle/Resources/config/doctrine_mongodb_odm.php +++ b/src/Symfony/Bundle/Resources/config/doctrine_mongodb_odm.php @@ -35,6 +35,8 @@ use ApiPlatform\Doctrine\Odm\State\CollectionProvider; use ApiPlatform\Doctrine\Odm\State\ItemProvider; use ApiPlatform\Doctrine\Odm\State\LinksHandler; +use ApiPlatform\State\ProcessorInterface; +use ApiPlatform\State\ProviderInterface; use Doctrine\Persistence\Mapping\ClassMetadataFactory; return function (ContainerConfigurator $container) { @@ -55,14 +57,20 @@ ->tag('api_platform.state_processor', ['priority' => -100, 'key' => 'api_platform.doctrine_mongodb.odm.state.remove_processor']) ->tag('api_platform.state_processor', ['priority' => -100, 'key' => 'ApiPlatform\Doctrine\Common\State\RemoveProcessor']); - $services->alias(RemoveProcessor::class, 'api_platform.doctrine_mongodb.odm.state.remove_processor'); + $services->alias(RemoveProcessor::class, 'api_platform.doctrine_mongodb.odm.state.remove_processor') + ->deprecate('api-platform/core', '5.1', 'The "%alias_id%" service alias is deprecated, use "#[Target(\'odmRemove\')] ProcessorInterface $processor" instead.'); + + $services->alias(ProcessorInterface::class.' $odmRemove', 'api_platform.doctrine_mongodb.odm.state.remove_processor'); $services->set('api_platform.doctrine_mongodb.odm.state.persist_processor', PersistProcessor::class) ->args([service('doctrine_mongodb')]) ->tag('api_platform.state_processor', ['priority' => -100, 'key' => 'api_platform.doctrine_mongodb.odm.state.persist_processor']) ->tag('api_platform.state_processor', ['priority' => -100, 'key' => 'ApiPlatform\Doctrine\Common\State\PersistProcessor']); - $services->alias(PersistProcessor::class, 'api_platform.doctrine_mongodb.odm.state.persist_processor'); + $services->alias(PersistProcessor::class, 'api_platform.doctrine_mongodb.odm.state.persist_processor') + ->deprecate('api-platform/core', '5.1', 'The "%alias_id%" service alias is deprecated, use "#[Target(\'odmPersist\')] ProcessorInterface $processor" instead.'); + + $services->alias(ProcessorInterface::class.' $odmPersist', 'api_platform.doctrine_mongodb.odm.state.persist_processor'); $services->set('api_platform.doctrine_mongodb.odm.search_filter', SearchFilter::class) ->abstract() @@ -215,7 +223,10 @@ ->tag('api_platform.state_provider', ['priority' => -100, 'key' => 'ApiPlatform\Doctrine\Odm\State\CollectionProvider']) ->tag('api_platform.state_provider', ['priority' => -100, 'key' => 'api_platform.doctrine_mongodb.odm.state.collection_provider']); - $services->alias(CollectionProvider::class, 'api_platform.doctrine_mongodb.odm.state.collection_provider'); + $services->alias(CollectionProvider::class, 'api_platform.doctrine_mongodb.odm.state.collection_provider') + ->deprecate('api-platform/core', '5.1', 'The "%alias_id%" service alias is deprecated, use "#[Target(\'odmCollection\')] ProviderInterface $provider" instead.'); + + $services->alias(ProviderInterface::class.' $odmCollection', 'api_platform.doctrine_mongodb.odm.state.collection_provider'); $services->set('api_platform.doctrine_mongodb.odm.state.item_provider', ItemProvider::class) ->args([ @@ -227,9 +238,12 @@ ->tag('api_platform.state_provider', ['priority' => -100, 'key' => 'ApiPlatform\Doctrine\Odm\State\ItemProvider']) ->tag('api_platform.state_provider', ['priority' => -100, 'key' => 'api_platform.doctrine_mongodb.odm.state.item_provider']); - $services->alias(ItemProvider::class, 'api_platform.doctrine_mongodb.odm.state.item_provider'); + $services->alias(ItemProvider::class, 'api_platform.doctrine_mongodb.odm.state.item_provider') + ->deprecate('api-platform/core', '5.1', 'The "%alias_id%" service alias is deprecated, use "#[Target(\'odmItem\')] ProviderInterface $provider" instead.'); + + $services->alias(ProviderInterface::class.' $odmItem', 'api_platform.doctrine_mongodb.odm.state.item_provider'); - $services->alias('api_platform.state.item_provider', 'ApiPlatform\Doctrine\Odm\State\ItemProvider'); + $services->alias('api_platform.state.item_provider', 'api_platform.doctrine_mongodb.odm.state.item_provider'); $services->set('api_platform.doctrine.odm.metadata.resource.metadata_collection_factory', DoctrineMongoDbOdmResourceCollectionMetadataFactory::class) ->decorate('api_platform.metadata.resource.metadata_collection_factory', null, -50) diff --git a/src/Symfony/Bundle/Resources/config/doctrine_orm.php b/src/Symfony/Bundle/Resources/config/doctrine_orm.php index 13806ccf0fb..58754626988 100644 --- a/src/Symfony/Bundle/Resources/config/doctrine_orm.php +++ b/src/Symfony/Bundle/Resources/config/doctrine_orm.php @@ -41,6 +41,8 @@ use ApiPlatform\Doctrine\Orm\State\CollectionProvider; use ApiPlatform\Doctrine\Orm\State\ItemProvider; use ApiPlatform\Doctrine\Orm\State\LinksHandler; +use ApiPlatform\State\ProcessorInterface; +use ApiPlatform\State\ProviderInterface; use Doctrine\Persistence\Mapping\ClassMetadataFactory; return function (ContainerConfigurator $container) { @@ -55,14 +57,20 @@ ->tag('api_platform.state_processor', ['priority' => -100, 'key' => 'api_platform.doctrine.orm.state.remove_processor']) ->tag('api_platform.state_processor', ['priority' => -100, 'key' => 'ApiPlatform\Doctrine\Common\State\RemoveProcessor']); - $services->alias(RemoveProcessor::class, 'api_platform.doctrine.orm.state.remove_processor'); + $services->alias(RemoveProcessor::class, 'api_platform.doctrine.orm.state.remove_processor') + ->deprecate('api-platform/core', '5.1', 'The "%alias_id%" service alias is deprecated, use "#[Target(\'ormRemove\')] ProcessorInterface $processor" instead.'); + + $services->alias(ProcessorInterface::class.' $ormRemove', 'api_platform.doctrine.orm.state.remove_processor'); $services->set('api_platform.doctrine.orm.state.persist_processor', PersistProcessor::class) ->args([service('doctrine')]) ->tag('api_platform.state_processor', ['priority' => -100, 'key' => 'api_platform.doctrine.orm.state.persist_processor']) ->tag('api_platform.state_processor', ['priority' => -100, 'key' => 'ApiPlatform\Doctrine\Common\State\PersistProcessor']); - $services->alias(PersistProcessor::class, 'api_platform.doctrine.orm.state.persist_processor'); + $services->alias(PersistProcessor::class, 'api_platform.doctrine.orm.state.persist_processor') + ->deprecate('api-platform/core', '5.1', 'The "%alias_id%" service alias is deprecated, use "#[Target(\'ormPersist\')] ProcessorInterface $processor" instead.'); + + $services->alias(ProcessorInterface::class.' $ormPersist', 'api_platform.doctrine.orm.state.persist_processor'); $services->set('api_platform.doctrine.orm.order_filter', OrderFilter::class) ->abstract() @@ -233,7 +241,10 @@ ->tag('api_platform.state_provider', ['priority' => -100, 'key' => 'ApiPlatform\Doctrine\Orm\State\CollectionProvider']) ->tag('api_platform.state_provider', ['priority' => -100, 'key' => 'api_platform.doctrine.orm.state.collection_provider']); - $services->alias(CollectionProvider::class, 'api_platform.doctrine.orm.state.collection_provider'); + $services->alias(CollectionProvider::class, 'api_platform.doctrine.orm.state.collection_provider') + ->deprecate('api-platform/core', '5.1', 'The "%alias_id%" service alias is deprecated, use "#[Target(\'ormCollection\')] ProviderInterface $provider" instead.'); + + $services->alias(ProviderInterface::class.' $ormCollection', 'api_platform.doctrine.orm.state.collection_provider'); $services->set('api_platform.doctrine.orm.state.item_provider', ItemProvider::class) ->args([ @@ -245,9 +256,12 @@ ->tag('api_platform.state_provider', ['priority' => -100, 'key' => 'ApiPlatform\Doctrine\Orm\State\ItemProvider']) ->tag('api_platform.state_provider', ['priority' => -100, 'key' => 'api_platform.doctrine.orm.state.item_provider']); - $services->alias(ItemProvider::class, 'api_platform.doctrine.orm.state.item_provider'); + $services->alias(ItemProvider::class, 'api_platform.doctrine.orm.state.item_provider') + ->deprecate('api-platform/core', '5.1', 'The "%alias_id%" service alias is deprecated, use "#[Target(\'ormItem\')] ProviderInterface $provider" instead.'); + + $services->alias(ProviderInterface::class.' $ormItem', 'api_platform.doctrine.orm.state.item_provider'); - $services->alias('api_platform.state.item_provider', 'ApiPlatform\Doctrine\Orm\State\ItemProvider'); + $services->alias('api_platform.state.item_provider', 'api_platform.doctrine.orm.state.item_provider'); $services->set('api_platform.doctrine.orm.search_filter', SearchFilter::class) ->abstract() diff --git a/src/Symfony/Bundle/Resources/config/elasticsearch.php b/src/Symfony/Bundle/Resources/config/elasticsearch.php index 267e19bbb66..7f614ac7b06 100644 --- a/src/Symfony/Bundle/Resources/config/elasticsearch.php +++ b/src/Symfony/Bundle/Resources/config/elasticsearch.php @@ -25,6 +25,7 @@ use ApiPlatform\Elasticsearch\Serializer\NameConverter\InnerFieldsNameConverter; use ApiPlatform\Elasticsearch\State\CollectionProvider; use ApiPlatform\Elasticsearch\State\ItemProvider; +use ApiPlatform\State\ProviderInterface; return function (ContainerConfigurator $container) { $services = $container->services(); @@ -119,7 +120,10 @@ ->tag('api_platform.state_provider', ['priority' => -100, 'key' => 'ApiPlatform\Elasticsearch\State\ItemProvider']) ->tag('api_platform.state_provider', ['priority' => -100]); - $services->alias(ItemProvider::class, 'api_platform.elasticsearch.state.item_provider'); + $services->alias(ItemProvider::class, 'api_platform.elasticsearch.state.item_provider') + ->deprecate('api-platform/core', '5.1', 'The "%alias_id%" service alias is deprecated, use "#[Target(\'elasticsearchItem\')] ProviderInterface $provider" instead.'); + + $services->alias(ProviderInterface::class.' $elasticsearchItem', 'api_platform.elasticsearch.state.item_provider'); $services->set('api_platform.elasticsearch.state.collection_provider', CollectionProvider::class) ->args([ @@ -132,7 +136,10 @@ ->tag('api_platform.state_provider', ['priority' => -100, 'key' => 'ApiPlatform\Elasticsearch\State\CollectionProvider']) ->tag('api_platform.state_provider', ['priority' => -100]); - $services->alias(CollectionProvider::class, 'api_platform.elasticsearch.state.collection_provider'); + $services->alias(CollectionProvider::class, 'api_platform.elasticsearch.state.collection_provider') + ->deprecate('api-platform/core', '5.1', 'The "%alias_id%" service alias is deprecated, use "#[Target(\'elasticsearchCollection\')] ProviderInterface $provider" instead.'); + + $services->alias(ProviderInterface::class.' $elasticsearchCollection', 'api_platform.elasticsearch.state.collection_provider'); $services->set('api_platform.elasticsearch.metadata.resource.metadata_collection_factory', ElasticsearchProviderResourceMetadataCollectionFactory::class) ->decorate('api_platform.metadata.resource.metadata_collection_factory', null, 40) diff --git a/src/Symfony/Tests/Bundle/DependencyInjection/ApiPlatformExtensionTest.php b/src/Symfony/Tests/Bundle/DependencyInjection/ApiPlatformExtensionTest.php index 6a0b2f737d2..f290ace872a 100644 --- a/src/Symfony/Tests/Bundle/DependencyInjection/ApiPlatformExtensionTest.php +++ b/src/Symfony/Tests/Bundle/DependencyInjection/ApiPlatformExtensionTest.php @@ -13,6 +13,14 @@ namespace ApiPlatform\Symfony\Tests\Bundle\DependencyInjection; +use ApiPlatform\Doctrine\Common\State\PersistProcessor; +use ApiPlatform\Doctrine\Common\State\RemoveProcessor; +use ApiPlatform\Doctrine\Odm\State\CollectionProvider as OdmCollectionProvider; +use ApiPlatform\Doctrine\Odm\State\ItemProvider as OdmItemProvider; +use ApiPlatform\Doctrine\Orm\State\CollectionProvider as OrmCollectionProvider; +use ApiPlatform\Doctrine\Orm\State\ItemProvider as OrmItemProvider; +use ApiPlatform\Elasticsearch\State\CollectionProvider as ElasticsearchCollectionProvider; +use ApiPlatform\Elasticsearch\State\ItemProvider as ElasticsearchItemProvider; use ApiPlatform\Metadata\Exception\ExceptionInterface; use ApiPlatform\Metadata\Exception\InvalidArgumentException; use ApiPlatform\Metadata\IdentifiersExtractorInterface; @@ -23,6 +31,8 @@ use ApiPlatform\Serializer\Filter\PropertyFilter; use ApiPlatform\State\Pagination\Pagination; use ApiPlatform\State\Pagination\PaginationOptions; +use ApiPlatform\State\ProcessorInterface; +use ApiPlatform\State\ProviderInterface; use ApiPlatform\State\SerializerContextBuilderInterface; use ApiPlatform\Symfony\Action\NotFoundAction; use ApiPlatform\Symfony\Bundle\DependencyInjection\ApiPlatformExtension; @@ -594,4 +604,50 @@ public function testHttpCachePurgersAndListenerRegisteredWhenInvalidationEnabled $this->assertContainerHasService('api_platform.http_cache_purger.processor.add_tags'); $this->assertContainerHasAlias('api_platform.http_cache.purger'); } + + public function testStateServicesHaveNamedAutowiringAliases(): void + { + $config = self::DEFAULT_CONFIG; + $config['api_platform']['elasticsearch']['enabled'] = true; + (new ApiPlatformExtension())->load($config, $this->container); + + $aliases = [ + ProcessorInterface::class.' $ormPersist' => 'api_platform.doctrine.orm.state.persist_processor', + ProcessorInterface::class.' $ormRemove' => 'api_platform.doctrine.orm.state.remove_processor', + ProviderInterface::class.' $ormItem' => 'api_platform.doctrine.orm.state.item_provider', + ProviderInterface::class.' $ormCollection' => 'api_platform.doctrine.orm.state.collection_provider', + ProcessorInterface::class.' $odmPersist' => 'api_platform.doctrine_mongodb.odm.state.persist_processor', + ProcessorInterface::class.' $odmRemove' => 'api_platform.doctrine_mongodb.odm.state.remove_processor', + ProviderInterface::class.' $odmItem' => 'api_platform.doctrine_mongodb.odm.state.item_provider', + ProviderInterface::class.' $odmCollection' => 'api_platform.doctrine_mongodb.odm.state.collection_provider', + ProviderInterface::class.' $elasticsearchItem' => 'api_platform.elasticsearch.state.item_provider', + ProviderInterface::class.' $elasticsearchCollection' => 'api_platform.elasticsearch.state.collection_provider', + ]; + + foreach ($aliases as $alias => $serviceId) { + $this->assertContainerHasAlias($alias); + $this->assertSame($serviceId, (string) $this->container->getAlias($alias)); + } + } + + public function testFinalStateServiceFqcnAliasesAreDeprecated(): void + { + $config = self::DEFAULT_CONFIG; + $config['api_platform']['elasticsearch']['enabled'] = true; + (new ApiPlatformExtension())->load($config, $this->container); + + foreach ([ + PersistProcessor::class, + RemoveProcessor::class, + OrmItemProvider::class, + OrmCollectionProvider::class, + OdmItemProvider::class, + OdmCollectionProvider::class, + ElasticsearchItemProvider::class, + ElasticsearchCollectionProvider::class, + ] as $fqcn) { + $this->assertContainerHasAlias($fqcn); + $this->assertTrue($this->container->getAlias($fqcn)->isDeprecated(), \sprintf('Alias "%s" is not deprecated.', $fqcn)); + } + } }