Magento 2 – Generate Conditions for Custom Rules

catalog-price-rulesmagento2

I've created my own custom rules which I'll be using later on for applying discounts on Catalog. It's extension of the catalog price rules. What I've done is actually replicated the functionality for creating custom rules from here.

However, I've override the class \Magento\Rule\Model\Condition\Combine by my own Combine Class for adding unique custom options to the rules, here is the code for that:

<?php

namespace RLTSquare\Rules\Model\Rule\Condition;

    class Combine extends \Magento\Rule\Model\Condition\Combine
    {
        /**
         * @var \RLTSquare\Rules\Model\Rule\Condition\ProductFactory
         */
        protected $_productFactory;

        /**
         * @param \Magento\Rule\Model\Condition\Context $context
         * @param \RLTSquare\Rules\Model\Rule\Condition\ProductFactory $conditionFactory
         * @param array $data
         */
        public function __construct(
            \Magento\Rule\Model\Condition\Context $context,
            \RLTSquare\Rules\Model\Rule\Condition\ProductFactory $conditionFactory,
            array $data = []
        )
        {
            $this->_productFactory = $conditionFactory;
            parent::__construct($context, $data);
            $this->setType(\RLTSquare\Rules\Model\Rule\Condition\Combine::class);
        }

        /**
         * @return array
         */
        public function getNewChildSelectOptions()
        {
            $productAttributes = $this->_productFactory->create()->loadAttributeOptions()->getAttributeOption();
            $attributes = [];
            foreach ($productAttributes as $code => $label) {
                $attributes[] = [
                    'value' => 'RLTSquare\Rules\Model\Rule\Condition\Product|' . $code,
                    'label' => $label,
                ];
            }

            //populating unique options
            $customOptions = $this->_productFactory->create()->getOptionNameList();
            $options = [];
            foreach ($customOptions as $option){
                $options[] = [
                    'value' => 'RLTSquare\Rules\Model\Rule\Condition\Product|' . $option,
                    'label' => $option
                ];
            }

            $conditions = parent::getNewChildSelectOptions();
            $conditions = array_merge_recursive(
                $conditions,
                [
                    [
                        'value' => \RLTSquare\Rules\Model\Rule\Condition\Combine::class,
                        'label' => __('Conditions Combination'),
                    ],
                    ['label' => __('Product Attribute'), 'value' => $attributes],
                    ['label' => __('Custom Option'), 'value' => $options],
                ]
            );
            return $conditions;
        }
    }

The getOptionNameList() method is in some other class that's not necessarily needed however, here is the chunk of code for that function:

public function getOptionNameList()
    {
        $storeId = $this->storeManager->getStore()->getId();

        $collection = $this->optionCollectionFactory->create()->addFieldToSelect('*');
        $collection->addTitleToResult($storeId);

        $options = [];
        foreach ($collection as $option){
            $options[$option->getTitle()] = $option->getTitle();
        }

        return $options;
    }

So the custom options are appearing like this:

Custom price rules conditions screenshot

Upon selecting product attribute and condition combination everything goes fine, and drop down like below appears:

enter image description here

The class which generates the new condition html has been extended from is here:

<?php

namespace RLTSquare\Rules\Controller\Adminhtml\Custom\Rule;

class NewConditionHtml extends \RLTSquare\Rules\Controller\Adminhtml\Custom\Rule
{
    /**
     * New condition html action
     *
     * @return void
     */
    public function execute()
    {
        $id = $this->getRequest()->getParam('id');
        $typeArr = explode('|', str_replace('-', '/', $this->getRequest()->getParam('type')));
        $type = $typeArr[0];

        $model = $this->_objectManager->create($type);
        $model->setId($id);
        $model->setType($type);
        $model->setRule($this->ruleFactory->create());
        $model->setPrefix('conditions');

        if (!empty($typeArr[1])) {
            $model->setAttribute($typeArr[1]);
        }

        if ($model instanceof \Magento\Rule\Model\Condition\AbstractCondition) {
            $model->setJsFormObject($this->getRequest()->getParam('form'));
            $html = $model->asHtmlRecursive();
        } else {
            $html = '';
        }
        $this->getResponse()->setBody($html);
    }
}

The html is generated through this class Magento\Rule\Model\Condition\AbstractCondition. I'm unable to know how can I get custom options value same like we get for custom attributes?

Best Answer

Ok, so I created my own class which extend Magento\Rule\Model\Condition\AbstractCondition, in the respective methods, I overrode method getValueSelectOptions() to get respective values from Magento tables.

<?php

namespace RLTSquare\Rules\Model\Rule\Condition;

use Magento\Rule\Model\Condition\Context;

class CustomOption extends \Magento\Rule\Model\Condition\AbstractCondition
{
    private $optionCollectionFactory;

    private $storeManager;

    public function __construct(
        Context $context,
        \Magento\Catalog\Model\ResourceModel\Product\Option\CollectionFactory $optionCollectionFactory,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        array $data = []

    )
    {
        $this->optionCollectionFactory = $optionCollectionFactory;
        $this->storeManager = $storeManager;
        parent::__construct($context, $data);
    }


    public function loadAttributeOptions()
    {
        $storeId = $this->storeManager->getStore()->getId();

        $collection = $this->optionCollectionFactory->create();
        $collection->addTitleToResult($storeId);

        $options = [];
        foreach ($collection as $option) {
            $options[$option->getTitle()] = $option->getTitle();
        }

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

        return $this;
    }

    public function getValueSelectOptions()
    {
        /**
         *
         * Multiple custom options can have same name, we will load unique custom options and unique values against them
         *
         */

        $storeId = $this->storeManager->getStore()->getId();

        $opt = [];
        $optionsCollection = $this->optionCollectionFactory->create();
        $optionsCollection->addTitleToResult($storeId);
        $optionsCollection->addFieldToFilter('default_option_title.title', ['eq' => $this->getAttribute()]);
        $optionsCollection->addValuesToResult($storeId);

        /** @var \Magento\Catalog\Model\Product\Option $option */
        foreach ($optionsCollection as $option) {
            foreach ($option->getValues() as $optionVal) {
                $opt[$optionVal->getTitle()] = ['value' => $optionVal->getTitle(), 'label' => $optionVal->getTitle()];
            }
        }

        return $opt;
    }


    public function getValueElementType()
    {
        return 'select';
    }
}

just for adding getValueElementType(), this method represent that the custom options values will be always a drop down.

Related Topic