Skip to content

Commit

Permalink
Bring back the transformer from ProductBundle and create a new one fo…
Browse files Browse the repository at this point in the history
…r Admin
  • Loading branch information
TheMilek authored and GSadee committed May 8, 2024
1 parent 10a2cc5 commit 165b84c
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\AdminBundle\Form\DataTransformer;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Sylius\Component\Product\Model\ProductAssociationInterface;
use Sylius\Component\Product\Model\ProductAssociationTypeInterface;
use Sylius\Component\Product\Model\ProductInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Symfony\Component\Form\DataTransformerInterface;
use Webmozart\Assert\Assert;

/**
* @implements DataTransformerInterface<Collection<array-key, ProductAssociationInterface>, array<string, Collection<array-key, ProductInterface>>>
*/
final class ProductsToProductAssociationsTransformer implements DataTransformerInterface
{
/** @var Collection<array-key, ProductAssociationInterface>|null */
private ?Collection $productAssociations = null;

/**
* @param FactoryInterface<ProductAssociationInterface> $productAssociationFactory
* @param RepositoryInterface<ProductAssociationTypeInterface> $productAssociationTypeRepository
*/
public function __construct(
private readonly FactoryInterface $productAssociationFactory,
private readonly RepositoryInterface $productAssociationTypeRepository,
) {
}

/**
* @return array<string, Collection<array-key, ProductInterface>>
*/
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<array-key, ProductAssociationInterface> $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<array-key, ProductInterface> $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<array-key, ProductAssociationInterface>|null $productAssociations
*/
private function setProductAssociations(?Collection $productAssociations): void
{
$this->productAssociations = $productAssociations instanceof Collection ? $productAssociations : new ArrayCollection();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace spec\Sylius\Bundle\AdminBundle\Form\DataTransformer;

use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Product\Model\ProductAssociationInterface;
use Sylius\Component\Product\Model\ProductAssociationTypeInterface;
use Sylius\Component\Product\Model\ProductInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Symfony\Component\Form\DataTransformerInterface;

final class ProductsToProductAssociationsTransformerSpec extends ObjectBehavior
{
function let(
FactoryInterface $productAssociationFactory,
RepositoryInterface $productAssociationTypeRepository,
): void {
$this->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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Collection<array-key, ProductAssociationInterface>, array<string, Collection<array-key, ProductInterface>>>
*/
final class ProductsToProductAssociationsTransformer implements DataTransformerInterface
{
/** @var Collection<array-key, ProductAssociationInterface>|null */
/** @var Collection<array-key, ProductAssociationInterface> */
private ?Collection $productAssociations = null;

/**
* @param FactoryInterface<ProductAssociationInterface> $productAssociationFactory
* @param ProductRepositoryInterface<ProductInterface> $productRepository
* @param RepositoryInterface<ProductAssociationTypeInterface> $productAssociationTypeRepository
*/
public function __construct(
private readonly FactoryInterface $productAssociationFactory,
private readonly ProductRepositoryInterface $productRepository,
private readonly RepositoryInterface $productAssociationTypeRepository,
) {
}

/**
* @return array<string, Collection<array-key, ProductInterface>>
*/
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<array-key, ProductAssociationInterface> $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);
}

Expand All @@ -85,6 +85,25 @@ public function reverseTransform(mixed $value): ?Collection
return $productAssociations;
}

/**
* @param Collection<array-key, ProductInterface> $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) {
Expand All @@ -105,25 +124,21 @@ private function getProductAssociationByTypeCode(string $productAssociationTypeC
return $productAssociation;
}

/**
* @param Collection<array-key, ProductInterface> $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);
$productAssociation->addAssociatedProduct($product);
}
}

/**
* @param Collection<array-key, ProductAssociationInterface>|null $productAssociations
*/
private function setProductAssociations(?Collection $productAssociations): void
{
$this->productAssociations = $productAssociations instanceof Collection ? $productAssociations : new ArrayCollection();
$this->productAssociations = $productAssociations;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@

<service id="sylius.form.type.data_transformer.products_to_product_associations" class="Sylius\Bundle\ProductBundle\Form\DataTransformer\ProductsToProductAssociationsTransformer">
<argument type="service" id="sylius.factory.product_association" />
<argument type="service" id="sylius.repository.product" />
<argument type="service" id="sylius.repository.product_association_type" />
</service>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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',
]);
}

Expand Down

0 comments on commit 165b84c

Please sign in to comment.