Skip to content

Commit

Permalink
OP-289: Implement handling unpacked bundles in API
Browse files Browse the repository at this point in the history
  • Loading branch information
hmfilar committed Sep 11, 2024
1 parent 3dff2a8 commit e36d8a0
Show file tree
Hide file tree
Showing 17 changed files with 230 additions and 11 deletions.
8 changes: 5 additions & 3 deletions src/Command/AddProductBundleToCartCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace BitBag\SyliusProductBundlePlugin\Command;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

final class AddProductBundleToCartCommand implements OrderIdentityAwareInterface, ProductCodeAwareInterface
Expand All @@ -19,10 +20,11 @@ final class AddProductBundleToCartCommand implements OrderIdentityAwareInterface
private Collection $productBundleItems;

public function __construct(
private int $orderId,
private string $productCode,
private int $quantity = 1,
private readonly int $orderId,
private readonly string $productCode,
private readonly int $quantity = 1,
) {
$this->productBundleItems = new ArrayCollection();
}

public function getOrderId(): int
Expand Down
16 changes: 15 additions & 1 deletion src/DataTransformer/AddProductBundleToCartDtoDataTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,19 @@
use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleToCartCommand;
use BitBag\SyliusProductBundlePlugin\Dto\Api\AddProductBundleToCartDto;
use BitBag\SyliusProductBundlePlugin\Provider\AddProductBundleItemToCartCommandProviderInterface;
use Sylius\Component\Order\Model\OrderInterface;
use Webmozart\Assert\Assert;

final class AddProductBundleToCartDtoDataTransformer implements DataTransformerInterface
{
public const OBJECT_TO_POPULATE = 'object_to_populate';

public function __construct(
private readonly AddProductBundleItemToCartCommandProviderInterface $addProductBundleItemToCartCommandProvider,
) {
}

/**
* @param AddProductBundleToCartDto|object $object
*/
Expand All @@ -37,8 +43,16 @@ public function transform(

$productCode = $object->getProductCode();
$quantity = $object->getQuantity();
$overwrittenVariants = $object->getOverwrittenVariants();
$addItemToCartCommands = $this->addProductBundleItemToCartCommandProvider->provide(
$productCode,
$overwrittenVariants,
);

$command = new AddProductBundleToCartCommand($cart->getId(), $productCode, $quantity);
$command->setProductBundleItems($addItemToCartCommands);

return new AddProductBundleToCartCommand($cart->getId(), $productCode, $quantity);
return $command;
}

public function supportsTransformation(
Expand Down
3 changes: 2 additions & 1 deletion src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface;
use BitBag\SyliusProductBundlePlugin\Form\Type\ProductBundleItemType;
use BitBag\SyliusProductBundlePlugin\Form\Type\ProductBundleType;
use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleItemRepository;
use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleRepository;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\Bundle\ResourceBundle\SyliusResourceBundle;
Expand Down Expand Up @@ -80,7 +81,7 @@ private function addResourcesSection(ArrayNodeDefinition $node): void
->scalarNode('interface')->defaultValue(ProductBundleItemInterface::class)->cannotBeEmpty()->end()
->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
->scalarNode('factory')->defaultValue(Factory::class)->cannotBeEmpty()->end()
->scalarNode('repository')->cannotBeEmpty()->end()
->scalarNode('repository')->defaultValue(ProductBundleItemRepository::class)->cannotBeEmpty()->end()
->scalarNode('form')->defaultValue(ProductBundleItemType::class)->cannotBeEmpty()->end()
->end()
->end()
Expand Down
6 changes: 6 additions & 0 deletions src/Dto/Api/AddProductBundleToCartDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public function __construct(
private string $productCode,
private int $quantity = 1,
private ?string $orderTokenValue = null,
private array $overwrittenVariants = [],
) {
}

Expand All @@ -41,4 +42,9 @@ public function getQuantity(): int
{
return $this->quantity;
}

public function getOverwrittenVariants(): array
{
return $this->overwrittenVariants;
}
}
2 changes: 1 addition & 1 deletion src/Factory/AddProductBundleToCartCommandFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

final class AddProductBundleToCartCommandFactory implements AddProductBundleToCartCommandFactoryInterface
{
/** @param Collection<int, AddProductBundleItemToCartCommand> */
/** @param Collection<int, AddProductBundleItemToCartCommand> $productBundleItems */
public function createNew(
int $orderId,
string $productCode,
Expand Down
4 changes: 2 additions & 2 deletions src/Factory/AddProductBundleToCartCommandFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@

namespace BitBag\SyliusProductBundlePlugin\Factory;

use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand;
use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleToCartCommand;
use BitBag\SyliusProductBundlePlugin\Dto\AddProductBundleToCartDtoInterface;
use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface;
use Doctrine\Common\Collections\Collection;

interface AddProductBundleToCartCommandFactoryInterface
{
/** @param Collection<int, ProductBundleOrderItemInterface> $productBundleItems */
/** @param Collection<int, AddProductBundleItemToCartCommand> $productBundleItems */
public function createNew(
int $orderId,
string $productCode,
Expand Down
6 changes: 3 additions & 3 deletions src/Handler/AddProductBundleToCartHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
final class AddProductBundleToCartHandler implements MessageHandlerInterface
{
public function __construct(
private OrderRepositoryInterface $orderRepository,
private ProductRepositoryInterface $productRepository,
private CartProcessorInterface $cartProcessor,
private readonly OrderRepositoryInterface $orderRepository,
private readonly ProductRepositoryInterface $productRepository,
private readonly CartProcessorInterface $cartProcessor,
) {
}

Expand Down
87 changes: 87 additions & 0 deletions src/Provider/AddProductBundleItemToCartCommandProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

/*
* This file has been created by developers from BitBag.
* Feel free to contact us once you face any issues or want to start
* You can find more information about us on https://bitbag.io and write us
* an email on [email protected].
*/

declare(strict_types=1);

namespace BitBag\SyliusProductBundlePlugin\Provider;

use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand;
use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface;
use BitBag\SyliusProductBundlePlugin\Factory\AddProductBundleItemToCartCommandFactoryInterface;
use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleItemRepositoryInterface;
use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleRepositoryInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Sylius\Component\Core\Model\ProductVariantInterface;
use Sylius\Component\Core\Repository\ProductVariantRepositoryInterface;

final class AddProductBundleItemToCartCommandProvider implements AddProductBundleItemToCartCommandProviderInterface
{
public function __construct(
private readonly AddProductBundleItemToCartCommandFactoryInterface $addProductBundleItemToCartCommandFactory,
private readonly ProductBundleItemRepositoryInterface $productBundleItemRepository,
private readonly ProductBundleRepositoryInterface $productBundleRepository,
private readonly ProductVariantRepositoryInterface $productVariantRepository,
) {
}

/**
* @return Collection<int, AddProductBundleItemToCartCommand>
*
* @throws \Exception
*/
public function provide(string $bundleCode, array $overwrittenVariants): Collection
{
$bundle = $this->productBundleRepository->findOneByProductCode($bundleCode);
if (null === $bundle) {
throw new \Exception('Product bundle not found');
}

$bundleItems = $this->productBundleItemRepository->findByBundleCode($bundleCode);

$commands = [];
foreach ($bundleItems as $bundleItem) {
$command = $this->addProductBundleItemToCartCommandFactory->createNew($bundleItem);
if (!$bundle->isPackedProduct() && [] !== $overwrittenVariants) {
$this->overwriteVariant($command, $bundleItem, $overwrittenVariants);
}
$commands[] = $command;
}

return new ArrayCollection($commands);
}

private function overwriteVariant(
AddProductBundleItemToCartCommand $command,
ProductBundleItemInterface $bundleItem,
array $overwrittenVariants,
): void {
foreach ($overwrittenVariants as $overwrittenVariant) {
if (null !== $overwrittenVariant['from'] && null !== $overwrittenVariant['to'] &&
$bundleItem->getProductVariant()?->getCode() === $overwrittenVariant['from'] &&
$this->shouldOverwriteVariant($overwrittenVariant['from'], $overwrittenVariant['to'])
) {
/** @var ProductVariantInterface $newVariant */
$newVariant = $this->productVariantRepository->findOneBy(['code' => $overwrittenVariant['to']]);
$command->setProductVariant($newVariant);
}
}
}

private function shouldOverwriteVariant(string $oldVariantCode, string $newVariantCode): bool
{
$oldVariant = $this->productVariantRepository->findOneBy(['code' => $oldVariantCode]);
$newVariant = $this->productVariantRepository->findOneBy(['code' => $newVariantCode]);

return
$oldVariant instanceof ProductVariantInterface &&
$newVariant instanceof ProductVariantInterface &&
$oldVariant->getProduct() === $newVariant->getProduct();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

/*
* This file has been created by developers from BitBag.
* Feel free to contact us once you face any issues or want to start
* You can find more information about us on https://bitbag.io and write us
* an email on [email protected].
*/

declare(strict_types=1);

namespace BitBag\SyliusProductBundlePlugin\Provider;

use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand;
use Doctrine\Common\Collections\Collection;

interface AddProductBundleItemToCartCommandProviderInterface
{
/** @return Collection<int, AddProductBundleItemToCartCommand> */
public function provide(string $bundleCode, array $overwrittenVariants): Collection;
}
31 changes: 31 additions & 0 deletions src/Repository/ProductBundleItemRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

/*
* This file has been created by developers from BitBag.
* Feel free to contact us once you face any issues or want to start
* You can find more information about us on https://bitbag.io and write us
* an email on [email protected].
*/

declare(strict_types=1);

namespace BitBag\SyliusProductBundlePlugin\Repository;

use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;

class ProductBundleItemRepository extends EntityRepository implements ProductBundleItemRepositoryInterface
{
/** @return ProductBundleItemInterface[] */
public function findByBundleCode(string $bundleCode): array
{
return $this
->createQueryBuilder('pbi')
->leftJoin('pbi.productBundle', 'pb')
->leftJoin('pb.product', 'p')
->where('p.code = :code')
->setParameter('code', $bundleCode)
->getQuery()
->getResult();
}
}
21 changes: 21 additions & 0 deletions src/Repository/ProductBundleItemRepositoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

/*
* This file has been created by developers from BitBag.
* Feel free to contact us once you face any issues or want to start
* You can find more information about us on https://bitbag.io and write us
* an email on [email protected].
*/

declare(strict_types=1);

namespace BitBag\SyliusProductBundlePlugin\Repository;

use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;

interface ProductBundleItemRepositoryInterface extends RepositoryInterface
{
/** @return ProductBundleItemInterface[] */
public function findByBundleCode(string $bundleCode): array;
}
12 changes: 12 additions & 0 deletions src/Repository/ProductBundleRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace BitBag\SyliusProductBundlePlugin\Repository;

use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface;
use Doctrine\Common\Collections\Collection;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
use Sylius\Component\Product\Model\ProductVariantInterface;
Expand All @@ -37,4 +38,15 @@ public function findBundlesByVariants(Collection $variants): array
->getQuery()
->getResult();
}

public function findOneByProductCode(string $productCode): ?ProductBundleInterface
{
return $this->createQueryBuilder('pb')
->select('pb')
->leftJoin('pb.product', 'p')
->andWhere('p.code = :productCode')
->setParameter('productCode', $productCode)
->getQuery()
->getSingleResult();
}
}
3 changes: 3 additions & 0 deletions src/Repository/ProductBundleRepositoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace BitBag\SyliusProductBundlePlugin\Repository;

use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface;
use Doctrine\Common\Collections\Collection;
use Sylius\Component\Product\Model\ProductVariantInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
Expand All @@ -21,4 +22,6 @@ public function getProductIds(): array;

/** @param Collection<int, ProductVariantInterface> $variants */
public function findBundlesByVariants(Collection $variants): array;

public function findOneByProductCode(string $productCode): ?ProductBundleInterface;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,8 @@
<attribute name="quantity">
<group>shop:cart:add_product_bundle</group>
</attribute>
<attribute name="overwrittenVariants">
<group>shop:cart:add_product_bundle</group>
</attribute>
</class>
</serializer>
1 change: 1 addition & 0 deletions src/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<import resource="services/controller.xml"/>
<import resource="services/handler.xml"/>
<import resource="services/processor.xml"/>
<import resource="services/provider.xml"/>
<import resource="services/transformer.xml"/>
<import resource="services/twig.xml"/>
<import resource="services/validator.xml"/>
Expand Down
16 changes: 16 additions & 0 deletions src/Resources/config/services/provider.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>

<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service
id="bitbag_sylius_product_bundle.provider.add_product_bundle_item_to_cart_command"
class="BitBag\SyliusProductBundlePlugin\Provider\AddProductBundleItemToCartCommandProvider"
>
<argument type="service" id="bitbag_sylius_product_bundle.factory.add_product_bundle_item_to_cart_command"/>
<argument type="service" id="bitbag_sylius_product_bundle.repository.product_bundle_item"/>
<argument type="service" id="bitbag_sylius_product_bundle.repository.product_bundle"/>
<argument type="service" id="sylius.repository.product_variant"/>
</service>
</services>
</container>
1 change: 1 addition & 0 deletions src/Resources/config/services/transformer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
id="bitbag_sylius_product_bundle.data_transformer.add_product_bundle_to_cart_dto"
class="BitBag\SyliusProductBundlePlugin\DataTransformer\AddProductBundleToCartDtoDataTransformer"
>
<argument type="service" id="bitbag_sylius_product_bundle.provider.add_product_bundle_item_to_cart_command"/>
<tag name="api_platform.data_transformer"/>
</service>
</services>
Expand Down

0 comments on commit e36d8a0

Please sign in to comment.