0

Does anyone have an idea of how to validate function works on product attribute while creating custom condition rule !? I'm stuck in validating condition rule Here, in my scenario, I've to validate my order's SKU with custom rule's saved condition SKU. enter image description here
Any Advice?

I've referred and tried to apply this solution but doesn't work for me Because in that case, she wants to fetch SKU from the cart (Quote) but in my case, I've to fetch SKU from ordered data that means sales_order_place_after. Maybe that doesn't make sense, I just have to understand that validate function with abstract model etc... The main thing is I'm stuck in that important concept of condition rule Any solution or explanation regarding this?

Here's my model code

Vendor\Module\Model\Rule\Condition\Customer.php

<?php

namespace Vendor\Module\Model\Rule\Condition;

class Customer extends \Magento\Rule\Model\Condition\Product\AbstractProduct
{

    protected function _addSpecialAttributes(array &$attributes)
    {
        parent::_addSpecialAttributes($attributes);
        $attributes['quote_item_qty'] = __('Quantity in cart');
        $attributes['quote_item_price'] = __('Price in cart');
        $attributes['quote_item_row_total'] = __('Row total in cart');

        $attributes['parent::category_ids'] = __('Category (Parent only)');
        $attributes['children::category_ids'] = __('Category (Children Only)');
    }

    public function getAttribute(): string
    {
        $attribute = $this->getData('attribute');
        if (strpos($attribute, '::') !== false) {
            list(, $attribute) = explode('::', $attribute);
        }

        return $attribute;
    }

    /**
     * @inheritdoc
     */
    public function getAttributeName()
    {
        $attribute = $this->getAttribute();
        if ($this->getAttributeScope()) {
            $attribute = $this->getAttributeScope() . '::' . $attribute;
        }

        return $this->getAttributeOption($attribute);
    }

    /**
     * @inheritdoc
     */
    public function loadAttributeOptions()
    {
        $productAttributes = $this->_productResource->loadAllAttributes()->getAttributesByCode();

        $attributes = [];
        foreach ($productAttributes as $attribute) {
            /* @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
            if (!$attribute->isAllowedForRuleCondition()
                || !$attribute->getDataUsingMethod($this->_isUsedForRuleProperty)
            ) {
                continue;
            }
            $frontLabel = $attribute->getFrontendLabel();
            $attributes[$attribute->getAttributeCode()] = $frontLabel;
            $attributes['parent::' . $attribute->getAttributeCode()] = $frontLabel . __('(Parent Only)');
            $attributes['children::' . $attribute->getAttributeCode()] = $frontLabel . __('(Children Only)');
        }

        $this->_addSpecialAttributes($attributes);

        asort($attributes);
        $this->setAttributeOption($attributes);

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function getAttributeElementHtml()
    {
        $html = parent::getAttributeElementHtml() .
            $this->getAttributeScopeElement()->getHtml();

        return $html;
    }

    private function getAttributeScopeElement(): \Magento\Framework\Data\Form\Element\AbstractElement
    {
        return $this->getForm()->addField(
            $this->getPrefix() . '__' . $this->getId() . '__attribute_scope',
            'hidden',
            [
                'name' => $this->elementName . '[' . $this->getPrefix() . '][' . $this->getId() . '][attribute_scope]',
                'value' => $this->getAttributeScope(),
                'no_span' => true,
                'class' => 'hidden',
                'data-form-part' => $this->getFormName(),
            ]
        );
    }

    public function setAttribute(string $value)
    {
        if (strpos($value, '::') !== false) {
            list($scope, $attribute) = explode('::', $value);
            $this->setData('attribute_scope', $scope);
            $this->setData('attribute', $attribute);
        } else {
            $this->setData('attribute', $value);
        }
    }

    /**
     * @inheritdoc
     */
    public function loadArray($arr)
    {
        parent::loadArray($arr);
        $this->setAttributeScope($arr['attribute_scope'] ?? null);

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function asArray(array $arrAttributes = [])
    {
        $out = parent::asArray($arrAttributes);
        $out['attribute_scope'] = $this->getAttributeScope();

        return $out;
    }

Vendor\Module\Model\Rule\Condition\Combine.php

<?php
 
namespace Vendor\Module\Model\Rule\Condition;

use Magento\Catalog\Model\ResourceModel\Product\Collection;
 
class Combine extends \Magento\Rule\Model\Condition\Combine
{
  
    protected $_ruleConditionProd;

  
    public function __construct(
        \Magento\Rule\Model\Condition\Context $context,
        \Magento\SalesRule\Model\Rule\Condition\Product $ruleConditionProduct,
        array $data = []
    ) {
        parent::__construct($context, $data);
        $this->_ruleConditionProd = $ruleConditionProduct;
        $this->setType(\Magento\SalesRule\Model\Rule\Condition\Product\Combine::class);
    }

    
    public function getNewChildSelectOptions()
    {
        $productAttributes = $this->_ruleConditionProd->loadAttributeOptions()->getAttributeOption();
        $pAttributes = [];
        $iAttributes = [];
        foreach ($productAttributes as $code => $label) {
            if (strpos($code, 'quote_item_') === 0) {
                $iAttributes[] = [
                    'value' => \Magento\SalesRule\Model\Rule\Condition\Product::class . '|' . $code,
                    'label' => $label,
                ];
            } else {
                $pAttributes[] = [
                    'value' => \Magento\SalesRule\Model\Rule\Condition\Product::class . '|' . $code,
                    'label' => $label,
                ];
            }
        }

        $conditions = parent::getNewChildSelectOptions();
        $conditions = array_merge_recursive(
            $conditions,
            [
                [
                    'value' => \Magento\SalesRule\Model\Rule\Condition\Product\Combine::class,
                    'label' => __('Conditions Combination'),
                ],
                ['label' => __('Cart Item Attribute'), 'value' => $iAttributes],
                ['label' => __('Product Attribute'), 'value' => $pAttributes]
            ]
        );
        return $conditions;
    }

    /**
     * Collect validated attributes
     *
     * @param Collection $productCollection
     * @return $this
     */
    public function collectValidatedAttributes($productCollection)
    {
        foreach ($this->getConditions() as $condition) {
            $condition->collectValidatedAttributes($productCollection);
        }
        return $this;
    }

    /**
     * @inheritdoc
     * @since 101.0.6
     */
    protected function _isValid($entity)
    {
        if (!$this->getConditions()) {
            return true;
        }

        $all = $this->getAggregator() === 'all';
        $true = (bool)$this->getValue();

        foreach ($this->getConditions() as $cond) {
            if ($entity instanceof \Magento\Framework\Model\AbstractModel) {
                $validated = $this->validateEntity($cond, $entity);
            } else {
                $validated = $cond->validateByEntityId($entity);
            }
            if ($all && $validated !== $true) {
                return false;
            } elseif (!$all && $validated === $true) {
                return true;
            }
        }
        return $all ? true : false;
    }

    /**
     * Validate entity.
     *
     * @param object $cond
     * @param \Magento\Framework\Model\AbstractModel $entity
     * @return bool
     */
    private function validateEntity($cond, \Magento\Framework\Model\AbstractModel $entity)
    {
        $true = (bool)$this->getValue();
        $validated = !$true;
        foreach ($this->retrieveValidateEntities($cond->getAttributeScope(), $entity) as $validateEntity) {
            $validated = $cond->validate($validateEntity);
            if ($validated === $true) {
                break;
            }
        }

        return $validated;
    }

 
    private function retrieveValidateEntities($attributeScope, \Magento\Framework\Model\AbstractModel $entity)
    {
        if ($attributeScope === 'parent') {
            $validateEntities = [$entity];
        } elseif ($attributeScope === 'children') {
            $validateEntities = $entity->getChildren() ?: [$entity];
        } else {
            $validateEntities = $entity->getChildren() ?: [];
            $validateEntities[] = $entity;
        }

        return $validateEntities;
    }
}

Vendor\Module\Model\Rule.php

    <?php

namespace Vendor\Module\Model;

use Magento\Quote\Model\Quote\Address;
use Magento\Rule\Model\AbstractModel;
use Magento\Framework\ObjectManagerInterface;

class Rule extends AbstractModel
{

    protected $_eventPrefix = 'vendor_module';

    protected $_eventObject = 'rule';
    protected $objectManager;

    protected $condCombineFactory;

    protected $condProdCombineF;


    protected $validatedAddresses = [];

    public function __construct(
        \Magento\Framework\Model\Context $context,
        ObjectManagerInterface $objectManager,
        \Magento\Framework\Registry $registry,
        \Magento\Framework\Data\FormFactory $formFactory,
        \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
        \Magento\SalesRule\Model\Rule\Condition\CombineFactory $condCombineFactory,
        \Magento\SalesRule\Model\Rule\Condition\Product\CombineFactory $condProdCombineF,
        \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
        \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
        array $data = []
    ) {
        $this->objectManager = $objectManager;
        $this->condCombineFactory = $condCombineFactory;
        $this->condProdCombineF = $condProdCombineF;
        parent::__construct($context, $registry, $formFactory, $localeDate, $resource, $resourceCollection, $data);
    }

    protected function _construct()
    {
        parent::_construct();
        $this->_init('Vendor\Module\Model\ResourceModel\Rule');
        $this->setIdFieldName('rule_id');
    }


    public function getConditionsInstance()
    {
        return $this->objectManager->create('Vendor\Module\Model\Rule\Condition\Combine');
    }

    public function getActionsInstance()
    {
        return $this->objectManager->create('Magento\SalesRule\Model\Rule\Condition\Product\Combine');
    }
 
    public function hasIsValidForAddress($address)
    {
        $addressId = $this->_getAddressId($address);
        return isset($this->validatedAddresses[$addressId]) ? true : false;
    }
 
    public function setIsValidForAddress($address, $validationResult)
    {
        $addressId = $this->_getAddressId($address);
        $this->validatedAddresses[$addressId] = $validationResult;
        return $this;
    }


    public function getIsValidForAddress($address)
    {
        $addressId = $this->_getAddressId($address);
        return isset($this->validatedAddresses[$addressId]) ? $this->validatedAddresses[$addressId] : false;
    }

    private function _getAddressId($address)
    {
        if ($address instanceof Address) {
            return $address->getId();
        }
        return $address;
    }
}

Vendor\Module\Model\Validator.php

<?php

namespace Vendor\Module\Model;

use Magento\Framework\DataObject;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\App\State;

class Validator extends DataObject
{
    protected $rateAdjustments = [];

    protected $objectManager;

    private $appState;

    public function __construct(ObjectManagerInterface $objectManager, State $appState, array $data = [])
    {
        $this->objectManager = $objectManager;
        $this->appState = $appState;
        parent::__construct($data);
    }

    public function init($request)
    {
        $this->setRequest($request);
        return $this;
    }

My Updated code... in if condition I'm checking whether the flag variable is true or false but it stops the execution from validate the condition $ruleConditionData prints my rule data also

Array ( [rule_id] => 1 [name] => update4 [is_active] => 1 [stores] => 2 [customer_groups] => 0 [conditions_serialized] => {"type":"Magento\SalesRule\Model\Rule\Condition\Product\Combine","attribute":null,"operator":null,"value":"1","is_value_processed":null,"aggregator":"all","conditions":[{"type":"Magento\SalesRule\Model\Rule\Condition\Product","attribute":"sku","operator":"==","value":"similar more","is_value_processed":false,"attribute_scope":""}]} )

 public function execute(Observer $observer)
{
  $flag = false;
    if ($this->helper->isEnabled()) {

        $orderIds = $observer->getOrderIds();
        $order = $this->orderFactory->create()->load($orderIds[0]);
        foreach ($order->getAllItems() as $orderItem) {
            $itemSku = $orderItem->getSku();
           
            $ruleId = 1;

            $prdData = $this->productloader->create()->loadByAttribute("sku", $itemSku );

            $ruleConditionData = $this->ruleFactory->create()->load($ruleId);
           
            print_r($ruleConditionData->getData());
          
                if ($ruleConditionData->getConditions()->validate($prdData)) {
                $flag = true;
               
            }

        }
}
6
  • Please share your Model file and your code. – Msquare Nov 28 '20 at 5:35
  • Please check my updated question! – Denlier1211 Nov 28 '20 at 6:28
  • Your rule value are store into your custom table or not ? – Msquare Nov 28 '20 at 8:42
  • Yes... my rule value is stored in my custom table in serialized form – Denlier1211 Nov 28 '20 at 8:48
  • {"type":"Magento\\SalesRule\\Model\\Rule\\Condition\\Product\\Combine","attribute":null,"operator":null,"value":"1","is_value_processed":null,"aggregator":"all","conditions":[{"type":"Magento\\SalesRule\\Model\\Rule\\Condition\\Product","attribute":"sku","operator":"==","value":"similar more","is_value_processed":false,"attribute_scope":""}]} It's stored like this – Denlier1211 Nov 28 '20 at 8:49
1

Try this code

protected $ruleFactory;
protected $productloader;

public function __construct(
    ...........................................
    \Vendor\Module\Model\RuleFactory $ruleFactory,
    \Magento\Catalog\Model\ProductFactory $productloader,
    ...........................................
) {
    ...........................................
    $this->ruleFactory = $ruleFactory;
    $this->productloader = $productloader;
    ...........................................
}

public function execute()
{
    ...........................................
    ...........................................

    
    $selectedValue = "SKU-Product";
    $prdData = $this->productloader->create()->loadByAttribute("sku", $selectedValue);

    $ruleId = 1 ;

    $ruleConditionData = $this->ruleFactory->create();
    $ruleConditionData->load($ruleId);
    //$ruleConditionData->getConditions()->validate($prdData);

    if ($ruleConditionData->getConditions()->validate($prdData)) {
        echo "Rule Match";
    }  else {
        echo "Rule Not Match";
    }
}

Update your Rule.php

<?php

namespace Vendor\Module\Model;

use Vendor\Module\Model\ResourceModel\Rule as RuleResourceModel;
use Magento\Quote\Model\Quote\Address;
use Magento\Rule\Model\AbstractModel;

class Rule extends AbstractModel
{
    protected $_eventPrefix = 'vendor_module';
    protected $_eventObject = 'rule';
    protected $condCombineFactory;
    protected $condProdCombineF;
    protected $validatedAddresses = [];

    public function __construct(
        \Magento\Framework\Model\Context $context,
        \Magento\Framework\Registry $registry,
        \Magento\Framework\Data\FormFactory $formFactory,
        \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
        \Magento\CatalogRule\Model\Rule\Condition\CombineFactory $condCombineFactory,
        \Magento\SalesRule\Model\Rule\Condition\Product\CombineFactory $condProdCombineF,
        \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
        \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
        array $data = []
    ) {
        $this->condCombineFactory = $condCombineFactory;
        $this->condProdCombineF = $condProdCombineF;
        parent::__construct($context, $registry, $formFactory, $localeDate, $resource, $resourceCollection, $data);
    }

    protected function _construct()
    {
        parent::_construct();
        $this->_init(RuleResourceModel::class);
        $this->setIdFieldName('rule_id'); // add your table primari key 
    }

    public function getConditionsInstance()
    {
        return $this->condCombineFactory->create();
    }

    public function getActionsInstance()
    {
        return $this->condCombineFactory->create();
    }

    public function hasIsValidForAddress($address)
    {
        $addressId = $this->_getAddressId($address);
        return isset($this->validatedAddresses[$addressId]) ? true : false;
    }
 
    public function setIsValidForAddress($address, $validationResult)
    {
        $addressId = $this->_getAddressId($address);
        $this->validatedAddresses[$addressId] = $validationResult;
        return $this;
    }

    public function getIsValidForAddress($address)
    {
        $addressId = $this->_getAddressId($address);
        return isset($this->validatedAddresses[$addressId]) ? $this->validatedAddresses[$addressId] : false;
    }

    private function _getAddressId($address)
    {
        if ($address instanceof Address) {
            return $address->getId();
        }
        return $address;
    }

    public function getConditionsFieldSetId($formName = '')
    {
        return $formName . 'rule_conditions_fieldset_' . $this->getId();
    }

    public function getActionFieldSetId($formName = '')
    {
        return $formName . 'rule_actions_fieldset_' . $this->getId();
    }
}
17
  • Thank you for your reply... :) Can I use this code in customer.php file directly? – Denlier1211 Nov 28 '20 at 9:45
  • your problem is solve or not ? – Msquare Nov 28 '20 at 10:43
  • At first, really appreciate your solution!... I used this code and I'm stuck in one error ... Uncaught Error: Call to a member function getTypeId() on null in C:\xampp\htdocs\mag233\vendor\magento\module-configurable-product\Plugin\SalesRule\Model\Rule\Condition\Product.php:59 – Denlier1211 Nov 28 '20 at 19:42
  • conditions validation is works or not ? – Msquare Nov 30 '20 at 5:09
  • Noo it stops in validate function – Denlier1211 Nov 30 '20 at 6:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.