From 165b84cd4041e3c793c9c48129f9631edecbba04 Mon Sep 17 00:00:00 2001 From: Kamil Grygierzec Date: Wed, 8 May 2024 12:16:47 +0200 Subject: [PATCH] Bring back the transformer from ProductBundle and create a new one for Admin --- ...oductsToProductAssociationsTransformer.php | 129 ++++++++++++++++++ ...tsToProductAssociationsTransformerSpec.php | 83 +++++++++++ ...oductsToProductAssociationsTransformer.php | 65 +++++---- .../Resources/config/services/form.xml | 1 + ...tsToProductAssociationsTransformerSpec.php | 11 +- 5 files changed, 257 insertions(+), 32 deletions(-) create mode 100644 src/Sylius/Bundle/AdminBundle/Form/DataTransformer/ProductsToProductAssociationsTransformer.php create mode 100644 src/Sylius/Bundle/AdminBundle/spec/Form/DataTransformer/ProductsToProductAssociationsTransformerSpec.php diff --git a/src/Sylius/Bundle/AdminBundle/Form/DataTransformer/ProductsToProductAssociationsTransformer.php b/src/Sylius/Bundle/AdminBundle/Form/DataTransformer/ProductsToProductAssociationsTransformer.php new file mode 100644 index 00000000000..cb6071735ea --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Form/DataTransformer/ProductsToProductAssociationsTransformer.php @@ -0,0 +1,129 @@ +, array>> + */ +final class ProductsToProductAssociationsTransformer implements DataTransformerInterface +{ + /** @var Collection|null */ + private ?Collection $productAssociations = null; + + /** + * @param FactoryInterface $productAssociationFactory + * @param RepositoryInterface $productAssociationTypeRepository + */ + public function __construct( + private readonly FactoryInterface $productAssociationFactory, + private readonly RepositoryInterface $productAssociationTypeRepository, + ) { + } + + /** + * @return array> + */ + public function transform(mixed $value): array + { + $this->setProductAssociations($value); + + if ($value->isEmpty()) { + return []; + } + + $values = []; + + /** @var ProductAssociationInterface $productAssociation */ + foreach ($value as $productAssociation) { + $values[$productAssociation->getType()->getCode()] = clone $productAssociation->getAssociatedProducts(); + } + + return $values; + } + + public function reverseTransform(mixed $value): ?Collection + { + if (!is_array($value)) { + return null; + } + + /** @var Collection $productAssociations */ + $productAssociations = new ArrayCollection(); + foreach ($value as $productAssociationTypeCode => $products) { + if ($products->isEmpty()) { + continue; + } + + $productAssociation = $this->getProductAssociationByTypeCode((string) $productAssociationTypeCode); + $this->linkProductsToAssociation($productAssociation, $products); + $productAssociations->add($productAssociation); + } + + $this->setProductAssociations(null); + + return $productAssociations; + } + + private function getProductAssociationByTypeCode(string $productAssociationTypeCode): ProductAssociationInterface + { + foreach ($this->productAssociations as $productAssociation) { + if ($productAssociationTypeCode === $productAssociation->getType()->getCode()) { + return $productAssociation; + } + } + + /** @var ProductAssociationTypeInterface $productAssociationType */ + $productAssociationType = $this->productAssociationTypeRepository->findOneBy([ + 'code' => $productAssociationTypeCode, + ]); + + /** @var ProductAssociationInterface $productAssociation */ + $productAssociation = $this->productAssociationFactory->createNew(); + $productAssociation->setType($productAssociationType); + + return $productAssociation; + } + + /** + * @param Collection $products + */ + private function linkProductsToAssociation( + ProductAssociationInterface $productAssociation, + Collection $products, + ): void { + $productAssociation->clearAssociatedProducts(); + foreach ($products as $product) { + Assert::isInstanceOf($product, ProductInterface::class); + $productAssociation->addAssociatedProduct($product); + } + } + + /** + * @param Collection|null $productAssociations + */ + private function setProductAssociations(?Collection $productAssociations): void + { + $this->productAssociations = $productAssociations instanceof Collection ? $productAssociations : new ArrayCollection(); + } +} diff --git a/src/Sylius/Bundle/AdminBundle/spec/Form/DataTransformer/ProductsToProductAssociationsTransformerSpec.php b/src/Sylius/Bundle/AdminBundle/spec/Form/DataTransformer/ProductsToProductAssociationsTransformerSpec.php new file mode 100644 index 00000000000..2def195a319 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/spec/Form/DataTransformer/ProductsToProductAssociationsTransformerSpec.php @@ -0,0 +1,83 @@ +beConstructedWith( + $productAssociationFactory, + $productAssociationTypeRepository, + ); + } + + function it_is_a_data_transformer(): void + { + $this->shouldImplement(DataTransformerInterface::class); + } + + function it_transforms_an_empty_collection_to_an_empty_array(): void + { + $this->transform(new ArrayCollection())->shouldReturn([]); + } + + function it_transforms_product_associations_to_array( + ProductAssociationInterface $productAssociation, + ProductAssociationTypeInterface $productAssociationType, + ProductInterface $firstAssociatedProduct, + ProductInterface $secondAssociatedProduct, + ): void { + $productAssociation->getType()->willReturn($productAssociationType); + $productAssociation->getAssociatedProducts()->willReturn( + new ArrayCollection([ + $firstAssociatedProduct->getWrappedObject(), + $secondAssociatedProduct->getWrappedObject(), + ]), + ); + + $firstAssociatedProduct->getCode()->willReturn('FIRST'); + $secondAssociatedProduct->getCode()->willReturn('SECOND'); + + $productAssociationType->getCode()->willReturn('accessories'); + + $this->transform(new ArrayCollection([$productAssociation->getWrappedObject()]))->shouldBeLike([ + 'accessories' => new ArrayCollection([ + $firstAssociatedProduct->getWrappedObject(), + $secondAssociatedProduct->getWrappedObject(), + ]), + ]); + } + + function it_reverse_transforms_null_into_null(): void + { + $this->reverseTransform(null)->shouldReturn(null); + } + + function it_reverse_transforms_empty_string_into_null(): void + { + $this->reverseTransform('')->shouldReturn(null); + } +} diff --git a/src/Sylius/Bundle/ProductBundle/Form/DataTransformer/ProductsToProductAssociationsTransformer.php b/src/Sylius/Bundle/ProductBundle/Form/DataTransformer/ProductsToProductAssociationsTransformer.php index c13e4c3ce80..58a7ebcbf51 100644 --- a/src/Sylius/Bundle/ProductBundle/Form/DataTransformer/ProductsToProductAssociationsTransformer.php +++ b/src/Sylius/Bundle/ProductBundle/Form/DataTransformer/ProductsToProductAssociationsTransformer.php @@ -18,65 +18,65 @@ use Sylius\Component\Product\Model\ProductAssociationInterface; use Sylius\Component\Product\Model\ProductAssociationTypeInterface; use Sylius\Component\Product\Model\ProductInterface; +use Sylius\Component\Product\Repository\ProductRepositoryInterface; use Sylius\Component\Resource\Factory\FactoryInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; use Symfony\Component\Form\DataTransformerInterface; use Webmozart\Assert\Assert; -/** - * @implements DataTransformerInterface, array>> - */ final class ProductsToProductAssociationsTransformer implements DataTransformerInterface { - /** @var Collection|null */ + /** @var Collection */ private ?Collection $productAssociations = null; /** * @param FactoryInterface $productAssociationFactory + * @param ProductRepositoryInterface $productRepository * @param RepositoryInterface $productAssociationTypeRepository */ public function __construct( private readonly FactoryInterface $productAssociationFactory, + private readonly ProductRepositoryInterface $productRepository, private readonly RepositoryInterface $productAssociationTypeRepository, ) { } - /** - * @return array> - */ - public function transform(mixed $value): array + public function transform($value) { $this->setProductAssociations($value); - if ($value->isEmpty()) { - return []; + if (null === $value) { + return ''; } $values = []; /** @var ProductAssociationInterface $productAssociation */ foreach ($value as $productAssociation) { - $values[$productAssociation->getType()->getCode()] = clone $productAssociation->getAssociatedProducts(); + $productCodesAsString = $this->getCodesAsStringFromProducts($productAssociation->getAssociatedProducts()); + + $values[$productAssociation->getType()->getCode()] = $productCodesAsString; } return $values; } - public function reverseTransform(mixed $value): ?Collection + public function reverseTransform($value): ?Collection { - if (!is_array($value)) { + if (null === $value || '' === $value || !is_array($value)) { return null; } /** @var Collection $productAssociations */ $productAssociations = new ArrayCollection(); - foreach ($value as $productAssociationTypeCode => $products) { - if ($products->isEmpty()) { + foreach ($value as $productAssociationTypeCode => $productCodes) { + if (null === $productCodes) { continue; } + /** @var ProductAssociationInterface $productAssociation */ $productAssociation = $this->getProductAssociationByTypeCode((string) $productAssociationTypeCode); - $this->linkProductsToAssociation($productAssociation, $products); + $this->setAssociatedProductsByProductCodes($productAssociation, $productCodes); $productAssociations->add($productAssociation); } @@ -85,6 +85,25 @@ public function reverseTransform(mixed $value): ?Collection return $productAssociations; } + /** + * @param Collection $products + */ + private function getCodesAsStringFromProducts(Collection $products): ?string + { + if ($products->isEmpty()) { + return null; + } + + $codes = []; + + /** @var ProductInterface $product */ + foreach ($products as $product) { + $codes[] = $product->getCode(); + } + + return implode(',', $codes); + } + private function getProductAssociationByTypeCode(string $productAssociationTypeCode): ProductAssociationInterface { foreach ($this->productAssociations as $productAssociation) { @@ -105,13 +124,12 @@ private function getProductAssociationByTypeCode(string $productAssociationTypeC return $productAssociation; } - /** - * @param Collection $products - */ - private function linkProductsToAssociation( + private function setAssociatedProductsByProductCodes( ProductAssociationInterface $productAssociation, - Collection $products, + string $productCodes, ): void { + $products = $this->productRepository->findBy(['code' => explode(',', $productCodes)]); + $productAssociation->clearAssociatedProducts(); foreach ($products as $product) { Assert::isInstanceOf($product, ProductInterface::class); @@ -119,11 +137,8 @@ private function linkProductsToAssociation( } } - /** - * @param Collection|null $productAssociations - */ private function setProductAssociations(?Collection $productAssociations): void { - $this->productAssociations = $productAssociations instanceof Collection ? $productAssociations : new ArrayCollection(); + $this->productAssociations = $productAssociations; } } diff --git a/src/Sylius/Bundle/ProductBundle/Resources/config/services/form.xml b/src/Sylius/Bundle/ProductBundle/Resources/config/services/form.xml index 48deb26cf6d..9bc70a1ef23 100644 --- a/src/Sylius/Bundle/ProductBundle/Resources/config/services/form.xml +++ b/src/Sylius/Bundle/ProductBundle/Resources/config/services/form.xml @@ -207,6 +207,7 @@ + diff --git a/src/Sylius/Bundle/ProductBundle/spec/Form/DataTransformer/ProductsToProductAssociationsTransformerSpec.php b/src/Sylius/Bundle/ProductBundle/spec/Form/DataTransformer/ProductsToProductAssociationsTransformerSpec.php index 7725309c617..a6fadbe26bf 100644 --- a/src/Sylius/Bundle/ProductBundle/spec/Form/DataTransformer/ProductsToProductAssociationsTransformerSpec.php +++ b/src/Sylius/Bundle/ProductBundle/spec/Form/DataTransformer/ProductsToProductAssociationsTransformerSpec.php @@ -42,9 +42,9 @@ function it_is_a_data_transformer(): void $this->shouldImplement(DataTransformerInterface::class); } - function it_transforms_an_empty_collection_to_an_empty_array(): void + function it_transforms_null_to_empty_string(): void { - $this->transform(new ArrayCollection())->shouldReturn([]); + $this->transform(null)->shouldReturn(''); } function it_transforms_product_associations_to_array( @@ -66,11 +66,8 @@ function it_transforms_product_associations_to_array( $productAssociationType->getCode()->willReturn('accessories'); - $this->transform(new ArrayCollection([$productAssociation->getWrappedObject()]))->shouldBeLike([ - 'accessories' => new ArrayCollection([ - $firstAssociatedProduct->getWrappedObject(), - $secondAssociatedProduct->getWrappedObject(), - ]) + $this->transform(new ArrayCollection([$productAssociation->getWrappedObject()]))->shouldReturn([ + 'accessories' => 'FIRST,SECOND', ]); }