custom/plugins/AcrisFaqCS/src/Storefront/Subscriber/ProductLoadedSubscriber.php line 154

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Acris\Faq\Storefront\Subscriber;
  3. use Acris\Faq\Components\Faq\FaqCmsPageService;
  4. use Acris\Faq\Components\Faq\FaqGateway;
  5. use Acris\Faq\Components\Faq\FaqService;
  6. use Acris\Faq\Core\Content\Cms\SalesChannel\Struct\FaqSingleStruct;
  7. use Acris\Faq\Core\Content\Cms\SalesChannel\Struct\FaqStruct;
  8. use Acris\Faq\Core\Framework\DataAbstractionLayer\Cache\FaqEntityCacheKeyGenerator;
  9. use Acris\Faq\Custom\FaqDefinition;
  10. use Acris\Faq\Custom\FaqEntity;
  11. use Acris\Faq\Custom\FaqGroupCollection;
  12. use Acris\Faq\Custom\FaqGroupEntity;
  13. use Shopware\Core\Content\Category\Event\CategoryRouteCacheTagsEvent;
  14. use Shopware\Core\Content\Category\SalesChannel\CategoryRouteResponse;
  15. use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity;
  16. use Shopware\Core\Content\Cms\CmsPageCollection;
  17. use Shopware\Core\Content\Cms\CmsPageEntity;
  18. use Shopware\Core\Content\Cms\DataResolver\ResolverContext\EntityResolverContext;
  19. use Shopware\Core\Content\Cms\SalesChannel\SalesChannelCmsPageLoaderInterface;
  20. use Shopware\Core\Content\Product\SalesChannel\Detail\CachedProductDetailRoute;
  21. use Shopware\Core\Framework\Adapter\Cache\CacheInvalidator;
  22. use Shopware\Core\Framework\Context;
  23. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
  24. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  25. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  26. use Shopware\Core\Framework\Struct\ArrayEntity;
  27. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  28. use Shopware\Storefront\Page\Navigation\NavigationPageLoadedEvent;
  29. use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
  30. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  31. use Symfony\Component\HttpFoundation\Request;
  32. class ProductLoadedSubscriber implements EventSubscriberInterface
  33. {
  34.     public const ACRIS_STREAM_IDS_EXTENSION 'acrisStreamIds';
  35.     /**
  36.      * @var FaqService
  37.      */
  38.     private FaqService $faqService;
  39.     /**
  40.      * @var FaqGateway
  41.      */
  42.     private FaqGateway $faqGateway;
  43.     /**
  44.      * @var CacheInvalidator
  45.      */
  46.     private CacheInvalidator $logger;
  47.     private FaqCmsPageService $faqCmsPageService;
  48.     public function __construct(
  49.         FaqService       $faqService,
  50.         FaqGateway       $faqGateway,
  51.         CacheInvalidator $logger,
  52.         FaqCmsPageService $faqCmsPageService
  53.     )
  54.     {
  55.         $this->faqService $faqService;
  56.         $this->faqGateway $faqGateway;
  57.         $this->logger $logger;
  58.         $this->faqCmsPageService $faqCmsPageService;
  59.     }
  60.     public static function getSubscribedEvents()
  61.     {
  62.         return [
  63.             ProductPageLoadedEvent::class => [
  64.                 ['productLoaded'200]
  65.             ],
  66.             NavigationPageLoadedEvent::class => [
  67.                 ['onProductListingLoaded'200]
  68.             ],
  69.             'acris_faq.written' => [
  70.                 ['onFaqWritten'200]
  71.             ],
  72.             'acris_faq_group.written' => [
  73.                 ['onFaqGroupWritten'200]
  74.             ],
  75.             CategoryRouteCacheTagsEvent::class => [
  76.                 ['onCategoryRouteCacheTags'200]
  77.             ]
  78.         ];
  79.     }
  80.     public function productLoaded(ProductPageLoadedEvent $event): void
  81.     {
  82.         $request $event->getRequest();
  83.         if(empty($request) === true) {
  84.             return;
  85.         }
  86.         if (empty($event->getPage()) || empty($event->getPage()->getProduct()) || empty($event->getContext()->getLanguageId())) return;
  87.         $languageId $event->getContext()->getLanguageId();
  88.         $product $event->getPage()->getProduct();
  89.         if ($product->hasExtension(self::ACRIS_STREAM_IDS_EXTENSION) && !empty($product->getExtension(self::ACRIS_STREAM_IDS_EXTENSION))) {
  90.             $productStreamIds $product->getExtension(self::ACRIS_STREAM_IDS_EXTENSION)->get('ids');
  91.         } else {
  92.             $productStreamIds $this->faqGateway->getProductStreamIds($product->getId(), $event->getSalesChannelContext()->getContext());
  93.             $product->addExtension(self::ACRIS_STREAM_IDS_EXTENSION, new ArrayEntity(['ids' => $productStreamIds]));
  94.         }
  95.         if (empty($productStreamIds)) return;
  96.         $faqGroupResult $this->faqGateway->getProductDetailFaqGroup($productStreamIds$event->getContext());
  97.         if ($faqGroupResult->count() === 0) {
  98.             return;
  99.         }
  100.         $faqGroupCollection $faqGroupResult->getEntities();
  101.         $faqGroups $this->faqService->checkLanguage($faqGroupCollection->getElements(), $languageId);
  102.         $this->assignFaqsVideos($faqGroupCollection$event->getSalesChannelContext());
  103.         $this->assignFaqsCmsPage($request$faqGroupCollection$event->getSalesChannelContext());
  104.         $this->faqService->assignPreviewImage($faqGroupCollection$event->getSalesChannelContext());
  105.         $product->addExtension('acrisFaq'$faqGroupCollection);
  106.     }
  107.     public function onProductListingLoaded(NavigationPageLoadedEvent $event): void
  108.     {
  109.         $request $event->getRequest();
  110.         if(empty($request) === true) {
  111.             return;
  112.         }
  113.         if (empty($event->getPage()) || empty($event->getPage()->getNavigationId()) || empty($event->getContext()->getLanguageId())) return;
  114.         $languageId $event->getContext()->getLanguageId();
  115.         $categoryId $event->getPage()->getNavigationId();
  116.         $faqGroupResult $this->faqGateway->getCategoryFaqGroup($categoryId$event->getContext());
  117.         if ($faqGroupResult->count() === 0) {
  118.             return;
  119.         }
  120.         $faqGroupCollection $faqGroupResult->getEntities();
  121.         $faqGroups $this->faqService->checkLanguage($faqGroupCollection->getElements(), $languageId);
  122.         $this->assignFaqsVideos($faqGroupCollection$event->getSalesChannelContext());
  123.         $this->assignFaqsCmsPage($request$faqGroupCollection$event->getSalesChannelContext());
  124.         $this->faqService->assignPreviewImage($faqGroupCollection$event->getSalesChannelContext());
  125.         $event->getPage()->addExtension('acrisFaq'$faqGroupCollection);
  126.     }
  127.     public function onFaqWritten(EntityWrittenEvent $event): void
  128.     {
  129.         $results $event->getWriteResults();
  130.         $faqIds = [];
  131.         foreach ($results as $result) {
  132.             $payload $result->getPayload();
  133.             if (!empty($payload) && array_key_exists('id'$payload) && !empty($payload['id'])) {
  134.                 $faqIds[] = $payload['id'];
  135.             }
  136.         }
  137.         if (!empty($faqIds)) {
  138.             $this->clearCacheForFaq($faqIds$event->getContext());
  139.             $this->upsertMetaDataWithFaqIds($faqIds$event->getContext());
  140.         }
  141.     }
  142.     public function onFaqGroupWritten(EntityWrittenEvent $event): void
  143.     {
  144.         $results $event->getWriteResults();
  145.         $faqIds = [];
  146.         $faqGroupIds = [];
  147.         foreach ($results as $result) {
  148.             $payload $result->getPayload();
  149.             if (!empty($payload) && array_key_exists('id'$payload) && !empty($payload['id'])) {
  150.                 $faqGroupIds[] = $payload['id'];
  151.             }
  152.         }
  153.         if (!empty($faqGroupIds)) {
  154.             $faqSearchResult $this->faqService->getFaqGroups($faqGroupIds$event->getContext());
  155.             if ($faqSearchResult->count() === 0) return;
  156.             $faqIds $this->clearCacheForFaqGroup($faqSearchResult->getEntities(), $event->getContext());
  157.         }
  158.         if (!empty($faqIds)) {
  159.             // invalidates all routes which loads faqs
  160.             $this->logger->invalidate(
  161.                 array_map([FaqEntityCacheKeyGenerator::class, 'buildFaqTag'], $faqIds)
  162.             );
  163.         }
  164.     }
  165.     public function onCategoryRouteCacheTags(CategoryRouteCacheTagsEvent $event): void
  166.     {
  167.         $event->addTags(
  168.             $this->extractFaqIds($event->getResponse())
  169.         );
  170.     }
  171.     public function extractFaqIds(CategoryRouteResponse $response): array
  172.     {
  173.         $page $response->getCategory()->getCmsPage();
  174.         if ($page === null) {
  175.             return [];
  176.         }
  177.         $ids = [];
  178.         $slots $page->getElementsOfType('acris-faq');
  179.         /** @var CmsSlotEntity $slot */
  180.         foreach ($slots as $slot) {
  181.             $faqStruct $slot->getData();
  182.             if (!$faqStruct instanceof FaqStruct) {
  183.                 continue;
  184.             }
  185.             if (empty($faqStruct->getFaq())) {
  186.                 continue;
  187.             }
  188.             if ($faqStruct->getFaq()->count() === 0) {
  189.                 continue;
  190.             }
  191.             foreach ($faqStruct->getFaq()->getElements() as $faqGroup) {
  192.                 if ($faqGroup->getFaqs()->count() === 0) continue;
  193.                 foreach ($faqGroup->getFaqs()->getElements() as $faq) {
  194.                     $ids[] = $faq->getId();
  195.                 }
  196.             }
  197.         }
  198.         $slots $page->getElementsOfType('acris-faq-single');
  199.         /** @var CmsSlotEntity $slot */
  200.         foreach ($slots as $slot) {
  201.             $faqSingleStruct $slot->getData();
  202.             if (!$faqSingleStruct instanceof FaqSingleStruct) {
  203.                 continue;
  204.             }
  205.             if ($faqSingleStruct->getFaqSingle()->count() === 0) {
  206.                 continue;
  207.             }
  208.             foreach ($faqSingleStruct->getFaqSingle()->getElements() as $faq) {
  209.                 $ids[] = $faq->getId();
  210.             }
  211.         }
  212.         $ids array_values(array_unique(array_filter($ids)));
  213.         return array_merge(
  214.             array_map([FaqEntityCacheKeyGenerator::class, 'buildFaqTag'], $ids),
  215.         );
  216.     }
  217.     private function assignFaqsVideos(FaqGroupCollection $faqGroupCollectionSalesChannelContext $context): void
  218.     {
  219.         if ($faqGroupCollection->count() === 0) return;
  220.         /** @var FaqGroupEntity $faqGroup */
  221.         foreach ($faqGroupCollection->getElements() as $faqGroup) {
  222.             if (empty($faqGroup->getFaqs()) || $faqGroup->getFaqs()->count() === 0) continue;
  223.             foreach ($faqGroup->getFaqs()->getElements() as $faq) {
  224.                 if (!empty($faq->getTranslation('embedCode')) && $faq->getVideoType() === FaqService::DEFAULT_FAQ_VIDEO_YOUTUBE_TYPE) {
  225.                     $this->faqService->loadFaqVideo($faq$context);
  226.                 }
  227.             }
  228.         }
  229.     }
  230.     private function assignFaqsCmsPage(Request $requestFaqGroupCollection $faqGroupCollectionSalesChannelContext $context): void
  231.     {
  232.         if ($faqGroupCollection->count() === 0) return;
  233.         /** @var FaqGroupEntity $faqGroup */
  234.         foreach ($faqGroupCollection->getElements() as $faqGroup) {
  235.             if (empty($faqGroup->getFaqs()) || $faqGroup->getFaqs()->count() === 0) continue;
  236.             foreach ($faqGroup->getFaqs()->getElements() as $faq) {
  237.                 if (!empty($faq->getLayout()) && $faq->getLayout() === 'cms') {
  238.                     $cmsPages $this->faqCmsPageService->getCmsPages($request$context$faq);
  239.                     $faq->setCmsPage($cmsPages->first());
  240.                 }
  241.             }
  242.         }
  243.     }
  244.     private function clearCacheForFaq(array $faqIdsContext $context): void
  245.     {
  246.         // invalidates all routes which loads faqs
  247.         $this->logger->invalidate(
  248.             array_map([FaqEntityCacheKeyGenerator::class, 'buildFaqTag'], $faqIds)
  249.         );
  250.         $faqSearchResult $this->faqService->getFaqs($faqIds$context);
  251.         if ($faqSearchResult->count() === 0) return;
  252.         $productIds = [];
  253.         $faqGroupIds = [];
  254.         /** @var FaqEntity $faq */
  255.         foreach ($faqSearchResult->getEntities()->getElements() as $faq) {
  256.             if (empty($faq->getGroups()) || $faq->getGroups()->count() === 0) continue;
  257.             foreach ($faq->getGroups()->getElements() as $faqGroup) {
  258.                 if (empty($faqGroup->getProductStreams()) || $faqGroup->getProductStreams()->count() === || in_array($faqGroup->getId(), $faqGroupIds)) continue;
  259.                 $faqGroupIds[] = $faqGroup->getId();
  260.                 $productStreamIds = [];
  261.                 foreach ($faqGroup->getProductStreams()->getElements() as $productStream) {
  262.                     $productStreamIds[] = $productStream->getId();
  263.                 }
  264.                 if (!empty($productStreamIds)) {
  265.                     $productIds array_unique(array_merge($productIds$this->faqService->getProductIdsFromProductStreams($productStreamIds$context)));
  266.                 }
  267.             }
  268.         }
  269.         if (!empty($productIds)) {
  270.             $this->logger->invalidate(
  271.                 array_map([CachedProductDetailRoute::class, 'buildName'], $productIds)
  272.             );
  273.         }
  274.     }
  275.     private function clearCacheForFaqGroup(FaqGroupCollection $faqGroupCollectionContext $context): array
  276.     {
  277.         $faqIds = [];
  278.         $productIds = [];
  279.         foreach ($faqGroupCollection->getElements() as $faqGroup) {
  280.             if (!empty($faqGroup->getFaqs()) && $faqGroup->getFaqs()->count() > 0) {
  281.                 foreach ($faqGroup->getFaqs()->getElements() as $faq) {
  282.                     if (in_array($faq->getId(), $faqIds)) continue;
  283.                     $faqIds[] = $faq->getId();
  284.                 }
  285.             }
  286.             if (empty($faqGroup->getProductStreams()) || $faqGroup->getProductStreams()->count() === 0) continue;
  287.             $productStreamIds = [];
  288.             foreach ($faqGroup->getProductStreams()->getElements() as $productStream) {
  289.                 $productStreamIds[] = $productStream->getId();
  290.             }
  291.             if (!empty($productStreamIds)) {
  292.                 $productIds array_unique(array_merge($productIds$this->faqService->getProductIdsFromProductStreams($productStreamIds$context)));
  293.             }
  294.         }
  295.         if (!empty($productIds)) {
  296.             $this->logger->invalidate(
  297.                 array_map([CachedProductDetailRoute::class, 'buildName'], $productIds)
  298.             );
  299.         }
  300.         return $faqIds;
  301.     }
  302.     private function upsertMetaDataWithFaqIds(array $faqIdsContext $context): void
  303.     {
  304.         $criteria $this->faqService->loadCriteria(new Criteria([$faqIds]));
  305.         $this->faqService->upsertYoutubeMetaDataWithFaqIds($criteria$context);
  306.     }
  307. }