mirror of
https://github.com/bdnugget/magento2.git
synced 2026-11-07 09:17:35 +00:00
500 lines
15 KiB
PHP
500 lines
15 KiB
PHP
<?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('store_id = :store_id')
|
|
->where('entity_type = :entity_type');
|
|
$bind = [
|
|
':pk_value' => $object->getEntityPkValue(),
|
|
':store_id' => $ratingSummaryObject->getStoreId(),
|
|
':entity_type' => $object->getEntityId(),
|
|
];
|
|
$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;
|
|
}
|
|
}
|