<?php
declare(strict_types=1);
namespace Zeobv\BundleProducts\Core\Subscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Framework\Struct\ArrayStruct;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Content\Product\ProductEvents;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityLoadedEvent;
use Shopware\Core\Content\Product\Aggregate\ProductPrice\ProductPriceCollection;
use Shopware\Core\Content\Product\DataAbstractionLayer\CheapestPrice\CheapestPriceContainer;
use Zeobv\BundleProducts\Factory\ProductBundleFactory;
use Zeobv\BundleProducts\Struct\BundleProduct\BundlePrice;
use Zeobv\BundleProducts\Service\ConfigService;
use Zeobv\BundleProducts\Repository\BundleProductRepository;
use Zeobv\BundleProducts\Struct\BundleProduct\ProductBundle;
class ProductSubscriber implements EventSubscriberInterface
{
protected BundleProductRepository $bundleProductRepository;
protected ProductBundleFactory $productBundleFactory;
protected ConfigService $configService;
public function __construct(
BundleProductRepository $bundleProductRepository,
ProductBundleFactory $productBundleFactory,
ConfigService $configService
) {
$this->bundleProductRepository = $bundleProductRepository;
$this->productBundleFactory = $productBundleFactory;
$this->configService = $configService;
}
/**
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
ProductEvents::PRODUCT_LOADED_EVENT => [
['onProductLoaded', -1]
],
];
}
/**
* @param EntityLoadedEvent $event
*
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
*/
public function onProductLoaded(EntityLoadedEvent $event): void
{
// We perform another query to get the bundle products,
// check for the context so we don't want to arrive in an infinite loop
if ($event->getContext()->hasExtension('zeobvBundleProductContext')) {
/** @var ArrayStruct $arrayStruct */
$arrayStruct = $event->getContext()->getExtension('zeobvBundleProductContext');
$contextIds = $arrayStruct->all();
$oldIds = $contextIds['productIds'];
if (count(array_diff($event->getIds(), $oldIds)) == 0) {
$event->getContext()->removeExtension('zeobvBundleProductContext');
return;
}
}
# Create an index to be able to quickly reference products later from the ProductCollection using the product ID
$index = array_flip($event->getIds());
# Get products of bundles grouped by product id, we can call these "Bundles"
$bundles = $this->bundleProductRepository->getBundleProductContents($event->getIds(), $event->getContext());
if (count($bundles) < 1) {
return;
}
# Loop over the bundles to determine availability of the so-called "bundle product"
# and create a ProductBundle struct for later use.
foreach ($bundles as $bundleProductId => $productsInBundle) {
/** @var ProductEntity $productEntity */
$productEntity = $event->getEntities()[$index[$bundleProductId]] ?? $event->getEntities()[$bundleProductId];
$productBundle = $this->productBundleFactory->create($bundleProductId, $productsInBundle);
if (
$productBundle->getBundlePriceMode() === BundlePrice::BUNDLE_PRICE_MODE_SUM
&& $productBundle->getBundlePrice()
) {
# Set new price
$productEntity->setPrice($productBundle->getBundlePrice()->getPrice());
$productEntity->setPrices($productBundle->getAdvancedPrices() ?: new ProductPriceCollection([]));
$productEntity->setCheapestPriceContainer(new CheapestPriceContainer([]));
# @feature-deprecated (flag:FEATURE_NEXT_15815) tag:v6.5.0 -
# CheapestPrice will only be available for SalesChannelProductEntity
if (method_exists($productEntity, 'setCheapestPrice')) {
$productEntity->setCheapestPrice(null);
}
}
if ($this->configService->overridePurchasePrice()) {
$productEntity->setPurchasePrices($productBundle->getBundlePurchasePrice()->getPrice());
}
if ($productBundle->getOverrideStockFieldOfBundleProduct()) {
$productEntity->setStock($productBundle->getVirtualStock());
}
$productEntity->setAvailableStock($productBundle->getVirtualAvailableStock());
if ($this->configService->getOverrideMaxPurchase()) {
$productEntity->setMaxPurchase($productBundle->getMaxPurchase());
}
$productEntity->setIsCloseout($productBundle->isBackordersDisabled());
if ($productBundle->getDeliveryTime() !== null) {
$productEntity->setDeliveryTimeId($productBundle->getDeliveryTime()->getId());
$productEntity->setDeliveryTime($productBundle->getDeliveryTime());
}
if (!$this->configService->getOverrideBundleWeight() && $productBundle->getWeight() > 0) {
$productEntity->setWeight($productBundle->getWeight());
} elseif ($this->configService->getOverrideBundleWeight()) {
$productEntity->setWeight($this->configService->getBundleWeightOverride());
}
if ($productBundle->isBackordersDisabled()) {
$productEntity->setAvailable($productBundle->isInStock());
}
$productEntity->addExtension(ProductBundle::EXTENSION_NAME, $productBundle);
}
}
}