123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\SalesRule\Model\ResourceModel\Rule;
- use Magento\Framework\DB\Select;
- use Magento\Framework\Serialize\Serializer\Json;
- use Magento\Quote\Model\Quote\Address;
- /**
- * Sales Rules resource collection model.
- *
- * @api
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- * @since 100.0.2
- */
- class Collection extends \Magento\Rule\Model\ResourceModel\Rule\Collection\AbstractCollection
- {
- /**
- * Store associated with rule entities information map
- *
- * @var array
- */
- protected $_associatedEntitiesMap;
- /**
- * @var \Magento\SalesRule\Model\ResourceModel\Rule\DateApplier
- * @since 100.1.0
- */
- protected $dateApplier;
- /**
- * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface
- */
- protected $_date;
- /**
- * @var Json $serializer
- */
- private $serializer;
- /**
- * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
- * @param \Psr\Log\LoggerInterface $logger
- * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
- * @param \Magento\Framework\Event\ManagerInterface $eventManager
- * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $date
- * @param mixed $connection
- * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource
- * @param Json $serializer Optional parameter for backward compatibility
- */
- public function __construct(
- \Magento\Framework\Data\Collection\EntityFactory $entityFactory,
- \Psr\Log\LoggerInterface $logger,
- \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
- \Magento\Framework\Event\ManagerInterface $eventManager,
- \Magento\Framework\Stdlib\DateTime\TimezoneInterface $date,
- \Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
- \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null,
- Json $serializer = null
- ) {
- parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource);
- $this->_date = $date;
- $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Json::class);
- $this->_associatedEntitiesMap = $this->getAssociatedEntitiesMap();
- }
- /**
- * Set resource model and determine field mapping
- *
- * @return void
- */
- protected function _construct()
- {
- $this->_init(\Magento\SalesRule\Model\Rule::class, \Magento\SalesRule\Model\ResourceModel\Rule::class);
- $this->_map['fields']['rule_id'] = 'main_table.rule_id';
- }
- /**
- * Map data for associated entities
- *
- * @param string $entityType
- * @param string $objectField
- * @throws \Magento\Framework\Exception\LocalizedException
- * @return void
- * @since 100.1.0
- */
- protected function mapAssociatedEntities($entityType, $objectField)
- {
- if (!$this->_items) {
- return;
- }
- $entityInfo = $this->_getAssociatedEntityInfo($entityType);
- $ruleIdField = $entityInfo['rule_id_field'];
- $entityIds = $this->getColumnValues($ruleIdField);
- $select = $this->getConnection()->select()->from(
- $this->getTable($entityInfo['associations_table'])
- )->where(
- $ruleIdField . ' IN (?)',
- $entityIds
- );
- $associatedEntities = $this->getConnection()->fetchAll($select);
- array_map(function ($associatedEntity) use ($entityInfo, $ruleIdField, $objectField) {
- $item = $this->getItemByColumnValue($ruleIdField, $associatedEntity[$ruleIdField]);
- $itemAssociatedValue = $item->getData($objectField) === null ? [] : $item->getData($objectField);
- $itemAssociatedValue[] = $associatedEntity[$entityInfo['entity_id_field']];
- $item->setData($objectField, $itemAssociatedValue);
- }, $associatedEntities);
- }
- /**
- * Add website ids and customer group ids to rules data
- *
- * @return $this
- * @throws \Exception
- * @since 100.1.0
- */
- protected function _afterLoad()
- {
- $this->mapAssociatedEntities('website', 'website_ids');
- $this->mapAssociatedEntities('customer_group', 'customer_group_ids');
- $this->setFlag('add_websites_to_result', false);
- return parent::_afterLoad();
- }
- /**
- * Filter collection by specified website, customer group, coupon code, date.
- * Filter collection to use only active rules.
- * Involved sorting by sort_order column.
- *
- * @param int $websiteId
- * @param int $customerGroupId
- * @param string $couponCode
- * @param string|null $now
- * @param Address $address allow extensions to further filter out rules based on quote address
- * @use $this->addWebsiteGroupDateFilter()
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- * @return $this
- */
- public function setValidationFilter(
- $websiteId,
- $customerGroupId,
- $couponCode = '',
- $now = null,
- Address $address = null
- ) {
- if (!$this->getFlag('validation_filter')) {
- /* We need to overwrite joinLeft if coupon is applied */
- $this->getSelect()->reset();
- parent::_initSelect();
- $this->addWebsiteGroupDateFilter($websiteId, $customerGroupId, $now);
- $select = $this->getSelect();
- $connection = $this->getConnection();
- if (strlen($couponCode)) {
- $noCouponWhereCondition = $connection->quoteInto(
- 'main_table.coupon_type = ?',
- \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON
- );
- $relatedRulesIds = $this->getCouponRelatedRuleIds($couponCode);
- $select->where(
- $noCouponWhereCondition . ' OR main_table.rule_id IN (?)',
- $relatedRulesIds,
- Select::TYPE_CONDITION
- );
- } else {
- $this->addFieldToFilter(
- 'main_table.coupon_type',
- \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON
- );
- }
- $this->setOrder('sort_order', self::SORT_ORDER_ASC);
- $this->setFlag('validation_filter', true);
- }
- return $this;
- }
- /**
- * Get rules ids related to coupon code
- *
- * @param string $couponCode
- * @return array
- */
- private function getCouponRelatedRuleIds(string $couponCode): array
- {
- $connection = $this->getConnection();
- $select = $connection->select()->from(
- ['main_table' => $this->getTable('salesrule')],
- 'rule_id'
- );
- $select->joinLeft(
- ['rule_coupons' => $this->getTable('salesrule_coupon')],
- $connection->quoteInto(
- 'main_table.rule_id = rule_coupons.rule_id AND main_table.coupon_type != ?',
- \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON,
- null
- )
- );
- $autoGeneratedCouponCondition = [
- $connection->quoteInto(
- "main_table.coupon_type = ?",
- \Magento\SalesRule\Model\Rule::COUPON_TYPE_AUTO
- ),
- $connection->quoteInto(
- "rule_coupons.type = ?",
- \Magento\SalesRule\Api\Data\CouponInterface::TYPE_GENERATED
- ),
- ];
- $orWhereConditions = [
- "(" . implode($autoGeneratedCouponCondition, " AND ") . ")",
- $connection->quoteInto(
- '(main_table.coupon_type = ? AND main_table.use_auto_generation = 1 AND rule_coupons.type = 1)',
- \Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC
- ),
- $connection->quoteInto(
- '(main_table.coupon_type = ? AND main_table.use_auto_generation = 0 AND rule_coupons.type = 0)',
- \Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC
- ),
- ];
- $andWhereConditions = [
- $connection->quoteInto(
- 'rule_coupons.code = ?',
- $couponCode
- ),
- $connection->quoteInto(
- '(rule_coupons.expiration_date IS NULL OR rule_coupons.expiration_date >= ?)',
- $this->_date->date()->format('Y-m-d')
- ),
- ];
- $orWhereCondition = implode(' OR ', $orWhereConditions);
- $andWhereCondition = implode(' AND ', $andWhereConditions);
- $select->where(
- '(' . $orWhereCondition . ') AND ' . $andWhereCondition,
- null,
- Select::TYPE_CONDITION
- );
- $select->group('main_table.rule_id');
- return $connection->fetchCol($select);
- }
- /**
- * Filter collection by website(s), customer group(s) and date.
- * Filter collection to only active rules.
- * Sorting is not involved
- *
- * @param int $websiteId
- * @param int $customerGroupId
- * @param string|null $now
- * @use $this->addWebsiteFilter()
- * @return $this
- */
- public function addWebsiteGroupDateFilter($websiteId, $customerGroupId, $now = null)
- {
- if (!$this->getFlag('website_group_date_filter')) {
- if ($now === null) {
- $now = $this->_date->date()->format('Y-m-d');
- }
- $this->addWebsiteFilter($websiteId);
- $entityInfo = $this->_getAssociatedEntityInfo('customer_group');
- $connection = $this->getConnection();
- $this->getSelect()->joinInner(
- ['customer_group_ids' => $this->getTable($entityInfo['associations_table'])],
- $connection->quoteInto(
- 'main_table.' .
- $entityInfo['rule_id_field'] .
- ' = customer_group_ids.' .
- $entityInfo['rule_id_field'] .
- ' AND customer_group_ids.' .
- $entityInfo['entity_id_field'] .
- ' = ?',
- (int)$customerGroupId
- ),
- []
- );
- $this->getDateApplier()->applyDate($this->getSelect(), $now);
- $this->addIsActiveFilter();
- $this->setFlag('website_group_date_filter', true);
- }
- return $this;
- }
- /**
- * Add primary coupon to collection
- *
- * @return $this
- */
- public function _initSelect()
- {
- parent::_initSelect();
- $this->getSelect()->joinLeft(
- ['rule_coupons' => $this->getTable('salesrule_coupon')],
- 'main_table.rule_id = rule_coupons.rule_id AND rule_coupons.is_primary = 1',
- ['code']
- );
- return $this;
- }
- /**
- * Find product attribute in conditions or actions
- *
- * @param string $attributeCode
- * @return $this
- */
- public function addAttributeInConditionFilter($attributeCode)
- {
- $match = sprintf('%%%s%%', substr($this->serializer->serialize(['attribute' => $attributeCode]), 1, -1));
- /**
- * Information about conditions and actions stored in table as JSON encoded array
- * in fields conditions_serialized and actions_serialized.
- * If you want to find rules that contains some particular attribute, the easiest way to do so is serialize
- * attribute code in the same way as it stored in the serialized columns and execute SQL search
- * with like condition.
- * Table
- * +-------------------------------------------------------------------+
- * | conditions_serialized | actions_serialized |
- * +-------------------------------------------------------------------+
- * | {..."attribute":"attr_name"...} | {..."attribute":"attr_name"...} |
- * +---------------------------------|---------------------------------+
- * From attribute code "attr_code", will be generated such SQL:
- * `condition_serialized` LIKE '%"attribute":"attr_name"%'
- * OR `actions_serialized` LIKE '%"attribute":"attr_name"%'
- */
- $field = $this->_getMappedField('conditions_serialized');
- $cCond = $this->_getConditionSql($field, ['like' => $match]);
- $field = $this->_getMappedField('actions_serialized');
- $aCond = $this->_getConditionSql($field, ['like' => $match]);
- $this->getSelect()->where(
- sprintf('(%s OR %s)', $cCond, $aCond),
- null,
- Select::TYPE_CONDITION
- );
- return $this;
- }
- /**
- * Excludes price rules with generated specific coupon codes from collection
- *
- * @return $this
- */
- public function addAllowedSalesRulesFilter()
- {
- $this->addFieldToFilter('main_table.use_auto_generation', ['neq' => 1]);
- return $this;
- }
- /**
- * Limit rules collection by specific customer group
- *
- * @param int $customerGroupId
- * @return $this
- * @since 100.1.0
- */
- public function addCustomerGroupFilter($customerGroupId)
- {
- $entityInfo = $this->_getAssociatedEntityInfo('customer_group');
- if (!$this->getFlag('is_customer_group_joined')) {
- $this->setFlag('is_customer_group_joined', true);
- $this->getSelect()->join(
- ['customer_group' => $this->getTable($entityInfo['associations_table'])],
- $this->getConnection()
- ->quoteInto('customer_group.' . $entityInfo['entity_id_field'] . ' = ?', $customerGroupId)
- . ' AND main_table.' . $entityInfo['rule_id_field'] . ' = customer_group.'
- . $entityInfo['rule_id_field'],
- []
- );
- }
- return $this;
- }
- /**
- * Getter for _associatedEntitiesMap property
- *
- * @return array
- * @deprecated 100.1.0
- */
- private function getAssociatedEntitiesMap()
- {
- if (!$this->_associatedEntitiesMap) {
- $this->_associatedEntitiesMap = \Magento\Framework\App\ObjectManager::getInstance()
- ->get(\Magento\SalesRule\Model\ResourceModel\Rule\AssociatedEntityMap::class)
- ->getData();
- }
- return $this->_associatedEntitiesMap;
- }
- /**
- * Getter for dateApplier property
- *
- * @return DateApplier
- * @deprecated 100.1.0
- */
- private function getDateApplier()
- {
- if (null === $this->dateApplier) {
- $this->dateApplier = \Magento\Framework\App\ObjectManager::getInstance()
- ->get(\Magento\SalesRule\Model\ResourceModel\Rule\DateApplier::class);
- }
- return $this->dateApplier;
- }
- }
|