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

  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(
  31.         private readonly AbstractProductReviewRoute $route,
  32.         private readonly EventDispatcherInterface $eventDispatcher
  33.     ) {
  34.     }
  35.     /**
  36.      * load reviews for one product. The request must contain the productId
  37.      * otherwise MissingRequestParameterException is thrown
  38.      *
  39.      * @throws MissingRequestParameterException
  40.      * @throws InconsistentCriteriaIdsException
  41.      */
  42.     public function load(Request $requestSalesChannelContext $context): ReviewLoaderResult
  43.     {
  44.         $productId $request->get('parentId') ?? $request->get('productId');
  45.         if (!$productId) {
  46.             throw new MissingRequestParameterException('productId');
  47.         }
  48.         $criteria $this->createCriteria($request$context);
  49.         $reviews $this->route
  50.             ->load($productId$request$context$criteria)
  51.             ->getResult();
  52.         $reviews StorefrontSearchResult::createFrom($reviews);
  53.         $this->eventDispatcher->dispatch(new ProductReviewsLoadedEvent($reviews$context$request));
  54.         $reviewResult ReviewLoaderResult::createFrom($reviews);
  55.         $reviewResult->setProductId($request->get('productId'));
  56.         $reviewResult->setParentId($request->get('parentId'));
  57.         $aggregation $reviews->getAggregations()->get('ratingMatrix');
  58.         $matrix = new RatingMatrix([]);
  59.         if ($aggregation instanceof TermsResult) {
  60.             $matrix = new RatingMatrix($aggregation->getBuckets());
  61.         }
  62.         $reviewResult->setMatrix($matrix);
  63.         $reviewResult->setCustomerReview($this->getCustomerReview($productId$context));
  64.         $reviewResult->setTotalReviews($matrix->getTotalReviewCount());
  65.         return $reviewResult;
  66.     }
  67.     private function createCriteria(Request $requestSalesChannelContext $context): Criteria
  68.     {
  69.         $limit = (int) $request->get('limit'self::LIMIT);
  70.         $page = (int) $request->get('p'self::DEFAULT_PAGE);
  71.         $offset $limit * ($page 1);
  72.         $criteria = new Criteria();
  73.         $criteria->setLimit($limit);
  74.         $criteria->setOffset($offset);
  75.         $criteria->setTotalCountMode(Criteria::TOTAL_COUNT_MODE_EXACT);
  76.         $sorting = new FieldSorting('createdAt''DESC');
  77.         if ($request->get('sort''createdAt') === 'points') {
  78.             $sorting = new FieldSorting('points''DESC');
  79.         }
  80.         $criteria->addSorting($sorting);
  81.         if ($request->get('language') === self::FILTER_LANGUAGE) {
  82.             $criteria->addPostFilter(
  83.                 new EqualsFilter('languageId'$context->getContext()->getLanguageId())
  84.             );
  85.         }
  86.         $this->handlePointsAggregation($request$criteria$context);
  87.         return $criteria;
  88.     }
  89.     /**
  90.      * get review by productId and customer
  91.      * a customer should only create one review per product, so if there are more than one
  92.      * review we only take one
  93.      *
  94.      * @throws InconsistentCriteriaIdsException
  95.      */
  96.     private function getCustomerReview(string $productIdSalesChannelContext $context): ?ProductReviewEntity
  97.     {
  98.         $customer $context->getCustomer();
  99.         if (!$customer) {
  100.             return null;
  101.         }
  102.         $criteria = new Criteria();
  103.         $criteria->setLimit(1);
  104.         $criteria->setOffset(0);
  105.         $criteria->addFilter(new EqualsFilter('customerId'$customer->getId()));
  106.         $customerReviews $this->route
  107.             ->load($productId, new Request(), $context$criteria)
  108.             ->getResult();
  109.         return $customerReviews->first();
  110.     }
  111.     private function handlePointsAggregation(Request $requestCriteria $criteriaSalesChannelContext $context): void
  112.     {
  113.         $reviewFilters = [];
  114.         $points $request->get('points', []);
  115.         if (\is_array($points) && \count($points) > 0) {
  116.             $pointFilter = [];
  117.             foreach ($points as $point) {
  118.                 $pointFilter[] = new RangeFilter('points', [
  119.                     'gte' => $point 0.5,
  120.                     'lt' => $point 0.5,
  121.                 ]);
  122.             }
  123.             $criteria->addPostFilter(new MultiFilter(MultiFilter::CONNECTION_OR$pointFilter));
  124.         }
  125.         $reviewFilters[] = new EqualsFilter('status'true);
  126.         if ($context->getCustomer() !== null) {
  127.             $reviewFilters[] = new EqualsFilter('customerId'$context->getCustomer()->getId());
  128.         }
  129.         $criteria->addAggregation(
  130.             new FilterAggregation(
  131.                 'customer-login-filter',
  132.                 new TermsAggregation('ratingMatrix''points'),
  133.                 [
  134.                     new MultiFilter(MultiFilter::CONNECTION_OR$reviewFilters),
  135.                 ]
  136.             )
  137.         );
  138.     }
  139. }