<?php
namespace BAL\Plugin\ThreeDimensionModelView\Subscriber;
use BAL\Plugin\ThreeDimensionModelView\Entity\Category\CategoryExtension;
use BAL\Plugin\ThreeDimensionModelView\Entity\CategoryExtension\CategoryExtensionEntity;
use BAL\Plugin\ThreeDimensionModelView\Entity\Product\ProductExtension;
use BAL\Plugin\ThreeDimensionModelView\Entity\ProductModel\ProductModelCollection;
use BAL\Plugin\ThreeDimensionModelView\Entity\ProductModel\ProductModelEntity;
use Shopware\Core\Content\Category\CategoryEntity;
use Shopware\Core\Content\Media\MediaDefinition;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityLoadedEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
use Shopware\Storefront\Page\Product\ProductPageCriteriaEvent;
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class ProductPageSubscriber implements EventSubscriberInterface
{
private const MODE_BREADCRUMB = 'breadcrumb';
private const MODE_TREE = 'tree';
private EventDispatcherInterface $eventDispatcher;
private MediaDefinition $mediaDefinition;
private EntityRepositoryInterface $categoryRepository;
public function __construct(
EventDispatcherInterface $eventDispatcher,
MediaDefinition $mediaDefinition,
EntityRepositoryInterface $categoryRepository
) {
$this->eventDispatcher = $eventDispatcher;
$this->mediaDefinition = $mediaDefinition;
$this->categoryRepository = $categoryRepository;
}
public static function getSubscribedEvents() : array
{
return [
ProductPageCriteriaEvent::class => 'onProductPageCriteriaEvent',
ProductPageLoadedEvent::class => 'onProductPageLoadedEvent',
];
}
public function onProductPageCriteriaEvent(ProductPageCriteriaEvent $event) : void
{
$productModelCriteria = $event->getCriteria()->getAssociation(ProductExtension::FIELD_NAME);
$productModelCriteria->addAssociation('threeDimensionModel');
$productModelCriteria->addFilter(
new EqualsFilter('active', true),
new EqualsFilter('threeDimensionModel.active', true),
);
$productModelCriteria->addSorting(new FieldSorting('position', FieldSorting::ASCENDING));
}
public function onProductPageLoadedEvent(ProductPageLoadedEvent $event) : void
{
/**
* @var SalesChannelProductEntity $productEntity
* @var CategoryEntity|null $seoCategoryEntity
* @var ProductModelCollection|null $productModelCollection
*/
$backgroundColor = null;
$categoryViewerConfigs = [];
$productEntity = $event->getPage()->getProduct();
$seoCategoryEntity = $productEntity->getSeoCategory();
$productModelCollection = $productEntity->getExtension(ProductExtension::FIELD_NAME);
if ($productModelCollection !== null) {
if ($seoCategoryEntity === null) {
$this->collectCategoryViewerConfigs(
$categoryViewerConfigs,
$backgroundColor,
self::MODE_TREE,
$productEntity->getCategoryTree(),
$event
);
} else {
$this->collectCategoryViewerConfigs(
$categoryViewerConfigs,
$backgroundColor,
self::MODE_BREADCRUMB,
array_keys($seoCategoryEntity->getPlainBreadcrumb()),
$event,
$productEntity->getCategoryIds()
);
}
$categoryConfig = $this->mergeViewerConfigs(...$categoryViewerConfigs);
$this->dispatchMediaLoadedEvent($productModelCollection, $event);
/** @var ProductModelEntity $productModelEntity */
foreach ($productModelCollection->getElements() as $productModelEntity) {
$threeDimensionModel = $productModelEntity->getThreeDimensionModel();
if ($threeDimensionModel === null || !$threeDimensionModel->isActive()) {
// shouldn't actually happen, because model is required and criteria with active filter
continue;
}
$productModelViewerConfig = $productModelEntity->getViewerConfig();
$modelViewerConfig = $threeDimensionModel->getViewerConfig();
$productModelEntity->setViewerConfig($this->mergeViewerConfigs($modelViewerConfig, $categoryConfig, $productModelViewerConfig));
$productModelEntity->setBackgroundColor(
$this->mergeColor(
$productModelEntity->getBackgroundColor(),
$backgroundColor,
$threeDimensionModel->getBackgroundColor()
)
);
}
}
}
protected function collectCategoryViewerConfigs(
array &$categoryViewerConfigs,
?string &$backgroundColor,
string $mode,
?array $categoryIds,
ProductPageLoadedEvent $event,
?array $leadCategoryIds = null
) {
if (empty($categoryIds)) {
return;
}
$searchResult = $this->categoryRepository->search(new Criteria($categoryIds), $event->getContext());
if ($searchResult->getTotal() === 0) {
return;
}
if ($mode === self::MODE_BREADCRUMB) {
foreach ($categoryIds as $categoryId) {
$this->collectCategoryViewerConfig(
$categoryViewerConfigs,
$backgroundColor,
$searchResult->get($categoryId)
);
}
} elseif ($mode === self::MODE_TREE) {
// other algorithmus, tree based? leaf's in $leadCategoryIds
foreach ($categoryIds as $categoryId) {
$this->collectCategoryViewerConfig(
$categoryViewerConfigs,
$backgroundColor,
$searchResult->get($categoryId)
);
}
}
}
protected function collectCategoryViewerConfig(
array &$categoryViewerConfigs,
?string &$backgroundColor,
?CategoryEntity $categoryEntity
) {
if ($categoryEntity !== null) {
/** @var CategoryExtensionEntity|null $configEntity */
$configEntity = $categoryEntity->getExtension(CategoryExtension::FIELD_NAME);
if ($configEntity !== null) {
$backgroundColor = $this->mergeColor($configEntity->getBackgroundColor(), $backgroundColor);
$categoryViewerConfigs[] = $configEntity->getViewerConfig();
}
}
}
protected function dispatchMediaLoadedEvent(ProductModelCollection $productModelCollection, ProductPageLoadedEvent $event)
{
$medias = [];
foreach ($productModelCollection->getElements() as $productModelEntity) {
/** @var ProductModelEntity $productModelEntity */
if ($productModelEntity->getThreeDimensionModel() !== null) {
foreach (
[
$productModelEntity->getThreeDimensionModel()->getModel(),
$productModelEntity->getThreeDimensionModel()->getBackgroundImage(),
$productModelEntity->getThreeDimensionModel()->getPreviewImage(),
] as $media
) {
if ($media !== null) {
$medias[$media->getId()] = $media;
}
}
}
}
$mediaLoadedEvent = new EntityLoadedEvent($this->mediaDefinition, $medias, $event->getContext());
$this->eventDispatcher->dispatch(
$mediaLoadedEvent,
$mediaLoadedEvent->getName()
);
}
protected function mergeViewerConfigs(array...$configs) : array
{
$configs = array_filter($configs, 'is_array');
return array_merge(...$configs);
}
protected function mergeColor(?string...$colors) : ?string
{
foreach ($colors as $color) {
if ( !empty($color)) {
return $color;
}
}
return null;
}
}