From 70cb272677f141ba7d1026fbdb0456c60aec6d07 Mon Sep 17 00:00:00 2001 From: kloz Date: Tue, 4 Jul 2023 17:46:23 +0200 Subject: [PATCH 01/19] [VSF2-244] Minor bugs fixed, found in migrations configuration and data generator --- .../ConsoleCommand/BulkDataGeneratorInterface.php | 2 +- .../Factory/Context/GeneratorContextFactory.php | 12 ++++++++---- .../ProductTaxonCollectionBulkGenerator.php | 4 ++++ .../WishlistProductCollectionBulkGenerator.php | 4 ++++ .../Generator/Entity/TaxonGenerator.php | 2 +- .../SimpleType/Integer/IntegerGenerator.php | 2 +- .../config/packages/doctrine_migrations.yaml | 2 +- 7 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/DataGenerator/ConsoleCommand/BulkDataGeneratorInterface.php b/src/DataGenerator/ConsoleCommand/BulkDataGeneratorInterface.php index 16df4b03..8f643585 100644 --- a/src/DataGenerator/ConsoleCommand/BulkDataGeneratorInterface.php +++ b/src/DataGenerator/ConsoleCommand/BulkDataGeneratorInterface.php @@ -14,7 +14,7 @@ interface BulkDataGeneratorInterface { const DEFAULT_TAXONS_QTY = 5000; - const DEFAULT_MAX_TAXON_LEVEL = 20; + const DEFAULT_MAX_TAXON_LEVEL = 15; const DEFAULT_MAX_CHILDREN_PER_TAXON_LEVEL = 5; diff --git a/src/DataGenerator/Factory/Context/GeneratorContextFactory.php b/src/DataGenerator/Factory/Context/GeneratorContextFactory.php index 30c5e1b7..799ffdd4 100644 --- a/src/DataGenerator/Factory/Context/GeneratorContextFactory.php +++ b/src/DataGenerator/Factory/Context/GeneratorContextFactory.php @@ -13,10 +13,14 @@ use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\DataGeneratorCommandContextInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\GeneratorContextInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\ProductGeneratorContext; +use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\ProductGeneratorContextInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\ProductTaxonGeneratorContext; +use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\ProductTaxonGeneratorContextInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\TaxonGeneratorContext; +use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\TaxonGeneratorContextInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\WishlistGeneratorContext; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\WishlistProductGeneratorContext; +use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\WishlistProductGeneratorContextInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Exception\UnknownBulkDataGeneratorException; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Generator\Bulk\BulkGeneratorInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Generator\Bulk\Collection\ProductTaxonCollectionBulkGeneratorInterface; @@ -53,7 +57,7 @@ public function fromCommandContext( private function productGeneratorContext( DataGeneratorCommandContextInterface $commandContext, - ): ProductGeneratorContext { + ): ProductGeneratorContextInterface { return new ProductGeneratorContext( $commandContext->getIO(), $commandContext->getProductsQty(), @@ -63,7 +67,7 @@ private function productGeneratorContext( private function taxonGeneratorContext( DataGeneratorCommandContextInterface $commandContext, - ): TaxonGeneratorContext { + ): TaxonGeneratorContextInterface { return new TaxonGeneratorContext( $commandContext->getIO(), $commandContext->getTaxonsQty(), @@ -84,7 +88,7 @@ private function wishlistGeneratorContext( private function productTaxonGeneratorContext( DataGeneratorCommandContextInterface $commandContext, - ): ProductTaxonGeneratorContext { + ): ProductTaxonGeneratorContextInterface { return new ProductTaxonGeneratorContext( $commandContext->getIO(), $commandContext->getProductsPerTaxonQty(), @@ -95,7 +99,7 @@ private function productTaxonGeneratorContext( private function wishlistProductGeneratorContext( DataGeneratorCommandContextInterface $commandContext, - ): WishlistProductGeneratorContext { + ): WishlistProductGeneratorContextInterface { return new WishlistProductGeneratorContext( $commandContext->getIO(), $commandContext->getProductsPerWishlistQty(), diff --git a/src/DataGenerator/Generator/Bulk/Collection/ProductTaxonCollectionBulkGenerator.php b/src/DataGenerator/Generator/Bulk/Collection/ProductTaxonCollectionBulkGenerator.php index 555d1d07..4c4ea037 100644 --- a/src/DataGenerator/Generator/Bulk/Collection/ProductTaxonCollectionBulkGenerator.php +++ b/src/DataGenerator/Generator/Bulk/Collection/ProductTaxonCollectionBulkGenerator.php @@ -37,6 +37,10 @@ public function generate(ContextInterface $context): void throw new InvalidContextException(); } + if ($context->getQuantity() === 0) { + return; + } + $io = $context->getIO(); $io->info(sprintf( diff --git a/src/DataGenerator/Generator/Bulk/Collection/WishlistProductCollectionBulkGenerator.php b/src/DataGenerator/Generator/Bulk/Collection/WishlistProductCollectionBulkGenerator.php index e5c17c36..b0c9119d 100644 --- a/src/DataGenerator/Generator/Bulk/Collection/WishlistProductCollectionBulkGenerator.php +++ b/src/DataGenerator/Generator/Bulk/Collection/WishlistProductCollectionBulkGenerator.php @@ -37,6 +37,10 @@ public function generate(ContextInterface $context): void throw new InvalidContextException(); } + if ($context->getQuantity() === 0) { + return; + } + $io = $context->getIO(); $io->info(sprintf( diff --git a/src/DataGenerator/Generator/Entity/TaxonGenerator.php b/src/DataGenerator/Generator/Entity/TaxonGenerator.php index e9826276..c9ca0c12 100644 --- a/src/DataGenerator/Generator/Entity/TaxonGenerator.php +++ b/src/DataGenerator/Generator/Entity/TaxonGenerator.php @@ -44,7 +44,7 @@ public function generate(GeneratorContextInterface $context): TaxonInterface } $translation = TaxonTranslationFactory::create( - $this->faker->sentence(3), + sprintf('%s %s', $this->faker->uuid, $this->faker->sentence(3)), $context::DEFAULT_LOCALE, ); diff --git a/src/DataGenerator/Generator/SimpleType/Integer/IntegerGenerator.php b/src/DataGenerator/Generator/SimpleType/Integer/IntegerGenerator.php index cefb4425..66de2e80 100644 --- a/src/DataGenerator/Generator/SimpleType/Integer/IntegerGenerator.php +++ b/src/DataGenerator/Generator/SimpleType/Integer/IntegerGenerator.php @@ -37,6 +37,6 @@ public function generateBiased( return $this->rand->rand($topValuesThreshold, $max); } - return $this->rand->rand($min, $topValuesThreshold - 1); + return $this->rand->rand($min, max(0, $topValuesThreshold - 1)); } } diff --git a/tests/Application/config/packages/doctrine_migrations.yaml b/tests/Application/config/packages/doctrine_migrations.yaml index 7ffde0f7..1d0a6140 100644 --- a/tests/Application/config/packages/doctrine_migrations.yaml +++ b/tests/Application/config/packages/doctrine_migrations.yaml @@ -4,4 +4,4 @@ doctrine_migrations: table_name: sylius_migrations migrations_paths: - 'migrations': '%kernel.project_dir%/../../migrations' + 'App\Migrations': '%kernel.project_dir%/../../migrations' From 7a5fc6c588367b24afe762d893d4134439f3ec50 Mon Sep 17 00:00:00 2001 From: kloz Date: Wed, 5 Jul 2023 10:05:34 +0200 Subject: [PATCH 02/19] [VSF2-244] PhpSpec tests fix --- .../ProductTaxonCollectionBulkGeneratorSpec.php | 9 +++++++++ .../WishlistProductCollectionBulkGeneratorSpec.php | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/spec/DataGenerator/Generator/Bulk/Collection/ProductTaxonCollectionBulkGeneratorSpec.php b/spec/DataGenerator/Generator/Bulk/Collection/ProductTaxonCollectionBulkGeneratorSpec.php index a5fa718d..70160f79 100644 --- a/spec/DataGenerator/Generator/Bulk/Collection/ProductTaxonCollectionBulkGeneratorSpec.php +++ b/spec/DataGenerator/Generator/Bulk/Collection/ProductTaxonCollectionBulkGeneratorSpec.php @@ -47,6 +47,7 @@ public function it_generates_product_taxon_collection( $offset = 0; $taxons = [$taxon1, $taxon2]; + $context->getQuantity()->willReturn(100); $context->getIO()->willReturn($io); $taxonRepository->getEntityCount()->willReturn($entityCount); @@ -70,6 +71,7 @@ public function it_does_nothing_if_no_taxons_found( $limit = 100; $offset = 0; + $context->getQuantity()->willReturn(100); $context->getIO()->willReturn($io); $taxonRepository->getEntityCount()->willReturn($entityCount); @@ -78,6 +80,13 @@ public function it_does_nothing_if_no_taxons_found( $this->generate($context); } + public function it_does_nothing_if_quantity_equals_to_zero(ProductTaxonGeneratorContextInterface $context,): void + { + $context->getQuantity()->willReturn(0)->shouldBeCalled(); + + $this->generate($context); + } + public function it_throws_exception_on_invalid_context(ContextInterface $context): void { $this->shouldThrow(InvalidContextException::class) diff --git a/spec/DataGenerator/Generator/Bulk/Collection/WishlistProductCollectionBulkGeneratorSpec.php b/spec/DataGenerator/Generator/Bulk/Collection/WishlistProductCollectionBulkGeneratorSpec.php index 0970ceb7..87dc1e93 100644 --- a/spec/DataGenerator/Generator/Bulk/Collection/WishlistProductCollectionBulkGeneratorSpec.php +++ b/spec/DataGenerator/Generator/Bulk/Collection/WishlistProductCollectionBulkGeneratorSpec.php @@ -49,6 +49,7 @@ public function it_generates_wishlist_product_collection( $offset = 0; $wishlists = [$wishlist1, $wishlist2]; + $context->getQuantity()->willReturn(100); $context->getIO()->willReturn($io); $context->getChannel()->willReturn($channel); $wishlistRepository->getEntityCount($channel)->willReturn($entityCount); @@ -74,6 +75,7 @@ public function it_does_nothing_if_no_wishlists_found( $limit = 100; $offset = 0; + $context->getQuantity()->willReturn(100); $context->getIO()->willReturn($io); $context->getChannel()->willReturn($channel); $wishlistRepository->getEntityCount($channel)->willReturn($entityCount); @@ -83,6 +85,13 @@ public function it_does_nothing_if_no_wishlists_found( $this->generate($context); } + public function it_does_nothing_if_quantity_equals_to_zero(WishlistProductGeneratorContextInterface $context): void + { + $context->getQuantity()->willReturn(0)->shouldBeCalled(); + + $this->generate($context); + } + public function it_throws_exception_on_invalid_context(ContextInterface $context): void { $this->shouldThrow(InvalidContextException::class) From 4ec1a3caac21d36dd19a11031881dee123f677d8 Mon Sep 17 00:00:00 2001 From: kloz Date: Wed, 5 Jul 2023 17:56:10 +0200 Subject: [PATCH 03/19] [VSF2-244] Data generator update - generate products with main taxon --- .../Factory/Entity/ProductFactorySpec.php | 3 +++ .../Generator/Entity/ProductGeneratorSpec.php | 17 +++++++++++----- .../BulkDataGeneratorInterface.php | 2 +- .../Doctrine/Repository/TaxonRepository.php | 20 +++++++++++++++++++ .../Repository/TaxonRepositoryInterface.php | 2 ++ .../Exception/NoTaxonFoundException.php | 17 ++++++++++++++++ .../Factory/Entity/ProductFactory.php | 3 +++ .../Entity/ProductFactoryInterface.php | 2 ++ .../Generator/Entity/ProductGenerator.php | 13 +++++++++--- .../Resources/services/generators.xml | 1 + .../DataGenerator/TaxonRepositoryTest.php | 12 +++++++++++ 11 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 src/DataGenerator/Exception/NoTaxonFoundException.php diff --git a/spec/DataGenerator/Factory/Entity/ProductFactorySpec.php b/spec/DataGenerator/Factory/Entity/ProductFactorySpec.php index cf0d17b9..a8fb88ed 100644 --- a/spec/DataGenerator/Factory/Entity/ProductFactorySpec.php +++ b/spec/DataGenerator/Factory/Entity/ProductFactorySpec.php @@ -17,6 +17,7 @@ use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductVariantInterface; +use Sylius\Component\Core\Model\TaxonInterface; use Sylius\Component\Resource\Factory\FactoryInterface; final class ProductFactorySpec extends ObjectBehavior @@ -36,6 +37,7 @@ public function it_creates_product( ProductVariantInterface $variant, ChannelInterface $channel, ProductInterface $product, + TaxonInterface $taxon, ): void { $productFactory->createNew()->willReturn($product); @@ -48,6 +50,7 @@ public function it_creates_product( $variant, $channel, new DateTime(), + $taxon, ) ->shouldReturn($product); } diff --git a/spec/DataGenerator/Generator/Entity/ProductGeneratorSpec.php b/spec/DataGenerator/Generator/Entity/ProductGeneratorSpec.php index 5e1a152a..6ec4afbd 100644 --- a/spec/DataGenerator/Generator/Entity/ProductGeneratorSpec.php +++ b/spec/DataGenerator/Generator/Entity/ProductGeneratorSpec.php @@ -12,6 +12,7 @@ use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\GeneratorContextInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\ProductGeneratorContextInterface; +use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Doctrine\Repository\TaxonRepositoryInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Exception\InvalidContextException; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Factory\Entity\ChannelPricingFactoryInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Factory\Entity\ProductFactoryInterface; @@ -24,6 +25,7 @@ use Sylius\Component\Core\Model\ChannelPricingInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductVariantInterface; +use Sylius\Component\Core\Model\TaxonInterface; final class ProductGeneratorSpec extends ObjectBehavior { @@ -31,8 +33,9 @@ public function let( ProductFactoryInterface $productFactory, ProductVariantFactoryInterface $productVariantFactory, ChannelPricingFactoryInterface $channelPricingFactory, + TaxonRepositoryInterface $taxonRepository, ): void { - $this->beConstructedWith($productFactory, $productVariantFactory, $channelPricingFactory); + $this->beConstructedWith($productFactory, $productVariantFactory, $channelPricingFactory, $taxonRepository); } public function it_is_initializable(): void @@ -49,6 +52,8 @@ public function it_generates_product( ProductVariantInterface $productVariant, ProductGeneratorContextInterface $context, ProductInterface $product, + TaxonRepositoryInterface $taxonRepository, + TaxonInterface $taxon, ): void { $context->getChannel()->willReturn($channel); $channel->getCode()->willReturn(Argument::type('string')); @@ -61,11 +66,12 @@ public function it_generates_product( ->create( Argument::type('string'), Argument::type('string'), - $channelPricing->getWrappedObject() + $channelPricing ) ->willReturn($productVariant); - $context->getChannel()->willReturn($channel->getWrappedObject()); + $context->getChannel()->willReturn($channel); + $taxonRepository->getRandomTaxon()->willReturn($taxon); $productFactory ->create( @@ -73,9 +79,10 @@ public function it_generates_product( Argument::type('string'), Argument::type('string'), Argument::type('string'), - $productVariant->getWrappedObject(), - $channel->getWrappedObject(), + $productVariant, + $channel, Argument::type(DateTime::class), + $taxon, ) ->willReturn($product); diff --git a/src/DataGenerator/ConsoleCommand/BulkDataGeneratorInterface.php b/src/DataGenerator/ConsoleCommand/BulkDataGeneratorInterface.php index 8f643585..e492953e 100644 --- a/src/DataGenerator/ConsoleCommand/BulkDataGeneratorInterface.php +++ b/src/DataGenerator/ConsoleCommand/BulkDataGeneratorInterface.php @@ -14,7 +14,7 @@ interface BulkDataGeneratorInterface { const DEFAULT_TAXONS_QTY = 5000; - const DEFAULT_MAX_TAXON_LEVEL = 15; + const DEFAULT_MAX_TAXON_LEVEL = 14; const DEFAULT_MAX_CHILDREN_PER_TAXON_LEVEL = 5; diff --git a/src/DataGenerator/Doctrine/Repository/TaxonRepository.php b/src/DataGenerator/Doctrine/Repository/TaxonRepository.php index 1f5e8828..e654625b 100644 --- a/src/DataGenerator/Doctrine/Repository/TaxonRepository.php +++ b/src/DataGenerator/Doctrine/Repository/TaxonRepository.php @@ -10,6 +10,7 @@ namespace BitBag\SyliusVueStorefront2Plugin\DataGenerator\Doctrine\Repository; +use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Exception\NoTaxonFoundException; use Sylius\Bundle\TaxonomyBundle\Doctrine\ORM\TaxonRepository as BaseTaxonRepository; use Sylius\Component\Core\Model\TaxonInterface; @@ -63,8 +64,27 @@ public function findBatch( public function getEntityCount(): int { $queryBuilder = $this->createQueryBuilder('taxon') + ->andWhere('taxon.enabled = true') ->select('COUNT(taxon)'); return (int)$queryBuilder->getQuery()->getSingleScalarResult(); } + + public function getRandomTaxon(): TaxonInterface + { + $randomOffset = max(0, rand(0, $this->getEntityCount() - 1)); + + $result = $this->createQueryBuilder('taxon') + ->where('taxon.enabled = true') + ->setFirstResult($randomOffset) + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult(); + + if ($result instanceof TaxonInterface) { + return $result; + } + + throw new NoTaxonFoundException(); + } } diff --git a/src/DataGenerator/Doctrine/Repository/TaxonRepositoryInterface.php b/src/DataGenerator/Doctrine/Repository/TaxonRepositoryInterface.php index bcb87e59..c89294f0 100644 --- a/src/DataGenerator/Doctrine/Repository/TaxonRepositoryInterface.php +++ b/src/DataGenerator/Doctrine/Repository/TaxonRepositoryInterface.php @@ -36,4 +36,6 @@ public function findBatch( ): array; public function getEntityCount(): int; + + public function getRandomTaxon(): TaxonInterface; } diff --git a/src/DataGenerator/Exception/NoTaxonFoundException.php b/src/DataGenerator/Exception/NoTaxonFoundException.php new file mode 100644 index 00000000..577d62e4 --- /dev/null +++ b/src/DataGenerator/Exception/NoTaxonFoundException.php @@ -0,0 +1,17 @@ +productFactory->createNew(); @@ -46,6 +48,7 @@ public function create( $product->setCreatedAt($createdAt); $product->addChannel($channel); $product->addVariant($variant); + $product->setMainTaxon($mainTaxon); return $product; } diff --git a/src/DataGenerator/Factory/Entity/ProductFactoryInterface.php b/src/DataGenerator/Factory/Entity/ProductFactoryInterface.php index ed10713e..3bc118e1 100644 --- a/src/DataGenerator/Factory/Entity/ProductFactoryInterface.php +++ b/src/DataGenerator/Factory/Entity/ProductFactoryInterface.php @@ -14,6 +14,7 @@ use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductVariantInterface; +use Sylius\Component\Core\Model\TaxonInterface; interface ProductFactoryInterface { @@ -25,5 +26,6 @@ public function create( ProductVariantInterface $variant, ChannelInterface $channel, DateTimeInterface $createdAt, + TaxonInterface $mainTaxon, ): ProductInterface; } diff --git a/src/DataGenerator/Generator/Entity/ProductGenerator.php b/src/DataGenerator/Generator/Entity/ProductGenerator.php index e52457e1..77d44bf3 100644 --- a/src/DataGenerator/Generator/Entity/ProductGenerator.php +++ b/src/DataGenerator/Generator/Entity/ProductGenerator.php @@ -12,6 +12,7 @@ use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\GeneratorContextInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\ContextModel\Generator\ProductGeneratorContextInterface; +use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Doctrine\Repository\TaxonRepositoryInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Exception\InvalidContextException; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Factory\Entity\ChannelPricingFactoryInterface; use BitBag\SyliusVueStorefront2Plugin\DataGenerator\Factory\Entity\ProductFactoryInterface; @@ -28,16 +29,20 @@ final class ProductGenerator implements GeneratorInterface private ChannelPricingFactoryInterface $channelPricingFactory; + private TaxonRepositoryInterface $taxonRepository; + private Generator $faker; public function __construct( ProductFactoryInterface $productFactory, ProductVariantFactoryInterface $productVariantFactory, ChannelPricingFactoryInterface $channelPricingFactory, + TaxonRepositoryInterface $taxonRepository, ) { $this->productFactory = $productFactory; $this->productVariantFactory = $productVariantFactory; $this->channelPricingFactory = $channelPricingFactory; + $this->taxonRepository = $taxonRepository; $this->faker = Factory::create(); } @@ -47,9 +52,10 @@ public function generate(GeneratorContextInterface $context): ProductInterface throw new InvalidContextException(); } + $channel = $context->getChannel(); $channelPricing = $this->channelPricingFactory->create( $this->faker->randomNumber(), - $context->getChannel(), + $channel, ); $uuid = $this->faker->uuid; @@ -65,8 +71,9 @@ public function generate(GeneratorContextInterface $context): ProductInterface $this->faker->sentence(15), $this->faker->sentence(), $variant, - $context->getChannel(), - $this->faker->dateTimeBetween('-1 year') + $channel, + $this->faker->dateTimeBetween('-1 year'), + $this->taxonRepository->getRandomTaxon(), ); } } diff --git a/src/DataGenerator/Resources/services/generators.xml b/src/DataGenerator/Resources/services/generators.xml index e866107b..af9b117a 100644 --- a/src/DataGenerator/Resources/services/generators.xml +++ b/src/DataGenerator/Resources/services/generators.xml @@ -18,6 +18,7 @@ + assertCount(0, $taxons); } + public function test_getting_random_shop_user(): void + { + $this->loadFixtures(); + + $repository = $this->getContainer() + ->get('bitbag.sylius_vue_storefront2_plugin.data_generator.repository.taxon_repository'); + + $taxon = $repository->getRandomTaxon(); + $this->assertInstanceOf(TaxonInterface::class, $taxon); + } + public function test_getting_entity_count(): void { $this->loadFixtures(); From 9613eb5d86af48ef5bd2415cdf2f89b491a903c0 Mon Sep 17 00:00:00 2001 From: kloz Date: Mon, 10 Jul 2023 17:32:50 +0200 Subject: [PATCH 04/19] [VSF2-244] Subresources data provider decorated, composite/strategy approach to retrieve products --- .../CachedCollectionDataProvider.php | 28 +++++++++++ .../CachedCollectionDataProviderInterface.php | 13 +++++ .../CompositeSubresourceDataProvider.php | 35 ++++++++++++++ .../ProductImageSubresourceDataProvider.php | 47 +++++++++++++++++++ ...rictedSubresourceDataProviderInterface.php | 16 +++++++ .../ProductQueryCollectionExtension.php | 30 ++++++++++++ .../AttachDefaultWishlistEventListener.php | 3 +- src/Resources/services/data_providers.xml | 32 +++++++++++++ src/Resources/services/extension.xml | 8 ++++ 9 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 src/DataProvider/CachedCollectionDataProvider.php create mode 100644 src/DataProvider/CachedCollectionDataProviderInterface.php create mode 100644 src/DataProvider/CompositeSubresourceDataProvider.php create mode 100644 src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php create mode 100644 src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php create mode 100644 src/Doctrine/Orm/Extension/ProductQueryCollectionExtension.php diff --git a/src/DataProvider/CachedCollectionDataProvider.php b/src/DataProvider/CachedCollectionDataProvider.php new file mode 100644 index 00000000..2640d14c --- /dev/null +++ b/src/DataProvider/CachedCollectionDataProvider.php @@ -0,0 +1,28 @@ +cachedData[] = $item; + } + + return $collection; + } + + public function getCachedData(): array + { + return $this->cachedData; + } +} diff --git a/src/DataProvider/CachedCollectionDataProviderInterface.php b/src/DataProvider/CachedCollectionDataProviderInterface.php new file mode 100644 index 00000000..434e856b --- /dev/null +++ b/src/DataProvider/CachedCollectionDataProviderInterface.php @@ -0,0 +1,13 @@ +subresourceProviders, RestrictedSubresourceDataProviderInterface::class); + } + + public function getSubresource( + string $resourceClass, + array $identifiers, + array $context, + string $operationName = null + ): iterable|object|null { + /** @var RestrictedSubresourceDataProviderInterface $provider */ + foreach ($this->subresourceProviders as $provider) { + if ($provider->supports($resourceClass, $context, $operationName)) { + return $provider->getSubresource($resourceClass, $identifiers, $context, $operationName); + } + } + + return $this->decoratedSubresourceProvider->getSubresource($resourceClass, $identifiers, $context, $operationName); + } +} diff --git a/src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php b/src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php new file mode 100644 index 00000000..ac9daa0e --- /dev/null +++ b/src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php @@ -0,0 +1,47 @@ +cachedCollectionDataProvider->getCachedData(); + + Assert::keyExists($identifiers, 'code'); + + $product = null; + + foreach ($data as $datum) { + if ($identifiers['code'] === $datum->getCode()) { + $product = $datum; + + break; + } + } + + if ($product !== null) { + return new ArrayPaginator($product->getImages()->toArray(), 0, $product->getImages()->count()); + } + + return new ArrayPaginator([], 0, 0); + } +} diff --git a/src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php b/src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php new file mode 100644 index 00000000..2baedd65 --- /dev/null +++ b/src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php @@ -0,0 +1,16 @@ +addSelect('oi') + ->addSelect('ovs') + ->addSelect('oav') + ->join('o.variants', 'ovs') + ->leftJoin('o.attributes', 'oav') + ->leftJoin('o.images', 'oi'); + } + } +} diff --git a/src/EventListener/AttachDefaultWishlistEventListener.php b/src/EventListener/AttachDefaultWishlistEventListener.php index 2858882b..13c2d9b9 100644 --- a/src/EventListener/AttachDefaultWishlistEventListener.php +++ b/src/EventListener/AttachDefaultWishlistEventListener.php @@ -12,7 +12,6 @@ use BitBag\SyliusVueStorefront2Plugin\Model\ShopUserTokenInterface; use BitBag\SyliusWishlistPlugin\Factory\WishlistFactoryInterface; -use BitBag\SyliusWishlistPlugin\Repository\WishlistRepository; use BitBag\SyliusWishlistPlugin\Repository\WishlistRepositoryInterface; use BitBag\SyliusWishlistPlugin\Resolver\WishlistCookieTokenResolverInterface; use Sylius\Component\Channel\Context\ChannelContextInterface; @@ -27,7 +26,7 @@ public function __construct( private WishlistRepositoryInterface $wishlistRepository, private WishlistFactoryInterface $wishlistFactory, private WishlistCookieTokenResolverInterface $wishlistCookieTokenResolver, - private ChannelContextInterface $channelContext + private ChannelContextInterface $channelContext, ) { } diff --git a/src/Resources/services/data_providers.xml b/src/Resources/services/data_providers.xml index 55471bda..f008b16a 100644 --- a/src/Resources/services/data_providers.xml +++ b/src/Resources/services/data_providers.xml @@ -40,5 +40,37 @@ We are hiring developers from all over the world. Join us and start your new, ex + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/services/extension.xml b/src/Resources/services/extension.xml index 18226f83..1344b27b 100644 --- a/src/Resources/services/extension.xml +++ b/src/Resources/services/extension.xml @@ -18,5 +18,13 @@ We are hiring developers from all over the world. Join us and start your new, ex > + + + + From 0fc6f29ece6e7b4453778b181fec14b6e5048d45 Mon Sep 17 00:00:00 2001 From: kloz Date: Tue, 11 Jul 2023 10:09:30 +0200 Subject: [PATCH 05/19] [VSF2-244] Missing service removed --- src/Resources/services/data_providers.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Resources/services/data_providers.xml b/src/Resources/services/data_providers.xml index f008b16a..bafdc6c1 100644 --- a/src/Resources/services/data_providers.xml +++ b/src/Resources/services/data_providers.xml @@ -66,11 +66,5 @@ We are hiring developers from all over the world. Join us and start your new, ex - - - - - From 6ebb63fc843fbf430fb9832d78f3ee1d7c30782f Mon Sep 17 00:00:00 2001 From: kloz Date: Tue, 11 Jul 2023 10:18:45 +0200 Subject: [PATCH 06/19] [VSF2-244] Minor styling fixes --- src/DataProvider/CachedCollectionDataProvider.php | 7 +++++-- .../ProductImageSubresourceDataProvider.php | 15 +++++++++++---- src/Resolver/Mutation/RefreshTokenResolver.php | 4 +--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/DataProvider/CachedCollectionDataProvider.php b/src/DataProvider/CachedCollectionDataProvider.php index 2640d14c..94e66e87 100644 --- a/src/DataProvider/CachedCollectionDataProvider.php +++ b/src/DataProvider/CachedCollectionDataProvider.php @@ -10,8 +10,11 @@ final class CachedCollectionDataProvider extends CollectionDataProvider implemen { private array $cachedData = []; - public function getCollection(string $resourceClass, string $operationName = null, array $context = []): iterable - { + public function getCollection( + string $resourceClass, + string $operationName = null, + array $context = [] + ): iterable { $collection = parent::getCollection($resourceClass, $operationName, $context); foreach ($collection as $item) { diff --git a/src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php b/src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php index ac9daa0e..7dcc54c5 100644 --- a/src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php +++ b/src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php @@ -16,13 +16,20 @@ public function __construct(private CachedCollectionDataProviderInterface $cache { } - public function supports(string $resourceClass, array $context, string $operationName = null): bool - { + public function supports( + string $resourceClass, + array $context, + string $operationName = null + ): bool { return is_a($resourceClass, ProductImageInterface::class, true); } - public function getSubresource(string $resourceClass, array $identifiers, array $context, string $operationName = null): ArrayPaginator - { + public function getSubresource( + string $resourceClass, + array $identifiers, + array $context, + string $operationName = null + ): ArrayPaginator { /** @var ProductInterface[] $data */ $data = $this->cachedCollectionDataProvider->getCachedData(); diff --git a/src/Resolver/Mutation/RefreshTokenResolver.php b/src/Resolver/Mutation/RefreshTokenResolver.php index dc78bb9f..dcf9d58c 100644 --- a/src/Resolver/Mutation/RefreshTokenResolver.php +++ b/src/Resolver/Mutation/RefreshTokenResolver.php @@ -74,11 +74,9 @@ public function __invoke($item, array $context): ?ShopUserTokenInterface Assert::keyExists($input, 'refreshToken'); $refreshTokenString = (string) $input['refreshToken']; + /** @var RefreshTokenInterface|null $refreshToken */ $refreshToken = $this->refreshTokenRepository->findOneBy(['refreshToken' => $refreshTokenString]); - Assert::notNull($refreshToken); - - /** @var RefreshTokenInterface $refreshToken */ $this->validateRefreshToken($refreshToken, $refreshTokenString); /** @var ShopUserInterface $user */ From 373f4a1ed9da0b6b1dbd67b327e8bb11ac3f1c0d Mon Sep 17 00:00:00 2001 From: kloz Date: Wed, 12 Jul 2023 17:25:13 +0200 Subject: [PATCH 07/19] [VSF2-244] New approach to providing subresources data [images] --- .../CachedCollectionDataProvider.php | 37 ++++++++-- .../CachedCollectionDataProviderInterface.php | 11 ++- .../CompositeSubresourceDataProvider.php | 6 ++ .../PreFetcher/CompositePreFetcher.php | 74 +++++++++++++++++++ .../PreFetcher/PreFetcherInterface.php | 24 ++++++ .../Product/ProductImagePreFetcher.php | 54 ++++++++++++++ .../RestrictedPreFetcherInterface.php | 19 +++++ .../ProductImageSubresourceDataProvider.php | 30 +++----- ...rictedSubresourceDataProviderInterface.php | 6 ++ .../ProductQueryCollectionExtension.php | 30 -------- .../Repository/ProductImageRepository.php | 45 +++++++++++ .../ProductImageRepositoryInterface.php | 19 +++++ src/Resources/services/data_providers.xml | 16 ++++ src/Resources/services/doctrine_orm.xml | 8 ++ src/Resources/services/extension.xml | 8 -- 15 files changed, 321 insertions(+), 66 deletions(-) create mode 100644 src/DataProvider/PreFetcher/CompositePreFetcher.php create mode 100644 src/DataProvider/PreFetcher/PreFetcherInterface.php create mode 100644 src/DataProvider/PreFetcher/Product/ProductImagePreFetcher.php create mode 100644 src/DataProvider/PreFetcher/RestrictedPreFetcherInterface.php delete mode 100644 src/Doctrine/Orm/Extension/ProductQueryCollectionExtension.php create mode 100644 src/Doctrine/Repository/ProductImageRepository.php create mode 100644 src/Doctrine/Repository/ProductImageRepositoryInterface.php diff --git a/src/DataProvider/CachedCollectionDataProvider.php b/src/DataProvider/CachedCollectionDataProvider.php index 94e66e87..9fbbaeb2 100644 --- a/src/DataProvider/CachedCollectionDataProvider.php +++ b/src/DataProvider/CachedCollectionDataProvider.php @@ -1,31 +1,52 @@ compositePreFetcher = $preFetchedDataProvider; + } public function getCollection( string $resourceClass, string $operationName = null, - array $context = [] + array $context = [], ): iterable { $collection = parent::getCollection($resourceClass, $operationName, $context); + $ids = array_map( + static fn ($object) => $object->getId(), + (array)$collection->getIterator(), + ); - foreach ($collection as $item) { - $this->cachedData[] = $item; - } + $this->compositePreFetcher->preFetchData($ids, $context); return $collection; } - public function getCachedData(): array - { - return $this->cachedData; + public function getCachedData( + string $identifier, + array $context, + ): array { + return $this->compositePreFetcher->getPreFetchedData($identifier, $context); } } diff --git a/src/DataProvider/CachedCollectionDataProviderInterface.php b/src/DataProvider/CachedCollectionDataProviderInterface.php index 434e856b..2f4374a3 100644 --- a/src/DataProvider/CachedCollectionDataProviderInterface.php +++ b/src/DataProvider/CachedCollectionDataProviderInterface.php @@ -1,5 +1,11 @@ preFetchers = $preFetchers; + } + + public function preFetchData( + array $parentIds, + array $context, + ): void { + $attributes = $this->gatherAttributesToPreFetch($context['attributes']); + + foreach ($attributes as $attribute => $fieldsCollection) { + /** @var RestrictedPreFetcherInterface $preFetcher */ + foreach ($this->preFetchers as $preFetcher) { + if ($preFetcher->supports($context, $attribute)) { + $preFetcher->preFetchData($parentIds, $context); + } + } + } + } + + public function getPreFetchedData( + string $identifier, + ?array $context = [], + ): array { + foreach ($this->preFetchers as $preFetcher) { + if ($preFetcher->supports($context)) { + return $preFetcher->getPreFetchedData($identifier); + } + } + + return []; + } + + private function gatherAttributesToPreFetch(array $attributes): array + { + $attributes = array_filter( + $attributes, + static fn($attr) => is_array($attr['collection'] ?? null) + ); + + foreach ($attributes as $attribute => $fields) { + $nestedAttributes = $this->gatherAttributesToPreFetch($fields['collection']); + if (count($nestedAttributes) > 0) { + foreach (array_keys($nestedAttributes) as $nestedAttribute) { + unset($attributes[$attribute]['collection'][$nestedAttribute]); + } + } + + $attributes = array_merge($attributes, $nestedAttributes); + } + + return $attributes; + } +} diff --git a/src/DataProvider/PreFetcher/PreFetcherInterface.php b/src/DataProvider/PreFetcher/PreFetcherInterface.php new file mode 100644 index 00000000..7fd29d70 --- /dev/null +++ b/src/DataProvider/PreFetcher/PreFetcherInterface.php @@ -0,0 +1,24 @@ +repository = $repository; + } + + public function preFetchData( + array $parentIds, + array $context, + ): void { + foreach ($this->repository->findByProductIds($parentIds, $context) as $result) { + $this->preFetchedData[$result['code']][] = $result; + } + } + + public function getPreFetchedData( + string $identifier, + ?array $context = [], + ): array { + return $this->preFetchedData[$identifier] ?? []; + } + + public function supports( + array $context, + ?string $attribute = null, + ): bool { + $resourceClass = $context['resource_class']; + + return is_a($resourceClass, ProductImageInterface::class, true) + || ($attribute === 'images' && is_a($resourceClass, ProductInterface::class, true)); + } +} diff --git a/src/DataProvider/PreFetcher/RestrictedPreFetcherInterface.php b/src/DataProvider/PreFetcher/RestrictedPreFetcherInterface.php new file mode 100644 index 00000000..34116a91 --- /dev/null +++ b/src/DataProvider/PreFetcher/RestrictedPreFetcherInterface.php @@ -0,0 +1,19 @@ +cachedCollectionDataProvider->getCachedData(); - Assert::keyExists($identifiers, 'code'); - $product = null; - - foreach ($data as $datum) { - if ($identifiers['code'] === $datum->getCode()) { - $product = $datum; - - break; - } - } - - if ($product !== null) { - return new ArrayPaginator($product->getImages()->toArray(), 0, $product->getImages()->count()); - } + /** @var ProductInterface[] $data */ + $data = $this->cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); - return new ArrayPaginator([], 0, 0); + return new ArrayPaginator($data, 0, count($data)); } } diff --git a/src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php b/src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php index 2baedd65..e095a0df 100644 --- a/src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php +++ b/src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php @@ -1,5 +1,11 @@ addSelect('oi') - ->addSelect('ovs') - ->addSelect('oav') - ->join('o.variants', 'ovs') - ->leftJoin('o.attributes', 'oav') - ->leftJoin('o.images', 'oi'); - } - } -} diff --git a/src/Doctrine/Repository/ProductImageRepository.php b/src/Doctrine/Repository/ProductImageRepository.php new file mode 100644 index 00000000..423b7f25 --- /dev/null +++ b/src/Doctrine/Repository/ProductImageRepository.php @@ -0,0 +1,45 @@ +entityManager = $entityManager; + } + + public function findByProductIds( + array $productIds, + array $context, + ): array { + $fields = array_map( + static fn(string $field) => 'image.' . $field, + array_keys($context['attributes']['images']['collection'] ?? []), + ); + $fields = implode(', ', $fields); + + return $this->entityManager->createQueryBuilder() + ->from(ProductImageInterface::class, 'image') + ->join('image.owner', 'product') + ->select($fields) + ->addSelect('product.code') + ->andWhere('product.id IN (:productIds)') + ->setParameter('productIds', $productIds) + ->getQuery() + ->getResult(); + } +} diff --git a/src/Doctrine/Repository/ProductImageRepositoryInterface.php b/src/Doctrine/Repository/ProductImageRepositoryInterface.php new file mode 100644 index 00000000..bc5dfad9 --- /dev/null +++ b/src/Doctrine/Repository/ProductImageRepositoryInterface.php @@ -0,0 +1,19 @@ + + @@ -59,6 +60,21 @@ We are hiring developers from all over the world. Join us and start your new, ex + + + + + + + + + + @@ -30,9 +31,16 @@ We are hiring developers from all over the world. Join us and start your new, ex + + + + + + diff --git a/src/Resources/services/extension.xml b/src/Resources/services/extension.xml index 1344b27b..18226f83 100644 --- a/src/Resources/services/extension.xml +++ b/src/Resources/services/extension.xml @@ -18,13 +18,5 @@ We are hiring developers from all over the world. Join us and start your new, ex > - - - - From b42797a803abea2d2f4dc77da8131714391bce27 Mon Sep 17 00:00:00 2001 From: kloz Date: Wed, 12 Jul 2023 17:37:29 +0200 Subject: [PATCH 08/19] [VSF2-244] Phpstan fix --- src/DataProvider/CachedCollectionDataProvider.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/DataProvider/CachedCollectionDataProvider.php b/src/DataProvider/CachedCollectionDataProvider.php index 9fbbaeb2..7bcb0db4 100644 --- a/src/DataProvider/CachedCollectionDataProvider.php +++ b/src/DataProvider/CachedCollectionDataProvider.php @@ -13,6 +13,7 @@ use ApiPlatform\Core\Bridge\Doctrine\Orm\CollectionDataProvider; use BitBag\SyliusVueStorefront2Plugin\DataProvider\PreFetcher\PreFetcherInterface; use Doctrine\Persistence\ManagerRegistry; +use Sylius\Component\Resource\Model\ResourceInterface; final class CachedCollectionDataProvider extends CollectionDataProvider implements CachedCollectionDataProviderInterface { @@ -33,10 +34,12 @@ public function getCollection( array $context = [], ): iterable { $collection = parent::getCollection($resourceClass, $operationName, $context); - $ids = array_map( - static fn ($object) => $object->getId(), - (array)$collection->getIterator(), - ); + + $ids = []; + /** @var ResourceInterface $item */ + foreach ($collection as $item) { + $ids[] = $item->getId(); + } $this->compositePreFetcher->preFetchData($ids, $context); From 63134540a55980d39b610856c7caaf0eae0f2c59 Mon Sep 17 00:00:00 2001 From: kloz Date: Thu, 13 Jul 2023 18:32:22 +0200 Subject: [PATCH 09/19] [VSF2-244] Optimize product attributes subresource --- .../PreFetcher/CompositePreFetcher.php | 2 +- .../Product/ProductAttributePreFetcher.php | 55 +++++++++++++++++++ .../Product/ProductImagePreFetcher.php | 2 +- ...roductAttributeSubresourceDataProvider.php | 46 ++++++++++++++++ .../ProductAttributeValueRepository.php | 53 ++++++++++++++++++ ...oductAttributeValueRepositoryInterface.php | 21 +++++++ .../{ => Product}/ProductImageRepository.php | 2 +- .../ProductImageRepositoryInterface.php | 2 +- src/Resources/services/data_providers.xml | 16 ++++++ src/Resources/services/doctrine_orm.xml | 8 ++- 10 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 src/DataProvider/PreFetcher/Product/ProductAttributePreFetcher.php create mode 100644 src/DataProvider/Subresource/ProductAttributeSubresourceDataProvider.php create mode 100644 src/Doctrine/Repository/Product/ProductAttributeValueRepository.php create mode 100644 src/Doctrine/Repository/Product/ProductAttributeValueRepositoryInterface.php rename src/Doctrine/Repository/{ => Product}/ProductImageRepository.php (95%) rename src/Doctrine/Repository/{ => Product}/ProductImageRepositoryInterface.php (87%) diff --git a/src/DataProvider/PreFetcher/CompositePreFetcher.php b/src/DataProvider/PreFetcher/CompositePreFetcher.php index 82dfa4f4..d3d1a7fa 100644 --- a/src/DataProvider/PreFetcher/CompositePreFetcher.php +++ b/src/DataProvider/PreFetcher/CompositePreFetcher.php @@ -28,7 +28,7 @@ public function preFetchData( ): void { $attributes = $this->gatherAttributesToPreFetch($context['attributes']); - foreach ($attributes as $attribute => $fieldsCollection) { + foreach (array_keys($attributes) as $attribute) { /** @var RestrictedPreFetcherInterface $preFetcher */ foreach ($this->preFetchers as $preFetcher) { if ($preFetcher->supports($context, $attribute)) { diff --git a/src/DataProvider/PreFetcher/Product/ProductAttributePreFetcher.php b/src/DataProvider/PreFetcher/Product/ProductAttributePreFetcher.php new file mode 100644 index 00000000..ad7174c5 --- /dev/null +++ b/src/DataProvider/PreFetcher/Product/ProductAttributePreFetcher.php @@ -0,0 +1,55 @@ +repository = $repository; + } + + public function preFetchData( + array $parentIds, + array $context, + ): void { + /** @var ProductAttributeValueInterface $result */ + foreach ($this->repository->findByProductIds($parentIds, $context) as $result) { + $this->preFetchedData[$result->getProduct()->getCode()][] = $result; + } + } + + public function getPreFetchedData( + string $identifier, + ?array $context = [], + ): array { + return $this->preFetchedData[$identifier] ?? []; + } + + public function supports( + array $context, + ?string $attribute = null, + ): bool { + $resourceClass = $context['resource_class']; + + return is_a($resourceClass, ProductAttributeValueInterface::class, true) + || ($attribute === 'attributes' && is_a($resourceClass, ProductInterface::class, true)); + } +} diff --git a/src/DataProvider/PreFetcher/Product/ProductImagePreFetcher.php b/src/DataProvider/PreFetcher/Product/ProductImagePreFetcher.php index 7761a229..5c611d3b 100644 --- a/src/DataProvider/PreFetcher/Product/ProductImagePreFetcher.php +++ b/src/DataProvider/PreFetcher/Product/ProductImagePreFetcher.php @@ -11,7 +11,7 @@ namespace BitBag\SyliusVueStorefront2Plugin\DataProvider\PreFetcher\Product; use BitBag\SyliusVueStorefront2Plugin\DataProvider\PreFetcher\RestrictedPreFetcherInterface; -use BitBag\SyliusVueStorefront2Plugin\Doctrine\Repository\ProductImageRepositoryInterface; +use BitBag\SyliusVueStorefront2Plugin\Doctrine\Repository\Product\ProductImageRepositoryInterface; use Sylius\Component\Core\Model\ProductImageInterface; use Sylius\Component\Core\Model\ProductInterface; diff --git a/src/DataProvider/Subresource/ProductAttributeSubresourceDataProvider.php b/src/DataProvider/Subresource/ProductAttributeSubresourceDataProvider.php new file mode 100644 index 00000000..b74558b8 --- /dev/null +++ b/src/DataProvider/Subresource/ProductAttributeSubresourceDataProvider.php @@ -0,0 +1,46 @@ +cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); + + return new ArrayPaginator($data, 0, count($data)); + } +} diff --git a/src/Doctrine/Repository/Product/ProductAttributeValueRepository.php b/src/Doctrine/Repository/Product/ProductAttributeValueRepository.php new file mode 100644 index 00000000..bc6d4558 --- /dev/null +++ b/src/Doctrine/Repository/Product/ProductAttributeValueRepository.php @@ -0,0 +1,53 @@ +getEntityManager(), $decoratedRepository->getClassMetadata()); + $this->decoratedRepository = $decoratedRepository; + } + + public function findByProductIds( + array $productIds, + array $context, + ): array { + $locale = $context[ContextKeys::LOCALE_CODE] ?? 'en_US'; + + return $this->decoratedRepository->createQueryBuilder('value') + ->leftJoin('value.attribute', 'attribute') + ->leftJoin('attribute.translations', 'translation') + ->leftJoin('value.subject', 'product') + ->addSelect('attribute') + ->addSelect('translation') + ->andWhere('product.id IN (:productIds)') + ->andWhere('translation.locale = :locale') + ->setParameter('productIds', $productIds) + ->setParameter('locale', $locale) + ->getQuery() + ->getResult(); + } + + public function findByJsonChoiceKey(string $choiceKey): array + { + return $this->decoratedRepository->findByJsonChoiceKey($choiceKey); + } +} diff --git a/src/Doctrine/Repository/Product/ProductAttributeValueRepositoryInterface.php b/src/Doctrine/Repository/Product/ProductAttributeValueRepositoryInterface.php new file mode 100644 index 00000000..ec418854 --- /dev/null +++ b/src/Doctrine/Repository/Product/ProductAttributeValueRepositoryInterface.php @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/src/Resources/services/doctrine_orm.xml b/src/Resources/services/doctrine_orm.xml index a15fdd94..33758c19 100644 --- a/src/Resources/services/doctrine_orm.xml +++ b/src/Resources/services/doctrine_orm.xml @@ -38,9 +38,15 @@ We are hiring developers from all over the world. Join us and start your new, ex + class="BitBag\SyliusVueStorefront2Plugin\Doctrine\Repository\Product\ProductImageRepository" > + + + + From 44eec25843e5c04682ead18814112323854b53a3 Mon Sep 17 00:00:00 2001 From: kloz Date: Thu, 13 Jul 2023 18:48:57 +0200 Subject: [PATCH 10/19] [VSF2-244] Phpstan fix --- .../PreFetcher/Product/ProductAttributePreFetcher.php | 2 +- .../Repository/Product/ProductAttributeValueRepository.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataProvider/PreFetcher/Product/ProductAttributePreFetcher.php b/src/DataProvider/PreFetcher/Product/ProductAttributePreFetcher.php index ad7174c5..ac4edbdb 100644 --- a/src/DataProvider/PreFetcher/Product/ProductAttributePreFetcher.php +++ b/src/DataProvider/PreFetcher/Product/ProductAttributePreFetcher.php @@ -32,7 +32,7 @@ public function preFetchData( ): void { /** @var ProductAttributeValueInterface $result */ foreach ($this->repository->findByProductIds($parentIds, $context) as $result) { - $this->preFetchedData[$result->getProduct()->getCode()][] = $result; + $this->preFetchedData[$result->getProduct()?->getCode()][] = $result; } } diff --git a/src/Doctrine/Repository/Product/ProductAttributeValueRepository.php b/src/Doctrine/Repository/Product/ProductAttributeValueRepository.php index bc6d4558..dad040b9 100644 --- a/src/Doctrine/Repository/Product/ProductAttributeValueRepository.php +++ b/src/Doctrine/Repository/Product/ProductAttributeValueRepository.php @@ -32,7 +32,7 @@ public function findByProductIds( ): array { $locale = $context[ContextKeys::LOCALE_CODE] ?? 'en_US'; - return $this->decoratedRepository->createQueryBuilder('value') + return $this->createQueryBuilder('value') ->leftJoin('value.attribute', 'attribute') ->leftJoin('attribute.translations', 'translation') ->leftJoin('value.subject', 'product') From 444dbdb996bd0a331ecb28a57b35974cac4f44a8 Mon Sep 17 00:00:00 2001 From: kloz Date: Fri, 14 Jul 2023 11:07:29 +0200 Subject: [PATCH 11/19] [VSF2-244] ProductVariant prefetcher --- .../Product/ProductVariantPreFetcher.php | 55 +++++++++ .../ProductVariantSubresourceDataProvider.php | 46 +++++++ .../ProductQueryCollectionExtension.php | 26 ++++ .../ProductAttributeValueRepository.php | 1 + .../Product/ProductVariantRepository.php | 116 ++++++++++++++++++ .../ProductVariantRepositoryInterface.php | 21 ++++ src/Resources/services/data_providers.xml | 16 +++ src/Resources/services/doctrine_orm.xml | 5 + src/Resources/services/extension.xml | 8 ++ 9 files changed, 294 insertions(+) create mode 100644 src/DataProvider/PreFetcher/Product/ProductVariantPreFetcher.php create mode 100644 src/DataProvider/Subresource/ProductVariantSubresourceDataProvider.php create mode 100644 src/Doctrine/Orm/Extension/ProductQueryCollectionExtension.php create mode 100644 src/Doctrine/Repository/Product/ProductVariantRepository.php create mode 100644 src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php diff --git a/src/DataProvider/PreFetcher/Product/ProductVariantPreFetcher.php b/src/DataProvider/PreFetcher/Product/ProductVariantPreFetcher.php new file mode 100644 index 00000000..6be0165c --- /dev/null +++ b/src/DataProvider/PreFetcher/Product/ProductVariantPreFetcher.php @@ -0,0 +1,55 @@ +repository = $repository; + } + + public function preFetchData( + array $parentIds, + array $context, + ): void { + /** @var ProductVariantInterface $result */ + foreach ($this->repository->findByProductIds($parentIds, $context) as $result) { + $this->preFetchedData[$result->getProduct()?->getCode()][] = $result; + } + } + + public function getPreFetchedData( + string $identifier, + ?array $context = [], + ): array { + return $this->preFetchedData[$identifier] ?? []; + } + + public function supports( + array $context, + ?string $attribute = null, + ): bool { + $resourceClass = $context['resource_class']; + + return is_a($resourceClass, ProductVariantInterface::class, true) + || ($attribute === 'variants' && is_a($resourceClass, ProductInterface::class, true)); + } +} diff --git a/src/DataProvider/Subresource/ProductVariantSubresourceDataProvider.php b/src/DataProvider/Subresource/ProductVariantSubresourceDataProvider.php new file mode 100644 index 00000000..9c6afb5b --- /dev/null +++ b/src/DataProvider/Subresource/ProductVariantSubresourceDataProvider.php @@ -0,0 +1,46 @@ +cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); + + return new ArrayPaginator($data, 0, count($data)); + } +} diff --git a/src/Doctrine/Orm/Extension/ProductQueryCollectionExtension.php b/src/Doctrine/Orm/Extension/ProductQueryCollectionExtension.php new file mode 100644 index 00000000..40f0af90 --- /dev/null +++ b/src/Doctrine/Orm/Extension/ProductQueryCollectionExtension.php @@ -0,0 +1,26 @@ +addSelect('ovs') + ->join('o.variants', 'ovs'); + } + } +} diff --git a/src/Doctrine/Repository/Product/ProductAttributeValueRepository.php b/src/Doctrine/Repository/Product/ProductAttributeValueRepository.php index dad040b9..5153dd71 100644 --- a/src/Doctrine/Repository/Product/ProductAttributeValueRepository.php +++ b/src/Doctrine/Repository/Product/ProductAttributeValueRepository.php @@ -40,6 +40,7 @@ public function findByProductIds( ->addSelect('translation') ->andWhere('product.id IN (:productIds)') ->andWhere('translation.locale = :locale') + ->andWhere('value.localeCode = :locale') ->setParameter('productIds', $productIds) ->setParameter('locale', $locale) ->getQuery() diff --git a/src/Doctrine/Repository/Product/ProductVariantRepository.php b/src/Doctrine/Repository/Product/ProductVariantRepository.php new file mode 100644 index 00000000..3bc9995a --- /dev/null +++ b/src/Doctrine/Repository/Product/ProductVariantRepository.php @@ -0,0 +1,116 @@ +getEntityManager(), $decoratedRepository->getClassMetadata()); + $this->decoratedRepository = $decoratedRepository; + } + + public function findByProductIds( + array $productIds, + array $context, + ): array { + $locale = $context[ContextKeys::LOCALE_CODE] ?? 'en_US'; + $channel = $context[ContextKeys::CHANNEL]; + Assert::isInstanceOf($channel, ChannelInterface::class); + + return $this->createQueryBuilder('variant') + ->leftJoin('variant.product', 'product') + ->leftJoin('variant.channelPricings', 'channelPricing') + ->leftJoin(ChannelInterface::class, 'channel', Join::WITH, 'channel.code = channelPricing.channelCode') + ->leftJoin('channelPricing.appliedPromotions', 'appliedPromotion') + ->leftJoin('variant.translations', 'translation') + ->addSelect('translation') + ->addSelect('channelPricing') + ->addSelect('appliedPromotion') + ->andWhere('product.id IN (:productIds)') + ->andWhere('translation.locale = :locale') + ->andWhere('channel = :channel') + ->setParameter('productIds', $productIds) + ->setParameter('locale', $locale) + ->setParameter('channel', $channel) + ->getQuery() + ->getResult(); + } + + public function createQueryBuilderByProductId(string $locale, $productId): QueryBuilder + { + return $this->decoratedRepository->createQueryBuilderByProductId($locale, $productId); + } + + public function createQueryBuilderByProductCode(string $locale, string $productCode): QueryBuilder + { + return $this->decoratedRepository->createQueryBuilderByProductCode($locale, $productCode); + } + + public function findByName(string $name, string $locale): array + { + return $this->decoratedRepository->findByName($name, $locale); + } + + public function findByNameAndProduct(string $name, string $locale, ProductInterface $product): array + { + return $this->decoratedRepository->findByNameAndProduct($name, $locale, $product); + } + + public function findOneByCodeAndProductCode(string $code, string $productCode): ?ProductVariantInterface + { + return $this->decoratedRepository->findOneByCodeAndProductCode($code, $productCode); + } + + public function findByCodesAndProductCode(array $codes, string $productCode): array + { + return $this->decoratedRepository->findByCodesAndProductCode($codes, $productCode); + } + + public function findByCodes(array $codes): array + { + return $this->decoratedRepository->findByCodes($codes); + } + + public function findOneByIdAndProductId($id, $productId): ?ProductVariantInterface + { + return $this->decoratedRepository->findOneByIdAndProductId($id, $productId); + } + + public function findByPhraseAndProductCode(string $phrase, string $locale, string $productCode): array + { + return $this->decoratedRepository->findByPhraseAndProductCode($phrase, $locale, $productCode); + } + + public function findByPhrase(string $phrase, string $locale, ?int $limit = null): array + { + return $this->decoratedRepository->findByPhrase($phrase, $locale, $limit); + } + + public function getCodesOfAllVariants(): array + { + return $this->decoratedRepository->getCodesOfAllVariants(); + } +} diff --git a/src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php b/src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php new file mode 100644 index 00000000..6039fa34 --- /dev/null +++ b/src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/src/Resources/services/doctrine_orm.xml b/src/Resources/services/doctrine_orm.xml index 33758c19..c86b1eca 100644 --- a/src/Resources/services/doctrine_orm.xml +++ b/src/Resources/services/doctrine_orm.xml @@ -48,5 +48,10 @@ We are hiring developers from all over the world. Join us and start your new, ex + + + diff --git a/src/Resources/services/extension.xml b/src/Resources/services/extension.xml index 18226f83..1344b27b 100644 --- a/src/Resources/services/extension.xml +++ b/src/Resources/services/extension.xml @@ -18,5 +18,13 @@ We are hiring developers from all over the world. Join us and start your new, ex > + + + + From 4d2c74b4627db7f9b9b12aa8cba69ab50f65c159 Mon Sep 17 00:00:00 2001 From: kloz Date: Fri, 14 Jul 2023 11:19:23 +0200 Subject: [PATCH 12/19] [VSF2-244] ProductVariantRepository decoration removed --- .../Product/ProductVariantRepository.php | 69 ------------------- .../ProductVariantRepositoryInterface.php | 4 +- src/Resources/services/doctrine_orm.xml | 11 ++- 3 files changed, 9 insertions(+), 75 deletions(-) diff --git a/src/Doctrine/Repository/Product/ProductVariantRepository.php b/src/Doctrine/Repository/Product/ProductVariantRepository.php index 3bc9995a..e561e8a5 100644 --- a/src/Doctrine/Repository/Product/ProductVariantRepository.php +++ b/src/Doctrine/Repository/Product/ProductVariantRepository.php @@ -11,27 +11,13 @@ namespace BitBag\SyliusVueStorefront2Plugin\Doctrine\Repository\Product; use Doctrine\ORM\Query\Expr\Join; -use Doctrine\ORM\QueryBuilder; use Sylius\Bundle\ApiBundle\Serializer\ContextKeys; use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository; use Sylius\Component\Core\Model\ChannelInterface; -use Sylius\Component\Product\Model\ProductInterface; -use Sylius\Component\Product\Model\ProductVariantInterface; -use Sylius\Component\Product\Repository\ProductVariantRepositoryInterface as BaseProductVariantRepositoryInterface; use Webmozart\Assert\Assert; final class ProductVariantRepository extends EntityRepository implements ProductVariantRepositoryInterface { - private BaseProductVariantRepositoryInterface $decoratedRepository; - - public function __construct(BaseProductVariantRepositoryInterface $decoratedRepository) - { - assert($decoratedRepository instanceof EntityRepository); - - parent::__construct($decoratedRepository->getEntityManager(), $decoratedRepository->getClassMetadata()); - $this->decoratedRepository = $decoratedRepository; - } - public function findByProductIds( array $productIds, array $context, @@ -58,59 +44,4 @@ public function findByProductIds( ->getQuery() ->getResult(); } - - public function createQueryBuilderByProductId(string $locale, $productId): QueryBuilder - { - return $this->decoratedRepository->createQueryBuilderByProductId($locale, $productId); - } - - public function createQueryBuilderByProductCode(string $locale, string $productCode): QueryBuilder - { - return $this->decoratedRepository->createQueryBuilderByProductCode($locale, $productCode); - } - - public function findByName(string $name, string $locale): array - { - return $this->decoratedRepository->findByName($name, $locale); - } - - public function findByNameAndProduct(string $name, string $locale, ProductInterface $product): array - { - return $this->decoratedRepository->findByNameAndProduct($name, $locale, $product); - } - - public function findOneByCodeAndProductCode(string $code, string $productCode): ?ProductVariantInterface - { - return $this->decoratedRepository->findOneByCodeAndProductCode($code, $productCode); - } - - public function findByCodesAndProductCode(array $codes, string $productCode): array - { - return $this->decoratedRepository->findByCodesAndProductCode($codes, $productCode); - } - - public function findByCodes(array $codes): array - { - return $this->decoratedRepository->findByCodes($codes); - } - - public function findOneByIdAndProductId($id, $productId): ?ProductVariantInterface - { - return $this->decoratedRepository->findOneByIdAndProductId($id, $productId); - } - - public function findByPhraseAndProductCode(string $phrase, string $locale, string $productCode): array - { - return $this->decoratedRepository->findByPhraseAndProductCode($phrase, $locale, $productCode); - } - - public function findByPhrase(string $phrase, string $locale, ?int $limit = null): array - { - return $this->decoratedRepository->findByPhrase($phrase, $locale, $limit); - } - - public function getCodesOfAllVariants(): array - { - return $this->decoratedRepository->getCodesOfAllVariants(); - } } diff --git a/src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php b/src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php index 6039fa34..847f78b6 100644 --- a/src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php +++ b/src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php @@ -10,9 +10,7 @@ namespace BitBag\SyliusVueStorefront2Plugin\Doctrine\Repository\Product; -use Sylius\Component\Product\Repository\ProductVariantRepositoryInterface as BaseProductVariantRepositoryInterface; - -interface ProductVariantRepositoryInterface extends BaseProductVariantRepositoryInterface +interface ProductVariantRepositoryInterface { public function findByProductIds( array $productIds, diff --git a/src/Resources/services/doctrine_orm.xml b/src/Resources/services/doctrine_orm.xml index c86b1eca..0919dce3 100644 --- a/src/Resources/services/doctrine_orm.xml +++ b/src/Resources/services/doctrine_orm.xml @@ -49,9 +49,14 @@ We are hiring developers from all over the world. Join us and start your new, ex - + class="BitBag\SyliusVueStorefront2Plugin\Doctrine\Repository\Product\ProductVariantRepository"> + + + + From 88be9627f5756154b2cb28f64b74130fa2755c01 Mon Sep 17 00:00:00 2001 From: kloz Date: Fri, 14 Jul 2023 12:40:04 +0200 Subject: [PATCH 13/19] [VSF2-244] ChannelPricing preFetcher --- .../PreFetcher/CompositePreFetcher.php | 6 +- .../Product/ChannelPricingPreFetcher.php | 55 +++++++++++++++++++ .../Product/ProductImagePreFetcher.php | 2 +- .../ChannelPricingSubresourceDataProvider.php | 46 ++++++++++++++++ .../Product/ChannelPricingRepository.php | 49 +++++++++++++++++ .../ChannelPricingRepositoryInterface.php | 19 +++++++ .../Product/ProductImageRepository.php | 10 +--- src/Resources/services/data_providers.xml | 16 ++++++ src/Resources/services/doctrine_orm.xml | 5 ++ 9 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 src/DataProvider/PreFetcher/Product/ChannelPricingPreFetcher.php create mode 100644 src/DataProvider/Subresource/ChannelPricingSubresourceDataProvider.php create mode 100644 src/Doctrine/Repository/Product/ChannelPricingRepository.php create mode 100644 src/Doctrine/Repository/Product/ChannelPricingRepositoryInterface.php diff --git a/src/DataProvider/PreFetcher/CompositePreFetcher.php b/src/DataProvider/PreFetcher/CompositePreFetcher.php index d3d1a7fa..db285ea0 100644 --- a/src/DataProvider/PreFetcher/CompositePreFetcher.php +++ b/src/DataProvider/PreFetcher/CompositePreFetcher.php @@ -26,7 +26,11 @@ public function preFetchData( array $parentIds, array $context, ): void { - $attributes = $this->gatherAttributesToPreFetch($context['attributes']); + $attributes = $context['attributes'] ?? null; + if ($attributes === null) { + return; + } + $attributes = $this->gatherAttributesToPreFetch($attributes); foreach (array_keys($attributes) as $attribute) { /** @var RestrictedPreFetcherInterface $preFetcher */ diff --git a/src/DataProvider/PreFetcher/Product/ChannelPricingPreFetcher.php b/src/DataProvider/PreFetcher/Product/ChannelPricingPreFetcher.php new file mode 100644 index 00000000..a9b6dff1 --- /dev/null +++ b/src/DataProvider/PreFetcher/Product/ChannelPricingPreFetcher.php @@ -0,0 +1,55 @@ +repository = $repository; + } + + public function preFetchData( + array $parentIds, + array $context, + ): void { + /** @var ChannelPricingInterface $result */ + foreach ($this->repository->findByProductIds($parentIds, $context) as $result) { + $this->preFetchedData[$result->getProductVariant()?->getCode()][] = $result; + } + } + + public function getPreFetchedData( + string $identifier, + ?array $context = [], + ): array { + return $this->preFetchedData[$identifier] ?? []; + } + + public function supports( + array $context, + ?string $attribute = null, + ): bool { + $resourceClass = $context['resource_class']; + + return is_a($resourceClass, ChannelPricingInterface::class, true) + || ($attribute === 'channelPricings' && is_a($resourceClass, ProductInterface::class, true)); + } +} diff --git a/src/DataProvider/PreFetcher/Product/ProductImagePreFetcher.php b/src/DataProvider/PreFetcher/Product/ProductImagePreFetcher.php index 5c611d3b..24c5c4ac 100644 --- a/src/DataProvider/PreFetcher/Product/ProductImagePreFetcher.php +++ b/src/DataProvider/PreFetcher/Product/ProductImagePreFetcher.php @@ -31,7 +31,7 @@ public function preFetchData( array $context, ): void { foreach ($this->repository->findByProductIds($parentIds, $context) as $result) { - $this->preFetchedData[$result['code']][] = $result; + $this->preFetchedData[$result['code']][] = $result[0]; } } diff --git a/src/DataProvider/Subresource/ChannelPricingSubresourceDataProvider.php b/src/DataProvider/Subresource/ChannelPricingSubresourceDataProvider.php new file mode 100644 index 00000000..65d03150 --- /dev/null +++ b/src/DataProvider/Subresource/ChannelPricingSubresourceDataProvider.php @@ -0,0 +1,46 @@ +cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); + + return new ArrayPaginator($data, 0, count($data)); + } +} diff --git a/src/Doctrine/Repository/Product/ChannelPricingRepository.php b/src/Doctrine/Repository/Product/ChannelPricingRepository.php new file mode 100644 index 00000000..3c8c0f5b --- /dev/null +++ b/src/Doctrine/Repository/Product/ChannelPricingRepository.php @@ -0,0 +1,49 @@ +entityManager = $entityManager; + } + + public function findByProductIds( + array $productIds, + array $context, + ): array { + $channel = $context[ContextKeys::CHANNEL]; + Assert::isInstanceOf($channel, ChannelInterface::class); + + return $this->entityManager->createQueryBuilder() + ->from(ChannelPricingInterface::class, 'channelPricing') + ->leftJoin('channelPricing.productVariant', 'variant') + ->leftJoin('variant.product', 'product') + ->leftJoin(ChannelInterface::class, 'channel', Join::WITH, 'channel.code = channelPricing.channelCode') + ->addSelect('channelPricing') + ->andWhere('product.id IN (:productIds)') + ->andWhere('channel = :channel') + ->setParameter('productIds', $productIds) + ->setParameter('channel', $channel) + ->getQuery() + ->getResult(); + } +} diff --git a/src/Doctrine/Repository/Product/ChannelPricingRepositoryInterface.php b/src/Doctrine/Repository/Product/ChannelPricingRepositoryInterface.php new file mode 100644 index 00000000..acd199a1 --- /dev/null +++ b/src/Doctrine/Repository/Product/ChannelPricingRepositoryInterface.php @@ -0,0 +1,19 @@ + 'image.' . $field, - array_keys($context['attributes']['images']['collection'] ?? []), - ); - $fields = implode(', ', $fields); - - return $this->entityManager->createQueryBuilder() + return $this->entityManager->createQueryBuilder() ->from(ProductImageInterface::class, 'image') ->join('image.owner', 'product') - ->select($fields) + ->select('image') ->addSelect('product.code') ->andWhere('product.id IN (:productIds)') ->setParameter('productIds', $productIds) diff --git a/src/Resources/services/data_providers.xml b/src/Resources/services/data_providers.xml index 1fde0977..131f22f5 100644 --- a/src/Resources/services/data_providers.xml +++ b/src/Resources/services/data_providers.xml @@ -114,5 +114,21 @@ We are hiring developers from all over the world. Join us and start your new, ex + + + + + + + + + + diff --git a/src/Resources/services/doctrine_orm.xml b/src/Resources/services/doctrine_orm.xml index 0919dce3..64975c29 100644 --- a/src/Resources/services/doctrine_orm.xml +++ b/src/Resources/services/doctrine_orm.xml @@ -58,5 +58,10 @@ We are hiring developers from all over the world. Join us and start your new, ex + + + + From ef813bad5455c17f55348adf2420c61f2dca90d3 Mon Sep 17 00:00:00 2001 From: kloz Date: Fri, 14 Jul 2023 16:25:55 +0200 Subject: [PATCH 14/19] [VSF2-244] ProductVariantOptions preFetcher --- .../PreFetcher/CompositePreFetcher.php | 47 ++++++++++++---- .../ProductVariantOptionPreFetcher.php | 56 +++++++++++++++++++ ...ctVariantOptionSubresourceDataProvider.php | 46 +++++++++++++++ .../Product/ProductVariantRepository.php | 31 ++++++++++ .../ProductVariantRepositoryInterface.php | 5 ++ src/Resources/services/data_providers.xml | 16 ++++++ 6 files changed, 191 insertions(+), 10 deletions(-) create mode 100644 src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php create mode 100644 src/DataProvider/Subresource/ProductVariantOptionSubresourceDataProvider.php diff --git a/src/DataProvider/PreFetcher/CompositePreFetcher.php b/src/DataProvider/PreFetcher/CompositePreFetcher.php index db285ea0..be841e98 100644 --- a/src/DataProvider/PreFetcher/CompositePreFetcher.php +++ b/src/DataProvider/PreFetcher/CompositePreFetcher.php @@ -30,6 +30,9 @@ public function preFetchData( if ($attributes === null) { return; } + + # todo: make it an array of keys in case of duplicated names; + # todo: the array should be nested according to the query/model structure, so the keys could be recognized in ->supports $attributes = $this->gatherAttributesToPreFetch($attributes); foreach (array_keys($attributes) as $attribute) { @@ -57,22 +60,46 @@ public function getPreFetchedData( private function gatherAttributesToPreFetch(array $attributes): array { - $attributes = array_filter( + $filteredAttributes = $this->filterAttributes($attributes); + + foreach ($filteredAttributes as $attribute => $fields) { + $isCollection = is_array($fields['collection'] ?? null); + + if ($isCollection) { + $nestedAttributes = $this->gatherAttributesToPreFetch($fields['collection']); + } else { + $nestedAttributes = $this->gatherAttributesToPreFetch($fields['edges']['node']); + } + + $this->removeNestedAttributes($filteredAttributes, $nestedAttributes, $attribute, $isCollection); + $filteredAttributes = array_merge($filteredAttributes, $nestedAttributes); + } + + return $filteredAttributes; + } + + private function filterAttributes(array $attributes): array + { + return array_filter( $attributes, - static fn($attr) => is_array($attr['collection'] ?? null) + static fn($attr) => is_array($attr['collection'] ?? $attr['edges'] ?? null) ); + } - foreach ($attributes as $attribute => $fields) { - $nestedAttributes = $this->gatherAttributesToPreFetch($fields['collection']); - if (count($nestedAttributes) > 0) { - foreach (array_keys($nestedAttributes) as $nestedAttribute) { + private function removeNestedAttributes( + array &$attributes, + array $nestedAttributes, + string $attribute, + bool $isCollection, + ): void { + if (count($nestedAttributes) > 0) { + foreach (array_keys($nestedAttributes) as $nestedAttribute) { + if ($isCollection) { unset($attributes[$attribute]['collection'][$nestedAttribute]); + } else { + unset($attributes[$attribute]['edges']['node'][$nestedAttribute]); } } - - $attributes = array_merge($attributes, $nestedAttributes); } - - return $attributes; } } diff --git a/src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php b/src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php new file mode 100644 index 00000000..5e976ce6 --- /dev/null +++ b/src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php @@ -0,0 +1,56 @@ +repository = $repository; + } + + public function preFetchData( + array $parentIds, + array $context, + ): void { + /** @var ProductVariantInterface $result */ + foreach ($this->repository->findOptionsByProductIds($parentIds, $context) as $result) { + $this->preFetchedData[$result->getCode()] = $result->getOptionValues()->toArray(); + } + } + + public function getPreFetchedData( + string $identifier, + ?array $context = [], + ): array { + return $this->preFetchedData[$identifier] ?? []; + } + + public function supports( + array $context, + ?string $attribute = null, + ): bool { + $resourceClass = $context['resource_class']; + + return is_a($resourceClass, ProductOptionValueInterface::class, true) + || ($attribute === 'optionValues' && is_a($resourceClass, ProductInterface::class, true)); + } +} diff --git a/src/DataProvider/Subresource/ProductVariantOptionSubresourceDataProvider.php b/src/DataProvider/Subresource/ProductVariantOptionSubresourceDataProvider.php new file mode 100644 index 00000000..75605c6c --- /dev/null +++ b/src/DataProvider/Subresource/ProductVariantOptionSubresourceDataProvider.php @@ -0,0 +1,46 @@ +cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); + + return new ArrayPaginator($data, 0, count($data)); + } +} diff --git a/src/Doctrine/Repository/Product/ProductVariantRepository.php b/src/Doctrine/Repository/Product/ProductVariantRepository.php index e561e8a5..477e0025 100644 --- a/src/Doctrine/Repository/Product/ProductVariantRepository.php +++ b/src/Doctrine/Repository/Product/ProductVariantRepository.php @@ -44,4 +44,35 @@ public function findByProductIds( ->getQuery() ->getResult(); } + + public function findOptionsByProductIds( + array $productIds, + array $context, + ): array { + $locale = $context[ContextKeys::LOCALE_CODE] ?? 'en_US'; + $channel = $context[ContextKeys::CHANNEL]; + Assert::isInstanceOf($channel, ChannelInterface::class); + + return $this->createQueryBuilder('variant') + ->leftJoin('variant.product', 'product') + ->leftJoin('product.options', 'productOption') + ->leftJoin('variant.optionValues', 'optionValue') + ->leftJoin('optionValue.translations', 'optionValueTranslation') + ->leftJoin('optionValue.option', 'option') + ->leftJoin('option.translations', 'optionTranslation') + ->addSelect('product') + ->addSelect('productOption') + ->addSelect('optionValue') + ->addSelect('optionValueTranslation') + ->addSelect('option') + ->addSelect('optionTranslation') + ->andWhere('product.id IN (:productIds)') + ->andWhere('optionValueTranslation.locale = :locale') + ->andWhere(':channel MEMBER OF product.channels') + ->setParameter('productIds', $productIds) + ->setParameter('locale', $locale) + ->setParameter('channel', $channel) + ->getQuery() + ->getResult(); + } } diff --git a/src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php b/src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php index 847f78b6..db4ee3d4 100644 --- a/src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php +++ b/src/Doctrine/Repository/Product/ProductVariantRepositoryInterface.php @@ -16,4 +16,9 @@ public function findByProductIds( array $productIds, array $context, ): array; + + public function findOptionsByProductIds( + array $productIds, + array $context, + ): array; } diff --git a/src/Resources/services/data_providers.xml b/src/Resources/services/data_providers.xml index 131f22f5..bc84a6e0 100644 --- a/src/Resources/services/data_providers.xml +++ b/src/Resources/services/data_providers.xml @@ -130,5 +130,21 @@ We are hiring developers from all over the world. Join us and start your new, ex + + + + + + + + + + From 97e0d01f3cbf8b6b3c2aec7930c02d96b70043f5 Mon Sep 17 00:00:00 2001 From: kloz Date: Mon, 17 Jul 2023 17:27:31 +0200 Subject: [PATCH 15/19] [VSF2-244] ProductOptions preFetcher --- .../PreFetcher/CompositePreFetcher.php | 26 ++------ .../PreFetcher/PreFetcherInterface.php | 2 +- .../ProductOptionsPreFetcherInterface.php | 26 ++++++++ .../ProductVariantOptionPreFetcher.php | 60 ++++++++++++++++--- .../ProductOptionSubresourceDataProvider.php | 46 ++++++++++++++ src/Resources/services/data_providers.xml | 12 +++- 6 files changed, 139 insertions(+), 33 deletions(-) create mode 100644 src/DataProvider/PreFetcher/Product/ProductOptionsPreFetcherInterface.php create mode 100644 src/DataProvider/Subresource/ProductOptionSubresourceDataProvider.php diff --git a/src/DataProvider/PreFetcher/CompositePreFetcher.php b/src/DataProvider/PreFetcher/CompositePreFetcher.php index be841e98..773e352a 100644 --- a/src/DataProvider/PreFetcher/CompositePreFetcher.php +++ b/src/DataProvider/PreFetcher/CompositePreFetcher.php @@ -31,8 +31,6 @@ public function preFetchData( return; } - # todo: make it an array of keys in case of duplicated names; - # todo: the array should be nested according to the query/model structure, so the keys could be recognized in ->supports $attributes = $this->gatherAttributesToPreFetch($attributes); foreach (array_keys($attributes) as $attribute) { @@ -40,6 +38,8 @@ public function preFetchData( foreach ($this->preFetchers as $preFetcher) { if ($preFetcher->supports($context, $attribute)) { $preFetcher->preFetchData($parentIds, $context); + + break; } } } @@ -47,11 +47,11 @@ public function preFetchData( public function getPreFetchedData( string $identifier, - ?array $context = [], + array $context, ): array { foreach ($this->preFetchers as $preFetcher) { if ($preFetcher->supports($context)) { - return $preFetcher->getPreFetchedData($identifier); + return $preFetcher->getPreFetchedData($identifier, $context); } } @@ -71,7 +71,6 @@ private function gatherAttributesToPreFetch(array $attributes): array $nestedAttributes = $this->gatherAttributesToPreFetch($fields['edges']['node']); } - $this->removeNestedAttributes($filteredAttributes, $nestedAttributes, $attribute, $isCollection); $filteredAttributes = array_merge($filteredAttributes, $nestedAttributes); } @@ -85,21 +84,4 @@ private function filterAttributes(array $attributes): array static fn($attr) => is_array($attr['collection'] ?? $attr['edges'] ?? null) ); } - - private function removeNestedAttributes( - array &$attributes, - array $nestedAttributes, - string $attribute, - bool $isCollection, - ): void { - if (count($nestedAttributes) > 0) { - foreach (array_keys($nestedAttributes) as $nestedAttribute) { - if ($isCollection) { - unset($attributes[$attribute]['collection'][$nestedAttribute]); - } else { - unset($attributes[$attribute]['edges']['node'][$nestedAttribute]); - } - } - } - } } diff --git a/src/DataProvider/PreFetcher/PreFetcherInterface.php b/src/DataProvider/PreFetcher/PreFetcherInterface.php index 7fd29d70..0731b966 100644 --- a/src/DataProvider/PreFetcher/PreFetcherInterface.php +++ b/src/DataProvider/PreFetcher/PreFetcherInterface.php @@ -19,6 +19,6 @@ public function preFetchData( public function getPreFetchedData( string $identifier, - ?array $context = [], + array $context, ): array; } diff --git a/src/DataProvider/PreFetcher/Product/ProductOptionsPreFetcherInterface.php b/src/DataProvider/PreFetcher/Product/ProductOptionsPreFetcherInterface.php new file mode 100644 index 00000000..2f8c3592 --- /dev/null +++ b/src/DataProvider/PreFetcher/Product/ProductOptionsPreFetcherInterface.php @@ -0,0 +1,26 @@ +isPrefetched === true) { + return; + } + + $result = $this->repository->findOptionsByProductIds($parentIds, $context); + /** @var ProductVariantInterface $result */ - foreach ($this->repository->findOptionsByProductIds($parentIds, $context) as $result) { - $this->preFetchedData[$result->getCode()] = $result->getOptionValues()->toArray(); + foreach ($result as $variant) { + $this->prepareProductOptions($variant); + $this->prepareOptionValues($variant); + $this->prepareVariantOptionValues($variant); } + + $this->isPrefetched = true; } public function getPreFetchedData( string $identifier, - ?array $context = [], + array $context, ): array { - return $this->preFetchedData[$identifier] ?? []; + return match ($context['property']) { + self::ELIGIBLE_ATTR_PRODUCT_OPTIONS => $this->productOptions[$identifier] ?? [], + self::ELIGIBLE_ATTR_PRODUCT_OPTION_VALUES => $this->optionValues[$identifier] ?? [], + self::ELIGIBLE_ATTR_VARIANT_OPTION_VALUES => $this->variantOptionValues[$identifier] ?? [], + default => [], + }; } public function supports( @@ -50,7 +72,29 @@ public function supports( ): bool { $resourceClass = $context['resource_class']; - return is_a($resourceClass, ProductOptionValueInterface::class, true) - || ($attribute === 'optionValues' && is_a($resourceClass, ProductInterface::class, true)); + return is_a($resourceClass, ProductOptionInterface::class, true) + || is_a($resourceClass, ProductOptionValueInterface::class, true) + || (in_array($attribute, self::ELIGIBLE_ATTRIBUTES) + && is_a($resourceClass, ProductInterface::class, true)); + } + + private function prepareProductOptions(ProductVariantInterface $variant): void + { + foreach ($variant->getOptionValues() as $optionValue) { + $option = $optionValue->getOption(); + $this->productOptions[$variant->getProduct()->getCode()][$option->getCode()] = $option; + } + } + + private function prepareOptionValues(ProductVariantInterface $variant): void + { + foreach ($variant->getOptionValues() as $optionValue) { + $this->optionValues[$optionValue->getOption()->getCode()][$optionValue->getCode()] = $optionValue; + } + } + + private function prepareVariantOptionValues(ProductVariantInterface $variant): void + { + $this->variantOptionValues[$variant->getCode()] = $variant->getOptionValues()->toArray(); } } diff --git a/src/DataProvider/Subresource/ProductOptionSubresourceDataProvider.php b/src/DataProvider/Subresource/ProductOptionSubresourceDataProvider.php new file mode 100644 index 00000000..8ebe8596 --- /dev/null +++ b/src/DataProvider/Subresource/ProductOptionSubresourceDataProvider.php @@ -0,0 +1,46 @@ +cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); + + return new ArrayPaginator($data, 0, count($data)); + } +} diff --git a/src/Resources/services/data_providers.xml b/src/Resources/services/data_providers.xml index bc84a6e0..caabd473 100644 --- a/src/Resources/services/data_providers.xml +++ b/src/Resources/services/data_providers.xml @@ -132,7 +132,7 @@ We are hiring developers from all over the world. Join us and start your new, ex @@ -140,11 +140,19 @@ We are hiring developers from all over the world. Join us and start your new, ex + + + + + From ac2ba6d76b316dceb62ab58b42c2de5880023b02 Mon Sep 17 00:00:00 2001 From: kloz Date: Mon, 17 Jul 2023 17:48:33 +0200 Subject: [PATCH 16/19] [VSF2-244] Removed redundant strategy --- .../CompositeSubresourceDataProvider.php | 41 -------------- .../ChannelPricingSubresourceDataProvider.php | 46 ---------------- ...roductAttributeSubresourceDataProvider.php | 46 ---------------- .../ProductImageSubresourceDataProvider.php | 46 ---------------- .../ProductOptionSubresourceDataProvider.php | 46 ---------------- ...ctVariantOptionSubresourceDataProvider.php | 46 ---------------- .../ProductVariantSubresourceDataProvider.php | 46 ---------------- ...rictedSubresourceDataProviderInterface.php | 22 -------- src/DataProvider/SubresourceDataProvider.php | 54 +++++++++++++++++++ .../SubresourceDataProviderInterface.php | 31 +++++++++++ src/Resources/services/data_providers.xml | 52 +----------------- 11 files changed, 87 insertions(+), 389 deletions(-) delete mode 100644 src/DataProvider/CompositeSubresourceDataProvider.php delete mode 100644 src/DataProvider/Subresource/ChannelPricingSubresourceDataProvider.php delete mode 100644 src/DataProvider/Subresource/ProductAttributeSubresourceDataProvider.php delete mode 100644 src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php delete mode 100644 src/DataProvider/Subresource/ProductOptionSubresourceDataProvider.php delete mode 100644 src/DataProvider/Subresource/ProductVariantOptionSubresourceDataProvider.php delete mode 100644 src/DataProvider/Subresource/ProductVariantSubresourceDataProvider.php delete mode 100644 src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php create mode 100644 src/DataProvider/SubresourceDataProvider.php create mode 100644 src/DataProvider/SubresourceDataProviderInterface.php diff --git a/src/DataProvider/CompositeSubresourceDataProvider.php b/src/DataProvider/CompositeSubresourceDataProvider.php deleted file mode 100644 index 5815bdad..00000000 --- a/src/DataProvider/CompositeSubresourceDataProvider.php +++ /dev/null @@ -1,41 +0,0 @@ -subresourceProviders, RestrictedSubresourceDataProviderInterface::class); - } - - public function getSubresource( - string $resourceClass, - array $identifiers, - array $context, - string $operationName = null - ): iterable|object|null { - /** @var RestrictedSubresourceDataProviderInterface $provider */ - foreach ($this->subresourceProviders as $provider) { - if ($provider->supports($resourceClass, $context, $operationName)) { - return $provider->getSubresource($resourceClass, $identifiers, $context, $operationName); - } - } - - return $this->decoratedSubresourceProvider->getSubresource($resourceClass, $identifiers, $context, $operationName); - } -} diff --git a/src/DataProvider/Subresource/ChannelPricingSubresourceDataProvider.php b/src/DataProvider/Subresource/ChannelPricingSubresourceDataProvider.php deleted file mode 100644 index 65d03150..00000000 --- a/src/DataProvider/Subresource/ChannelPricingSubresourceDataProvider.php +++ /dev/null @@ -1,46 +0,0 @@ -cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); - - return new ArrayPaginator($data, 0, count($data)); - } -} diff --git a/src/DataProvider/Subresource/ProductAttributeSubresourceDataProvider.php b/src/DataProvider/Subresource/ProductAttributeSubresourceDataProvider.php deleted file mode 100644 index b74558b8..00000000 --- a/src/DataProvider/Subresource/ProductAttributeSubresourceDataProvider.php +++ /dev/null @@ -1,46 +0,0 @@ -cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); - - return new ArrayPaginator($data, 0, count($data)); - } -} diff --git a/src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php b/src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php deleted file mode 100644 index ed3eced3..00000000 --- a/src/DataProvider/Subresource/ProductImageSubresourceDataProvider.php +++ /dev/null @@ -1,46 +0,0 @@ -cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); - - return new ArrayPaginator($data, 0, count($data)); - } -} diff --git a/src/DataProvider/Subresource/ProductOptionSubresourceDataProvider.php b/src/DataProvider/Subresource/ProductOptionSubresourceDataProvider.php deleted file mode 100644 index 8ebe8596..00000000 --- a/src/DataProvider/Subresource/ProductOptionSubresourceDataProvider.php +++ /dev/null @@ -1,46 +0,0 @@ -cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); - - return new ArrayPaginator($data, 0, count($data)); - } -} diff --git a/src/DataProvider/Subresource/ProductVariantOptionSubresourceDataProvider.php b/src/DataProvider/Subresource/ProductVariantOptionSubresourceDataProvider.php deleted file mode 100644 index 75605c6c..00000000 --- a/src/DataProvider/Subresource/ProductVariantOptionSubresourceDataProvider.php +++ /dev/null @@ -1,46 +0,0 @@ -cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); - - return new ArrayPaginator($data, 0, count($data)); - } -} diff --git a/src/DataProvider/Subresource/ProductVariantSubresourceDataProvider.php b/src/DataProvider/Subresource/ProductVariantSubresourceDataProvider.php deleted file mode 100644 index 9c6afb5b..00000000 --- a/src/DataProvider/Subresource/ProductVariantSubresourceDataProvider.php +++ /dev/null @@ -1,46 +0,0 @@ -cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); - - return new ArrayPaginator($data, 0, count($data)); - } -} diff --git a/src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php b/src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php deleted file mode 100644 index e095a0df..00000000 --- a/src/DataProvider/Subresource/RestrictedSubresourceDataProviderInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -supports($resourceClass)) { + Assert::keyExists($identifiers, 'code'); + + /** @var ProductInterface[] $data */ + $data = $this->cachedCollectionDataProvider->getCachedData($identifiers['code'], $context); + + return new ArrayPaginator($data, 0, count($data)); + } + + return $this->decoratedSubresourceProvider->getSubresource($resourceClass, $identifiers, $context, $operationName); + } + + private function supports(string $resourceClass): bool + { + foreach (self::ELIGIBLE_ENTITIES as $entity) { + if (is_a($resourceClass, $entity, true)) { + return true; + } + } + + return false; + } +} diff --git a/src/DataProvider/SubresourceDataProviderInterface.php b/src/DataProvider/SubresourceDataProviderInterface.php new file mode 100644 index 00000000..b4100a81 --- /dev/null +++ b/src/DataProvider/SubresourceDataProviderInterface.php @@ -0,0 +1,31 @@ + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 0fb1d0002f8cd5f5d15965fd57d9b4688565f9c6 Mon Sep 17 00:00:00 2001 From: kloz Date: Mon, 17 Jul 2023 17:54:51 +0200 Subject: [PATCH 17/19] [VSF2-244] Adjusted cached collection data provider service priority --- src/Resources/services/data_providers.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/services/data_providers.xml b/src/Resources/services/data_providers.xml index 696fc70a..78544db6 100644 --- a/src/Resources/services/data_providers.xml +++ b/src/Resources/services/data_providers.xml @@ -48,7 +48,7 @@ We are hiring developers from all over the world. Join us and start your new, ex - + Date: Mon, 17 Jul 2023 17:57:49 +0200 Subject: [PATCH 18/19] [VSF2-244] Phpstan fixes --- .../PreFetcher/Product/ProductVariantOptionPreFetcher.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php b/src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php index 639d82b2..1218ba39 100644 --- a/src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php +++ b/src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php @@ -74,7 +74,7 @@ public function supports( return is_a($resourceClass, ProductOptionInterface::class, true) || is_a($resourceClass, ProductOptionValueInterface::class, true) - || (in_array($attribute, self::ELIGIBLE_ATTRIBUTES) + || (in_array($attribute, self::ELIGIBLE_ATTRIBUTES, true) && is_a($resourceClass, ProductInterface::class, true)); } @@ -82,14 +82,14 @@ private function prepareProductOptions(ProductVariantInterface $variant): void { foreach ($variant->getOptionValues() as $optionValue) { $option = $optionValue->getOption(); - $this->productOptions[$variant->getProduct()->getCode()][$option->getCode()] = $option; + $this->productOptions[$variant->getProduct()?->getCode()][$option?->getCode()] = $option; } } private function prepareOptionValues(ProductVariantInterface $variant): void { foreach ($variant->getOptionValues() as $optionValue) { - $this->optionValues[$optionValue->getOption()->getCode()][$optionValue->getCode()] = $optionValue; + $this->optionValues[$optionValue->getOption()?->getCode()][$optionValue?->getCode()] = $optionValue; } } From 284eae2cda2ab393ec7b91c16a45f9c040e08c2b Mon Sep 17 00:00:00 2001 From: kloz Date: Mon, 17 Jul 2023 18:04:46 +0200 Subject: [PATCH 19/19] [VSF2-244] Phpstan fixes --- .../PreFetcher/Product/ProductVariantOptionPreFetcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php b/src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php index 1218ba39..4d238b85 100644 --- a/src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php +++ b/src/DataProvider/PreFetcher/Product/ProductVariantOptionPreFetcher.php @@ -89,7 +89,7 @@ private function prepareProductOptions(ProductVariantInterface $variant): void private function prepareOptionValues(ProductVariantInterface $variant): void { foreach ($variant->getOptionValues() as $optionValue) { - $this->optionValues[$optionValue->getOption()?->getCode()][$optionValue?->getCode()] = $optionValue; + $this->optionValues[$optionValue->getOption()?->getCode()][$optionValue->getCode()] = $optionValue; } }