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->request->get('no-aggregations') === true) {
  88.             $criteria->resetAggregations();
  89.         }
  90.         // switch all post filters to normal filters to reduce remaining aggregations
  91.         if ($request->request->get('reduce-aggregations')) {
  92.             foreach ($criteria->getPostFilters() as $filter) {
  93.                 $criteria->addFilter($filter);
  94.             }
  95.             $criteria->resetPostFilters();
  96.         }
  97.         if ($request->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.         $event->getResult()->setPage($this->getPage($event->getRequest()));
  125.         $event->getResult()->setLimit($this->getLimit($event->getRequest()));
  126.     }
  127.     /**
  128.      * @param Request $request
  129.      * @param Criteria $criteria
  130.      */
  131.     private function handleFilters(Request $requestCriteria $criteria): void
  132.     {
  133.         $this->handlePropertyFilter($request$criteria);
  134.     }
  135.     /**
  136.      * @param Request $request
  137.      * @param Criteria $criteria
  138.      */
  139.     private function handlePagination(Request $requestCriteria $criteria): void
  140.     {
  141.         $limit $this->getLimit($request);
  142.         $page $this->getPage($request);
  143.         $criteria->setOffset(($page 1) * $limit);
  144.         $criteria->setLimit($limit);
  145.         $criteria->setTotalCountMode(Criteria::TOTAL_COUNT_MODE_EXACT);
  146.     }
  147.     /**
  148.      * @param Request $request
  149.      * @param Criteria $criteria
  150.      */
  151.     private function handlePropertyFilter(Request $requestCriteria $criteria): void
  152.     {
  153.         $criteria->addAggregation(
  154.             new TermsAggregation('properties''h1webblog_blog.properties.id')
  155.         );
  156.         $criteria->addAggregation(
  157.             new TermsAggregation('options''h1webblog_blog.options.id')
  158.         );
  159.         $ids $this->getPropertyIds($request);
  160.         if (empty($ids)) {
  161.             return;
  162.         }
  163.         $grouped $this->connection->fetchAll(
  164.             'SELECT LOWER(HEX(property_group_id)) as property_group_id, LOWER(HEX(id)) as id FROM property_group_option WHERE id IN (:ids)',
  165.             ['ids' => Uuid::fromHexToBytesList($ids)],
  166.             ['ids' => Connection::PARAM_STR_ARRAY]
  167.         );
  168.         $grouped FetchModeHelper::group($grouped);
  169.         foreach ($grouped as $options) {
  170.             $options array_column($options'id');
  171.             $criteria->addPostFilter(
  172.                 new MultiFilter(
  173.                     MultiFilter::CONNECTION_OR,
  174.                     [
  175.                         new EqualsAnyFilter('h1webblog_blog.optionIds'$options),
  176.                         new EqualsAnyFilter('h1webblog_blog.propertyIds'$options),
  177.                     ]
  178.                 )
  179.             );
  180.         }
  181.     }
  182.     /**
  183.      * @param Request $request
  184.      * @param Criteria $criteria
  185.      * @param SalesChannelContext|null $context
  186.      */
  187.     private function handleSorting(Request $requestCriteria $criteriaSalesChannelContext $context null): void
  188.     {
  189.         $currentSorting $this->getCurrentSorting($request$context);
  190.         if (!$currentSorting) {
  191.             return;
  192.         }
  193.         $sorting $this->sortingRegistry->get(
  194.             $currentSorting
  195.         );
  196.         if (!$sorting) {
  197.             return;
  198.         }
  199.         foreach ($sorting->createDalSortings() as $fieldSorting) {
  200.             $criteria->addSorting($fieldSorting);
  201.         }
  202.     }
  203.     /**
  204.      * @param BlogListingResultEvent $event
  205.      * @return array
  206.      */
  207.     private function collectOptionIds(BlogListingResultEvent $event): array
  208.     {
  209.         $aggregations $event->getResult()->getAggregations();
  210.         /** @var TermsResult|null $properties */
  211.         $properties $aggregations->get('properties');
  212.         /** @var TermsResult|null $options */
  213.         $options $aggregations->get('options');
  214.         $options $options $options->getKeys() : [];
  215.         $properties $properties $properties->getKeys() : [];
  216.         return array_unique(array_filter(array_merge($options$properties)));
  217.     }
  218.     /**
  219.      * @param BlogListingResultEvent $event
  220.      */
  221.     private function groupOptionAggregations(BlogListingResultEvent $event): void
  222.     {
  223.         $ids $this->collectOptionIds($event);
  224.         if (empty($ids)) {
  225.             return;
  226.         }
  227.         $criteria = new Criteria($ids);
  228.         $criteria->addAssociation('group');
  229.         $criteria->addAssociation('media');
  230.         $result $this->optionRepository->search($criteria$event->getContext());
  231.         /** @var PropertyGroupOptionCollection $options */
  232.         $options $result->getEntities();
  233.         // group options by their property-group
  234.         $grouped $options->groupByPropertyGroups();
  235.         $grouped->sortByConfig();
  236.         // remove id results to prevent wrong usages
  237.         $event->getResult()->getAggregations()->remove('properties');
  238.         $event->getResult()->getAggregations()->remove('configurators');
  239.         $event->getResult()->getAggregations()->remove('options');
  240.         $event->getResult()->getAggregations()->add(new EntityResult('properties'$grouped));
  241.     }
  242.     /**
  243.      * @param Request $request
  244.      * @return string|null
  245.      */
  246.     private function getCurrentSorting(Request $requestSalesChannelContext $context null): ?string
  247.     {
  248.         // @deprecated tag:v6.3.0 - use `order` instead
  249.         $sort $request->request->get('sort'null);
  250.         if (is_string($sort) && !empty($sort)) {
  251.             $request->query->set('order'$sort);
  252.             $request->request->set('order'$sort);
  253.             $request->query->set('sort'null);
  254.             $request->request->set('sort'null);
  255.         }
  256.         $key $request->request->get('order'$this->getDefaultSort($context));
  257.         if (!$key) {
  258.             return null;
  259.         }
  260.         if ($this->sortingRegistry->has($key)) {
  261.             return $key;
  262.         }
  263.         return $this->getDefaultSort($context);
  264.     }
  265.     /**
  266.      * @param Request $request
  267.      * @return array
  268.      */
  269.     private function getPropertyIds(Request $request): array
  270.     {
  271.         $ids $request->query->get('properties''');
  272.         if ($request->isMethod(Request::METHOD_POST)) {
  273.             $ids $request->request->get('properties''');
  274.         }
  275.         if (is_string($ids)) {
  276.             $ids explode('|'$ids);
  277.         }
  278.         return array_filter($ids);
  279.     }
  280.     /**
  281.      * @param Request $request
  282.      * @return int
  283.      */
  284.     private function getLimit(Request $request): int
  285.     {
  286.         $default $this->getDefaultPageSize();
  287.         $limit $request->query->getInt('limit'$default);
  288.         if ($request->isMethod(Request::METHOD_POST)) {
  289.             $limit $request->request->getInt('limit'$limit);
  290.         }
  291.         return $limit <= $default $limit;
  292.     }
  293.     /**
  294.      * @param Request $request
  295.      * @return int
  296.      */
  297.     private function getPage(Request $request): int
  298.     {
  299.         $page $request->query->getInt('p'1);
  300.         if ($request->isMethod(Request::METHOD_POST)) {
  301.             $page $request->request->getInt('p'$page);
  302.         }
  303.         return $page <= $page;
  304.     }
  305.     /**
  306.      * @param SalesChannelContext|null $context
  307.      * @return string|null
  308.      */
  309.     public function getDefaultSort(SalesChannelContext $context null): ?string
  310.     {
  311.         if ($this->defaultSort === null) {
  312.             $salesChannelId $context $context->getSalesChannel()->getId() : null;
  313.             $this->defaultSort $this->configService->get('H1webBlog.config.blogDefaultSortOrder'$salesChannelId) ?: static::DEFAULT_SORT;
  314.         }
  315.         return $this->defaultSort;
  316.     }
  317.     /**
  318.      * @param SalesChannelContext|null $context
  319.      * @return int|null
  320.      */
  321.     public function getDefaultPageSize(SalesChannelContext $context null): ?int
  322.     {
  323.         if ($this->defaultPagesize === null) {
  324.             $salesChannelId $context $context->getSalesChannel()->getId() : null;
  325.             $this->defaultPagesize $this->configService->getInt('H1webBlog.config.blogDefaultPostsPerPage'$salesChannelId) ?: static::DEFAULT_POSTS_PER_PAGE;
  326.         }
  327.         return $this->defaultPagesize;
  328.     }
  329. }