<?php declare(strict_types = 1);
namespace Cbax\ModulManufacturers\Components;
use Doctrine\DBAL\Connection;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingLoader;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
use Shopware\Core\Content\Cms\CmsPageEntity;
use Shopware\Core\Content\Cms\DataResolver\CmsSlotsDataResolver;
use Shopware\Core\Content\Cms\DataResolver\ResolverContext\ResolverContext;
use Shopware\Core\Content\Cms\Aggregate\CmsSection\CmsSectionCollection;
use Shopware\Core\Framework\Uuid\Uuid;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Cbax\ModulManufacturers\Core\Content\Events\CbaxManufacturersProductListingCriteriaEvent;
class ManufacturersHelper
{
/**
* @var SystemConfigService
*/
private $systemConfigService;
/**
* @var Connection
*/
private $connection;
/**
* @var SalesChannelRepositoryInterface
*/
private $productRepository;
/**
* @var EntityRepositoryInterface
*/
private $manufacturerRepository;
/**
* @var EntityRepositoryInterface
*/
private $cmsPageRepository;
/**
* @var CmsSlotsDataResolver
*/
private $slotDataResolver;
/**
* @var ProductListingLoader
*/
private $listingLoader;
/**
* @var EventDispatcherInterface
*/
private $eventDispatcher;
public function __construct(
SystemConfigService $systemConfigService,
Connection $connection,
SalesChannelRepositoryInterface $productRepository,
EntityRepositoryInterface $manufacturerRepository,
EntityRepositoryInterface $cmsPageRepository,
CmsSlotsDataResolver $slotDataResolver,
ProductListingLoader $listingLoader,
EventDispatcherInterface $eventDispatcher
)
{
$this->connection = $connection;
$this->systemConfigService = $systemConfigService;
$this->productRepository = $productRepository;
$this->manufacturerRepository = $manufacturerRepository;
$this->cmsPageRepository = $cmsPageRepository;
$this->slotDataResolver = $slotDataResolver;
$this->listingLoader = $listingLoader;
$this->eventDispatcher = $eventDispatcher;
}
/**
* weitere allgemeine Templatedaten ermitteln, aktuell Top Hersteller
* @param $unsortedData array Liste von Herstellern
* @param $config array
* @return array Top Hersteller
*/
public function getTemplateData($unsortedData, $config)
{
$result['premiums'] = array();
foreach ($unsortedData as $manufacturer)
{
if (!empty($manufacturer->countArticle) || $config['displayFilter'] === 'showAll')
{
$customFields = $manufacturer->get('translated')['customFields'];
if (!empty($customFields['cbaxManufacturerPremium']) && empty($customFields['cbaxManufacturerIsHidden']))
{
array_push($result['premiums'], $manufacturer);
}
}
}
return $result;
}
/** Daten für Slider in Product Detail Page
* @param $manufacturerId
* @param $categoryId
* @param $salesChannelContext
* @return mixed
* @throws \Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException
*/
public function getProductsByManufacturer($manufacturerId, $categoryId, $salesChannelContext, $sorting, $limit, $currentProductId = null)
{
$salesChannelId = $salesChannelContext->getSalesChannel()->get('id');
$hideCloseoutProductsWhenOutOfStock = $this->systemConfigService->get("core.listing.hideCloseoutProductsWhenOutOfStock", $salesChannelId);
$qb = $this->connection->createQueryBuilder();
$query = $qb
->select([
'`product`.id as id'
])
->from('`product`', '`product`')
->innerJoin('`product`', 'product_visibility', 'visibilities',
'product.visibilities = visibilities.product_id AND
visibilities.sales_channel_id = UNHEX(:salesChannelId) AND
visibilities.product_version_id = :versionId')
->andWhere('visibilities.visibility = 30')
->andWhere('`product`.version_id = :versionId')
->andWhere('`product`.active = 1')
->andWhere('`product`.parent_id IS NULL')
->andWhere('`product`.product_manufacturer_id = UNHEX(:manufacturerId)')
->setParameter('manufacturerId', $manufacturerId)
->setParameter('salesChannelId', $salesChannelId)
->setParameter('versionId', Uuid::fromHexToBytes(Defaults::LIVE_VERSION));
if ($hideCloseoutProductsWhenOutOfStock == true)
{
$query->andWhere('(
(`product`.is_closeout != 1 OR `product`.available = 1)
OR (
`product`.child_count > 0
AND
EXISTS
(SELECT prod2.id FROM `product` AS prod2 WHERE prod2.parent_id=`product`.id
AND prod2.version_id = :versionId
AND (IFNULL(prod2.is_closeout,`product`.is_closeout) != 1 OR prod2.available = 1)
)
)
)');
}
$productsResult = $query->execute()->fetchAll();
$productsIds = [];
if (!empty($productsResult))
{
foreach ($productsResult as $product)
{
if ($this->convertId($product['id']) == $currentProductId) {
continue;
}
$productsIds[] = $this->convertId($product['id']);
}
} else {
return $productsIds;
}
$criteria = new Criteria();
$criteria->addFilter(new EqualsAnyFilter('id', $productsIds));
if ($categoryId)
{
$criteria->addAssociation('categories');
$criteria->addFilter(new EqualsFilter('categories.id', $categoryId));
}
if ($sorting == 'name_asc')
{
$criteria->addSorting(new FieldSorting('name', FieldSorting::ASCENDING));
}
elseif ($sorting == 'name_desc')
{
$criteria->addSorting(new FieldSorting('name', FieldSorting::DESCENDING));
}
elseif ($sorting == 'date_asc')
{
$criteria->addSorting(new FieldSorting('releaseDate', FieldSorting::ASCENDING));
}
elseif ($sorting == 'date_desc')
{
$criteria->addSorting(new FieldSorting('releaseDate', FieldSorting::DESCENDING));
}
elseif ($sorting == 'price_asc')
{
$criteria->addSorting(new FieldSorting('cheapestPrice', FieldSorting::ASCENDING));
}
elseif ($sorting == 'price_desc')
{
$criteria->addSorting(new FieldSorting('cheapestPrice', FieldSorting::DESCENDING));
}
if ($limit >= 0)
{
$criteria->setLimit($limit);
}
$searchResult = $this->productRepository->search($criteria, $salesChannelContext);
$products = $searchResult->getElements();
return $products;
}
/** Daten für Slider in Product Detail Page
* @param $manufacturerId
* @param $salesChannelContext
* @return mixed
* @throws \Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException
*/
public function getManufacturerById($manufacturerId, $salesChannelContext)
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('id', $manufacturerId));
$criteria->addAssociation('media');
$searchResult = $this->manufacturerRepository->search($criteria, $salesChannelContext->getContext());
$manufacturer = $searchResult->getElements();
return $manufacturer;
}
/** Daten für Hersteller detail und index pages
* @param $showProductCount bool
* @param $config array
* @param $salesChannelId string
* @param $context Context
* @return array sorted manufacturers
* @throws \Exception
*/
public function getManufacturerByChar($showProductCount, $config, $salesChannelId, $context, $overviewPage = false)
{
$criteria = new Criteria();
$criteria->addSorting(new FieldSorting('name'));
if ($overviewPage) {
$criteria->addAssociation('media');
}
$manufacturers = $this->manufacturerRepository->search($criteria, $context)->getElements();
foreach ($manufacturers as &$item)
{
$item->countArticle = 0;
}
unset($item);
$hideCloseoutProductsWhenOutOfStock = $this->systemConfigService->get("core.listing.hideCloseoutProductsWhenOutOfStock", $salesChannelId);
$qb = $this->connection->createQueryBuilder();
$query = $qb
->select([
'COUNT(`product`.id) as count',
'LOWER(HEX(`product`.product_manufacturer_id)) as manufacturerId'
])
->from('`product`', '`product`')
->innerJoin('`product`', 'product_visibility', 'visibilities',
'product.visibilities = visibilities.product_id AND
visibilities.sales_channel_id = :salesChannelId AND
visibilities.product_version_id = :versionId')
->andWhere('`product`.version_id = :versionId')
->andWhere('visibilities.visibility = 30')
->andWhere('`product`.active = 1')
->andWhere('`product`.parent_id IS NULL')
->andWhere('`product`.product_manufacturer_id IS NOT NULL')
->setParameter('versionId', Uuid::fromHexToBytes(Defaults::LIVE_VERSION))
->setParameter('salesChannelId', Uuid::fromHexToBytes($salesChannelId))
->groupBy('manufacturerId');
if ($hideCloseoutProductsWhenOutOfStock == true)
{
$query->andWhere('(
(`product`.is_closeout != 1 OR `product`.available = 1)
OR (
`product`.child_count > 0
AND
EXISTS
(SELECT prod2.id FROM `product` AS prod2 WHERE prod2.parent_id=`product`.id
AND prod2.version_id = :versionId
AND (IFNULL(prod2.is_closeout,`product`.is_closeout) != 1 OR prod2.available = 1)
)
)
)');
}
$productsResult = $query->execute()->fetchAll();
foreach ($productsResult as $item)
{
if (!empty($manufacturers[$item['manufacturerId']]))
{
$manufacturers[$item['manufacturerId']]->countArticle = $item['count'];
}
}
$letters = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z');
$sortedManufacturers = array('letters' => array());
foreach ($manufacturers as $manufacturer) {
// Hersteller, die ausgeblendet werden sollen überspringen
if (!empty($manufacturer->getTranslated()['customFields']['cbaxManufacturerIsHidden'])) {
continue;
}
if (!($config['displayFilter'] === 'showAll') && empty($manufacturer->countArticle)) {
continue;
}
//alte Version, Umlaute in #
//$letter = ucfirst(substr($manufacturer->getTranslated()['name'], 0, 1));
//neue Version, Ä->A, ...
//zuerst Umlaute umwandeln, sonst Probleme
$changedName = strtr($manufacturer->getTranslated()['name'],['Ä'=>'A','ä'=>'a','Ü'=>'u','ü'=>'u','Ö'=>'o','ö'=>'o']);
$letter = ucfirst(substr($changedName, 0, 1));
// prüfen ob das erste Zeichen bereits in der Liste existiert und ggf. initiales leeres Array anlegen
if (empty($sortedManufacturers['letters'][$letter]) && in_array($letter, $letters)) {
$sortedManufacturers['letters'][$letter] = array();
} elseif (!in_array($letter, $letters)) {
// Zeichen, die nicht in $letters enthalten sind unter '#' zusammenfassen
if (!isset($sortedManufacturers['letters']['#'])) {
$sortedManufacturers['letters']['#'] = array();
}
$letter = '#';
}
array_push($sortedManufacturers['letters'][$letter], $manufacturer);
}
ksort($sortedManufacturers['letters']);
$result = array ();
// Eintrag für Sonderzeichen anlegen
if (!empty($sortedManufacturers['letters']['#'])) {
$res0 = $sortedManufacturers['letters']['#'];
} else {
$res0 = array();
}
$countRes0 = 0;
foreach ($res0 as $manuf) {
// alle Produkte des Buchstaben
$countRes0 += $manuf->countArticle;
}
$result[0] = array(
'char' => '#',
'active' => (count($res0) > 0),
'countManufacturer' => count($res0),
'countArticle' => $countRes0,
'items' => $res0
);
// Einträge für $letters anlegen
foreach ($letters as $letter) {
if (!empty($sortedManufacturers['letters'][$letter])) {
$res = $sortedManufacturers['letters'][$letter];
$count = 0;
foreach ($res as $manuf) {
$count += $manuf->countArticle;
}
array_push($result, array(
'char' => $letter,
'active' => (count($res) > 0),
'countManufacturer' => count($res),
'countArticle' => $count,
'items' => $res,
));
} else {
array_push($result, array(
'char' => $letter,
'active' => false,
'countManufacturer' => 0,
'countArticle' => 0,
'items' => array()
));
}
}
if ($overviewPage) {
//overview page
return [
'sortedData' => $result,
'unsortedData' => $manufacturers
];
}
//detail page
return $result;
}
public function convertId($value)
{
$newValue = null;
try {
$newValue = Uuid::fromBytesToHex($value);
} catch (\Exception $e) {
// use if needed
}
return $newValue;
}
public function getCmsPage($id, $context)
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('id', $id));
$criteria->addAssociation('sections');
$criteria->addAssociation('sections.blocks');
$criteria
->getAssociation('sections')
->addAssociation('backgroundMedia');
$criteria
->getAssociation('sections.blocks')
->addAssociation('backgroundMedia')
->addAssociation('slots');
$searchResult = $this->cmsPageRepository->search($criteria, $context);
$cmsPage = $searchResult->first();
return $cmsPage;
}
public function loadSlotData(CmsPageEntity $page, ResolverContext $resolverContext): void
{
$slots = $this->slotDataResolver->resolve($page->getSections()->getBlocks()->getSlots(), $resolverContext);
$page->getSections()->getBlocks()->setSlots($slots);
}
public function filterEmptyCmsBlocksDetail($page, $cbaxModulManufacturers)
{
$cmsPage = $page->getCmsPage();
if (empty($cmsPage)) {
return $page;
}
$cmsSectionCollection = new CmsSectionCollection();
foreach ($cmsPage->getSections() as $section) {
$newBlocksCollection = $section->getBlocks();
foreach ($section->getBlocks() as $block) {
//Banner Block
if ($block->getType() === 'cbax-manufacturers-banner') {
$slot = $block->getSlots()->first();
if ($slot->getType() === 'cbax-manufacturers-banner' &&
empty($cbaxModulManufacturers['bannerMedia']) &&
(empty($slot->getConfig()) ||
(!empty($slot->getConfig()) &&
empty($slot->getConfig()['altUrl']['value'])
)
)
)
{
$newBlocksCollection->filterAndReduceByProperty('id', $block->getId());
}
} //Cbax Image-Text Block
elseif ($block->getType() === 'cbax-manufacturers-image-text') {
$imageIsEmpty = false;
$textIsEmpty = false;
foreach ($block->getSlots() as $slot) {
if ($slot->getType() === 'image' &&
empty($cbaxModulManufacturers['manufacturer']->getMedia()) &&
(empty($slot->getConfig()) ||
(!empty($slot->getConfig()) &&
$slot->getConfig()['media']['source'] === 'mapped' &&
$slot->getConfig()['media']['value'] === 'product_manufacturer.media'
)
)
)
{
$imageIsEmpty = true;
}
if ($slot->getType() === 'text' && empty($slot->getConfig()))
{
$textIsEmpty = true;
}
if ($slot->getType() === 'text' &&
!empty($slot->getConfig()) &&
!empty($slot->getConfig()['content']) &&
$slot->getConfig()['content']['source'] === 'mapped'
)
{
$propertyArray = explode('.', $slot->getConfig()['content']['value']);
if (!empty($propertyArray)) {
$property = end($propertyArray);
if ($property && !in_array('customFields', $propertyArray) &&
empty($cbaxModulManufacturers['manufacturer']->getTranslated()[$property])
)
{
$textIsEmpty = true;
} elseif ($property &&
in_array('customFields', $propertyArray) &&
(empty($cbaxModulManufacturers['manufacturer']->getTranslated()['customFields']) ||
(!empty($cbaxModulManufacturers['manufacturer']->getTranslated()['customFields']) &&
empty($cbaxModulManufacturers['manufacturer']->getTranslated()['customFields'][$property])
)
)
)
{
$textIsEmpty = true;
}
}
}
}
$blockIsEmpty = $imageIsEmpty && $textIsEmpty;
if ($blockIsEmpty) {
$newBlocksCollection->filterAndReduceByProperty('id', $block->getId());
}
}
}
$section->setBlocks($newBlocksCollection);
$cmsSectionCollection->add($section);
}
$cmsPage->setSections($cmsSectionCollection);
$page->setCmsPage($cmsPage);
return $page;
}
public function filterEmptyCmsBlocksIndex($page, $cbaxModulManufacturers)
{
$cmsPage = $page->getCmsPage();
if (empty($cmsPage)) {
return $page;
}
$cmsSectionCollection = new CmsSectionCollection();
foreach ($cmsPage->getSections() as $section) {
$newBlocksCollection = $section->getBlocks();
foreach ($section->getBlocks() as $block) {
//Image Banner Block
if ($block->getType() === 'image-cover' || $block->getType() === 'image') {
$slot = $block->getSlots()->first();
if ($slot->getType() === 'image' &&
(empty($slot->getConfig()) ||
(!empty($slot->getConfig()) &&
!empty($slot->getConfig()['media']) &&
empty($slot->getConfig()['media']['value'])
)
)
)
{
$newBlocksCollection->filterAndReduceByProperty('id', $block->getId());
}
} //Text Block
elseif ($block->getType() === 'text') {
$slot = $block->getSlots()->first();
if ($slot->getType() === 'text' &&
(empty($slot->getConfig()) ||
(!empty($slot->getConfig()) &&
!empty($slot->getConfig()['content']) &&
empty($slot->getConfig()['content']['value'])
)
)
)
{
$newBlocksCollection->filterAndReduceByProperty('id', $block->getId());
}
} // Top Slider
elseif ($block->getType() === 'cbax-manufacturers-topslider') {
$slot = $block->getSlots()->first();
if ($slot->getType() === 'cbax-manufacturers-topslider' &&
empty($cbaxModulManufacturers['templateData']['premiums']))
{
$newBlocksCollection->filterAndReduceByProperty('id', $block->getId());
}
}
}
$section->setBlocks($newBlocksCollection);
$cmsSectionCollection->add($section);
}
$cmsPage->setSections($cmsSectionCollection);
$page->setCmsPage($cmsPage);
return $page;
}
// daten für Hersteller Detail page
public function getProductsListing($manufacturerId, $pageNumber, $salesChannelContext): EntitySearchResult
{
$criteria = new Criteria();
$criteria->addAssociation('cover');
$criteria->addAssociation('prices');
$criteria->addAssociation('unit');
$criteria->addAssociation('deliveryTime');
$criteria->addAssociation('visibilities');
$criteria->addFilter(new EqualsFilter('manufacturerId', $manufacturerId));
$saleschannelId = $salesChannelContext->getSalesChannel()->getId();
$criteria->addFilter(new MultiFilter(
MultiFilter::CONNECTION_AND,
[
new EqualsFilter('visibilities.visibility', 30),
new EqualsFilter('visibilities.salesChannelId', $saleschannelId)
]
));
// Sortierung der Produkte nach Namen
$criteria->addSorting(new FieldSorting('name'));
// Pagenierung
$limit = $this->systemConfigService->get("core.listing.productsPerPage", $saleschannelId);
$limit = !empty($limit) ? (int)$limit : 20;
$criteria->setLimit($limit);
// Offset für Pagenierung ermitteln
if ($pageNumber > 0) {
$offset = $limit * ($pageNumber -1);
$criteria->setOffset($offset);
}
// Pagenierung aktivieren
$criteria->setTotalCountMode(1);
$event = $this->eventDispatcher->dispatch(new CbaxManufacturersProductListingCriteriaEvent($criteria, $manufacturerId));
$criteria = $event->getCriteria();
$result = $this->listingLoader->load($criteria, $salesChannelContext);
return $result;
}
}