vendor/store.shopware.com/h1webblog/src/Blog/Listing/BlogListingFeaturesSubscriber.php line 96

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace H1web\Blog\Blog\Listing;
  3. use Doctrine\DBAL\Connection;
  4. use H1web\Blog\Blog\Events\BlogListingCriteriaEvent;
  5. use H1web\Blog\Blog\Events\BlogListingResultEvent;
  6. use Shopware\Core\Content\Property\Aggregate\PropertyGroupOption\PropertyGroupOptionCollection;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Doctrine\FetchModeHelper;
  8. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\TermsAggregation;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\Bucket\TermsResult;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\Metric\EntityResult;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
  15. use Shopware\Core\Framework\Uuid\Uuid;
  16. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  17. use Shopware\Core\System\SystemConfig\SystemConfigService;
  18. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  19. use Symfony\Component\HttpFoundation\Request;
  20. class BlogListingFeaturesSubscriber implements EventSubscriberInterface
  21. {
  22.     public const DEFAULT_SORT 'publishedAt--desc';
  23.     public const DEFAULT_SEARCH_SORT 'score';
  24.     public const DEFAULT_POSTS_PER_PAGE 24;
  25.     /**
  26.      * @var EntityRepositoryInterface
  27.      */
  28.     private $optionRepository;
  29.     /**
  30.      * @var BlogListingSortingRegistry
  31.      */
  32.     private $sortingRegistry;
  33.     /**
  34.      * @var Connection
  35.      */
  36.     private $connection;
  37.     /**
  38.      * @var SystemConfigService
  39.      */
  40.     private $configService;
  41.     /**
  42.      * @var string
  43.      */
  44.     private $defaultSort;
  45.     /**
  46.      * @var int
  47.      */
  48.     private $defaultPagesize;
  49.     /**
  50.      * BlogListingFeaturesSubscriber constructor.
  51.      * @param Connection $connection
  52.      * @param EntityRepositoryInterface $optionRepository
  53.      * @param BlogListingSortingRegistry $sortingRegistry
  54.      */
  55.     public function __construct(
  56.         Connection $connection,
  57.         EntityRepositoryInterface $optionRepository,
  58.         BlogListingSortingRegistry $sortingRegistry,
  59.         SystemConfigService $configService
  60.     )
  61.     {
  62.         $this->optionRepository $optionRepository;
  63.         $this->connection $connection;
  64.         $this->sortingRegistry $sortingRegistry;
  65.         $this->configService $configService;
  66.     }
  67.     /**
  68.      * @return array
  69.      */
  70.     public static function getSubscribedEvents()
  71.     {
  72.         return [
  73.             BlogListingCriteriaEvent::class => [
  74.                 ['handleListingRequest'100],
  75.                 ['switchFilter', -100],
  76.             ],
  77.             BlogListingResultEvent::class => 'handleResult',
  78.         ];
  79.     }
  80.     /**
  81.      * @param BlogListingCriteriaEvent $event
  82.      */
  83.     public function switchFilter(BlogListingCriteriaEvent $event): void
  84.     {
  85.         $request $event->getRequest();
  86.         $criteria $event->getCriteria();
  87.         if ($request->get('no-aggregations') === true) {
  88.             $criteria->resetAggregations();
  89.         }
  90.         // switch all post filters to normal filters to reduce remaining aggregations
  91.         if ($request->get('reduce-aggregations')) {
  92.             foreach ($criteria->getPostFilters() as $filter) {
  93.                 $criteria->addFilter($filter);
  94.             }
  95.             $criteria->resetPostFilters();
  96.         }
  97.         if ($request->get('only-aggregations') === true) {
  98.             // set limit to zero to fetch no blogs.
  99.             $criteria->setLimit(0);
  100.             // no total count required
  101.             $criteria->setTotalCountMode(Criteria::TOTAL_COUNT_MODE_NONE);
  102.             // sorting and association are only required for the blog data
  103.             $criteria->resetSorting();
  104.             $criteria->resetAssociations();
  105.         }
  106.     }
  107.     /**
  108.      * @param BlogListingCriteriaEvent $event
  109.      */
  110.     public function handleListingRequest(BlogListingCriteriaEvent $event): void
  111.     {
  112.         $request $event->getRequest();
  113.         $criteria $event->getCriteria();
  114.         $this->handlePagination($request$criteria);
  115.         $this->handleFilters($request$criteria);
  116.         $this->handleSorting($request$criteria$event->getSalesChannelContext());
  117.     }
  118.     /**
  119.      * @param BlogListingResultEvent $event
  120.      */
  121.     public function handleResult(BlogListingResultEvent $event): void
  122.     {
  123.         $this->groupOptionAggregations($event);
  124.         $this->addCurrentFilters($event);
  125.         $currentSorting $this->getCurrentSorting($event->getRequest());
  126.         $event->getResult()->setSorting($currentSorting);
  127.         $sortings $this->sortingRegistry->getSortings();
  128.         /** @var BlogListingSorting $sorting */
  129.         foreach ($sortings as $sorting) {
  130.             $sorting->setActive($sorting->getKey() === $currentSorting);
  131.         }
  132.         $event->getResult()->setSortings($sortings);
  133.         $event->getResult()->setPage($this->getPage($event->getRequest()));
  134.         $event->getResult()->setLimit($this->getLimit($event->getRequest()));
  135.     }
  136.     /**
  137.      * @param Request $request
  138.      * @param Criteria $criteria
  139.      */
  140.     private function handleFilters(Request $requestCriteria $criteria): void
  141.     {
  142.         $this->handlePropertyFilter($request$criteria);
  143.     }
  144.     /**
  145.      * @param Request $request
  146.      * @param Criteria $criteria
  147.      */
  148.     private function handlePagination(Request $requestCriteria $criteria): void
  149.     {
  150.         $limit $this->getLimit($request);
  151.         $page $this->getPage($request);
  152.         $criteria->setOffset(($page 1) * $limit);
  153.         $criteria->setLimit($limit);
  154.         $criteria->setTotalCountMode(Criteria::TOTAL_COUNT_MODE_EXACT);
  155.     }
  156.     /**
  157.      * @param Request $request
  158.      * @param Criteria $criteria
  159.      */
  160.     private function handlePropertyFilter(Request $requestCriteria $criteria): void
  161.     {
  162.         $criteria->addAggregation(
  163.             new TermsAggregation('properties''h1webblog_blog.properties.id')
  164.         );
  165.         $criteria->addAggregation(
  166.             new TermsAggregation('options''h1webblog_blog.options.id')
  167.         );
  168.         $ids $this->getPropertyIds($request);
  169.         if (empty($ids)) {
  170.             return;
  171.         }
  172.         $grouped $this->connection->fetchAll(
  173.             'SELECT LOWER(HEX(property_group_id)) as property_group_id, LOWER(HEX(id)) as id FROM property_group_option WHERE id IN (:ids)',
  174.             ['ids' => Uuid::fromHexToBytesList($ids)],
  175.             ['ids' => Connection::PARAM_STR_ARRAY]
  176.         );
  177.         $grouped FetchModeHelper::group($grouped);
  178.         foreach ($grouped as $options) {
  179.             $options array_column($options'id');
  180.             $criteria->addPostFilter(
  181.                 new MultiFilter(
  182.                     MultiFilter::CONNECTION_OR,
  183.                     [
  184.                         new EqualsAnyFilter('h1webblog_blog.optionIds'$options),
  185.                         new EqualsAnyFilter('h1webblog_blog.propertyIds'$options),
  186.                     ]
  187.                 )
  188.             );
  189.         }
  190.     }
  191.     /**
  192.      * @param Request $request
  193.      * @param Criteria $criteria
  194.      * @param SalesChannelContext|null $context
  195.      */
  196.     private function handleSorting(Request $requestCriteria $criteriaSalesChannelContext $context null): void
  197.     {
  198.         $currentSorting $this->getCurrentSorting($request$context);
  199.         if (!$currentSorting) {
  200.             return;
  201.         }
  202.         $sorting $this->sortingRegistry->get(
  203.             $currentSorting
  204.         );
  205.         if (!$sorting) {
  206.             return;
  207.         }
  208.         foreach ($sorting->createDalSortings() as $fieldSorting) {
  209.             $criteria->addSorting($fieldSorting);
  210.         }
  211.     }
  212.     /**
  213.      * @param BlogListingResultEvent $event
  214.      * @return array
  215.      */
  216.     private function collectOptionIds(BlogListingResultEvent $event): array
  217.     {
  218.         $aggregations $event->getResult()->getAggregations();
  219.         /** @var TermsResult|null $properties */
  220.         $properties $aggregations->get('properties');
  221.         /** @var TermsResult|null $options */
  222.         $options $aggregations->get('options');
  223.         $options $options $options->getKeys() : [];
  224.         $properties $properties $properties->getKeys() : [];
  225.         return array_unique(array_filter(array_merge($options$properties)));
  226.     }
  227.     /**
  228.      * @param BlogListingResultEvent $event
  229.      */
  230.     private function groupOptionAggregations(BlogListingResultEvent $event): void
  231.     {
  232.         $ids $this->collectOptionIds($event);
  233.         if (empty($ids)) {
  234.             return;
  235.         }
  236.         $criteria = new Criteria($ids);
  237.         $criteria->addAssociation('group');
  238.         $criteria->addAssociation('media');
  239.         $result $this->optionRepository->search($criteria$event->getContext());
  240.         /** @var PropertyGroupOptionCollection $options */
  241.         $options $result->getEntities();
  242.         // group options by their property-group
  243.         $grouped $options->groupByPropertyGroups();
  244.         $grouped->sortByConfig();
  245.         // remove id results to prevent wrong usages
  246.         $event->getResult()->getAggregations()->remove('properties');
  247.         $event->getResult()->getAggregations()->remove('configurators');
  248.         $event->getResult()->getAggregations()->remove('options');
  249.         $event->getResult()->getAggregations()->add(new EntityResult('properties'$grouped));
  250.     }
  251.     /**
  252.      * @param BlogListingResultEvent $event
  253.      */
  254.     private function addCurrentFilters(BlogListingResultEvent $event): void
  255.     {
  256.         $event->getResult()->addCurrentFilter('properties'$this->getPropertyIds($event->getRequest()));
  257.     }
  258.     /**
  259.      * @param Request $request
  260.      * @return string|null
  261.      */
  262.     private function getCurrentSorting(Request $requestSalesChannelContext $context null): ?string
  263.     {
  264.         // @deprecated tag:v6.3.0 - use `order` instead
  265.         $sort $request->get('sort'null);
  266.         if (is_string($sort) && !empty($sort)) {
  267.             $request->query->set('order'$sort);
  268.             $request->request->set('order'$sort);
  269.             $request->query->set('sort'null);
  270.             $request->request->set('sort'null);
  271.         }
  272.         $key $request->get('order'$this->getDefaultSort($context));
  273.         if (!$key) {
  274.             return null;
  275.         }
  276.         if ($this->sortingRegistry->has($key)) {
  277.             return $key;
  278.         }
  279.         return $this->getDefaultSort($context);
  280.     }
  281.     /**
  282.      * @param Request $request
  283.      * @return array
  284.      */
  285.     private function getPropertyIds(Request $request): array
  286.     {
  287.         $ids $request->query->get('properties''');
  288.         if ($request->isMethod(Request::METHOD_POST)) {
  289.             $ids $request->request->get('properties''');
  290.         }
  291.         if (is_string($ids)) {
  292.             $ids explode('|'$ids);
  293.         }
  294.         return array_filter($ids);
  295.     }
  296.     /**
  297.      * @param Request $request
  298.      * @return int
  299.      */
  300.     private function getLimit(Request $request): int
  301.     {
  302.         $default $this->getDefaultPageSize();
  303.         $limit $request->query->getInt('limit'$default);
  304.         if ($request->isMethod(Request::METHOD_POST)) {
  305.             $limit $request->request->getInt('limit'$limit);
  306.         }
  307.         return $limit <= $default $limit;
  308.     }
  309.     /**
  310.      * @param Request $request
  311.      * @return int
  312.      */
  313.     private function getPage(Request $request): int
  314.     {
  315.         $page $request->query->getInt('p'1);
  316.         if ($request->isMethod(Request::METHOD_POST)) {
  317.             $page $request->request->getInt('p'$page);
  318.         }
  319.         return $page <= $page;
  320.     }
  321.     /**
  322.      * @param SalesChannelContext|null $context
  323.      * @return string|null
  324.      */
  325.     public function getDefaultSort(SalesChannelContext $context null): ?string
  326.     {
  327.         if ($this->defaultSort === null) {
  328.             $salesChannelId $context $context->getSalesChannel()->getId() : null;
  329.             $this->defaultSort $this->configService->get('H1webBlog.config.blogDefaultSortOrder'$salesChannelId) ?: static::DEFAULT_SORT;
  330.         }
  331.         return $this->defaultSort;
  332.     }
  333.     /**
  334.      * @param SalesChannelContext|null $context
  335.      * @return int|null
  336.      */
  337.     public function getDefaultPageSize(SalesChannelContext $context null): ?int
  338.     {
  339.         if ($this->defaultPagesize === null) {
  340.             $salesChannelId $context $context->getSalesChannel()->getId() : null;
  341.             $this->defaultPagesize $this->configService->getInt('H1webBlog.config.blogDefaultPostsPerPage'$salesChannelId) ?: static::DEFAULT_POSTS_PER_PAGE;
  342.         }
  343.         return $this->defaultPagesize;
  344.     }
  345. }