vendor/shopware/core/Content/Product/Cms/ProductDescriptionReviewsCmsElementResolver.php line 77

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Product\Cms;
  3. use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity;
  4. use Shopware\Core\Content\Cms\DataResolver\Element\ElementDataCollection;
  5. use Shopware\Core\Content\Cms\DataResolver\ResolverContext\EntityResolverContext;
  6. use Shopware\Core\Content\Cms\DataResolver\ResolverContext\ResolverContext;
  7. use Shopware\Core\Content\Cms\SalesChannel\Struct\ProductDescriptionReviewsStruct;
  8. use Shopware\Core\Content\Product\Aggregate\ProductReview\ProductReviewEntity;
  9. use Shopware\Core\Content\Product\SalesChannel\Review\AbstractProductReviewRoute;
  10. use Shopware\Core\Content\Product\SalesChannel\Review\ProductReviewResult;
  11. use Shopware\Core\Content\Product\SalesChannel\Review\RatingMatrix;
  12. use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\FilterAggregation;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\TermsAggregation;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\Bucket\TermsResult;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\RangeFilter;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
  22. use Shopware\Core\Framework\Log\Package;
  23. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  24. use Symfony\Component\HttpFoundation\Request;
  25. #[Package('inventory')]
  26. class ProductDescriptionReviewsCmsElementResolver extends AbstractProductDetailCmsElementResolver
  27. {
  28. private const LIMIT = 10;
  29. private const DEFAULT_PAGE = 1;
  30. private const FILTER_LANGUAGE = 'filter-language';
  31. private AbstractProductReviewRoute $productReviewRoute;
  32. /**
  33. * @internal
  34. */
  35. public function __construct(AbstractProductReviewRoute $productReviewRoute)
  36. {
  37. $this->productReviewRoute = $productReviewRoute;
  38. }
  39. public function getType(): string
  40. {
  41. return 'product-description-reviews';
  42. }
  43. public function enrich(CmsSlotEntity $slot, ResolverContext $resolverContext, ElementDataCollection $result): void
  44. {
  45. $data = new ProductDescriptionReviewsStruct();
  46. $slot->setData($data);
  47. $productConfig = $slot->getFieldConfig()->get('product');
  48. if ($productConfig === null) {
  49. return;
  50. }
  51. $request = $resolverContext->getRequest();
  52. $ratingSuccess = (bool) $request->get('success', false);
  53. $data->setRatingSuccess($ratingSuccess);
  54. $product = null;
  55. if ($productConfig->isMapped() && $resolverContext instanceof EntityResolverContext) {
  56. $product = $this->resolveEntityValue($resolverContext->getEntity(), $productConfig->getStringValue());
  57. }
  58. if ($productConfig->isStatic()) {
  59. $product = $this->getSlotProduct($slot, $result, $productConfig->getStringValue());
  60. }
  61. /** @var SalesChannelProductEntity|null $product */
  62. if ($product !== null) {
  63. $data->setProduct($product);
  64. $data->setReviews($this->loadProductReviews($product, $request, $resolverContext->getSalesChannelContext()));
  65. }
  66. }
  67. private function loadProductReviews(SalesChannelProductEntity $product, Request $request, SalesChannelContext $context): ProductReviewResult
  68. {
  69. $reviewCriteria = $this->createReviewCriteria($request, $context);
  70. $reviews = $this->productReviewRoute
  71. ->load($product->getParentId() ?? $product->getId(), $request, $context, $reviewCriteria)
  72. ->getResult();
  73. $matrix = $this->getReviewRatingMatrix($reviews);
  74. $reviewResult = ProductReviewResult::createFrom($reviews);
  75. $reviewResult->setMatrix($matrix);
  76. $reviewResult->setProductId($product->getId());
  77. $reviewResult->setCustomerReview($this->getCustomerReview($product->getId(), $context));
  78. $reviewResult->setTotalReviews($matrix->getTotalReviewCount());
  79. $reviewResult->setProductId($product->getId());
  80. $reviewResult->setParentId($product->getParentId() ?? $product->getId());
  81. return $reviewResult;
  82. }
  83. private function createReviewCriteria(Request $request, SalesChannelContext $context): Criteria
  84. {
  85. $limit = (int) $request->get('limit', self::LIMIT);
  86. $page = (int) $request->get('p', self::DEFAULT_PAGE);
  87. $offset = $limit * ($page - 1);
  88. $criteria = new Criteria();
  89. $criteria->setLimit($limit);
  90. $criteria->setOffset($offset);
  91. $sorting = new FieldSorting('createdAt', 'DESC');
  92. if ($request->get('sort', 'points') === 'points') {
  93. $sorting = new FieldSorting('points', 'DESC');
  94. }
  95. $criteria->addSorting($sorting);
  96. if ($request->get('language') === self::FILTER_LANGUAGE) {
  97. $criteria->addPostFilter(
  98. new EqualsFilter('languageId', $context->getContext()->getLanguageId())
  99. );
  100. }
  101. $this->handlePointsAggregation($request, $criteria);
  102. return $criteria;
  103. }
  104. private function handlePointsAggregation(Request $request, Criteria $criteria): void
  105. {
  106. $points = $request->get('points', []);
  107. if (\is_array($points) && \count($points) > 0) {
  108. $pointFilter = [];
  109. foreach ($points as $point) {
  110. $pointFilter[] = new RangeFilter('points', [
  111. 'gte' => $point - 0.5,
  112. 'lt' => $point + 0.5,
  113. ]);
  114. }
  115. $criteria->addPostFilter(new MultiFilter(MultiFilter::CONNECTION_OR, $pointFilter));
  116. }
  117. $criteria->addAggregation(
  118. new FilterAggregation(
  119. 'status-filter',
  120. new TermsAggregation('ratingMatrix', 'points'),
  121. [new EqualsFilter('status', 1)]
  122. )
  123. );
  124. }
  125. private function getCustomerReview(string $productId, SalesChannelContext $context): ?ProductReviewEntity
  126. {
  127. $customer = $context->getCustomer();
  128. if (!$customer) {
  129. return null;
  130. }
  131. $criteria = new Criteria();
  132. $criteria->setLimit(1);
  133. $criteria->setOffset(0);
  134. $criteria->addFilter(new EqualsFilter('customerId', $customer->getId()));
  135. $customerReviews = $this->productReviewRoute
  136. ->load($productId, new Request(), $context, $criteria)
  137. ->getResult();
  138. return $customerReviews->first();
  139. }
  140. private function getReviewRatingMatrix(EntitySearchResult $reviews): RatingMatrix
  141. {
  142. $aggregation = $reviews->getAggregations()->get('ratingMatrix');
  143. if ($aggregation instanceof TermsResult) {
  144. return new RatingMatrix($aggregation->getBuckets());
  145. }
  146. return new RatingMatrix([]);
  147. }
  148. }