-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Adding Product Reviews GraphQl support #27882
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
9365b57
ddf7d53
51e2fbc
0564f78
1d75c53
e21d3a8
dd796f3
1b7bade
9b8912e
20dfcc6
5809bee
4fedd42
64f9f00
5a4b007
3592cf9
7089708
017da27
a020625
0111b2d
f168ca6
f443741
11c7fd4
c9184f6
b974b48
de8598c
9230db6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Magento\Review\Model\Review; | ||
|
||
use Magento\Review\Model\ResourceModel\Rating\Option\Vote\Collection as OptionVoteCollection; | ||
use Magento\Review\Model\ResourceModel\Rating\Option\Vote\CollectionFactory as OptionVoteCollectionFactory; | ||
use Magento\Review\Model\ResourceModel\Review\Product\Collection; | ||
|
||
/** | ||
* The model that adds the rating votes to reviews | ||
*/ | ||
class AddRatingVotesToCustomerReviews | ||
{ | ||
/** | ||
* @var RatingOptionCollectionFactory | ||
*/ | ||
private $ratingOptionCollectionFactory; | ||
|
||
/** | ||
* @param OptionVoteCollectionFactory $ratingOptionCollectionFactory | ||
*/ | ||
public function __construct(OptionVoteCollectionFactory $ratingOptionCollectionFactory) | ||
{ | ||
$this->ratingOptionCollectionFactory = $ratingOptionCollectionFactory; | ||
} | ||
|
||
/** | ||
* Add rating votes to customer reviews | ||
* | ||
* @param Collection $collection | ||
*/ | ||
public function execute(Collection $collection): void | ||
{ | ||
$connection = $collection->getConnection(); | ||
|
||
foreach ($collection->getItems() as &$item) { | ||
/** @var OptionVoteCollection $votesCollection */ | ||
$votesCollection = $this->ratingOptionCollectionFactory->create(); | ||
|
||
$votesCollection->addFieldToFilter('main_table.review_id', $item->getData('review_id')); | ||
$votesCollection->getSelect() | ||
->join( | ||
['rating' => $connection->getTableName('rating')], | ||
'rating.rating_id = main_table.rating_id', | ||
['rating_code'] | ||
); | ||
$item->setRatingVotes($votesCollection); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Magento\Review\Service; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's move to |
||
|
||
/** | ||
* Get review average rating | ||
*/ | ||
class GetReviewAverageRatingService | ||
{ | ||
/** | ||
* Get average rating per review | ||
* | ||
* @param array $ratingVotes | ||
* | ||
* @return float | ||
*/ | ||
public function execute(array $ratingVotes): float | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this logic extracted from some other method? If yes, please make sure we do not have code duplicating and reuse this class in place from where it is extracted. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually there is no place where I've extracted this one. There is a different approach to calculate the average on the Storefront (showing the percentage of the review), and probably will try to use the same approach.
The output should be?
Thank you. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After the latest updates, I'm just returning here |
||
{ | ||
$averageRating = 0; | ||
|
||
foreach ($ratingVotes as $ratingVote) { | ||
$averageRating += (int) $ratingVote->getData('value'); | ||
} | ||
|
||
return $averageRating > 0 ? (float) number_format($averageRating / count($ratingVotes), 2) : 0; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Magento\ReviewGraphQl\Mapper; | ||
|
||
use Magento\Catalog\Model\Product; | ||
use Magento\Review\Model\Review; | ||
|
||
/** | ||
* Converts the review data from review object to an associative array | ||
*/ | ||
class ReviewDataMapper | ||
{ | ||
/** | ||
* Mapping the review data | ||
* | ||
* @param Review|Product $review | ||
* | ||
* @return array | ||
*/ | ||
public function map($review): array | ||
{ | ||
return [ | ||
'summary' => $review->getData('title'), | ||
'text' => $review->getData('detail'), | ||
'nickname' => $review->getData('nickname'), | ||
'created_at' => $review->getData('created_at'), | ||
'rating_votes' => $review->getData('rating_votes'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great work @eduard13. very nice PR! I just have a couple of comments. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, looks like after refactoring the code, I've missed to remove this one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed this one. |
||
'sku' => $review->getSku() | ||
]; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Magento\ReviewGraphQl\Model\DataProvider; | ||
|
||
use Magento\Framework\GraphQl\Exception\GraphQlInputException; | ||
use Magento\Review\Model\ResourceModel\Review\Collection as ReviewCollection; | ||
use Magento\Review\Model\ResourceModel\Review\Product\Collection as ProductCollection; | ||
use Magento\ReviewGraphQl\Mapper\ReviewDataMapper; | ||
|
||
/** | ||
* Provides aggregated reviews result | ||
* | ||
* The following class prepares the GraphQl endpoints' result for Customer and Product reviews | ||
*/ | ||
class AggregatedReviewsDataProvider | ||
{ | ||
/** | ||
* @var ReviewDataMapper | ||
*/ | ||
private $reviewDataMapper; | ||
|
||
/** | ||
* @param ReviewDataMapper $reviewDataMapper | ||
*/ | ||
public function __construct(ReviewDataMapper $reviewDataMapper) | ||
{ | ||
$this->reviewDataMapper = $reviewDataMapper; | ||
} | ||
|
||
/** | ||
* Get reviews result | ||
* | ||
* @param ProductCollection|ReviewCollection $reviewsCollection | ||
* | ||
* @return array | ||
*/ | ||
public function getData($reviewsCollection): array | ||
{ | ||
if ($reviewsCollection->getPageSize()) { | ||
$maxPages = ceil($reviewsCollection->getSize() / $reviewsCollection->getPageSize()); | ||
} else { | ||
$maxPages = 0; | ||
} | ||
|
||
$currentPage = $reviewsCollection->getCurPage(); | ||
if ($reviewsCollection->getCurPage() > $maxPages && $reviewsCollection->getSize() > 0) { | ||
$currentPage = new GraphQlInputException( | ||
__( | ||
'currentPage value %1 specified is greater than the number of pages available.', | ||
[$maxPages] | ||
) | ||
); | ||
} | ||
|
||
$items = []; | ||
foreach ($reviewsCollection->getItems() as $item) { | ||
$items[] = $this->reviewDataMapper->map($item); | ||
} | ||
|
||
return [ | ||
'total_count' => $reviewsCollection->getSize(), | ||
'items' => $items, | ||
'page_info' => [ | ||
'page_size' => $reviewsCollection->getPageSize(), | ||
'current_page' => $currentPage, | ||
'total_pages' => $maxPages | ||
] | ||
]; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Magento\ReviewGraphQl\Model\DataProvider; | ||
|
||
use Magento\Review\Model\ResourceModel\Review\Product\Collection as ProductReviewsCollection; | ||
use Magento\Review\Model\ResourceModel\Review\Product\CollectionFactory; | ||
use Magento\Review\Model\Review\AddRatingVotesToCustomerReviews; | ||
|
||
/** | ||
* Provides customer reviews | ||
*/ | ||
class CustomerReviewsDataProvider | ||
{ | ||
/** | ||
* @var CollectionFactory | ||
*/ | ||
private $collectionFactory; | ||
|
||
/** | ||
* @var AddRatingVotesToCustomerReviews | ||
*/ | ||
private $addRatingVotesToCustomerReviews; | ||
|
||
/** | ||
* @param CollectionFactory $collectionFactory | ||
* @param AddRatingVotesToCustomerReviews $addRatingVotesToCustomerReviews | ||
*/ | ||
public function __construct( | ||
CollectionFactory $collectionFactory, | ||
AddRatingVotesToCustomerReviews $addRatingVotesToCustomerReviews | ||
) { | ||
$this->collectionFactory = $collectionFactory; | ||
$this->addRatingVotesToCustomerReviews = $addRatingVotesToCustomerReviews; | ||
} | ||
|
||
/** | ||
* Get customer reviews | ||
* | ||
* @param int $customerId | ||
* @param int $currentPage | ||
* @param int $pageSize | ||
* | ||
* @return ProductReviewsCollection | ||
*/ | ||
public function getData(int $customerId, int $currentPage, int $pageSize): ProductReviewsCollection | ||
{ | ||
/** @var ProductReviewsCollection $reviewsCollection */ | ||
$reviewsCollection = $this->collectionFactory->create(); | ||
$reviewsCollection->addCustomerFilter($customerId) | ||
->setPageSize($pageSize) | ||
->setCurPage($currentPage) | ||
->setDateOrder(); | ||
$this->addRatingVotesToCustomerReviews->execute($reviewsCollection); | ||
|
||
return $reviewsCollection; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Magento\ReviewGraphQl\Model\DataProvider; | ||
|
||
use Magento\Review\Model\ResourceModel\Review\Collection; | ||
use Magento\Review\Model\ResourceModel\Review\CollectionFactory; | ||
use Magento\Review\Model\Review; | ||
|
||
/** | ||
* Provides product reviews | ||
*/ | ||
class ProductReviewsDataProvider | ||
{ | ||
/** | ||
* @var CollectionFactory | ||
*/ | ||
private $collectionFactory; | ||
|
||
/** | ||
* @param CollectionFactory $collectionFactory | ||
*/ | ||
public function __construct( | ||
CollectionFactory $collectionFactory | ||
) { | ||
$this->collectionFactory = $collectionFactory; | ||
} | ||
|
||
/** | ||
* Get product reviews | ||
* | ||
* @param int $productId | ||
* @param int $currentPage | ||
* @param int $pageSize | ||
* | ||
* @return Collection | ||
*/ | ||
public function getData(int $productId, int $currentPage, int $pageSize): Collection | ||
{ | ||
/** @var Collection $reviewsCollection */ | ||
$reviewsCollection = $this->collectionFactory->create() | ||
->addStatusFilter(Review::STATUS_APPROVED) | ||
->addEntityFilter(Review::ENTITY_PRODUCT_CODE, $productId) | ||
->setPageSize($pageSize) | ||
->setCurPage($currentPage) | ||
->setDateOrder(); | ||
$reviewsCollection->getSelect()->join( | ||
['cpe' => $reviewsCollection->getTable('catalog_product_entity')], | ||
'cpe.entity_id = main_table.entity_pk_value', | ||
['sku'] | ||
); | ||
$reviewsCollection->addRateVotes(); | ||
|
||
return $reviewsCollection; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Magento\ReviewGraphQl\Model\DataProvider; | ||
|
||
/** | ||
* Provides rating votes | ||
*/ | ||
class ReviewRatingsDataProvider | ||
{ | ||
/** | ||
* Providing rating votes | ||
* | ||
* @param array $ratingVotes | ||
* | ||
* @return array | ||
*/ | ||
public function getData(array $ratingVotes): array | ||
{ | ||
$data = []; | ||
|
||
foreach ($ratingVotes as $ratingVote) { | ||
$data[] = [ | ||
'name' => $ratingVote->getData('rating_code'), | ||
'value' => $ratingVote->getData('value') | ||
]; | ||
} | ||
|
||
return $data; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this logic extracted from some other method? If yes, please make sure we do not have code duplicating and reuse this class in place from where it is extracted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've refactored a bit the implementation, and this model isn't needed anymore.