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

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