| Current Path : /var/www/clients/client3/web2/web/vendor/dotmailer/dotmailer-magento2-extension/Model/ |
| Current File : /var/www/clients/client3/web2/web/vendor/dotmailer/dotmailer-magento2-extension/Model/Rules.php |
<?php
namespace Dotdigitalgroup\Email\Model;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Sales\Model\ResourceModel\Order\Collection as OrderCollection;
use Magento\Quote\Model\ResourceModel\Quote\Collection as QuoteCollection;
class Rules extends \Magento\Framework\Model\AbstractModel
{
/**
* Exclusion Rule for Abandoned Cart.
*/
const ABANDONED = 1;
/**
* Exclusion Rule for Product Review.
*/
const REVIEW = 2;
/**
* Condition combination all.
*/
const COMBINATION_TYPE_ALL = 1;
/**
* Condition combination any.
*/
const COMBINATION_TYPE_ANY = 2;
/**
* @var int
*/
public $ruleType;
/**
* @var \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory
*/
private $quoteCollectionFactory;
/**
* @var ResourceModel\Rules
*/
private $rulesResource;
/**
* @var array
*/
private $conditionMap;
/**
* @var array
*/
private $defaultOptions;
/**
* @var array
*/
public $attributeMapForQuote;
/**
* @var array
*/
private $attributeMapForOrder;
/**
* @var array
*/
private $productAttribute;
/**
* @var array
*/
private $used = [];
/**
* @var Adminhtml\Source\Rules\Type
*/
private $rulesType;
/**
* @var \Magento\Eav\Model\Config
*/
private $config;
/**
* @var SerializerInterface
*/
private $serializer;
/**
* Rules constructor.
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory $quoteCollectionFactory
* @param Adminhtml\Source\Rules\Type $rulesType
* @param \Magento\Eav\Model\Config $config
* @param SerializerInterface $serializer
* @param ResourceModel\Rules $rulesResource
* @param array $data
* @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource
* @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
*/
public function __construct(
\Magento\Framework\Model\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Quote\Model\ResourceModel\Quote\CollectionFactory $quoteCollectionFactory,
\Dotdigitalgroup\Email\Model\Adminhtml\Source\Rules\Type $rulesType,
\Magento\Eav\Model\Config $config,
SerializerInterface $serializer,
\Dotdigitalgroup\Email\Model\ResourceModel\Rules $rulesResource,
array $data = [],
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null
) {
$this->serializer = $serializer;
$this->config = $config;
$this->rulesType = $rulesType;
$this->rulesResource = $rulesResource;
$this->quoteCollectionFactory = $quoteCollectionFactory;
parent::__construct(
$context,
$registry,
$resource,
$resourceCollection,
$data
);
}
/**
* Construct.
*
* @return null
*/
public function _construct()
{
$this->defaultOptions = $this->rulesType->defaultOptions();
$this->conditionMap = [
'eq' => 'neq',
'neq' => 'eq',
'gteq' => 'lteq',
'lteq' => 'gteq',
'gt' => 'lt',
'lt' => 'gt',
'like' => 'nlike',
'nlike' => 'like',
];
$this->attributeMapForQuote = [
'method' => 'method',
'shipping_method' => 'shipping_method',
'country_id' => 'country_id',
'city' => 'city',
'region_id' => 'region_id',
'customer_group_id' => 'main_table.customer_group_id',
'coupon_code' => 'main_table.coupon_code',
'subtotal' => 'main_table.subtotal',
'grand_total' => 'main_table.grand_total',
'items_qty' => 'main_table.items_qty',
'customer_email' => 'main_table.customer_email',
];
$this->attributeMapForOrder = [
'method' => 'method',
'shipping_method' => 'main_table.shipping_method',
'country_id' => 'country_id',
'city' => 'city',
'region_id' => 'region_id',
'customer_group_id' => 'main_table.customer_group_id',
'coupon_code' => 'main_table.coupon_code',
'subtotal' => 'main_table.subtotal',
'grand_total' => 'main_table.grand_total',
'items_qty' => 'items_qty',
'customer_email' => 'main_table.customer_email',
];
parent::_construct();
$this->_init(\Dotdigitalgroup\Email\Model\ResourceModel\Rules::class);
}
/**
* @return $this
*/
public function beforeSave()
{
parent::beforeSave();
if ($this->isObjectNew()) {
$this->setCreatedAt(time());
} else {
$this->setUpdatedAt(time());
}
$this->setConditions($this->serializer->serialize($this->getConditions()));
$this->setWebsiteIds(implode(',', $this->getWebsiteIds()));
return $this;
}
/**
* Check if rule already exist for website.
*
* @param int $websiteId
* @param string $type
* @param bool $ruleId
*
* @return bool
*/
public function checkWebsiteBeforeSave($websiteId, $type, $ruleId = false)
{
return $this->getCollection()
->hasCollectionAnyItemsByWebsiteAndType($websiteId, $type, $ruleId);
}
/**
* Get rule for website.
*
* @param string $type
* @param int $websiteId
*
* @return array|\Dotdigitalgroup\Email\Model\Rules
*/
public function getActiveRuleForWebsite($type, $websiteId)
{
return $this->getCollection()
->getActiveRuleByWebsiteAndType($type, $websiteId);
}
/**
* Process rule on collection.
*
* @param \Magento\Sales\Model\ResourceModel\Order\Collection|
* \Magento\Quote\Model\ResourceModel\Quote\Collection $collection
* @param string $type
* @param int $websiteId
*
* @return \Magento\Sales\Model\ResourceModel\Order\Collection|
* \Magento\Quote\Model\ResourceModel\Quote\Collection $collection
*/
public function process($collection, $type, $websiteId)
{
$this->ruleType = $type;
$emailRules = $this->getActiveRuleForWebsite($type, $websiteId);
//if no rule or condition then return the collection untouched
if (empty($emailRules)) {
return $collection;
}
try {
$condition = $this->serializer->unserialize($emailRules->getConditions());
} catch (\InvalidArgumentException $e) {
return $collection;
}
if (empty($condition)) {
return $collection;
}
//process rule on collection according to combination
$combination = $emailRules->getCombination();
//join tables to collection according to type
$collection = $this->rulesResource->joinTablesOnCollectionByType($collection, $type);
if ($combination == self::COMBINATION_TYPE_ALL) {
$collection = $this->processAndCombination($collection, $condition);
}
if ($combination == self::COMBINATION_TYPE_ANY) {
$collection = $this->processOrCombination($collection, $condition);
}
return $collection;
}
/**
* Process And combination on collection.
*
* @param \Magento\Sales\Model\ResourceModel\Order\Collection|
* \Magento\Quote\Model\ResourceModel\Quote\Collectio $collection
* @param array $conditions
*
* @return \Magento\Sales\Model\ResourceModel\Order\Collection|
* \Magento\Quote\Model\ResourceModel\Quote\Collection $collection
*/
public function processAndCombination($collection, $conditions)
{
foreach ($conditions as $condition) {
$attribute = $condition['attribute'];
$cond = $condition['conditions'];
$value = $condition['cvalue'];
//ignore condition if value is null or empty
if ($value == '' || $value == null) {
continue;
}
//ignore conditions for already used attribute
if (in_array($attribute, $this->used)) {
continue;
}
//set used to check later
$this->used[] = $attribute;
//product review
if ($this->ruleType == self::REVIEW && isset($this->attributeMapForQuote[$attribute])) {
$attribute = $this->attributeMapForOrder[$attribute];
//abandoned cart
} elseif ($this->ruleType == self::ABANDONED && isset($this->attributeMapForOrder[$attribute])) {
$attribute = $this->attributeMapForQuote[$attribute];
} else {
$this->productAttribute[] = $condition;
continue;
}
$collection = $this->processProcessAndCombinationCondition($collection, $cond, $value, $attribute);
}
return $this->processProductAttributes($collection);
}
/**
* @param \Magento\Sales\Model\ResourceModel\Order\Collection|
* \Magento\Quote\Model\ResourceModel\Quote\Collection $collection
* @param string $cond
* @param string $value
* @param string $attribute
*
* @return null
*/
private function processProcessAndCombinationCondition($collection, $cond, $value, $attribute)
{
if ($cond == 'null') {
if ($value == '1') {
$condition = ['notnull' => true];
} elseif ($value == '0') {
$condition = [$cond => true];
}
} else {
if ($cond == 'like' || $cond == 'nlike') {
$value = '%' . $value . '%';
}
//condition with null values can't be filtered using string, include to filter null values
$conditionMap = [$this->conditionMap[$cond] => $value];
if ($cond == 'eq' || $cond == 'neq') {
$conditionMap[] = ['null' => true];
}
$condition = $conditionMap;
}
//filter by quote attribute
if ($attribute == 'items_qty' && $this->ruleType == self::REVIEW) {
$collection = $this->filterCollectionByQuoteAttribute($collection, $attribute, $condition);
} else {
$collection->addFieldToFilter($attribute, $condition);
}
return $collection;
}
/**
* process Or combination on collection.
*
* @param \Magento\Sales\Model\ResourceModel\Order\Collection|
* \Magento\Quote\Model\ResourceModel\Quote\Collectio $collection
* @param array $conditions
* @param string $type
*
* @return \Magento\Sales\Model\ResourceModel\Order\Collection|
* \Magento\Quote\Model\ResourceModel\Quote\Collection $collection
*
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function processOrCombination($collection, $conditions)
{
$fieldsConditions = [];
$multiFieldsConditions = [];
foreach ($conditions as $condition) {
$attribute = $condition['attribute'];
$cond = $condition['conditions'];
$value = $condition['cvalue'];
//ignore condition if value is null or empty
if ($value == '' || $value == null) {
continue;
}
if ($this->ruleType == self::REVIEW && isset($this->attributeMapForQuote[$attribute])) {
$attribute = $this->attributeMapForOrder[$attribute];
} elseif ($this->ruleType == self::ABANDONED && isset($this->attributeMapForOrder[$attribute])) {
$attribute = $this->attributeMapForQuote[$attribute];
} else {
$this->productAttribute[] = $condition;
continue;
}
if ($cond == 'null') {
if ($value == '1') {
if (isset($fieldsConditions[$attribute])) {
$multiFieldsConditions[$attribute][]
= ['notnull' => true];
continue;
}
$fieldsConditions[$attribute] = ['notnull' => true];
} elseif ($value == '0') {
if (isset($fieldsConditions[$attribute])) {
$multiFieldsConditions[$attribute][]
= [$cond => true];
continue;
}
$fieldsConditions[$attribute] = [$cond => true];
}
} else {
if ($cond == 'like' || $cond == 'nlike') {
$value = '%' . $value . '%';
}
if (isset($fieldsConditions[$attribute])) {
$multiFieldsConditions[$attribute][]
= [$this->conditionMap[$cond] => $value];
continue;
}
$fieldsConditions[$attribute]
= [$this->conditionMap[$cond] => $value];
}
}
//all rules condition will be with or combination
if (!empty($fieldsConditions)) {
$column = $cond = [];
foreach ($fieldsConditions as $key => $fieldsCondition) {
$column[] = (string)$key;
$cond[] = $fieldsCondition;
if (!empty($multiFieldsConditions[$key])) {
foreach ($multiFieldsConditions[$key] as $multiFieldsCondition) {
$column[] = (string)$key;
$cond[] = $multiFieldsCondition;
}
}
}
$collection->addFieldToFilter(
$column,
$cond
);
}
return $this->processProductAttributes($collection);
}
/**
* Process product attributes on collection.
*
* @param \Magento\Sales\Model\ResourceModel\Order\Collection|
* \Magento\Quote\Model\ResourceModel\Quote\Collection $collection
*
* @return \Magento\Sales\Model\ResourceModel\Order\Collection|
* \Magento\Quote\Model\ResourceModel\Quote\Collection $collection
*/
private function processProductAttributes($collection)
{
//if no product attribute or collection empty return collection
if (empty($this->productAttribute) || !$collection->getSize()) {
return $collection;
}
$collection = $this->processProductAttributesInCollection($collection);
return $collection;
}
/**
* Evaluate two values against condition.
*
* @param string $varOne
* @param string $op
* @param string $varTwo
*
* @return bool
*/
public function _evaluate($varOne, $op, $varTwo)
{
switch ($op) {
case 'eq':
return $varOne == $varTwo;
case 'neq':
return $varOne != $varTwo;
case 'gteq':
return $varOne >= $varTwo;
case 'lteq':
return $varOne <= $varTwo;
case 'gt':
return $varOne > $varTwo;
case 'lt':
return $varOne < $varTwo;
}
return false;
}
/**
* Process product attributes on collection.
*
* @param \Magento\Sales\Model\ResourceModel\Order\Collection|
* \Magento\Quote\Model\ResourceModel\Quote\Collectio $collection
*
* @return \Magento\Sales\Model\ResourceModel\Order\Collection|
* \Magento\Quote\Model\ResourceModel\Quote\Collection $collection
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
private function processProductAttributesInCollection($collection)
{
foreach ($collection as $collectionItem) {
foreach ($collectionItem->getAllItems() as $item) {
$this->processCollectionItem($collection, $collectionItem->getId(), $item);
}
}
return $collection;
}
/**
* @param QuoteCollection|OrderCollection $collection
* @param int $collectionItemId
* @param $item
* @throws \Magento\Framework\Exception\LocalizedException
*/
private function processCollectionItem($collection, $collectionItemId, $item)
{
$product = $item->getProduct();
$attributes = $this->getAttributesArrayFromLoadedProduct($product);
$attributes[] = 'attribute_set_id';
foreach ($this->productAttribute as $productAttribute) {
$attribute = $productAttribute['attribute'];
$cond = $productAttribute['conditions'];
$value = $productAttribute['cvalue'];
if ($cond == 'null') {
if ($value == '0') {
$cond = 'neq';
} elseif ($value == '1') {
$cond = 'eq';
}
$value = '';
}
//if attribute is in product's attributes array
if (!in_array($attribute, $attributes)) {
continue;
}
$attr = $this->config->getAttribute('catalog_product', $attribute);
//frontend type
$frontType = $attr->getFrontend()->getInputType();
//if type is select
if (in_array($frontType, ['select', 'multiselect'])) {
$attributeValue = $product->getAttributeText($attribute);
//evaluate conditions on values. if true then unset item from collection
if ($this->_evaluate($value, $cond, $attributeValue)) {
$collection->removeItemByKey($collectionItemId);
return;
}
} else {
$getter = 'get';
foreach (explode('_', $attribute) as $one) {
$getter .= ucfirst($one);
}
$attributeValue = $product->$getter();
//if retrieved value is an array then loop through all array values.
// example can be categories
if (is_array($attributeValue)) {
foreach ($attributeValue as $attrValue) {
//evaluate conditions on values. if true then unset item from collection
if ($this->_evaluate($value, $cond, $attrValue)) {
$collection->removeItemByKey($collectionItemId);
return;
}
}
} else {
//evaluate conditions on values. if true then unset item from collection
if ($this->_evaluate($value, $cond, $attributeValue)) {
$collection->removeItemByKey($collectionItemId);
return;
}
}
}
}
}
/**
* @param \Magento\Catalog\Model\Product $product
*
* @return array
*/
private function getAttributesArrayFromLoadedProduct($product)
{
//attributes array from loaded product
$attributes = $this->config->getEntityAttributes(
\Magento\Catalog\Model\Product::ENTITY,
$product
);
return array_keys($attributes);
}
/**
* @param \Magento\Sales\Model\ResourceModel\Order\Collection $collection
* @param string $attribute
* @param array $condition
* @return \Magento\Sales\Model\ResourceModel\Order\Collection
*/
private function filterCollectionByQuoteAttribute($collection, $attribute, array $condition)
{
$originalCollection = clone $collection;
$quoteCollection = $this->quoteCollectionFactory->create();
$quoteIds = $originalCollection->getColumnValues('quote_id');
if ($quoteIds) {
$quoteCollection->addFieldToFilter('entity_id', ['in' => $quoteIds])
->addFieldToFilter($attribute, $condition);
//no need for empty check - because should include the null result, it should work like exclusion filter!
$collection->addFieldToFilter('quote_id', ['in' => $quoteCollection->getAllIds()]);
}
return $collection;
}
}