diff --git a/src/Bundle/Resources/config/services/integrations/doctrine/orm.xml b/src/Bundle/Resources/config/services/integrations/doctrine/orm.xml index 0ca399cfe..5f19df807 100644 --- a/src/Bundle/Resources/config/services/integrations/doctrine/orm.xml +++ b/src/Bundle/Resources/config/services/integrations/doctrine/orm.xml @@ -20,6 +20,24 @@ + + + + + + + + + + + + + + diff --git a/src/Bundle/Resources/config/services/metadata.xml b/src/Bundle/Resources/config/services/metadata.xml index 18fea2a9b..988619146 100644 --- a/src/Bundle/Resources/config/services/metadata.xml +++ b/src/Bundle/Resources/config/services/metadata.xml @@ -34,15 +34,6 @@ %sylius.state_machine_component.default% - - - - - - - - - - - - diff --git a/src/Component/spec/Symfony/Request/State/ProviderSpec.php b/src/Component/spec/Symfony/Request/State/ProviderSpec.php deleted file mode 100644 index 05d3fbb89..000000000 --- a/src/Component/spec/Symfony/Request/State/ProviderSpec.php +++ /dev/null @@ -1,175 +0,0 @@ -beConstructedWith($locator, new RepositoryArgumentResolver(), $argumentParser); - } - - function it_is_initializable(): void - { - $this->shouldHaveType(Provider::class); - } - - function it_calls_repository_as_callable( - Operation $operation, - Request $request, - ): void { - $operation->getRepository()->willReturn([RepositoryWithCallables::class, 'find']); - $operation->getRepositoryArguments()->willReturn(null); - - $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id']]); - $request->query = new InputBag([]); - $request->request = new InputBag(); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldHaveType(\stdClass::class); - $response->id->shouldReturn('my_id'); - } - - function it_calls_repository_as_string( - Operation $operation, - Request $request, - ContainerInterface $locator, - RepositoryInterface $repository, - \stdClass $stdClass, - ): void { - $operation->getRepository()->willReturn('App\Repository'); - $operation->getRepositoryMethod()->willReturn(null); - $operation->getRepositoryArguments()->willReturn(null); - - $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); - $request->query = new InputBag([]); - $request->request = new InputBag(); - - $locator->has('App\Repository')->willReturn(true); - $locator->get('App\Repository')->willReturn($repository); - - $repository->findOneBy(['id' => 'my_id'])->willReturn($stdClass); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldReturn($stdClass); - } - - function it_calls_create_paginator_by_default_on_collection_operations( - Request $request, - ContainerInterface $locator, - RepositoryInterface $repository, - Pagerfanta $pagerfanta, - ): void { - $operation = new Index(repository: 'App\Repository'); - - $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); - $request->query = new InputBag([]); - $request->request = new InputBag(); - - $locator->has('App\Repository')->willReturn(true); - $locator->get('App\Repository')->willReturn($repository); - - $repository->createPaginator()->willReturn($pagerfanta)->shouldBeCalled(); - $pagerfanta->setCurrentPage(1)->willReturn($pagerfanta)->shouldBeCalled(); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldReturn($pagerfanta); - } - - function it_sets_current_page_from_request_when_data_is_a_paginator( - Request $request, - ContainerInterface $locator, - RepositoryInterface $repository, - Pagerfanta $pagerfanta, - ): void { - $operation = new Index(repository: 'App\Repository'); - - $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); - $request->query = new InputBag(['page' => 42]); - $request->request = new InputBag(); - - $locator->has('App\Repository')->willReturn(true); - $locator->get('App\Repository')->willReturn($repository); - - $repository->createPaginator()->willReturn($pagerfanta)->shouldBeCalled(); - $pagerfanta->setCurrentPage(42)->willReturn($pagerfanta)->shouldBeCalled(); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldReturn($pagerfanta); - $pagerfanta->getCurrentPage()->willReturn(42); - } - - function it_calls_repository_as_string_with_specific_repository_method( - Operation $operation, - Request $request, - ContainerInterface $locator, - RepositoryInterface $repository, - \stdClass $stdClass, - ): void { - $operation->getRepository()->willReturn('App\Repository'); - $operation->getRepositoryMethod()->willReturn('find'); - $operation->getRepositoryArguments()->willReturn(null); - - $request->attributes = new ParameterBag(['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); - $request->query = new InputBag([]); - $request->request = new InputBag(); - - $locator->has('App\Repository')->willReturn(true); - $locator->get('App\Repository')->willReturn($repository); - - $repository->find('my_id')->willReturn($stdClass); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldReturn($stdClass); - } - - function it_calls_repository_as_string_with_specific_repository_method_an_arguments( - Operation $operation, - Request $request, - ContainerInterface $locator, - RepositoryInterface $repository, - ArgumentParserInterface $argumentParser, - \stdClass $stdClass, - ): void { - $operation->getRepository()->willReturn('App\Repository'); - $operation->getRepositoryMethod()->willReturn('find'); - $operation->getRepositoryArguments()->willReturn(['id' => "request.attributes.get('id')"]); - - $argumentParser->parseExpression("request.attributes.get('id')")->willReturn('my_id'); - - $locator->has('App\Repository')->willReturn(true); - $locator->get('App\Repository')->willReturn($repository); - - $repository->find('my_id')->willReturn($stdClass); - - $response = $this->provide($operation, new Context(new RequestOption($request->getWrappedObject()))); - $response->shouldReturn($stdClass); - } -} diff --git a/src/Component/src/Doctrine/Common/Metadata/Resource/Factory/DoctrineResourceMetadataCollectionFactory.php b/src/Component/src/Doctrine/Common/Metadata/Resource/Factory/DoctrineResourceMetadataCollectionFactory.php index b6141ac35..f4ae8daac 100644 --- a/src/Component/src/Doctrine/Common/Metadata/Resource/Factory/DoctrineResourceMetadataCollectionFactory.php +++ b/src/Component/src/Doctrine/Common/Metadata/Resource/Factory/DoctrineResourceMetadataCollectionFactory.php @@ -15,6 +15,7 @@ use Sylius\Resource\Doctrine\Common\State\PersistProcessor; use Sylius\Resource\Doctrine\Common\State\RemoveProcessor; +use Sylius\Resource\Doctrine\ORM\Metadata\Resource\Factory\DoctrineORMResourceMetadataCollectionFactory; use Sylius\Resource\Metadata\DeleteOperationInterface; use Sylius\Resource\Metadata\Operation; use Sylius\Resource\Metadata\Operations; @@ -29,6 +30,13 @@ public function __construct( private RegistryInterface $resourceRegistry, private ResourceMetadataCollectionFactoryInterface $decorated, ) { + trigger_deprecation( + 'sylius/resource', + '1.13', + 'The "%s" class is deprecated, use "%s instead. It will be removed in 2.0.', + self::class, + DoctrineORMResourceMetadataCollectionFactory::class, + ); } public function create(string $resourceClass): ResourceMetadataCollection diff --git a/src/Component/src/Doctrine/ORM/Metadata/Resource/Factory/DoctrineORMResourceMetadataCollectionFactory.php b/src/Component/src/Doctrine/ORM/Metadata/Resource/Factory/DoctrineORMResourceMetadataCollectionFactory.php new file mode 100644 index 000000000..93293d7a3 --- /dev/null +++ b/src/Component/src/Doctrine/ORM/Metadata/Resource/Factory/DoctrineORMResourceMetadataCollectionFactory.php @@ -0,0 +1,103 @@ +decorated->create($resourceClass); + + /** @var ResourceMetadata $resource */ + foreach ($resourceCollectionMetadata->getIterator() as $i => $resource) { + $operations = $resource->getOperations() ?? new Operations(); + $entityClass = $resource->getClass(); + + if (null === $entityClass) { + continue; + } + + /** @var Operation $operation */ + foreach ($operations as $operation) { + /** @var string $key */ + $key = $operation->getName(); + $entityManager = $this->managerRegistry->getManagerForClass($entityClass); + + if (!$entityManager instanceof EntityManagerInterface) { + $operations->add($key, $operation); + + continue; + } + + $operations->add($key, $this->addDefaults($operation)); + } + + $resource = $resource->withOperations($operations); + $resourceCollectionMetadata[$i] = $resource; + } + + return $resourceCollectionMetadata; + } + + private function addDefaults(Operation $operation): Operation + { + $operation = $operation->withProvider($this->getProvider($operation)); + + return $operation->withProcessor($this->getProcessor($operation)); + } + + private function getProvider(Operation $operation): callable|string|null + { + if (null !== $provider = $operation->getProvider()) { + return $provider; + } + + if ($operation instanceof GridAwareOperationInterface && null !== $operation->getGrid()) { + return null; + } + + return 'sylius.state_provider.doctrine.orm.state.provider'; + } + + private function getProcessor(Operation $operation): callable|string + { + if (null !== $processor = $operation->getProcessor()) { + return $processor; + } + + if ($operation instanceof DeleteOperationInterface) { + return RemoveProcessor::class; + } + + return PersistProcessor::class; + } +} diff --git a/src/Component/src/Symfony/Request/State/Provider.php b/src/Component/src/Doctrine/ORM/State/Provider.php similarity index 52% rename from src/Component/src/Symfony/Request/State/Provider.php rename to src/Component/src/Doctrine/ORM/State/Provider.php index cea4eda91..cac55efa3 100644 --- a/src/Component/src/Symfony/Request/State/Provider.php +++ b/src/Component/src/Doctrine/ORM/State/Provider.php @@ -11,9 +11,11 @@ declare(strict_types=1); -namespace Sylius\Resource\Symfony\Request\State; +namespace Sylius\Resource\Doctrine\ORM\State; -use Pagerfanta\Pagerfanta; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Persistence\ManagerRegistry; +use Pagerfanta\PagerfantaInterface; use Psr\Container\ContainerInterface; use Sylius\Resource\Context\Context; use Sylius\Resource\Context\Option\RequestOption; @@ -31,49 +33,33 @@ final class Provider implements ProviderInterface { public function __construct( - private ContainerInterface $locator, + private ManagerRegistry $managerRegistry, private RepositoryArgumentResolver $argumentResolver, private ArgumentParserInterface $argumentParser, + private ContainerInterface $locator, ) { } public function provide(Operation $operation, Context $context): object|array|null { $request = $context->get(RequestOption::class)?->request(); - $repository = $operation->getRepository(); - if ( - null === $request || - null === $repository - ) { + if (null === $request) { return null; } + $repository = $operation->getRepository(); $repositoryInstance = null; - $arguments = $this->parseArgumentValues($operation->getRepositoryArguments() ?? []); - - if (\is_string($repository)) { - $defaultMethod = $operation instanceof CollectionOperationInterface ? 'createPaginator' : 'findOneBy'; - - if ($operation instanceof BulkOperationInterface) { - $defaultMethod = 'findById'; - } - - $method = $operation->getRepositoryMethod() ?? $defaultMethod; - if (!$this->locator->has($repository)) { - throw new \RuntimeException(sprintf('Repository "%s" not found on operation "%s"', $repository, $operation->getName() ?? '')); - } - - $repositoryInstance = $this->locator->get($repository); - - // make it as callable - /** @var callable $repository */ - $repository = [$repositoryInstance, $method]; + if (\is_callable($repository)) { + $callableRepository = $repository; + } else { + $repositoryInstance = $this->getRepositoryInstance($operation); + $callableRepository = $this->createCallableRepository($operation, $repositoryInstance); } try { - $reflector = CallableReflection::from($repository); + $reflector = CallableReflection::from($callableRepository); } catch (\ReflectionException $exception) { if (null === $repositoryInstance) { throw $exception; @@ -85,13 +71,15 @@ public function provide(Operation $operation, Context $context): object|array|nu $reflector = CallableReflection::from($callable); } + $arguments = $this->parseArgumentValues($operation->getRepositoryArguments() ?? []); + if ([] === $arguments) { $arguments = $this->argumentResolver->getArguments($request, $reflector); } - $data = $repository(...$arguments); + $data = $callableRepository(...$arguments); - if ($data instanceof Pagerfanta) { + if ($data instanceof PagerfantaInterface) { $currentPage = $request->query->getInt('page', 1); $data->setCurrentPage($currentPage); } @@ -107,4 +95,43 @@ private function parseArgumentValues(array $arguments): array return $arguments; } + + private function createCallableRepository(Operation $operation, mixed $repositoryInstance): callable + { + $defaultMethod = $operation instanceof CollectionOperationInterface ? 'createPaginator' : 'findOneBy'; + + if ($operation instanceof BulkOperationInterface) { + $defaultMethod = 'findById'; + } + + $method = $operation->getRepositoryMethod() ?? $defaultMethod; + + // make it as callable + /** @var callable $repository */ + $repository = [$repositoryInstance, $method]; + + return $repository; + } + + private function getRepositoryInstance(Operation $operation): mixed + { + /** @var string|null $repository */ + $repository = $operation->getRepository(); + + if (null === $repository) { + /** @var class-string $entityClass */ + $entityClass = $operation->getResource()?->getClass(); + + /** @var EntityManagerInterface $manager */ + $manager = $this->managerRegistry->getManagerForClass($entityClass); + + return $manager->getRepository($entityClass); + } + + if (!$this->locator->has($repository)) { + throw new \RuntimeException(sprintf('Repository "%s" not found on operation "%s"', $repository, $operation->getName() ?? '')); + } + + return $this->locator->get($repository); + } } diff --git a/src/Component/src/Grid/State/RequestGridProvider.php b/src/Component/src/Grid/State/RequestGridProvider.php index 8b5311840..79237872f 100644 --- a/src/Component/src/Grid/State/RequestGridProvider.php +++ b/src/Component/src/Grid/State/RequestGridProvider.php @@ -13,7 +13,7 @@ namespace Sylius\Resource\Grid\State; -use Pagerfanta\Pagerfanta; +use Pagerfanta\PagerfantaInterface; use Sylius\Component\Grid\Parameters; use Sylius\Component\Grid\Provider\GridProviderInterface; use Sylius\Resource\Context\Context; @@ -63,7 +63,7 @@ public function provide(Operation $operation, Context $context): object|array|nu $data = $gridView->getData(); - if ($data instanceof Pagerfanta) { + if ($data instanceof PagerfantaInterface) { $currentPage = $request->query->getInt('page', 1); $data->setCurrentPage($currentPage); diff --git a/src/Component/src/Metadata/AsResource.php b/src/Component/src/Metadata/AsResource.php index f2eb89938..df0925b2e 100644 --- a/src/Component/src/Metadata/AsResource.php +++ b/src/Component/src/Metadata/AsResource.php @@ -16,6 +16,9 @@ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)] final class AsResource { + /** + * @param class-string|null $class + */ public function __construct( private ?string $alias = null, private ?string $section = null, diff --git a/src/Component/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php b/src/Component/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php index 23e4d1de8..0ae0097c1 100644 --- a/src/Component/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php +++ b/src/Component/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php @@ -48,6 +48,7 @@ public function create(string $resourceClass): ResourceMetadataCollection /** * @param \ReflectionAttribute[] $attributes + * @param class-string $resourceClass * * @return ResourceMetadata[] */ @@ -118,6 +119,9 @@ private function buildResourceOperations(array $attributes, string $resourceClas return $resources; } + /** + * @param class-string $resourceClass + */ private function getResourceWithDefaults(string $resourceClass, ResourceMetadata $resource, MetadataInterface $resourceConfiguration): ResourceMetadata { $resource = $resource->withClass($resourceClass); @@ -171,10 +175,6 @@ private function getOperationWithDefaults(ResourceMetadata $resource, Operation $operation = $operation->withResource($resource); - if (null === $operation->getRepository()) { - $operation = $operation->withRepository($resourceConfiguration->getServiceId('repository')); - } - if (null === $operation->getFormType()) { $formType = $resource->getFormType() ?? $resourceConfiguration->getClass('form'); $operation = $operation->withFormType($formType); diff --git a/src/Component/src/Metadata/Resource/Factory/ProviderResourceMetadataCollectionFactory.php b/src/Component/src/Metadata/Resource/Factory/ProviderResourceMetadataCollectionFactory.php index f5f57d1fe..27ff1ec3f 100644 --- a/src/Component/src/Metadata/Resource/Factory/ProviderResourceMetadataCollectionFactory.php +++ b/src/Component/src/Metadata/Resource/Factory/ProviderResourceMetadataCollectionFactory.php @@ -19,7 +19,6 @@ use Sylius\Resource\Metadata\Operations; use Sylius\Resource\Metadata\Resource\ResourceMetadataCollection; use Sylius\Resource\Metadata\ResourceMetadata; -use Sylius\Resource\Symfony\Request\State\Provider; final class ProviderResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface { @@ -62,10 +61,6 @@ private function addDefaults(Operation $operation): Operation $operation = $operation->withProvider(RequestGridProvider::class); } - if (null === $operation->getProvider()) { - $operation = $operation->withProvider(Provider::class); - } - return $operation; } } diff --git a/src/Component/src/Metadata/ResourceMetadata.php b/src/Component/src/Metadata/ResourceMetadata.php index c2ee0a9c3..404618c3f 100644 --- a/src/Component/src/Metadata/ResourceMetadata.php +++ b/src/Component/src/Metadata/ResourceMetadata.php @@ -17,6 +17,9 @@ final class ResourceMetadata { private ?Operations $operations; + /** + * @param class-string|null $class + */ public function __construct( private ?string $alias = null, private ?string $section = null, @@ -36,13 +39,27 @@ public function __construct( ?array $operations = null, ) { $this->operations = null === $operations ? null : new Operations($operations); + + if (null !== $driver && false !== $driver) { + trigger_deprecation( + 'sylius/resource', + '1.13', + 'Using driver is deprecated. If your resource is managed by Doctrine you have nothing to do, otherwise use a custom provider.', + ); + } } + /** + * @return class-string|null + */ public function getClass(): ?string { return $this->class; } + /** + * @param class-string $class + */ public function withClass(string $class): self { $self = clone $this; diff --git a/src/Component/tests/Doctrine/ORM/Metadata/Resource/Factory/DoctrineORMResourceMetadataCollectionFactoryTest.php b/src/Component/tests/Doctrine/ORM/Metadata/Resource/Factory/DoctrineORMResourceMetadataCollectionFactoryTest.php new file mode 100644 index 000000000..a760f8cd5 --- /dev/null +++ b/src/Component/tests/Doctrine/ORM/Metadata/Resource/Factory/DoctrineORMResourceMetadataCollectionFactoryTest.php @@ -0,0 +1,123 @@ +managerRegistry = $this->prophesize(ManagerRegistry::class); + $this->decorated = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class); + + $this->factory = new DoctrineORMResourceMetadataCollectionFactory( + $this->managerRegistry->reveal(), + $this->decorated->reveal(), + ); + } + + public function testItIsInitializable(): void + { + $this->assertInstanceOf(DoctrineORMResourceMetadataCollectionFactory::class, $this->factory); + } + + public function testItAddsPersistProcessorToOperationsForResourceManagedByDoctrineOrm(): void + { + $entityManager = $this->createMock(EntityManagerInterface::class); + + $operation = new Create(name: 'app_dummy_create'); + $resource = (new ResourceMetadata(alias: 'app.dummy')) + ->withOperations(new Operations([$operation])) + ->withClass('App\Dummy') + ; + + $resourceMetadataCollection = new ResourceMetadataCollection([$resource]); + + $this->decorated->create('App\Resource')->willReturn($resourceMetadataCollection); + $this->managerRegistry->getManagerForClass('App\Dummy')->willReturn($entityManager); + + $result = $this->factory->create('App\Resource'); + + $this->assertEquals( + PersistProcessor::class, + $result->getOperation('app.dummy', 'app_dummy_create')->getProcessor(), + ); + } + + public function testItAddsRemoveProcessorToDeleteOperationsForResourceManagedByDoctrineOrm(): void + { + $entityManager = $this->createMock(EntityManagerInterface::class); + + $operation = new Delete(name: 'app_dummy_delete'); + $resource = (new ResourceMetadata(alias: 'app.dummy')) + ->withOperations(new Operations([$operation])) + ->withClass('App\Dummy') + ; + + $resourceMetadataCollection = new ResourceMetadataCollection([$resource]); + + $this->decorated->create('App\Resource')->willReturn($resourceMetadataCollection); + $this->managerRegistry->getManagerForClass('App\Dummy')->willReturn($entityManager); + + $result = $this->factory->create('App\Resource'); + + $this->assertEquals( + RemoveProcessor::class, + $result->getOperation('app.dummy', 'app_dummy_delete')->getProcessor(), + ); + } + + public function testItDoesNothingWhenResourceIsNotManagedByDoctrineOrm(): void + { + $operation = new Create(name: 'app_dummy_create'); + $resource = (new ResourceMetadata(alias: 'app.dummy')) + ->withOperations(new Operations([$operation])) + ->withClass('App\Dummy') + ; + + $resourceMetadataCollection = new ResourceMetadataCollection([$resource]); + + $this->decorated->create('App\Resource')->willReturn($resourceMetadataCollection); + $this->managerRegistry->getManagerForClass('App\Dummy')->willReturn(null); + + $result = $this->factory->create('App\Resource'); + + $this->assertEquals( + null, + $result->getOperation('app.dummy', 'app_dummy_create')->getProcessor(), + ); + } +} diff --git a/src/Component/tests/Doctrine/ORM/State/ProviderTest.php b/src/Component/tests/Doctrine/ORM/State/ProviderTest.php new file mode 100644 index 000000000..4e39b5f4f --- /dev/null +++ b/src/Component/tests/Doctrine/ORM/State/ProviderTest.php @@ -0,0 +1,116 @@ +managerRegistry = $this->createMock(ManagerRegistry::class); + $this->locator = $this->createMock(ContainerInterface::class); + $this->argumentParser = $this->createMock(ArgumentParserInterface::class); + $this->provider = new Provider($this->managerRegistry, new RepositoryArgumentResolver(), $this->argumentParser, $this->locator); + } + + public function testItCallsRepositoryFromDoctrineManagerRegistry(): void + { + $operation = $this->createMock(Operation::class); + $entityManager = $this->createMock(EntityManagerInterface::class); + $unitOfWork = $this->createMock(UnitOfWork::class); + $entityPersister = $this->createMock(EntityPersister::class); + + $operation->method('getRepository')->willReturn(null); + $operation->method('getRepositoryArguments')->willReturn(null); + $operation->method('getResource')->willReturn((new ResourceMetadata())->withClass('App\Dummy')); + + $this->managerRegistry->method('getManagerForClass')->with('App\Dummy')->willReturn($entityManager); + $entityRepository = new EntityRepository($entityManager, new ClassMetadata('App\Dummy')); + $entityManager->method('getRepository')->willReturn($entityRepository); + + $entityManager->method('getUnitOfWork')->willReturn($unitOfWork); + $unitOfWork->method('getEntityPersister')->willReturn($entityPersister); + + $expectedResult = (object) ['id' => 'my_id']; + $entityPersister->method('load')->with(['id' => 'my_id'], null, null, [], null, 1)->willReturn($expectedResult); + + $request = new Request([], [], ['_route_params' => ['id' => 'my_id']]); + + $response = $this->provider->provide($operation, new Context(new RequestOption($request))); + $this->assertEquals($expectedResult, $response); + } + + public function testItCallsRepositoryAsCallable(): void + { + $operation = $this->createMock(Operation::class); + $operation->method('getRepository')->willReturn([RepositoryWithCallables::class, 'find']); + $operation->method('getRepositoryArguments')->willReturn(null); + + $request = new Request([], [], ['_route_params' => ['id' => 'my_id']]); + + $response = $this->provider->provide($operation, new Context(new RequestOption($request))); + $this->assertInstanceOf(\stdClass::class, $response); + $this->assertEquals('my_id', $response->id); + } + + public function testItCallsRepositoryAsString(): void + { + $operation = $this->createMock(Operation::class); + $operation->method('getRepository')->willReturn('App\\Repository'); + $operation->method('getRepositoryMethod')->willReturn(null); + $operation->method('getRepositoryArguments')->willReturn(null); + + $request = new Request([], [], ['_route_params' => ['id' => 'my_id', '_sylius' => ['resource' => 'app.dummy']]]); + + $repository = $this->createMock(RepositoryInterface::class); + $stdClass = new \stdClass(); + + $this->locator->method('has')->with('App\\Repository')->willReturn(true); + $this->locator->method('get')->with('App\\Repository')->willReturn($repository); + $repository->method('findOneBy')->with(['id' => 'my_id'])->willReturn($stdClass); + + $response = $this->provider->provide($operation, new Context(new RequestOption($request))); + $this->assertSame($stdClass, $response); + } +} diff --git a/src/Component/tests/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php b/src/Component/tests/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php index 86b64c378..d5c307812 100644 --- a/src/Component/tests/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php +++ b/src/Component/tests/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php @@ -156,21 +156,18 @@ public function testItCreatesMultiResourcesMetadataWithOperations(): void $this->assertInstanceOf(Index::class, $operation); $this->assertSame('app_order_index', $operation->getName()); $this->assertSame(['GET'], $operation->getMethods()); - $this->assertSame('app.repository.order', $operation->getRepository()); $this->assertSame('App\Form\OrderType', $operation->getFormType()); $operation = $metadataCollection->getOperation('app.cart', 'app_cart_index'); $this->assertInstanceOf(Index::class, $operation); $this->assertSame('app_cart_index', $operation->getName()); $this->assertSame(['GET'], $operation->getMethods()); - $this->assertSame('app.repository.cart', $operation->getRepository()); $this->assertSame('App\Form\CartType', $operation->getFormType()); $operation = $metadataCollection->getOperation('app.cart', 'app_cart_show'); $this->assertInstanceOf(Show::class, $operation); $this->assertSame('app_cart_show', $operation->getName()); $this->assertSame(['GET'], $operation->getMethods()); - $this->assertSame('app.repository.cart', $operation->getRepository()); $this->assertSame('App\Form\CartType', $operation->getFormType()); } diff --git a/src/Component/tests/Metadata/Resource/Factory/ProviderResourceMetadataCollectionFactoryTest.php b/src/Component/tests/Metadata/Resource/Factory/ProviderResourceMetadataCollectionFactoryTest.php index ff76e9c57..6e777f28c 100644 --- a/src/Component/tests/Metadata/Resource/Factory/ProviderResourceMetadataCollectionFactoryTest.php +++ b/src/Component/tests/Metadata/Resource/Factory/ProviderResourceMetadataCollectionFactoryTest.php @@ -23,7 +23,6 @@ use Sylius\Resource\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; use Sylius\Resource\Metadata\Resource\ResourceMetadataCollection; use Sylius\Resource\Metadata\ResourceMetadata; -use Sylius\Resource\Symfony\Request\State\Provider; final class ProviderResourceMetadataCollectionFactoryTest extends TestCase { @@ -44,27 +43,6 @@ public function testItIsInitializable(): void $this->assertInstanceOf(ProviderResourceMetadataCollectionFactory::class, $this->factory); } - public function testItCreatesResourceMetadataWithDefaultProviderOnHttpOperations(): void - { - $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); - - $index = (new Index(name: 'app_book_index'))->withResource($resource); - - $resource = $resource->withOperations(new Operations([ - $index->getName() => $index, - ])); - - $resourceMetadataCollection = new ResourceMetadataCollection(); - $resourceMetadataCollection[] = $resource; - - $this->decorated->create('App\Resource')->willReturn($resourceMetadataCollection); - - $resourceMetadataCollection = $this->factory->create('App\Resource'); - - $index = $resourceMetadataCollection->getOperation('app.book', 'app_book_index'); - $this->assertSame(Provider::class, $index->getProvider()); - } - public function testItConfiguresRequestGridProviderIfOperationHasAGrid(): void { $resource = new ResourceMetadata(alias: 'app.book', name: 'book', applicationName: 'app'); diff --git a/tests/Application/config/services.yaml b/tests/Application/config/services.yaml index e688256a7..b41a36298 100644 --- a/tests/Application/config/services.yaml +++ b/tests/Application/config/services.yaml @@ -99,9 +99,6 @@ services: App\Subscription\: resource: '../src/Subscription' - App\Subscription\Factory\SubscriptionFactory: - decorates: 'app.factory.subscription' - app.service.legacy_autowired_repository: class: App\Service\LegacyAutowiredRepositoryService autowire: true diff --git a/tests/Application/src/Subscription/Entity/Subscription.php b/tests/Application/src/Subscription/Entity/Subscription.php index 1e2aa6e50..e721ece8e 100644 --- a/tests/Application/src/Subscription/Entity/Subscription.php +++ b/tests/Application/src/Subscription/Entity/Subscription.php @@ -13,7 +13,9 @@ namespace App\Subscription\Entity; +use App\Subscription\Factory\SubscriptionFactory; use App\Subscription\Form\Type\SubscriptionType; +use App\Subscription\Repository\SubscriptionRepository; use App\Subscription\Twig\Context\Factory\ShowSubscriptionContextFactory; use Doctrine\ORM\Mapping as ORM; use Sylius\Resource\Metadata\Api; @@ -35,9 +37,10 @@ formType: SubscriptionType::class, templatesDir: 'crud', routePrefix: '/admin', + driver: false, )] #[Index(grid: 'app_subscription')] -#[Create] +#[Create(factory: [SubscriptionFactory::class, 'createNew'])] #[Update] #[Delete] #[BulkDelete] @@ -59,6 +62,7 @@ routePrefix: '/ajax', normalizationContext: ['groups' => 'subscription:read'], denormalizationContext: ['groups' => 'subscription:write'], + driver: false, )] #[Api\GetCollection] #[Api\Post] @@ -66,7 +70,7 @@ #[Api\Delete] #[Api\Get] -#[ORM\Entity] +#[ORM\Entity(repositoryClass: SubscriptionRepository::class)] class Subscription implements ResourceInterface { #[ORM\Column(type: 'string')] diff --git a/tests/Application/src/Subscription/Factory/SubscriptionFactory.php b/tests/Application/src/Subscription/Factory/SubscriptionFactory.php index 01c35ffb1..230c3a7c1 100644 --- a/tests/Application/src/Subscription/Factory/SubscriptionFactory.php +++ b/tests/Application/src/Subscription/Factory/SubscriptionFactory.php @@ -14,11 +14,10 @@ namespace App\Subscription\Factory; use App\Subscription\Entity\Subscription; -use Sylius\Resource\Factory\FactoryInterface; -final class SubscriptionFactory implements FactoryInterface +final class SubscriptionFactory { - public function createNew(): Subscription + public static function createNew(): Subscription { return new Subscription(email: 'new@example.com'); } diff --git a/tests/Application/src/Subscription/Repository/SubscriptionRepository.php b/tests/Application/src/Subscription/Repository/SubscriptionRepository.php new file mode 100644 index 000000000..ca5b9ed45 --- /dev/null +++ b/tests/Application/src/Subscription/Repository/SubscriptionRepository.php @@ -0,0 +1,29 @@ +assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); - /** @var Subscription $subscription */ - $subscription = static::getContainer()->get('app.repository.subscription')->findOneBy(['email' => 'biff.tannen@bttf.com']); + $subscription = $this->getSubscriptionRepository()->findOneBy(['email' => 'biff.tannen@bttf.com']); $this->assertNotNull($subscription); $this->assertSame('biff.tannen@bttf.com', (string) $subscription->email); @@ -180,8 +181,7 @@ public function it_allows_deleting_a_subscription(): void $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); - /** @var Subscription[] $subscriptions */ - $subscriptions = static::getContainer()->get('app.repository.subscription')->findAll(); + $subscriptions = $this->getSubscriptionRepository()->findAll(); $this->assertEmpty($subscriptions); } @@ -196,8 +196,7 @@ public function it_allows_deleting_multiple_subscriptions(): void $this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND); - /** @var Subscription[] $subscriptions */ - $subscriptions = static::getContainer()->get('app.repository.subscription')->findAll(); + $subscriptions = $this->getSubscriptionRepository()->findAll(); $this->assertEmpty($subscriptions); } @@ -240,4 +239,12 @@ protected function buildMatcher(): Matcher { return $this->matcherFactory->createMatcher(new VoidBacktrace()); } + + /** + * @return ObjectRepository + */ + private function getSubscriptionRepository(): ObjectRepository + { + return static::getContainer()->get(EntityManagerInterface::class)->getRepository(Subscription::class); + } }