<?php
declare(strict_types=1);
namespace EsmComputer\Storefront\Subscriber;
use Shopware\Core\Content\Category\CategoryEntity;
use Shopware\Core\Content\Product\Events\ProductListingResultEvent;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Struct\ArrayStruct;
use Shopware\Storefront\Page\Navigation\NavigationPageLoadedEvent;
use Shopware\Storefront\Page\Page;
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use function array_key_exists;
use function array_values;
use function date;
use function header;
use function html_entity_decode;
use function implode;
use function str_ends_with;
use function var_dump;
class SeoSubscriber implements EventSubscriberInterface
{
private CONST CUSTOM_FIELD_NOODP = 'custom_category_noodp';
private EntityRepository $categoryRepository;
private ?EntityRepository $ratingRepository;
public function __construct(EntityRepository $categoryRepository, ?EntityRepository $ratingRepository)
{
$this->categoryRepository = $categoryRepository;
$this->ratingRepository = $ratingRepository;
}
public static function getSubscribedEvents(): array
{
return [
NavigationPageLoadedEvent::class => 'onNavigationPageLoaded',
ProductPageLoadedEvent::class => 'onProductPageLoaded',
ExceptionEvent::class => 'redirectToPageWithSlash'
];
}
public function redirectToPageWithSlash(ExceptionEvent $event): void
{
$path = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
if($event->getThrowable() instanceof NotFoundHttpException && substr($path, -1) !== '/') {
header("Location: " . $path . '/', true, 302);
exit;
}
}
public function onNavigationPageLoaded(NavigationPageLoadedEvent $event): void
{
$this->setMetaInformation($event->getPage(), $event->getRequest());
$this->loadReviews($event);
}
public function onProductPageLoaded(ProductPageLoadedEvent $event): void
{
$this->setMetaInformation($event->getPage(), $event->getRequest());
$this->loadExpertRating($event);
$this->loadEnvironmentSavings($event);
}
protected function setMetaInformation(Page $page, Request $request): void
{
$metaInformation = $page->getMetaInformation();
if($metaInformation) {
$metaInformation->setRobots($this->getRobots($page, $request));
$metaInformation->setMetaTitle($this->getTitle($page, $request));
$metaInformation->setMetaDescription(html_entity_decode($metaInformation->getMetaDescription()));
$metaInformation->setCopyrightYear(date('Y'));
$page->setMetaInformation($metaInformation);
}
}
private function getRobots(Page $page, Request $request): string
{
$robots = ['index' => 'index', 'follow' => 'follow'];
/** @var CategoryEntity $category */
$category = $page->getExtension('category');
if($category && $category->getCustomFields() && array_key_exists(self::CUSTOM_FIELD_NOODP, $category->getCustomFields()) && $category->getCustomFields()[self::CUSTOM_FIELD_NOODP] === true) {
$robots['noodp'] = 'noodp';
}
if($request->get('p')) {
$robots['index'] = 'noindex';
}
return implode(',', array_values($robots));
}
private function getTitle(Page $page, Request $request): string
{
return html_entity_decode($page->getMetaInformation()->getMetaTitle()) . ($request->getRequestUri() !== '/' ? ' | ESM Computer' : '');
}
private function loadReviews(NavigationPageLoadedEvent $event): void
{
$navigationId = $event->getRequest()->attributes->get('navigationId');
if(!$navigationId || ($event->getPage()->getCmsPage() && $event->getPage()->getCmsPage()->getType() !== 'product_list')) {
return;
}
$apiUrl = 'https://api.trustedshops.com/rest/public/v2/shops/XE152DC4802D12725E85E22A77A76A902/quality/reviews.json';
$tmpFolder = sys_get_temp_dir();
$tmpFile = $tmpFolder . '/reviews.json';
$timeOut = 86400;
if(!file_exists($tmpFile) || time() - filemtime($tmpFile) > $timeOut) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, false);
curl_setopt($ch, CURLOPT_URL, $apiUrl);
$output = curl_exec($ch); curl_close($ch);
// Write the contents back to the file
// Make sure you can write to file's destination
file_put_contents($tmpFile, $output);
}
$trustedShopMarkup = '';
if ($jsonObject = json_decode(file_get_contents($tmpFile), true)) {
$result = $jsonObject['response']['data']['shop']['qualityIndicators']['reviewIndicator']['overallMark'];
$count = $jsonObject['response']['data'] ['shop']['qualityIndicators']['reviewIndicator']['activeReviewCount'];
$shopName = $jsonObject['response']['data']['shop']['name'];
$max = "5.00";
if ($count > 0) {
$trustedShopMarkup = '<script type="application/ld+json">';
$trustedShopMarkup .= '{';
$trustedShopMarkup .= '"@context": "http://schema.org",';
$trustedShopMarkup .= '"@type": "Organization",';
$trustedShopMarkup .= '"name": "'.$shopName.'",';
$trustedShopMarkup .= '"aggregateRating" : {';
$trustedShopMarkup .= '"@type": "AggregateRating",';
$trustedShopMarkup .= '"ratingValue" : "'.$result.'",';
$trustedShopMarkup .= '"bestRating" : "'.$max.'",';
$trustedShopMarkup .= '"ratingCount" : "'.$count.'"';
$trustedShopMarkup .= '}}';
$trustedShopMarkup .= '</script>';
}
}
$event->getPage()->addExtension('trustedShopsRating', new ArrayStruct([$trustedShopMarkup]));
}
public function loadExpertRating(ProductPageLoadedEvent $event)
{
$product = $event->getPage()->getProduct();
if($product->hasExtension('esm_expertrating') || !array_key_exists('custom_product_model', $product->getCustomFields())) {
return;
}
$model = $product->getCustomFields()['custom_product_model'];
if(!$model) {
return;
}
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('product.customFields.custom_product_model', $model));
$rating = $this->ratingRepository->search($criteria, $event->getContext())->first();
$product->addExtension('esm_expertrating', $rating);
}
public function loadEnvironmentSavings(ProductPageLoadedEvent $event)
{
$category = $event->getPage()->getProduct()->getSeoCategory();
if(!$category) {
return;
}
if(!array_key_exists('custom_category_co2_saving', $category->getCustomFields()) || !array_key_exists('custom_category_water_saving', $category->getCustomFields())) {
$category = $this->categoryRepository->search(new Criteria([$category->getParentId()]), $event->getContext())->first();
}
$customFields = $category->getCustomFields();
if(array_key_exists('custom_category_co2_saving', $customFields) && array_key_exists('custom_category_water_saving', $customFields)) {
$event->getPage()->addExtension('savings', new ArrayStruct(['co2' => $customFields['custom_category_co2_saving'] ?? 0, 'water' => $customFields['custom_category_water_saving'] ?? 0]));
}
}
}