<?php declare(strict_types=1);
namespace Ew\GrecoBundle\Subscriber;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\Event\AfterLineItemAddedEvent;
use Shopware\Core\Checkout\Cart\Event\BeforeLineItemAddedEvent;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\LineItemFactoryRegistry;
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\Struct\ArrayStruct;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
class LineItemSubscriber implements EventSubscriberInterface
{
private CartService $cartService;
private LineItemFactoryRegistry $factory;
private EntityRepository $productRepository;
public function __construct(
CartService $cartService,
LineItemFactoryRegistry $factory,
EntityRepository $productRepository
) {
$this->cartService = $cartService;
$this->factory = $factory;
$this->productRepository = $productRepository;
}
/**
* @inheritDoc
*/
public static function getSubscribedEvents(): array
{
return [
BeforeLineItemAddedEvent::class => 'onLineItemAdded'
];
}
/**
* @param BeforeLineItemAddedEvent $event
* @return void
*/
public function onLineItemAdded(BeforeLineItemAddedEvent $event)
{
$updatedOnce = $event->getContext()->getExtension('updatedOnce');
if (!$updatedOnce) {
$cart = $event->getCart();
// Get product ids from cart
$productIds = $cart->getLineItems()
->filterType(LineItem::PRODUCT_LINE_ITEM_TYPE)
->map(fn(LineItem $lineItem) => $lineItem->getReferencedId());
// Fetch products in cart
$products = $this->getProducts($productIds, $event->getContext());
// Retrieve all bundle products from cart and remove the ones that are already added
foreach ($event->getCart()->getLineItems() as $lineItem) {
if ($products->has($lineItem->getReferencedId())) {
$product = $products->get($lineItem->getReferencedId());
if (isset($product->getCustomFields()['greco_bundle_article']) && $product->getCustomFields()['greco_bundle_article']) {
if ($cart->getLineItems()->has($lineItem->getReferencedId())) {
if (isset($lineItem->getPayload()['added'])) {
$this->removeProductFromCart($event->getCart(), $lineItem->getReferencedId(), $event->getSalesChannelContext());
}
}
}
}
}
$pseudoArticles = [];
// Clean cart for removed bundle products
foreach ($cart->getLineItems() as $lineItem) {
if ($products->has($lineItem->getReferencedId())) {
$product = $products->get($lineItem->getReferencedId());
if (isset($product->getCustomFields()['greco_pseudo_article']) && $product->getCustomFields()['greco_pseudo_article']) {
$this->removeProductFromCart($cart, $lineItem->getId(), $event->getSalesChannelContext());
if (isset($product->getCustomFields()['greco_pseudo_articles']) && $product->getCustomFields()['greco_pseudo_articles']) {
foreach ($product->getCustomFields()['greco_pseudo_articles'] as $productId) {
$pseudoArticles[] = $productId;
}
}
}
}
}
// Add missing bundle items to cart
if (!empty($pseudoArticles)) {
foreach ($pseudoArticles as $productId) {
if (!in_array($productId, $productIds)) {
// Add bundle item to cart
$this->addProductToCart($cart, $productId, 1, $event->getSalesChannelContext());
}
}
}
$event->getContext()->setExtensions(['updatedOnce' => new ArrayStruct(['isUpdated' => true])]);
}
}
/**
* Add product to cart
*
* @param Cart $cart
* @param string $productId
* @param int $quantity
* @param SalesChannelContext $context
*/
private function addProductToCart(Cart $cart, string $productId, int $quantity, SalesChannelContext $context): void
{
$lineItem = $this->factory->create([
'type' => LineItem::PRODUCT_LINE_ITEM_TYPE,
'referencedId' => $productId,
'quantity' => $quantity,
], $context);
$lineItem->setPayloadValue('added', true);
$this->cartService->add($cart, $lineItem, $context);
}
/**
* Remove product from cart
*
* @param Cart $cart
* @param string $itemId
* @param SalesChannelContext $context
*/
private function removeProductFromCart(Cart $cart, string $itemId, SalesChannelContext $context): void
{
$this->cartService->remove($cart, $itemId, $context);
}
/**
* Get product
*
* @param array $productIds
* @param Context $context
* @return EntityCollection
*/
private function getProducts(array $productIds, Context $context): EntityCollection
{
$criteria = (new Criteria($productIds));
return $this->productRepository->search($criteria, $context)->getEntities();
}
}