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;
 }
asked Dec 12, 2019 at 7:00
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.

answered Dec 12, 2019 at 12:38
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;
 }
answered Sep 11, 2020 at 12:55
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); 
answered Dec 13, 2019 at 6:57
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
 }
answered Jul 13, 2021 at 10:37
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.

answered Aug 14, 2021 at 12:05

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.