<?php
declare(strict_types=1);
namespace WbfkExtensions\Subscriber;
use Shopware\Core\Content\Cms\Aggregate\CmsBlock\CmsBlockEntity;
use Shopware\Core\Content\Cms\Aggregate\CmsSection\CmsSectionEntity;
use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity;
use Shopware\Core\Content\Cms\CmsPageCollection;
use Shopware\Core\Content\Cms\CmsPageEntity;
use Shopware\Core\Content\Cms\Events\CmsPageLoadedEvent;
use Shopware\Core\Content\Cms\SalesChannel\Struct\ProductListingStruct;
use Shopware\Core\Content\Product\ProductCollection;
use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingResult;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use WbfkExtensions\Core\Content\Cms\SalesChannel\Struct\ProductPartialListingStruct;
class MultiPartProductListingSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
CmsPageLoadedEvent::class => 'onCmsPageLoadedEvent',
];
}
public function onCmsPageLoadedEvent(CmsPageLoadedEvent $event): void
{
/** @var CmsPageCollection $pageCol */
$pageColl = $event->getResult();
$pages = $pageColl->getElements();
foreach ($pages as $page) {
if ($page->getType() === 'product_list') {
$this->handleMultipartListingPage($page);
}
}
}
private function handleMultipartListingPage(CmsPageEntity &$page): void
{
/** @var CmsSlotEntity $primaryListingElement */
$primaryListingElement = null;
/** @var CmsSlotEntity[] $subListingElements */
$subListingElements = [];
/** @var CmsSectionEntity $section */
foreach ($page->getSections()->getElements() as $section) {
/** @var CmsBlockEntity $block */
foreach ($section->getBlocks()->getElements() as $block) {
/** @var CmsSlotEntity $slotEntity */
foreach ($block->getSlots()->getElements() as $slotEntity) {
switch ($slotEntity->getType()) {
case 'product-listing':
$primaryListingElement = $slotEntity;
break;
case 'wbfk-multipart-product-listing':
$subListingElements[] = $slotEntity;
break;
}
}
}
}
if ($primaryListingElement && $subListingElements) {
/** @var ProductListingStruct $prodListData */
$prodListData = $primaryListingElement->getData();
/** @var ProductListingResult $prodListing */
$prodListing = $prodListData->getListing();
$addProdListElement = function (CmsSlotEntity $element, $start, $numProducts = null) use (&$prodListing) {
$numProductsOnPage = $prodListing->getLimit();
$numProducts = $numProducts ?: $numProductsOnPage;
$end = $start + $numProducts;
$partialSearchResult = $this->createPartialProdListingFromProdListing($prodListing, $start, $numProducts);
$productListingStruct = new ProductPartialListingStruct();
$productListingStruct->setListing($partialSearchResult);
$productListingStruct->setHideTopNav($start > 0);
$productListingStruct->setHideBottomNav($end < $numProductsOnPage);
$productListingStruct->setStart($start);
$productListingStruct->setEnd($end);
$element->setData($productListingStruct);
// We cannot remove the empty product partial lists because there may be a filter applied.
// When the user removes the filter (AJAX), the product partial lists should appear and be populated.
// else $this->removeMultiPartElement($element);
};
$start = 0;
$previous = $primaryListingElement;
foreach ($subListingElements as $listElement) {
$conf = $listElement->getConfig();
$numProducts = $conf && isset($conf['numberProducts']) && isset($conf['numberProducts']['value']) ? (int)$conf['numberProducts']['value'] : 8;
$addProdListElement($previous, $start, $numProducts);
$previous = $listElement;
$start += $numProducts;
}
$addProdListElement($previous, $start);
}
}
private function createPartialProdListingFromProdListing(ProductListingResult &$prodListing, int $start, int $numProducts): ProductListingResult
{
$partialList = array_slice($prodListing->getElements(), $start, $numProducts, true);
$partialCollection = new ProductCollection($partialList);
$partialSearchResult = new ProductListingResult(
$prodListing->getEntity(),
$prodListing->getTotal(),
$partialCollection,
$prodListing->getAggregations(),
$prodListing->getCriteria(),
$prodListing->getContext()
);
$partialSearchResult->setSorting($prodListing->getSorting());
$partialSearchResult->setAvailableSortings($prodListing->getAvailableSortings());
$partialSearchResult->setStreamId($prodListing->getStreamId());
foreach ($prodListing->getCurrentFilters() as $key => $filter) {
$partialSearchResult->addCurrentFilter($key, $filter);
}
return $partialSearchResult;
}
private function removeMultiPartElement(CmsSlotEntity $toRemove)
{
if ($toRemove->getType() !== 'wbfk-multipart-product-listing') {
return;
}
$block = $toRemove->getBlock();
if ($block) {
$section = $block->getSection();
if ($section && count($block->getSlots()->getElements()) === 1) {
// This is the only element in the block, Remove the block from the section,
$page = $section->getPage();
if ($page && count($section->getBlocks()->getElements()) === 1) {
// This is the only block in the section, Remove the section from the page,
$page->getSections()->remove($section->getUniqueIdentifier());
} else {
$section->getBlocks()->remove($block->getUniqueIdentifier());
}
} else {
$block->getSlots()->remove($toRemove->getUniqueIdentifier());
}
}
}
}