<?php declare(strict_types=1);
namespace S360ProductRecommendations\Subscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Content\Cms\SalesChannel\Struct\ProductSliderStruct;
use Shopware\Core\Content\ProductStream\Service\ProductStreamBuilderInterface;
use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingLoader;
class ProductDetailSubscriber implements EventSubscriberInterface
{
private EntityRepositoryInterface $categoryRepository;
private ProductStreamBuilderInterface $productStreamBuilder;
private ProductListingLoader $listingLoader;
public function __construct(
EntityRepositoryInterface $categoryRepository,
ProductStreamBuilderInterface $productStreamBuilder,
ProductListingLoader $listingLoader
){
$this->categoryRepository = $categoryRepository;
$this->productStreamBuilder = $productStreamBuilder;
$this->listingLoader = $listingLoader;
}
public static function getSubscribedEvents(): array
{
// Return the events to listen to as array like this: <event to listen to> => <method to execute>
return [
ProductPageLoadedEvent::class => 'onProductPageLoaded'
];
}
// load either based on dynamic product group id or based on parent category
public function onProductPageLoaded(ProductPageLoadedEvent $event)
{
$productSliderStruct = new ProductSliderStruct();
$productPage = $event->getPage();
$context = $event->getContext();
$limit = 10;
$activeCategory = $productPage->getHeader()->getNavigation()->getActive();
$translated = $activeCategory->getTranslated();
$productStreamId = $translated["customFields"]["s360_product_recommendations_dynamic_product_group"] ?? false;
$currentProductId = $productPage->getProduct()->getId();
try {
if($productStreamId){
$filters = $this->productStreamBuilder->buildFilters($productStreamId, $context);
$criteria = new Criteria();
$criteria->addFilter(...$filters)->setLimit($limit);
$searchResult = $this->listingLoader->load($criteria, $event->getSalesChannelContext());
$products = $searchResult->getEntities();
if($products->has($currentProductId)){
$products->remove($currentProductId);
}
$productSliderStruct->setProducts($products);
} else {
// no dynamic product group assigned: try getting 10 products from parent category
$parentCategoryId = $activeCategory->getParentId();
$productCollection = $this->getProductCollectionFromCat($parentCategoryId, $limit, $context);
// if no products assigned to category, get from current category
if($productCollection->count() === 0){
$productCollection = $this->getProductCollectionFromCat($activeCategory->getId(), $limit, $context);
}
if($productCollection->count() > 0){
// remove current product id in case it happens to be included
$productIds = $productCollection->getIds();
unset($productIds[$currentProductId]);
if(count($productIds) > 0){
$searchResult = $this->listingLoader->load(new Criteria($productIds), $event->getSalesChannelContext());
$products = $searchResult->getEntities();
$productSliderStruct->setProducts($products);
}
}
}
// if no products found, struct will be empty
$productPage->addExtension("s360ProductRecommendations", $productSliderStruct);
} catch (\Exception $e){
// if exception, empty struct will be added
$productPage->addExtension("s360ProductRecommendations", $productSliderStruct);
}
}
private function getProductCollectionFromCat($categoryId, $limit, $context){
$criteria = new Criteria([$categoryId]);
$criteria->addAssociation('products');
$criteria->getAssociation('products')->setLimit($limit);
$categorySearchResult = $this->categoryRepository->search($criteria, $context)->first();
// products that are assigned directly
return $categorySearchResult->getProducts();
}
}