123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- declare(strict_types=1);
- namespace Magento\CatalogRule\Model\Rule\Condition;
- use Magento\Framework\Exception\InputException;
- use Magento\Rule\Model\Condition\ConditionInterface;
- use Magento\CatalogRule\Model\Rule\Condition\Combine as CombinedCondition;
- use Magento\CatalogRule\Model\Rule\Condition\Product as SimpleCondition;
- use Magento\Framework\Api\CombinedFilterGroup as FilterGroup;
- use Magento\Framework\Api\Filter;
- use Magento\Framework\Api\SearchCriteria;
- /**
- * Maps catalog price rule conditions to search criteria
- */
- class ConditionsToSearchCriteriaMapper
- {
- /**
- * @var \Magento\Framework\Api\SearchCriteriaBuilderFactory
- */
- private $searchCriteriaBuilderFactory;
- /**
- * @var \Magento\Framework\Api\CombinedFilterGroupFactory
- */
- private $combinedFilterGroupFactory;
- /**
- * @var \Magento\Framework\Api\FilterFactory
- */
- private $filterFactory;
- /**
- * @param \Magento\Framework\Api\SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory
- * @param \Magento\Framework\Api\CombinedFilterGroupFactory $combinedFilterGroupFactory
- * @param \Magento\Framework\Api\FilterFactory $filterFactory
- */
- public function __construct(
- \Magento\Framework\Api\SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory,
- \Magento\Framework\Api\CombinedFilterGroupFactory $combinedFilterGroupFactory,
- \Magento\Framework\Api\FilterFactory $filterFactory
- ) {
- $this->searchCriteriaBuilderFactory = $searchCriteriaBuilderFactory;
- $this->combinedFilterGroupFactory = $combinedFilterGroupFactory;
- $this->filterFactory = $filterFactory;
- }
- /**
- * Maps catalog price rule conditions to search criteria
- *
- * @param CombinedCondition $conditions
- * @return SearchCriteria
- * @throws InputException
- */
- public function mapConditionsToSearchCriteria(CombinedCondition $conditions): SearchCriteria
- {
- $filterGroup = $this->mapCombinedConditionToFilterGroup($conditions);
- $searchCriteriaBuilder = $this->searchCriteriaBuilderFactory->create();
- if ($filterGroup !== null) {
- $searchCriteriaBuilder->setFilterGroups([$filterGroup]);
- }
- return $searchCriteriaBuilder->create();
- }
- /**
- * Convert condition to filter group
- *
- * @param ConditionInterface $condition
- * @return null|\Magento\Framework\Api\CombinedFilterGroup|\Magento\Framework\Api\Filter
- * @throws InputException
- */
- private function mapConditionToFilterGroup(ConditionInterface $condition)
- {
- if ($condition->getType() === CombinedCondition::class) {
- return $this->mapCombinedConditionToFilterGroup($condition);
- } elseif ($condition->getType() === SimpleCondition::class) {
- return $this->mapSimpleConditionToFilterGroup($condition);
- }
- throw new InputException(
- __('Undefined condition type "%1" passed in.', $condition->getType())
- );
- }
- /**
- * Convert combined condition to filter group
- *
- * @param Combine $combinedCondition
- * @return null|\Magento\Framework\Api\CombinedFilterGroup
- * @throws InputException
- */
- private function mapCombinedConditionToFilterGroup(CombinedCondition $combinedCondition)
- {
- $filters = [];
- foreach ($combinedCondition->getConditions() as $condition) {
- $filter = $this->mapConditionToFilterGroup($condition);
- if ($filter === null) {
- continue;
- }
- // This required to solve cases when condition is configured like:
- // "If ALL/ANY of these conditions are FALSE" - we need to reverse SQL operator for this "FALSE"
- if ((bool)$combinedCondition->getValue() === false) {
- $this->reverseSqlOperatorInFilter($filter);
- }
- $filters[] = $filter;
- }
- if (count($filters) === 0) {
- return null;
- }
- return $this->createCombinedFilterGroup($filters, $combinedCondition->getAggregator());
- }
- /**
- * Convert simple condition to filter group
- *
- * @param ConditionInterface $productCondition
- * @return FilterGroup|Filter
- * @throws InputException
- */
- private function mapSimpleConditionToFilterGroup(ConditionInterface $productCondition)
- {
- if (is_array($productCondition->getValue())) {
- return $this->processSimpleConditionWithArrayValue($productCondition);
- }
- return $this->createFilter(
- $productCondition->getAttribute(),
- (string) $productCondition->getValue(),
- $productCondition->getOperator()
- );
- }
- /**
- * Convert simple condition with array value to filter group
- *
- * @param ConditionInterface $productCondition
- * @return FilterGroup
- * @throws InputException
- */
- private function processSimpleConditionWithArrayValue(ConditionInterface $productCondition): FilterGroup
- {
- $filters = [];
- foreach ($productCondition->getValue() as $subValue) {
- $filters[] = $this->createFilter(
- $productCondition->getAttribute(),
- (string) $subValue,
- $productCondition->getOperator()
- );
- }
- $combinationMode = $this->getGlueForArrayValues($productCondition->getOperator());
- return $this->createCombinedFilterGroup($filters, $combinationMode);
- }
- /**
- * Get glue for multiple values by operator
- *
- * @param string $operator
- * @return string
- */
- private function getGlueForArrayValues(string $operator): string
- {
- if (in_array($operator, ['!=', '!{}', '!()'], true)) {
- return 'all';
- }
- return 'any';
- }
- /**
- * Reverse sql conditions to their corresponding negative analog
- *
- * @param Filter $filter
- * @return void
- * @throws InputException
- */
- private function reverseSqlOperatorInFilter(Filter $filter)
- {
- $operatorsMap = [
- 'eq' => 'neq',
- 'neq' => 'eq',
- 'gteq' => 'lt',
- 'lteq' => 'gt',
- 'gt' => 'lteq',
- 'lt' => 'gteq',
- 'like' => 'nlike',
- 'nlike' => 'like',
- 'in' => 'nin',
- 'nin' => 'in',
- ];
- if (!array_key_exists($filter->getConditionType(), $operatorsMap)) {
- throw new InputException(
- __(
- 'Undefined SQL operator "%1" passed in. Valid operators are: %2',
- $filter->getConditionType(),
- implode(',', array_keys($operatorsMap))
- )
- );
- }
- $filter->setConditionType(
- $operatorsMap[$filter->getConditionType()]
- );
- }
- /**
- * Convert filters array into combined filter group
- *
- * @param array $filters
- * @param string $combinationMode
- * @return FilterGroup
- * @throws InputException
- */
- private function createCombinedFilterGroup(array $filters, string $combinationMode): FilterGroup
- {
- return $this->combinedFilterGroupFactory->create([
- 'data' => [
- FilterGroup::FILTERS => $filters,
- FilterGroup::COMBINATION_MODE => $this->mapRuleAggregatorToSQLAggregator($combinationMode)
- ]
- ]);
- }
- /**
- * Creating of filter object by filtering params
- *
- * @param string $field
- * @param string $value
- * @param string $conditionType
- * @return Filter
- * @throws InputException
- */
- private function createFilter(string $field, string $value, string $conditionType): Filter
- {
- return $this->filterFactory->create([
- 'data' => [
- Filter::KEY_FIELD => $field,
- Filter::KEY_VALUE => $value,
- Filter::KEY_CONDITION_TYPE => $this->mapRuleOperatorToSQLCondition($conditionType)
- ]
- ]);
- }
- /**
- * Maps catalog price rule operators to their corresponding operators in SQL
- *
- * @param string $ruleOperator
- * @return string
- * @throws InputException
- */
- private function mapRuleOperatorToSQLCondition(string $ruleOperator): string
- {
- $operatorsMap = [
- '==' => 'eq', // is
- '!=' => 'neq', // is not
- '>=' => 'gteq', // equals or greater than
- '<=' => 'lteq', // equals or less than
- '>' => 'gt', // greater than
- '<' => 'lt', // less than
- '{}' => 'like', // contains
- '!{}' => 'nlike', // does not contains
- '()' => 'in', // is one of
- '!()' => 'nin', // is not one of
- '<=>' => 'is_null'
- ];
- if (!array_key_exists($ruleOperator, $operatorsMap)) {
- throw new InputException(
- __(
- 'Undefined rule operator "%1" passed in. Valid operators are: %2',
- $ruleOperator,
- implode(',', array_keys($operatorsMap))
- )
- );
- }
- return $operatorsMap[$ruleOperator];
- }
- /**
- * Map rule combine aggregations to corresponding SQL operator
- *
- * @param string $ruleAggregator
- * @return string
- * @throws InputException
- */
- private function mapRuleAggregatorToSQLAggregator(string $ruleAggregator): string
- {
- $operatorsMap = [
- 'all' => 'AND',
- 'any' => 'OR',
- ];
- if (!array_key_exists(strtolower($ruleAggregator), $operatorsMap)) {
- throw new InputException(
- __(
- 'Undefined rule aggregator "%1" passed in. Valid operators are: %2',
- $ruleAggregator,
- implode(',', array_keys($operatorsMap))
- )
- );
- }
- return $operatorsMap[$ruleAggregator];
- }
- }
|