custom/plugins/WbfkExpertRating/src/CmsElementResolver/ProductReviewsCmsElementResolver.php line 94

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Wbfk\ExpertRating\CmsElementResolver;
  3. use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity;
  4. use Shopware\Core\Content\Cms\DataResolver\CriteriaCollection;
  5. use Shopware\Core\Content\Cms\DataResolver\Element\ElementDataCollection;
  6. use Shopware\Core\Content\Cms\DataResolver\ResolverContext\ResolverContext;
  7. use Shopware\Core\Content\Product\Aggregate\ProductReview\ProductReviewEntity;
  8. use Shopware\Core\Content\Product\Cms\AbstractProductDetailCmsElementResolver;
  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\OrFilter;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\RangeFilter;
  22. use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
  23. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  24. use Symfony\Component\HttpFoundation\Request;
  25. use Wbfk\ExpertRating\Core\Content\WbfkExpertRating\WbfkExpertRatingDefinition;
  26. class ProductReviewsCmsElementResolver extends AbstractProductDetailCmsElementResolver
  27. {
  28. private const LIMIT = 10;
  29. private const DEFAULT_PAGE = 1;
  30. private const FILTER_LANGUAGE = 'filter-language';
  31. /**
  32. * @internal
  33. */
  34. public function __construct(
  35. private readonly AbstractProductReviewRoute $productReviewRoute,
  36. ) {
  37. }
  38. public function getType(): string
  39. {
  40. return 'product-reviews';
  41. }
  42. public function collect(CmsSlotEntity $slot, ResolverContext $resolverContext): ?CriteriaCollection
  43. {
  44. $criteriaCollection = new CriteriaCollection();
  45. $product = $resolverContext->getEntity();
  46. if (!$product instanceof SalesChannelProductEntity) {
  47. return $criteriaCollection;
  48. }
  49. $prodFilters = [new EqualsFilter('expertRatingExtensions.productId', $product->getId())];
  50. if ($product->getParentId()) {
  51. $prodFilters[] = new EqualsFilter('expertRatingExtensions.productId', $product->getParentId());
  52. }
  53. $criteria = new Criteria();
  54. $criteria->addAssociation('expert');
  55. $criteria->addAssociation('expert.media');
  56. $criteria->addAssociation('expertRatingProperties');
  57. $criteria->addAssociation('expertRatingProperties.expertRatingTemplateProperty');
  58. $criteria->addFilter(new OrFilter($prodFilters));
  59. $criteriaCollection->add('expert_rating_' . $slot->getUniqueIdentifier(), WbfkExpertRatingDefinition::class, $criteria);
  60. return $criteriaCollection;
  61. }
  62. public function enrich(CmsSlotEntity $slot, ResolverContext $resolverContext, ElementDataCollection $result): void
  63. {
  64. $product = $resolverContext->getEntity();
  65. if (!$product instanceof SalesChannelProductEntity) {
  66. return;
  67. }
  68. $expertRating = $result->get('expert_rating_' . $slot->getUniqueIdentifier())?->first();
  69. $request = $resolverContext->getRequest();
  70. $slot->assign([
  71. "reviews" => $this->loadProductReviews($product, $request, $resolverContext->getSalesChannelContext()),
  72. "product" => $product,
  73. "expertReview" => $expertRating,
  74. ]);
  75. }
  76. private function loadProductReviews(SalesChannelProductEntity $product, Request $request, SalesChannelContext $context): ProductReviewResult
  77. {
  78. $reviewCriteria = $this->createReviewCriteria($request, $context);
  79. $reviews = $this->productReviewRoute
  80. ->load($product->getParentId() ?? $product->getId(), $request, $context, $reviewCriteria)
  81. ->getResult();
  82. $matrix = $this->getReviewRatingMatrix($reviews);
  83. $reviewResult = ProductReviewResult::createFrom($reviews);
  84. $reviewResult->setMatrix($matrix);
  85. $reviewResult->setProductId($product->getId());
  86. $reviewResult->setCustomerReview($this->getCustomerReview($product->getId(), $context));
  87. $reviewResult->setTotalReviews($matrix->getTotalReviewCount());
  88. $reviewResult->setProductId($product->getId());
  89. $reviewResult->setParentId($product->getParentId() ?? $product->getId());
  90. return $reviewResult;
  91. }
  92. private function createReviewCriteria(Request $request, SalesChannelContext $context): Criteria
  93. {
  94. $limit = (int)$request->get('limit', self::LIMIT);
  95. $page = (int)$request->get('p', self::DEFAULT_PAGE);
  96. $offset = $limit * ($page - 1);
  97. $criteria = new Criteria();
  98. $criteria->setLimit($limit);
  99. $criteria->setOffset($offset);
  100. $sorting = new FieldSorting('createdAt', 'DESC');
  101. if ($request->get('sort', 'points') === 'points') {
  102. $sorting = new FieldSorting('points', 'DESC');
  103. }
  104. $criteria->addSorting($sorting);
  105. if ($request->get('language') === self::FILTER_LANGUAGE) {
  106. $criteria->addPostFilter(
  107. new EqualsFilter('languageId', $context->getContext()->getLanguageId())
  108. );
  109. }
  110. $this->handlePointsAggregation($request, $criteria);
  111. return $criteria;
  112. }
  113. private function handlePointsAggregation(Request $request, Criteria $criteria): void
  114. {
  115. $points = $request->get('points', []);
  116. if (\is_array($points) && \count($points) > 0) {
  117. $pointFilter = [];
  118. foreach ($points as $point) {
  119. $pointFilter[] = new RangeFilter('points', [
  120. 'gte' => $point - 0.5,
  121. 'lt' => $point + 0.5,
  122. ]);
  123. }
  124. $criteria->addPostFilter(new MultiFilter(MultiFilter::CONNECTION_OR, $pointFilter));
  125. }
  126. $criteria->addAggregation(
  127. new FilterAggregation(
  128. 'status-filter',
  129. new TermsAggregation('ratingMatrix', 'points'),
  130. [new EqualsFilter('status', 1)]
  131. )
  132. );
  133. }
  134. private function getCustomerReview(string $productId, SalesChannelContext $context): ?ProductReviewEntity
  135. {
  136. $customer = $context->getCustomer();
  137. if (!$customer) {
  138. return null;
  139. }
  140. $criteria = new Criteria();
  141. $criteria->setLimit(1);
  142. $criteria->setOffset(0);
  143. $criteria->addFilter(new EqualsFilter('customerId', $customer->getId()));
  144. $customerReviews = $this->productReviewRoute
  145. ->load($productId, new Request(), $context, $criteria)
  146. ->getResult();
  147. return $customerReviews->first();
  148. }
  149. private function getReviewRatingMatrix(EntitySearchResult $reviews): RatingMatrix
  150. {
  151. $aggregation = $reviews->getAggregations()->get('ratingMatrix');
  152. if ($aggregation instanceof TermsResult) {
  153. return new RatingMatrix($aggregation->getBuckets());
  154. }
  155. return new RatingMatrix([]);
  156. }
  157. }