5

How to validate a condition rules based on product attribute in magento 2

backend:

enter image description here

code:

    $product_id = '3';  // Crown Summit Backpack sku is 24-MB03
    $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
    $rules = $objectManager->create('Vendor\Module\Model\Rule')->getCollection();
    foreach ($rules as $rule) {
        $product = $objectManager->get('Magento\Catalog\Model\Product')->load($product_id);
        $item = $objectManager->create('Magento\Catalog\Model\Product');
        $item->setProduct($product);
        $validate = $rule->getActions()->validate($item);
    }
    var_dump($validate);
    var_dump($product);

Expected result is false but it always shows true. validation not done properly on product attributes.

var_dump($validate);

enter image description here

var_dump($product);

enter image description here

Vendor\Module\Model\Rule.php

<?php
namespace Vendor\Module\Model;
use Magento\Quote\Model\Quote\Address;
use Magento\Rule\Model\AbstractModel;
/**
 * Class Rule
 * @package Vendor\Module\Model
 *
 * @method int|null getRuleId()
 * @method Rule setRuleId(int $id)
 */
class Rule extends AbstractModel
{
    /**
     * Prefix of model events names
     *
     * @var string
     */
    protected $_eventPrefix = 'vendor_module';
    /**
     * Parameter name in event
     *
     * In observe method you can use $observer->getEvent()->getRule() in this case
     *
     * @var string
     */
    protected $_eventObject = 'rule';
    /** @var \Magento\SalesRule\Model\Rule\Condition\CombineFactory */
    protected $condCombineFactory;
    /** @var \Magento\SalesRule\Model\Rule\Condition\Product\CombineFactory */
    protected $condProdCombineF;
    /**
     * Store already validated addresses and validation results
     *
     * @var array
     */
    protected $validatedAddresses = [];
    /**
     * @param \Magento\Framework\Model\Context $context
     * @param \Magento\Framework\Registry $registry
     * @param \Magento\Framework\Data\FormFactory $formFactory
     * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
     * @param \Magento\SalesRule\Model\Rule\Condition\CombineFactory $condCombineFactory
     * @param \Magento\SalesRule\Model\Rule\Condition\Product\CombineFactory $condProdCombineF
     * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
     * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
     * @param array $data
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
     */
    public function __construct(
        \Magento\Framework\Model\Context $context,
        \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->condCombineFactory = $condCombineFactory;
        $this->condProdCombineF = $condProdCombineF;
        parent::__construct($context, $registry, $formFactory, $localeDate, $resource, $resourceCollection, $data);
    }
    /**
     * Set resource model and Id field name
     *
     * @return void
     */
    protected function _construct()
    {
        parent::_construct();
        $this->_init('Vendor\Module\Model\ResourceModel\Rule');
        $this->setIdFieldName('rule_id');
    }
    /**
     * Get rule condition combine model instance
     *
     * @return \Magento\SalesRule\Model\Rule\Condition\Combine
     */
      public function getConditionsInstance()
    {
        return $this->condCombineFactory->create();
    }

    /**
     * Get rule condition product combine model instance
     *
     * @return \Magento\SalesRule\Model\Rule\Condition\Product\Combine
     */
    public function getActionsInstance()
    {
        return $this->condProdCombineF->create();
    }
    /**
     * Check cached validation result for specific address
     *
     * @param Address $address
     * @return bool
     */
    public function hasIsValidForAddress($address)
    {
        $addressId = $this->_getAddressId($address);
        return isset($this->validatedAddresses[$addressId]) ? true : false;
    }
    /**
     * Set validation result for specific address to results cache
     *
     * @param Address $address
     * @param bool $validationResult
     * @return $this
     */
    public function setIsValidForAddress($address, $validationResult)
    {
        $addressId = $this->_getAddressId($address);
        $this->validatedAddresses[$addressId] = $validationResult;
        return $this;
    }
    /**
     * Get cached validation result for specific address
     *
     * @param Address $address
     * @return bool
     * @SuppressWarnings(PHPMD.BooleanGetMethodName)
     */
    public function getIsValidForAddress($address)
    {
        $addressId = $this->_getAddressId($address);
        return isset($this->validatedAddresses[$addressId]) ? $this->validatedAddresses[$addressId] : false;
    }
    /**
     * Return id for address
     *
     * @param Address $address
     * @return string
     */
    private function _getAddressId($address)
    {
        if ($address instanceof Address) {
            return $address->getId();
        }
        return $address;
    }
}

Finally i done it, here my updated code

public function execute()
    {  
      $validate  = array();
      $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
      $quoteId = $this->cart->getQuote()->getId();
      $quote =  $this->quoteFactory->load($quoteId);
      $fakeQuote = clone $quote;
      $fakeQuote->setId(null);
       $items = $this->cart->getQuote()->getAllItems();
       $rules = $objectManager->create('Vendor\Module\Model\Rule')->getCollection();
      foreach ($rules as $rule){
          foreach($items as $item){
            $productId = $item->getProductId();
            $product = $objectManager->create('Magento\Catalog\Model\Product')->load($productId);
            $quoteItem = $objectManager->create('Magento\Quote\Model\Quote\Item');
            $quoteItem->setQuote($fakeQuote)->setProduct($product);
            $quoteItem->setAllItems(array($product));
            $quoteItem->getProduct()->setProductId($product->getEntityId());
            $validate = $rule->getConditions()->validate($quoteItem);
        } 
    }
   // var_dump($validate); 
    return $validate;
    }
7
  • You need to check with all the cart items? Commented Dec 13, 2019 at 6:43
  • yes, i need to check with all cart item Commented Dec 13, 2019 at 6:44
  • Did you tried here with cart items? I think the issue is because you are sending product object instead of cart item object. Commented Dec 13, 2019 at 6:45
  • first of all i tried static product object its not validate properly Commented Dec 13, 2019 at 6:51
  • I don't know about these...My assumption is you have to send cart item object...Let me try this with my env Commented Dec 13, 2019 at 6:52

5 Answers 5

2

Seems like you validating your model (product) using the actions, but logically you should use the conditions, like:

$rule->getConditions()->validate($item);

Here is full code with changes:

$product_id = '3';  // Crown Summit Backpack sku is 24-MB03
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$rules = $objectManager->create('Vendor\Module\Model\Rule')->getCollection();
foreach ($rules as $rule) {
    $product = $objectManager->get('Magento\Catalog\Model\Product')->load($product_id);
    $item = $objectManager->create('Magento\Catalog\Model\Product');
    $item->setProduct($product);
    $validate = $rule->getConditions()->validate($item);
}
var_dump($validate);
var_dump($product);

I think the getActions() is a conditions for the Apply the rule to (see the select below your main block) or other conditions in your model.

You can detect what you are using for validation pretty simple, just print out the $rule->getConditions()->asStringRecursive() and $rule->getActions()->asStringRecursive(). You must see which method returns desired conditions.

PS: the $rule->getActions()->validate($item); always returns true because empty conditions always means the any item is valid.

10
  • 1
    i used $rule->getConditions()->validate($item) but it reflects a error like Warning: Invalid argument supplied for foreach() in D:\wamp64\www\m2\magento\vendor\magento\module-sales-rule\Model\Rule\Condition\Product\Found.php on line 65 Commented Dec 12, 2019 at 12:44
  • 3
    @divyasekar It mean that you have error somewhere in your model. The line 65 is foreach ($model->getAllItems() as $item) { so it try to work with you wrapper-model as with a Quote, but you set the Product. Try this: $item = $objectManager->create('Magento\Quote\Model\Quote'); then add item to quote and try to validate using quote. Commented Dec 12, 2019 at 12:49
  • i cant get you clearly , what i m understand of your suggestion is try with quote item instead of product if i m wright Commented Dec 12, 2019 at 14:08
  • 1
    @divyasekar Sorry, my english is not good enough :) Yes, you should try with a Quote object in which your product must be set as an quote item (you can use the addItem method). I will try to cover this issue more broadly in our blog article in future. Commented Dec 12, 2019 at 14:22
  • Thanks @Siarhey Uchukhlebau i was done it by your suggestion Commented Dec 13, 2019 at 10:51
1

Finally i done it, here my updated code

public function execute()
    {  
      $validate  = array();
      $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
      $quoteId = $this->cart->getQuote()->getId();
      $quote =  $this->quoteFactory->load($quoteId);
      $fakeQuote = clone $quote;
      $fakeQuote->setId(null);
       $items = $this->cart->getQuote()->getAllItems();
       $rules = $objectManager->create('Vendor\Module\Model\Rule')->getCollection();
      foreach ($rules as $rule){
          foreach($items as $item){
            $productId = $item->getProductId();
            $product = $objectManager->create('Magento\Catalog\Model\Product')->load($productId);
            $quoteItem = $objectManager->create('Magento\Quote\Model\Quote\Item');
            $quoteItem->setQuote($fakeQuote)->setProduct($product);
            $quoteItem->setAllItems(array($product));
            $quoteItem->getProduct()->setProductId($product->getEntityId());
            $validate = $rule->getConditions()->validate($quoteItem);
        } 
    }
   // var_dump($validate); 
    return $validate;
    }
0

Try with cart items using below code

$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$cart = $objectManager->get('\Magento\Checkout\Model\Cart');
$items = $cart->getQuote()->getAllItems();

$rules = $objectManager->create('Vendor\Module\Model\Rule')->getCollection();

foreach($items as $item) {
    $validate = $rule->getActions()->validate($item);
}
var_dump($validate);
var_dump($product);            
7
  • @divyasekar can you please update Vendor\Module\Model\Rule class code? Commented Dec 13, 2019 at 7:14
  • i update my code in the post Commented Dec 13, 2019 at 7:53
  • You are getting rules by getCollection but in Siarhey Uchukhlebau previous answer he is getting using getRules Commented Dec 13, 2019 at 8:55
  • i also used a getting rules by getRules but the validation not done properly Commented Dec 13, 2019 at 9:48
  • ok let me check Commented Dec 13, 2019 at 9:56
0

To validate products with rules we need to

// object manager to replace with properties created in __construct()
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();

foreach ($products as $product) {
   $quote = $objectManager->create('Magento\Quote\Model\QuoteFactory')->create();
   $quote->setId(null);
        
   $quoteItem = $objectManager->create('Magento\Quote\Model\Quote\Item');
   $quoteItem->setQuote($quote)->setProduct($product);
   $quoteItem->setAllItems([$product]);
   $quoteItem->getProduct()->setProductId($product->getEntityId());
      
   $result = $rule->getConditions()->validate($quoteItem);
   //$matched = !$result, result is false when it matches
 }
0

Method-1 by using getConditions()->validate()

  protected $customConditionModel;
    protected $productFactory;
 
    public function __construct(
        ....................................................
        \Magento\Catalog\Model\ProductFactory $productFactory,
        \VendoreName\AddConditionFiled\Model\CustomConditionFactory $customConditionModel,
        ....................................................
    ) {
        ....................................................
        $this->productFactory = $productFactory;
        $this->customConditionModel = $customConditionModel;
        ....................................................
    }
 
    public function checkCondition()
    {
        echo "<pre>";
        $prdId = 6; //bag product id
        // Get product data by id
        $prdData = $this->productFactory->create()->load($prdId);
        // get rule collection
        $ruleColl = $this->customConditionModel->create()->getCollection();
        foreach ($ruleColl as $ruleKey => $ruleVal) {
            // check Product with condition
            if ($ruleVal->getConditions()->validate($prdData)) {
                echo "Rule Id " . $ruleVal->getId() . " Match<br/>";
            }
        }
    }

Method-2 by using the Model function getMatchProductIds() which we already created.

protected $customConditionModel;

public function __construct(
    ....................................................
    \VendoreName\AddConditionFiled\Model\CustomConditionFactory $customConditionModel,
    ....................................................
) {
    ....................................................
    $this->customConditionModel = $customConditionModel;
    ....................................................

}

public function checkCondition()
{
    echo "<pre>";
    // get rule collection
    $ruleColl = $this->customConditionModel->create()->getCollection();
    foreach ($ruleColl as $ruleKey => $ruleVal) {
        // get all product's Ids with match with rule
        echo "***** Rule Id " . $ruleVal->getId() . " Match Product Ids ***** <br/>";
        $matchIds = array_values(array_unique($ruleVal->getMatchProductIds()));
        print_r($matchIds);
    }
}

Model file give below:

<?php

namespace VendoreName\AddConditionFiled\Model;

use VendoreName\AddConditionFiled\Model\ResourceModel\CustomCondition as CustomConditionResourceModel;
use Magento\Quote\Model\Quote\Address;
use Magento\Rule\Model\AbstractModel;

class CustomCondition extends AbstractModel
{
    protected $_eventPrefix = 'VendoreName_addconditionfiled';
    protected $_eventObject = 'rule';
    protected $condCombineFactory;
    protected $condProdCombineF;
    protected $validatedAddresses = [];
    protected $_selectProductIds;
    protected $_displayProductIds;

    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(CustomConditionResourceModel::class);
        $this->setIdFieldName('rule_id');
    }

    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();
    }

    public function getMatchProductIds()
    {
        $productCollection = \Magento\Framework\App\ObjectManager::getInstance()->create(
            '\Magento\Catalog\Model\ResourceModel\Product\Collection'
        );
        $productFactory = \Magento\Framework\App\ObjectManager::getInstance()->create(
            '\Magento\Catalog\Model\ProductFactory'
        );
        $this->_selectProductIds = [];
        $this->setCollectedAttributes([]);
        $this->getConditions()->collectValidatedAttributes($productCollection);
        \Magento\Framework\App\ObjectManager::getInstance()->create(
            '\Magento\Framework\Model\ResourceModel\Iterator'
        )->walk(
            $productCollection->getSelect(),
            [[$this, 'callbackValidateProductCondition']],
            [
                'attributes' => $this->getCollectedAttributes(),
                'product' => $productFactory->create(),
            ]
        );
        return $this->_selectProductIds;
    }

    public function callbackValidateProductCondition($args)
    {
        $product = clone $args['product'];
        $product->setData($args['row']);
        $websites = $this->_getWebsitesMap();
        foreach ($websites as $websiteId => $defaultStoreId) {
            $product->setStoreId($defaultStoreId);
            if ($this->getConditions()->validate($product)) {
                $this->_selectProductIds[] = $product->getId();
            }
        }
    }

    protected function _getWebsitesMap()
    {
        $map = [];
        $websites = \Magento\Framework\App\ObjectManager::getInstance()->create(
            '\Magento\Store\Model\StoreManagerInterface'
        )->getWebsites();
        foreach ($websites as $website) {
            if ($website->getDefaultStore() === null) {
                continue;
            }
            $map[$website->getId()] = $website->getDefaultStore()->getId();
        }
        return $map;
    }
}

Check Full Example click Here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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