Skip to content

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

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9365b57
Adding Product and Customer Reviews GraphQl support
eduard13 Apr 17, 2020
ddf7d53
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 May 6, 2020
51e2fbc
Small refactoring to re-implement the customer reviews
eduard13 May 9, 2020
0564f78
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 May 14, 2020
1d75c53
Small improvements
eduard13 May 14, 2020
e21d3a8
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 May 20, 2020
dd796f3
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 May 21, 2020
1b7bade
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 May 21, 2020
9b8912e
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 May 25, 2020
20dfcc6
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 May 28, 2020
5809bee
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 Jun 4, 2020
4fedd42
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 Jun 4, 2020
64f9f00
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 Jun 11, 2020
5a4b007
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 Jun 11, 2020
3592cf9
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 Jun 15, 2020
7089708
Merge branch '2.4-develop' into feature/review-graphql-261
avattam06 Jun 17, 2020
017da27
Updating schema description
eduard13 Jun 22, 2020
a020625
Adding reviews config checking
eduard13 Jun 24, 2020
0111b2d
Adding return types
eduard13 Jun 24, 2020
f168ca6
Small improvements
eduard13 Jun 25, 2020
f443741
Merge branch '2.4-develop' into feature/review-graphql-261
dthampy Jun 26, 2020
11c7fd4
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 Jul 1, 2020
c9184f6
Merge branch '2.4-develop' into feature/review-graphql-261
eduard13 Jul 6, 2020
b974b48
Adding rollback fixtures and providing small adjustments
eduard13 Jul 7, 2020
de8598c
Updating WebAPI tests
eduard13 Jul 7, 2020
9230db6
Merge branch '2.4-develop' into feature/review-graphql-261
Jul 10, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
Copy link
Contributor

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.

Copy link
Contributor Author

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.


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);
}
}
}
32 changes: 32 additions & 0 deletions app/code/Magento/Review/Service/GetReviewAverageRatingService.php
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move to Model folder. Service is not a common approach.


/**
* Get review average rating
*/
class GetReviewAverageRatingService
{
/**
* Get average rating per review
*
* @param array $ratingVotes
*
* @return float
*/
public function execute(array $ratingVotes): float
Copy link
Contributor

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.

Copy link
Contributor Author

@eduard13 eduard13 May 8, 2020

Choose a reason for hiding this comment

The 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.
However, I have a question here (just to make sure that the value is the expected one). Does this average_rating represent a percentage like 80%, or is should be kind of 4 - being the average rating that for that review?
E.g.

  • Price 4 stars
  • Quality 4 stars

The output should be?

  1. 80
  2. 4

Thank you.

Copy link
Contributor Author

@eduard13 eduard13 May 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the latest updates, I'm just returning here 4. Please let me know if it needs to be changed.

{
$averageRating = 0;

foreach ($ratingVotes as $ratingVote) {
$averageRating += (int) $ratingVote->getData('value');
}

return $averageRating > 0 ? (float) number_format($averageRating / count($ratingVotes), 2) : 0;
}
}
36 changes: 36 additions & 0 deletions app/code/Magento/ReviewGraphQl/Mapper/ReviewDataMapper.php
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'),
Copy link
Contributor

Choose a reason for hiding this comment

The 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.
is rating_votes being used anywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.
Thank you for pointing up.
Will wait for the feedback regarding the other points and will make all the needed adjustments at once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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;
}
}
Loading