2

I have need to add dynamic text field in cart price rule edit section. when click on Add button text field should be added. And save data in salesrule table.

When select from Apply Percent Discount: Buy Min N Products then only this text field should be display else hide it.

I have created one field in salesrule table for save data. Need to implement Add multiple text field in cart price rule using UI Components when click on Add button.

I have checked but we can do this thing in system.xml but I need to do in UI Components for cart price rule action section.

Admin Path: Admin > Marketing > Promotions > Cart Price Rules > Edit > Actions

enter image description here

app/code/Vendor/Module/Model/DataProvider.php

<?php
namespace Vendor\Module\Model\Rule;

use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\SalesRule\Model\ResourceModel\Rule\Collection;
use Magento\SalesRule\Model\ResourceModel\Rule\CollectionFactory;
use Magento\SalesRule\Model\Rule;
use Magento\Framework\Registry;
use Magento\SalesRule\Model\Rule\Metadata\ValueProvider;
use Magento\Framework\Json\DecoderInterface;

class DataProvider extends \Magento\SalesRule\Model\Rule\DataProvider
{
    /**
     * @var $collection
     */
    protected $collection;

    /**
     * @var array
     */
    protected $loadedData;

    /**
     * @var \Magento\Framework\Registry
     */
    protected $coreRegistry;

    /**
     * @var \Magento\SalesRule\Model\Rule\Metadata\ValueProvider
     */
    protected $metadataValueProvider;

    /**
     * @var DataPersistorInterface
     */
    private $dataPersistor;

    /**
     * @var DecoderInterface
     */
    protected $jsonDecoder;

    /**
     * Initialize dependencies.
     *
     * @param string $name
     * @param string $primaryFieldName
     * @param string $requestFieldName
     * @param CollectionFactory $collectionFactory
     * @param Registry $registry
     * @param ValueProvider $metadataValueProvider
     * @param array $meta
     * @param array $data
     * @param DataPersistorInterface $dataPersistor
     * @param DecoderInterface $jsonDecoder
     */
    public function __construct(
        $name,
        $primaryFieldName,
        $requestFieldName,
        CollectionFactory $collectionFactory,
        Registry $registry,
        ValueProvider $metadataValueProvider,
        array $meta = [],
        array $data = [],
        DataPersistorInterface $dataPersistor = null,
        DecoderInterface $jsonDecoder
    ) {
        $this->collection = $collectionFactory->create();
        $this->coreRegistry = $registry;
        $this->metadataValueProvider = $metadataValueProvider;
        $meta = array_replace_recursive($this->getMetadataValues(), $meta);
        $this->dataPersistor = $dataPersistor ?? \Magento\Framework\App\ObjectManager::getInstance()->get(
            DataPersistorInterface::class
        );
        $this->jsonDecoder = $jsonDecoder;
        parent::__construct(
            $name,
            $primaryFieldName,
            $requestFieldName,
            $collectionFactory,
            $registry,
            $metadataValueProvider,
            $meta,
            $data,
            $dataPersistor
        );
    }
    
    protected function getMetadataValues()
    {
        $rule = $this->coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE);
        return $this->metadataValueProvider->getMetadataValues($rule);
    }
    
    public function getData()
    {
        if (isset($this->loadedData)) {
            return $this->loadedData;
        }
        $items = $this->collection->getItems();
        /** @var Rule $rule */
        foreach ($items as $rule) {
            $rule->load($rule->getId());
            $rule->setDiscountAmount($rule->getDiscountAmount() * 1);
            $rule->setDiscountQty($rule->getDiscountQty() * 1);

            $this->loadedData[$rule->getId()] = $rule->getData();
        }
        $data = $this->dataPersistor->get('sale_rule');
        if (!empty($data)) {
            $rule = $this->collection->getNewEmptyItem();
            $rule->setData($data);
            $this->loadedData[$rule->getId()] = $rule->getData();
            $this->dataPersistor->clear('sale_rule');
        }       
        $jsonData = $rule->getQtyDiscount();
        if ($jsonData != '') {
            $discountArray = $this->jsonDecoder->decode($jsonData);
            foreach ($discountArray as $key => $discount) {
                $discountData[] = ["qty_discount" => $discount['qty_discount'], "record_id" => $key];
            }
            $this->loadedData[$rule->getId()]['qty'] = $discountData;
        }        
        return $this->loadedData;
    }
}

enter image description here

1 Answer 1

3

You can add your custom dynamic fields to the sales rule form by customizing the vendor/magento/module-sales-rule/view/adminhtml/ui_component/sales_rule_form.xml file.

To customize the above file, we need to create a custom module and customize this file in the below path:

app/code/Vendor/Module/view/adminhtml/ui_component/sales_rule_form.xml

The customization code should be like the below code:

    <?xml version="1.0" encoding="UTF-8"?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <fieldset name="actions" sortOrder="30">

        <dynamicRows name="dynamic_rows" sortOrder="0">
            <settings>
                <addButtonLabel translate="true">Add Record</addButtonLabel>
                <additionalClasses>
                    <class name="admin__field-wide">true</class>
                </additionalClasses>
                <componentType>dynamicRows</componentType>
            </settings>
            <container name="record" component="Magento_Ui/js/dynamic-rows/record">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="isTemplate" xsi:type="boolean">true</item>
                        <item name="is_collection" xsi:type="boolean">true</item>
                        <item name="componentType" xsi:type="string">container</item>
                    </item>
                </argument>
                <field name="field_1" formElement="input">
                    <argument name="data" xsi:type="array">
                        <item name="config" xsi:type="array">
                            <item name="fit" xsi:type="boolean">false</item>
                        </item>
                    </argument>
                    <settings>
                        <validation>
                            <rule name="required-entry" xsi:type="boolean">true</rule>
                        </validation>
                        <dataType>text</dataType>
                        <label>Field #1</label>
                    </settings>
                </field>

                <actionDelete template="Magento_Backend/dynamic-rows/cells/action-delete">
                    <argument name="data" xsi:type="array">
                        <item name="config" xsi:type="array">
                            <item name="fit" xsi:type="boolean">false</item>
                        </item>
                    </argument>
                    <settings>
                        <additionalClasses>
                            <class name="some-class">true</class>
                        </additionalClasses>
                        <dataType>text</dataType>
                        <label>Actions</label>
                        <componentType>actionDelete</componentType>
                    </settings>
                </actionDelete>
            </container>
        </dynamicRows>
    </fieldset>
</form>

The output will look like this:

enter image description here

To save the custom field in salesrule table:

The salesrules are saved into the salesrule table by sales_rule/promo_quote/save controller. You can find this controller by looking into the network tab while saving the rule. Pls check the attached screenshot

enter image description here

From that above controller, we can now know the controller page. The controller path will be vendor/magento/module-sales-rule/Controller/Adminhtml/Promo/Quote/Save.php

From the above Save.php file, you can find a line $model->save(); which means, the salesrule table Model and resource Model are:

Model => vendor/magento/module-sales-rule/Model/Rule.php ResourceModel => vendor/magento/module-sales-rule/Model/ResourceModel/Rule.php

Every table data is saved from the ResourceModel's save method. So we can customize the save method to inject our new data. For this follow the below steps:

Step 1:

Declare a plugin for vendor/magento/module-sales-rule/Model/ResourceModel/Rule.php class save method in your custom module.

app/code/Vendor/Module/etc/adminhtml/di.xml

<?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    
        <type name="Magento\SalesRule\Model\ResourceModel\Rule">
            <plugin name="before_save_rule" type="Vendor\Module\Plugin\BeforeSaveCartRule" sortOrder="10"/>
        </type>
    </config>

Step 2: create a plugin class and write the save logic method:

app/code/Vendor/Module/Plugin/BeforeSaveCartRule.php

<?php
declare(strict_types=1);

namespace Vendor\Module\Plugin;

use Magento\Framework\App\RequestInterface;
use Magento\SalesRule\Model\ResourceModel\Rule;

class BeforeSaveCartRule
{
    /**
     * @var RequestInterface
     */
    private $request;

    public function __construct(
        RequestInterface $request
    ) {
        $this->request = $request;
    }
    public function beforeSave(Rule $subject, $object) {
        $data = $this->request->getPostValue();

        $convertedToJson = json_encode($data['dynamic_rows']);
        $object->setCustomField($convertedToJson); /*Here you have to mention your custom field name. 
                                                    For example if the custom field is multiple_actions then the method should be like setMultipleActions()*/

        return null;
    }
}

In the below screenshot you can see, how your custom dynamic fields data is posted from request. I saved this custom field by converting it into an JSON and saved in custom field. You can customise it as per your need and save it.

enter image description here

Once you saved the salesrule you can check your salesrule table and you can see the saved custom field value

Result: enter image description here

Note: Once you saved the salesrule you cannot find the saved data into your sales rule admin form. Only you can find the data in your database salesrule table, because you might need some dataprovider customisation to fetch and display the data into your custom dynamic row field.

7
  • Thank you @Siranjeevi K S But how to save data and fetch data in this dynamic row. we need to save it in 'salesrule' table to add one custom field. Commented Mar 9, 2023 at 13:18
  • @Kirti Nariya I updated my answer, please check Commented Mar 10, 2023 at 7:14
  • Thank you. It's fine. Facing issue for fetch data while edit form. As you said use dataprovide for fetch and display data in admin edit rule. I have try this but not fetching data in edit rule for dynamic row. Commented Mar 10, 2023 at 7:28
  • Please let me know if you have idea about fetch dynamic row data from salesrule table in cart rule edit. Commented Mar 10, 2023 at 9:21
  • Can you update your dataprovider customization code? and let me know what was the exact issue you are facing Commented Mar 10, 2023 at 9:48

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.