<?php declare(strict_types=1);
/**
* gb media
* All Rights Reserved.
*
* Unauthorized copying of this file, via any medium is strictly prohibited.
* The content of this file is proprietary and confidential.
*
* @category Shopware
* @package Shopware_Plugins
* @subpackage GbmedEnev
* @copyright Copyright (c) 2019, gb media
* @license proprietary
* @author Giuseppe Bottino
* @link http://www.gb-media.biz
*/
namespace Gbmed\Enev\Subscriber;
use Gbmed\Enev\Core\Content\Enev\EnevCollection;
use Gbmed\Enev\Core\Content\Enev\EnevDefinition;
use Gbmed\Enev\Service\Filter;
use Gbmed\Enev\Service\Product as ProductService;
use Shopware\Core\Content\Product\Events\ProductCrossSellingCriteriaEvent;
use Shopware\Core\Content\Product\Events\ProductCrossSellingIdsCriteriaEvent;
use Shopware\Core\Content\Product\Events\ProductListingCriteriaEvent;
use Shopware\Core\Content\Product\Events\ProductListingResultEvent;
use Shopware\Core\Content\Product\Events\ProductSearchCriteriaEvent;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Content\ProductExport\Event\ProductExportProductCriteriaEvent;
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelEntityLoadedEvent;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Storefront\Page\Product\ProductPageCriteriaEvent;
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Exception;
use Shopware\Core\Content\Product\Events\ProductCrossSellingsLoadedEvent;
class ProductPage implements EventSubscriberInterface
{
/** @var ProductService */
private $productService;
/**
* @var Filter
*/
private $filterService;
private SystemConfigService $systemConfigService;
/**
* Product constructor.
*
* @param ProductService $productService
* @param Filter $filterService
* @param SystemConfigService $systemConfigService
*/
public function __construct(
ProductService $productService,
Filter $filterService,
SystemConfigService $systemConfigService
) {
$this->productService = $productService;
$this->filterService = $filterService;
$this->systemConfigService = $systemConfigService;
}
public static function getSubscribedEvents(): array
{
return [
ProductExportProductCriteriaEvent::class => 'onProductExportProductCriteriaEvent',
ProductSearchCriteriaEvent::class => 'onProductSearchCriteriaEvent',
ProductListingCriteriaEvent::class => 'onProductListingCriteriaEvent',
ProductListingResultEvent::class => 'onProductListingResultEvent',
ProductPageCriteriaEvent::class => 'onProductPageCriteriaEvent',
ProductPageLoadedEvent::class => 'onProductPageLoadedEvent',
ProductCrossSellingCriteriaEvent::class => 'onProductCrossSellingCriteriaEvent',
ProductCrossSellingIdsCriteriaEvent::class => 'onProductCrossSellingIdsCriteriaEvent',
ProductCrossSellingsLoadedEvent::class => 'onProductCrossSellingsLoadedEvent',
'sales_channel.product.loaded' => 'onSalesChannelEntityLoadedEvent',
];
}
public function onProductExportProductCriteriaEvent(ProductExportProductCriteriaEvent $event): void
{
$this->addCriteria($event->getCriteria());
}
public function onProductSearchCriteriaEvent(ProductSearchCriteriaEvent $event): void
{
$this->addCriteria($event->getCriteria());
}
public function onProductListingCriteriaEvent(ProductListingCriteriaEvent $event): void
{
$this->addCriteria($event->getCriteria());
}
public function onProductPageCriteriaEvent(ProductPageCriteriaEvent $event): void
{
$this->addCriteria($event->getCriteria());
}
public function onProductCrossSellingCriteriaEvent(ProductCrossSellingCriteriaEvent $event): void
{
$this->addCriteria($event->getCriteria());
}
public function onProductCrossSellingIdsCriteriaEvent(ProductCrossSellingIdsCriteriaEvent $event): void
{
$this->addCriteria($event->getCriteria());
}
/**
* add data into listing
*
* @param ProductListingResultEvent $event
* @throws Exception
*/
public function onProductListingResultEvent(ProductListingResultEvent $event): void
{
$salesChannelContext = $event->getSalesChannelContext();
$products = $event->getResult();
/** workaround to set the correct filter list */
$this->filterService->setFilterList($products, EnevDefinition::ENTITY_NAME, 'class');
/** @var SalesChannelProductEntity $product */
foreach ($products as $product) {
$this->prepareProduct($product, $salesChannelContext);
}
}
/**
* add data into detail
*
* @param ProductPageLoadedEvent $event
* @throws Exception
*/
public function onProductPageLoadedEvent(ProductPageLoadedEvent $event): void
{
$product = $event->getPage()->getProduct();
$salesChannelContext = $event->getSalesChannelContext();
$this->prepareProduct($product, $salesChannelContext);
}
/**
* add data into product-slider
*
* @param ProductCrossSellingsLoadedEvent $event
* @throws Exception
*/
public function onProductCrossSellingsLoadedEvent(ProductCrossSellingsLoadedEvent $event): void
{
$salesChannelContext = $event->getSalesChannelContext();
$crossSellings = $event->getCrossSellings()->getElements();
/** @var \Shopware\Core\Content\Product\SalesChannel\CrossSelling\CrossSellingElement $crossSelling */
foreach ($crossSellings as $crossSelling) {
/** @var SalesChannelProductEntity $product */
foreach ($crossSelling->getProducts() as $product) {
$this->prepareProduct($product, $salesChannelContext);
}
}
}
/**
* add data into product-slider
*
* @param SalesChannelEntityLoadedEvent $event
* @throws Exception
*/
public function onSalesChannelEntityLoadedEvent(SalesChannelEntityLoadedEvent $event): void
{
$salesChannelContext = $event->getSalesChannelContext();
$products = $event->getEntities();
/** @var SalesChannelProductEntity $product */
foreach ($products as $product) {
$this->prepareProduct($product, $salesChannelContext);
}
}
private function prepareProduct(
ProductEntity $product,
SalesChannelContext $salesChannelContext
): void {
$result = null;
/** @var EnevCollection $collection */
$collection = $product->getExtension(EnevDefinition::propertyName());
if ($collection instanceof EnevCollection) {
$result = $this->productService->prepareEprel(
$collection,
$salesChannelContext
);
}
// TODO, add systemConfig to find always enev data (e.g. productSlider doesn't have CriteriaEvent)
$configFindAlways = $this->systemConfigService->get('GbmedEnev.config.findAlways');
$findAlways = !($configFindAlways !== null) || (bool)$configFindAlways;
if ($findAlways && (!($result instanceof EnevCollection) || !count($result->getElements()))) {
$result = $this->findEnevByProduct($product, $salesChannelContext);
}
if ($result instanceof EnevCollection) {
$product->addExtension(EnevDefinition::propertyName(), $result);
}
}
/**
* helper to find enev
*
* @param ProductEntity $product
* @param SalesChannelContext $salesChannelContext
* @return EntityCollection|null
* @throws \GuzzleHttp\Exception\GuzzleException
*/
private function findEnevByProduct(
ProductEntity $product,
SalesChannelContext $salesChannelContext
): ?EntityCollection {
$result = null;
try {
$result = $this->productService->findEnevByProductId($product->getId(), $salesChannelContext->getContext());
if (!$result->getTotal() && $product->getParentId()) {
$result = $this->productService->findEnevByProductId(
$product->getParentId(),
$salesChannelContext->getContext()
);
}
$result = $result->getTotal()
? $this->productService->prepareEprel($result->getEntities(), $salesChannelContext)
: null;
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
return $result;
}
private function addCriteria(Criteria $criteria): void
{
$criteria->addAssociation(EnevDefinition::propertyName())
->addAssociation(EnevDefinition::propertyName() . '.media')
->addAssociation(EnevDefinition::propertyName() . '.datasheet')
->addAssociation(EnevDefinition::propertyName() . '.icon');
}
}