This article is dedicated to an important Magento 2 Professional Developer certification – cart rules and its management. We will walk through the following aspects:
When would you use catalog price rules?
How do they impact performance?
How would you debug problems with catalog price rules?
When would you use catalog price rules in Magento 2?
Discounts are a powerful tool of existing customers retention and attracting new ones, so the majority of online shops usually have them – either seasonal or associated with some special occasion. Magento 2 ecommerce platform allows admin to greatly automate the process of assigning discounts with the help of catalog price rules. Сatalog price rules are used to give a discount on a product or a bundle of products.
How do they impact performance in Magento 2?
It has minimal impact on loading of the page because price calculations are performed during reindexing. Therefore, the more massive the catalog price rules are, the longer the reindexing will be performed
To make sure that catalog price rules don’t overload the performance let’s have a look at Magento code. In the vendor\magento\module-catalog-rule\etc\frontend\events.xml file we install the observer for catalog_product_get_final_price event.
1 2 3 4 5 6 7 8 |
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> <event name="catalog_product_get_final_price"> <observer name="catalogrule" instance="Magento\CatalogRule\Observer\ProcessFrontFinalPriceObserver" /> </event> <event name="prepare_catalog_product_collection_prices"> <observer name="catalogrule" instance="Magento\CatalogRule\Observer\PrepareCatalogProductCollectionPricesObserver" /> </event> </config> |
Then we get the price from magento\module-catalog-rule\Observer\ProcessFrontFinalPriceObserver.php file and set it up for the product.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public function execute(\Magento\Framework\Event\Observer $observer) { $product = $observer->getEvent()->getProduct(); $pId = $product->getId(); $storeId = $product->getStoreId(); if ($observer->hasDate()) { $date = new \DateTime($observer->getEvent()->getDate()); } else { $date = $this->localeDate->scopeDate($storeId); } if ($observer->hasWebsiteId()) { $wId = $observer->getEvent()->getWebsiteId(); } else { $wId = $this->storeManager->getStore($storeId)->getWebsiteId(); } if ($observer->hasCustomerGroupId()) { $gId = $observer->getEvent()->getCustomerGroupId(); } elseif ($product->hasCustomerGroupId()) { $gId = $product->getCustomerGroupId(); } else { $gId = $this->customerSession->getCustomerGroupId(); } $key = "{$date->format('Y-m-d H:i:s')}|{$wId}|{$gId}|{$pId}"; if (!$this->rulePricesStorage->hasRulePrice($key)) { $rulePrice = $this->resourceRuleFactory->create()->getRulePrice($date, $wId, $gId, $pId); $this->rulePricesStorage->setRulePrice($key, $rulePrice); } if ($this->rulePricesStorage->getRulePrice($key) !== false) { $finalPrice = min($product->getData('final_price'), $this->rulePricesStorage->getRulePrice($key)); $product->setFinalPrice($finalPrice); } return $this; } |
Now let’s consider the getRulePrice() method. Find this method in Z:\m2.loc\vendor\magento\module-catalog-rule\Model\ResourceModel\Rule.php file and find this method here; getRulePrice() method will call the getRulePrices() that performs a simple inquiry to the catalogrule_product_price table from the database. This is how you get the price.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public function getRulePrice($date, $wId, $gId, $pId) { $data = $this->getRulePrices($date, $wId, $gId, [$pId]); if (isset($data[$pId])) { return $data[$pId]; } return false; } public function getRulePrices(\DateTimeInterface $date, $websiteId, $customerGroupId, $productIds) { $connection = $this->getConnection(); $select = $connection->select() ->from($this->getTable('catalogrule_product_price'), ['product_id', 'rule_price']) ->where('rule_date = ?', $date->format('Y-m-d')) ->where('website_id = ?', $websiteId) ->where('customer_group_id = ?', $customerGroupId) ->where('product_id IN(?)', $productIds); return $connection->fetchPairs($select); } |
How would you debug problems with catalog price rules in Magento 2?
To begin with, make sure that the price rule is active by going to Marketing -> Promotions > Catalog Price Rule and checking that the status is set at active. Now let’s apply the rules by pressing Apply Rules button.
After that, the Catalog Rule Product indexes and Product Price, connected with it, are marked as invalid.
Meanwhile, in the controller vendor\magento\module-catalog-rule\Controller\Adminhtml\Promo\Catalog\ApplyRules.php
an increment of \Magento\CatalogRule\Model\Rule\Job class is created; this increment calls the applyAll() method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public function execute() { $errorMessage = __('We can\'t apply the rules.'); try { /** @var Job $ruleJob */ $ruleJob = $this->_objectManager->get(\Magento\CatalogRule\Model\Rule\Job::class); $ruleJob->applyAll(); if ($ruleJob->hasSuccess()) { $this->messageManager->addSuccess($ruleJob->getSuccess()); $this->_objectManager->create(\Magento\CatalogRule\Model\Flag::class)->loadSelf()->setState(0)->save(); } elseif ($ruleJob->hasError()) { $this->messageManager->addError($errorMessage . ' ' . $ruleJob->getError()); } } catch (\Exception $e) { $this->_objectManager->create(\Psr\Log\LoggerInterface::class)->critical($e); $this->messageManager->addError($errorMessage); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); return $resultRedirect->setPath('catalog_rule/*'); } |
In this method the index is marked as not valid:
1 2 3 4 5 6 7 8 9 10 |
public function applyAll() { try { $this->ruleProcessor->markIndexerAsInvalid(); $this->setSuccess(__('Updated rules applied.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->setError($e->getMessage()); } return $this; } |
Then we need to launch reindex and clear the cash.
bin/magento indexer:reindex
bin/magento cache:flush
Afterwards, the rules will work, if, of course, price rule, its date ranges and conditions are configured correctly (date ranges and conditions).
In case they don’t work, you need to go to catalogrule_product_price table and check the product price there. If you found that the data is not correct, you will need to check the logs or try to disable the third-party modules if you have one; you can also go over the Catalog Price Rule reindexing with Xdebug extension – it will detect the issue.
This is all on the topic of cart rules management in Magento 2. If you have any questions or comments, please leave them down below.