vendor/shopware/storefront/Page/Product/Review/ProductReviewLoader.php line 55

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Page\Product\Review;
  3. use Shopware\Core\Content\Product\Aggregate\ProductReview\ProductReviewEntity;
  4. use Shopware\Core\Content\Product\SalesChannel\Review\AbstractProductReviewRoute;
  5. use Shopware\Core\Content\Product\SalesChannel\Review\RatingMatrix;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\FilterAggregation;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\TermsAggregation;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\Bucket\TermsResult;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\RangeFilter;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
  15. use Shopware\Core\Framework\Log\Package;
  16. use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
  17. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  18. use Shopware\Storefront\Framework\Page\StorefrontSearchResult;
  19. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  20. use Symfony\Component\HttpFoundation\Request;
  21. #[Package('storefront')]
  22. class ProductReviewLoader
  23. {
  24.     private const LIMIT 10;
  25.     private const DEFAULT_PAGE 1;
  26.     private const FILTER_LANGUAGE 'filter-language';
  27.     /**
  28.      * @internal
  29.      */
  30.     public function __construct(private readonly AbstractProductReviewRoute $route, private readonly EventDispatcherInterface $eventDispatcher)
  31.     {
  32.     }
  33.     /**
  34.      * load reviews for one product. The request must contain the productId
  35.      * otherwise MissingRequestParameterException is thrown
  36.      *
  37.      * @throws MissingRequestParameterException
  38.      * @throws InconsistentCriteriaIdsException
  39.      */
  40.     public function load(Request $requestSalesChannelContext $context): ReviewLoaderResult
  41.     {
  42.         $productId $request->get('parentId') ?? $request->get('productId');
  43.         if (!$productId) {
  44.             throw new MissingRequestParameterException('productId');
  45.         }
  46.         $criteria $this->createCriteria($request$context);
  47.         $reviews $this->route
  48.             ->load($productId$request$context$criteria)
  49.             ->getResult();
  50.         $reviews StorefrontSearchResult::createFrom($reviews);
  51.         $this->eventDispatcher->dispatch(new ProductReviewsLoadedEvent($reviews$context$request));
  52.         $reviewResult ReviewLoaderResult::createFrom($reviews);
  53.         $reviewResult->setProductId($request->get('productId'));
  54.         $reviewResult->setParentId($request->get('parentId'));
  55.         $aggregation $reviews->getAggregations()->get('ratingMatrix');
  56.         $matrix = new RatingMatrix([]);
  57.         if ($aggregation instanceof TermsResult) {
  58.             $matrix = new RatingMatrix($aggregation->getBuckets());
  59.         }
  60.         $reviewResult->setMatrix($matrix);
  61.         $reviewResult->setCustomerReview($this->getCustomerReview($productId$context));
  62.         $reviewResult->setTotalReviews($matrix->getTotalReviewCount());
  63.         return $reviewResult;
  64.     }
  65.     private function createCriteria(Request $requestSalesChannelContext $context): Criteria
  66.     {
  67.         $limit = (int) $request->get('limit'self::LIMIT);
  68.         $page = (int) $request->get('p'self::DEFAULT_PAGE);
  69.         $offset $limit * ($page 1);
  70.         $criteria = new Criteria();
  71.         $criteria->setLimit($limit);
  72.         $criteria->setOffset($offset);
  73.         $criteria->setTotalCountMode(Criteria::TOTAL_COUNT_MODE_EXACT);
  74.         $sorting = new FieldSorting('createdAt''DESC');
  75.         if ($request->get('sort''createdAt') === 'points') {
  76.             $sorting = new FieldSorting('points''DESC');
  77.         }
  78.         $criteria->addSorting($sorting);
  79.         if ($request->get('language') === self::FILTER_LANGUAGE) {
  80.             $criteria->addPostFilter(
  81.                 new EqualsFilter('languageId'$context->getContext()->getLanguageId())
  82.             );
  83.         }
  84.         $this->handlePointsAggregation($request$criteria$context);
  85.         return $criteria;
  86.     }
  87.     /**
  88.      * get review by productId and customer
  89.      * a customer should only create one review per product, so if there are more than one
  90.      * review we only take one
  91.      *
  92.      * @throws InconsistentCriteriaIdsException
  93.      */
  94.     private function getCustomerReview(string $productIdSalesChannelContext $context): ?ProductReviewEntity
  95.     {
  96.         $customer $context->getCustomer();
  97.         if (!$customer) {
  98.             return null;
  99.         }
  100.         $criteria = new Criteria();
  101.         $criteria->setLimit(1);
  102.         $criteria->setOffset(0);
  103.         $criteria->addFilter(new EqualsFilter('customerId'$customer->getId()));
  104.         $customerReviews $this->route
  105.             ->load($productId, new Request(), $context$criteria)
  106.             ->getResult();
  107.         return $customerReviews->first();
  108.     }
  109.     private function handlePointsAggregation(Request $requestCriteria $criteriaSalesChannelContext $context): void
  110.     {
  111.         $reviewFilters = [];
  112.         $points $request->get('points', []);
  113.         if (\is_array($points) && \count($points) > 0) {
  114.             $pointFilter = [];
  115.             foreach ($points as $point) {
  116.                 $pointFilter[] = new RangeFilter('points', [
  117.                     'gte' => $point 0.5,
  118.                     'lt' => $point 0.5,
  119.                 ]);
  120.             }
  121.             $criteria->addPostFilter(new MultiFilter(MultiFilter::CONNECTION_OR$pointFilter));
  122.         }
  123.         $reviewFilters[] = new EqualsFilter('status'true);
  124.         if ($context->getCustomer() !== null) {
  125.             $reviewFilters[] = new EqualsFilter('customerId'$context->getCustomer()->getId());
  126.         }
  127.         $criteria->addAggregation(
  128.             new FilterAggregation(
  129.                 'customer-login-filter',
  130.                 new TermsAggregation('ratingMatrix''points'),
  131.                 [
  132.                     new MultiFilter(MultiFilter::CONNECTION_OR$reviewFilters),
  133.                 ]
  134.             )
  135.         );
  136.     }
  137. }