How to validate a condition rules based on product attribute in magento 2
backend:
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);
var_dump($product);
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;
}
-
You need to check with all the cart items?Ranganathan– Ranganathan2019年12月13日 06:43:27 +00:00Commented Dec 13, 2019 at 6:43
-
yes, i need to check with all cart itemDivya Sekar– Divya Sekar2019年12月13日 06:44:24 +00:00Commented 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.Ranganathan– Ranganathan2019年12月13日 06:45:35 +00:00Commented Dec 13, 2019 at 6:45
-
first of all i tried static product object its not validate properlyDivya Sekar– Divya Sekar2019年12月13日 06:51:11 +00:00Commented 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 envRanganathan– Ranganathan2019年12月13日 06:52:57 +00:00Commented Dec 13, 2019 at 6:52
5 Answers 5
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.
-
1i 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 65Divya Sekar– Divya Sekar2019年12月12日 12:44:31 +00:00Commented 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.Siarhey Uchukhlebau– Siarhey Uchukhlebau2019年12月12日 12:49:36 +00:00Commented 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 wrightDivya Sekar– Divya Sekar2019年12月12日 14:08:38 +00:00Commented 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
addItemmethod). I will try to cover this issue more broadly in our blog article in future.Siarhey Uchukhlebau– Siarhey Uchukhlebau2019年12月12日 14:22:37 +00:00Commented Dec 12, 2019 at 14:22 -
Thanks @Siarhey Uchukhlebau i was done it by your suggestionDivya Sekar– Divya Sekar2019年12月13日 10:51:43 +00:00Commented Dec 13, 2019 at 10:51
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;
}
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);
-
@divyasekar can you please update
Vendor\Module\Model\Ruleclass code?Ranganathan– Ranganathan2019年12月13日 07:14:17 +00:00Commented Dec 13, 2019 at 7:14 -
i update my code in the postDivya Sekar– Divya Sekar2019年12月13日 07:53:15 +00:00Commented Dec 13, 2019 at 7:53
-
You are getting rules by
getCollectionbut inSiarhey Uchukhlebauprevious answer he is getting usinggetRulesRanganathan– Ranganathan2019年12月13日 08:55:31 +00:00Commented Dec 13, 2019 at 8:55 -
i also used a getting rules by getRules but the validation not done properlyDivya Sekar– Divya Sekar2019年12月13日 09:48:41 +00:00Commented Dec 13, 2019 at 9:48
-
ok let me checkRanganathan– Ranganathan2019年12月13日 09:56:57 +00:00Commented Dec 13, 2019 at 9:56
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
}
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;
}
}
Explore related questions
See similar questions with these tags.