123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Review\Model\ResourceModel;
- use Magento\Framework\Model\AbstractModel;
- /**
- * Review resource model
- *
- * @api
- * @since 100.0.2
- */
- class Review extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
- {
- /**
- * Review table
- *
- * @var string
- */
- protected $_reviewTable;
- /**
- * Review Detail table
- *
- * @var string
- */
- protected $_reviewDetailTable;
- /**
- * Review status table
- *
- * @var string
- */
- protected $_reviewStatusTable;
- /**
- * Review entity table
- *
- * @var string
- */
- protected $_reviewEntityTable;
- /**
- * Review store table
- *
- * @var string
- */
- protected $_reviewStoreTable;
- /**
- * Review aggregate table
- *
- * @var string
- */
- protected $_aggregateTable;
- /**
- * Cache of deleted rating data
- *
- * @var array
- */
- private $_deleteCache = [];
- /**
- * Core date model
- *
- * @var \Magento\Framework\Stdlib\DateTime\DateTime
- */
- protected $_date;
- /**
- * Core model store manager interface
- *
- * @var \Magento\Store\Model\StoreManagerInterface
- */
- protected $_storeManager;
- /**
- * Rating model
- *
- * @var \Magento\Review\Model\RatingFactory
- */
- protected $_ratingFactory;
- /**
- * Rating resource model
- *
- * @var \Magento\Review\Model\ResourceModel\Rating\Option
- */
- protected $_ratingOptions;
- /**
- * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
- * @param \Magento\Framework\Stdlib\DateTime\DateTime $date
- * @param \Magento\Store\Model\StoreManagerInterface $storeManager
- * @param \Magento\Review\Model\RatingFactory $ratingFactory
- * @param \Magento\Review\Model\ResourceModel\Rating\Option $ratingOptions
- * @param string $connectionName
- */
- public function __construct(
- \Magento\Framework\Model\ResourceModel\Db\Context $context,
- \Magento\Framework\Stdlib\DateTime\DateTime $date,
- \Magento\Store\Model\StoreManagerInterface $storeManager,
- \Magento\Review\Model\RatingFactory $ratingFactory,
- Rating\Option $ratingOptions,
- $connectionName = null
- ) {
- $this->_date = $date;
- $this->_storeManager = $storeManager;
- $this->_ratingFactory = $ratingFactory;
- $this->_ratingOptions = $ratingOptions;
- parent::__construct($context, $connectionName);
- }
- /**
- * Define main table. Define other tables name
- *
- * @return void
- */
- protected function _construct()
- {
- $this->_init('review', 'review_id');
- $this->_reviewTable = $this->getTable('review');
- $this->_reviewDetailTable = $this->getTable('review_detail');
- $this->_reviewStatusTable = $this->getTable('review_status');
- $this->_reviewEntityTable = $this->getTable('review_entity');
- $this->_reviewStoreTable = $this->getTable('review_store');
- $this->_aggregateTable = $this->getTable('review_entity_summary');
- }
- /**
- * Retrieve select object for load object data
- *
- * @param string $field
- * @param mixed $value
- * @param AbstractModel $object
- * @return \Magento\Framework\DB\Select
- */
- protected function _getLoadSelect($field, $value, $object)
- {
- $select = parent::_getLoadSelect($field, $value, $object);
- $select->join(
- $this->_reviewDetailTable,
- $this->getMainTable() . ".review_id = {$this->_reviewDetailTable}.review_id"
- );
- return $select;
- }
- /**
- * Perform actions before object save
- *
- * @param AbstractModel $object
- * @return $this
- */
- protected function _beforeSave(AbstractModel $object)
- {
- if (!$object->getId()) {
- $object->setCreatedAt($this->_date->gmtDate());
- }
- if ($object->hasData('stores') && is_array($object->getStores())) {
- $stores = $object->getStores();
- $stores[] = 0;
- $object->setStores($stores);
- } elseif ($object->hasData('stores')) {
- $object->setStores([$object->getStores(), 0]);
- }
- return $this;
- }
- /**
- * Perform actions after object save
- *
- * @param \Magento\Framework\Model\AbstractModel $object
- * @return $this
- */
- protected function _afterSave(AbstractModel $object)
- {
- $connection = $this->getConnection();
- /**
- * save detail
- */
- $detail = [
- 'title' => $object->getTitle(),
- 'detail' => $object->getDetail(),
- 'nickname' => $object->getNickname(),
- ];
- $select = $connection->select()->from($this->_reviewDetailTable, 'detail_id')->where('review_id = :review_id');
- $detailId = $connection->fetchOne($select, [':review_id' => $object->getId()]);
- if ($detailId) {
- $condition = ["detail_id = ?" => $detailId];
- $connection->update($this->_reviewDetailTable, $detail, $condition);
- } else {
- $detail['store_id'] = $object->getStoreId();
- $detail['customer_id'] = $object->getCustomerId();
- $detail['review_id'] = $object->getId();
- $connection->insert($this->_reviewDetailTable, $detail);
- }
- /**
- * save stores
- */
- $stores = $object->getStores();
- if (!empty($stores)) {
- $condition = ['review_id = ?' => $object->getId()];
- $connection->delete($this->_reviewStoreTable, $condition);
- $insertedStoreIds = [];
- foreach ($stores as $storeId) {
- if (in_array($storeId, $insertedStoreIds)) {
- continue;
- }
- $insertedStoreIds[] = $storeId;
- $storeInsert = ['store_id' => $storeId, 'review_id' => $object->getId()];
- $connection->insert($this->_reviewStoreTable, $storeInsert);
- }
- }
- // reaggregate ratings, that depend on this review
- $this->_aggregateRatings($this->_loadVotedRatingIds($object->getId()), $object->getEntityPkValue());
- return $this;
- }
- /**
- * Perform actions after object load
- *
- * @param \Magento\Framework\Model\AbstractModel $object
- * @return $this
- */
- protected function _afterLoad(AbstractModel $object)
- {
- $connection = $this->getConnection();
- $select = $connection->select()->from(
- $this->_reviewStoreTable,
- ['store_id']
- )->where(
- 'review_id = :review_id'
- );
- $stores = $connection->fetchCol($select, [':review_id' => $object->getId()]);
- if (empty($stores) && $this->_storeManager->hasSingleStore()) {
- $object->setStores([$this->_storeManager->getStore(true)->getId()]);
- } else {
- $object->setStores($stores);
- }
- return $this;
- }
- /**
- * Action before delete
- *
- * @param \Magento\Framework\Model\AbstractModel $object
- * @return $this
- */
- protected function _beforeDelete(AbstractModel $object)
- {
- // prepare rating ids, that depend on review
- $this->_deleteCache = [
- 'ratingIds' => $this->_loadVotedRatingIds($object->getId()),
- 'entityPkValue' => $object->getEntityPkValue(),
- ];
- return $this;
- }
- /**
- * Perform actions after object delete
- *
- * @param \Magento\Framework\Model\AbstractModel $object
- * @return $this
- */
- public function afterDeleteCommit(AbstractModel $object)
- {
- $this->aggregate($object);
- // reaggregate ratings, that depended on this review
- $this->_aggregateRatings($this->_deleteCache['ratingIds'], $this->_deleteCache['entityPkValue']);
- $this->_deleteCache = [];
- return $this;
- }
- /**
- * Retrieves total reviews
- *
- * @param int $entityPkValue
- * @param bool $approvedOnly
- * @param int $storeId
- * @return int
- */
- public function getTotalReviews($entityPkValue, $approvedOnly = false, $storeId = 0)
- {
- $connection = $this->getConnection();
- $select = $connection->select()->from(
- $this->_reviewTable,
- ['review_count' => new \Zend_Db_Expr('COUNT(*)')]
- )->where(
- "{$this->_reviewTable}.entity_pk_value = :pk_value"
- );
- $bind = [':pk_value' => $entityPkValue];
- if ($storeId > 0) {
- $select->join(
- ['store' => $this->_reviewStoreTable],
- $this->_reviewTable . '.review_id=store.review_id AND store.store_id = :store_id',
- []
- );
- $bind[':store_id'] = (int) $storeId;
- }
- if ($approvedOnly) {
- $select->where("{$this->_reviewTable}.status_id = :status_id");
- $bind[':status_id'] = \Magento\Review\Model\Review::STATUS_APPROVED;
- }
- return $connection->fetchOne($select, $bind);
- }
- /**
- * Aggregate
- *
- * @param \Magento\Framework\Model\AbstractModel $object
- * @return void
- */
- public function aggregate($object)
- {
- if (!$object->getEntityPkValue() && $object->getId()) {
- $object->load($object->getReviewId());
- }
- $ratingModel = $this->_ratingFactory->create();
- $ratingSummaries = $ratingModel->getEntitySummary($object->getEntityPkValue(), false);
- foreach ($ratingSummaries as $ratingSummaryObject) {
- $this->aggregateReviewSummary($object, $ratingSummaryObject);
- }
- }
- /**
- * Aggregate review summary
- *
- * @param \Magento\Framework\Model\AbstractModel $object
- * @param \Magento\Review\Model\Rating $ratingSummaryObject
- * @return void
- */
- protected function aggregateReviewSummary($object, $ratingSummaryObject)
- {
- $connection = $this->getConnection();
- if ($ratingSummaryObject->getCount()) {
- $ratingSummary = round($ratingSummaryObject->getSum() / $ratingSummaryObject->getCount());
- } else {
- $ratingSummary = $ratingSummaryObject->getSum();
- }
- $reviewsCount = $this->getTotalReviews(
- $object->getEntityPkValue(),
- true,
- $ratingSummaryObject->getStoreId()
- );
- $select = $connection->select()->from($this->_aggregateTable)
- ->where('entity_pk_value = :pk_value')
- ->where('entity_type = :entity_type')
- ->where('store_id = :store_id');
- $bind = [
- ':pk_value' => $object->getEntityPkValue(),
- ':entity_type' => $object->getEntityId(),
- ':store_id' => $ratingSummaryObject->getStoreId(),
- ];
- $oldData = $connection->fetchRow($select, $bind);
- $data = new \Magento\Framework\DataObject();
- $data->setReviewsCount($reviewsCount)
- ->setEntityPkValue($object->getEntityPkValue())
- ->setEntityType($object->getEntityId())
- ->setRatingSummary($ratingSummary > 0 ? $ratingSummary : 0)
- ->setStoreId($ratingSummaryObject->getStoreId());
- $this->writeReviewSummary($oldData, $data);
- }
- /**
- * Write rating summary
- *
- * @param array|bool $oldData
- * @param \Magento\Framework\DataObject $data
- * @return void
- */
- protected function writeReviewSummary($oldData, \Magento\Framework\DataObject $data)
- {
- $connection = $this->getConnection();
- $connection->beginTransaction();
- try {
- if (isset($oldData['primary_id']) && $oldData['primary_id'] > 0) {
- $condition = ["{$this->_aggregateTable}.primary_id = ?" => $oldData['primary_id']];
- $connection->update($this->_aggregateTable, $data->getData(), $condition);
- } else {
- $connection->insert($this->_aggregateTable, $data->getData());
- }
- $connection->commit();
- } catch (\Exception $e) {
- $connection->rollBack();
- }
- }
- /**
- * Get rating IDs from review votes
- *
- * @param int $reviewId
- * @return array
- */
- protected function _loadVotedRatingIds($reviewId)
- {
- $connection = $this->getConnection();
- if (empty($reviewId)) {
- return [];
- }
- $select = $connection->select()->from(['v' => $this->getTable('rating_option_vote')], 'r.rating_id')
- ->joinInner(['r' => $this->getTable('rating')], 'v.rating_id=r.rating_id')
- ->where('v.review_id = :revire_id');
- return $connection->fetchCol($select, [':revire_id' => $reviewId]);
- }
- /**
- * Aggregate this review's ratings.
- * Useful, when changing the review.
- *
- * @param int[] $ratingIds
- * @param int $entityPkValue
- * @return $this
- */
- protected function _aggregateRatings($ratingIds, $entityPkValue)
- {
- if ($ratingIds && !is_array($ratingIds)) {
- $ratingIds = [(int)$ratingIds];
- }
- if ($ratingIds && $entityPkValue) {
- foreach ($ratingIds as $ratingId) {
- $this->_ratingOptions->aggregateEntityByRatingId($ratingId, $entityPkValue);
- }
- }
- return $this;
- }
- /**
- * Reaggregate this review's ratings.
- *
- * @param int $reviewId
- * @param int $entityPkValue
- * @return void
- */
- public function reAggregateReview($reviewId, $entityPkValue)
- {
- $this->_aggregateRatings($this->_loadVotedRatingIds($reviewId), $entityPkValue);
- }
- /**
- * Get review entity type id by code
- *
- * @param string $entityCode
- * @return int|bool
- */
- public function getEntityIdByCode($entityCode)
- {
- $connection = $this->getConnection();
- $select = $connection->select()->from($this->_reviewEntityTable, ['entity_id'])
- ->where('entity_code = :entity_code');
- return $connection->fetchOne($select, [':entity_code' => $entityCode]);
- }
- /**
- * Delete reviews by product id.
- * Better to call this method in transaction, because operation performed on two separated tables
- *
- * @param int $productId
- * @return $this
- */
- public function deleteReviewsByProductId($productId)
- {
- $this->getConnection()->delete(
- $this->_reviewTable,
- [
- 'entity_pk_value=?' => $productId,
- 'entity_id=?' => $this->getEntityIdByCode(\Magento\Review\Model\Review::ENTITY_PRODUCT_CODE)
- ]
- );
- $this->getConnection()->delete(
- $this->getTable('review_entity_summary'),
- [
- 'entity_pk_value=?' => $productId,
- 'entity_type=?' => $this->getEntityIdByCode(\Magento\Review\Model\Review::ENTITY_PRODUCT_CODE)
- ]
- );
- return $this;
- }
- }
|