1

Magento 2 custom product grid on the custom module not updating hidden values of enter image description here

It should be like this like category - enter image description here

AssignProducts.php
<?php
/**
 * Created By : RH
 */
namespace Social\Media\Block\Adminhtml;

class AssignProducts extends \Magento\Backend\Block\Template
{
    /**
     * Block template
     *
     * @var string
     */
    protected $_template = 'products/assign_products.phtml';

    /**
     * @var \Magento\Catalog\Block\Adminhtml\Category\Tab\Product
     */
    protected $blockGrid;

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

    /**
     * @var \Magento\Framework\Json\EncoderInterface
     */
    protected $jsonEncoder;

    /**
     * @var \RH\CustProductGrid\Model\ResourceModel\Product\CollectionFactory
     */
    protected $productFactory;

    /**
     * @param \Magento\Backend\Block\Template\Context                           $context
     * @param \Magento\Framework\Registry                                       $registry
     * @param \Magento\Framework\Json\EncoderInterface                          $jsonEncoder
     * @param \RH\CustProductGrid\Model\ResourceModel\Product\CollectionFactory $productFactory
     * @param array                                                             $data
     */
    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Framework\Registry $registry,
        \Magento\Framework\Json\EncoderInterface $jsonEncoder,
        \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productFactory,
        array $data = []
    ) {
        $this->registry = $registry;
        $this->jsonEncoder = $jsonEncoder;
        $this->productFactory = $productFactory;
        parent::__construct($context, $data);
    }

    /**
     * Retrieve instance of grid block
     *
     * @return \Magento\Framework\View\Element\BlockInterface
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function getBlockGrid()
    {
        if (null === $this->blockGrid) {
            $this->blockGrid = $this->getLayout()->createBlock(
                'Social\Media\Block\Adminhtml\Tab\Productgrid',
                'category.product.grid'
            );
        }
        return $this->blockGrid;
    }

    /**
     * Return HTML of grid block
     *
     * @return string
     */
    public function getGridHtml()
    {
        return $this->getBlockGrid()->toHtml();
    }

    /**
     * @return string
     */
    public function getProductsJson()
    {
        $entity_id = $this->getRequest()->getParam('entity_id');
        $productFactory = $this->productFactory->create();
        $productFactory->addFieldToSelect(['product_id', 'position']);
        $productFactory->addFieldToFilter('entity_id', ['eq' => $entity_id]);
        $result = [];
        if (!empty($productFactory->getData())) {
            foreach ($productFactory->getData() as $rhProducts) {
                $result[$rhProducts['product_id']] = '';
            }
            return $this->jsonEncoder->encode($result);
        }
        return '{}';
    }

    public function getItem()
    {
        return $this->registry->registry('galaxymedia');
    }
}

assign_products.phtml

    <?php
    /**
     * Created By : RH
     */
    ?>
    <?php
        $blockGrid = $block->getBlockGrid();
        $gridJsObjectName = $blockGrid->getJsObjectName();
    ?>
    <?= $block->getGridHtml() ?>
    <input type="hidden" name="related_products" data-form-part="social_form" id="rh_products" value=''  />
    <script type="text/x-magento-init">
        {
            "*": {
Social_Media/js/assign-products": {
                    "selectedProducts":  <?= /* @escapeNotVerified */ $block->getProductsJson() ?>,
                    "gridJsObjectName": <?= /* @escapeNotVerified */ '"' . $gridJsObjectName . '"' ?: '{}' ?>
                }
            }
        }
    </script>
    <script>
        require('mage/apply/main').apply();
    </script>

Productgrid.php

<?php
/**
 * Created By : RH
 */
namespace Social\Media\Block\Adminhtml\Tab;

use Magento\Catalog\Model\Product\Visibility;
use Magento\Framework\App\ObjectManager;
use Magento\Store\Model\Store;

class Productgrid extends \Magento\Backend\Block\Widget\Grid\Extended
{

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

    /**
     * @var \Magento\Catalog\Model\ProductFactory
     */
    protected $productFactory;

    /**
     * @var \RH\CustProductGrid\Model\ResourceModel\Product\CollectionFactory
     */
    protected $productCollFactory;

    protected $_socialform;


    /**
     * @param \Magento\Backend\Block\Template\Context    $context
     * @param \Magento\Backend\Helper\Data               $backendHelper
     * @param \Magento\Catalog\Model\ProductFactory      $productFactory
     * @param \Magento\Framework\Registry                $coreRegistry
     * @param \Magento\Framework\Module\Manager          $moduleManager
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param Visibility|null                            $visibility
     * @param array                                      $data
     */
    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Backend\Helper\Data $backendHelper,
        \Magento\Catalog\Model\ProductFactory $productFactory,
        \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollFactory,
        \Magento\Framework\Registry $coreRegistry,
        \Magento\Framework\Module\Manager $moduleManager,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Social\Media\Model\SocialformFactory $socialform,
        Visibility $visibility = null,
        array $data = []
    ) {
        $this->productFactory = $productFactory;
        $this->productCollFactory = $productCollFactory;
        $this->coreRegistry = $coreRegistry;
        $this->moduleManager = $moduleManager;
        $this->_storeManager = $storeManager;
        $this->_socialform = $socialform;
        $this->visibility = $visibility ?: ObjectManager::getInstance()->get(Visibility::class);
        parent::__construct($context, $backendHelper, $data);
    }

    /**
     * [_construct description]
     * @return [type] [description]
     */
    protected function _construct()
    {
        parent::_construct();
        $this->setId('rh_grid_products');
        $this->setDefaultSort('entity_id');
        $this->setDefaultDir('ASC');
        $this->setUseAjax(true);
        if ($this->getRequest()->getParam('id')) {
            $this->setDefaultFilter(['in_products' => 1]);
        } else {
            $this->setDefaultFilter(['in_products' => 0]);
        }
        $this->setSaveParametersInSession(false);
    }

    /**
     * [get store id]
     *
     * @return Store
     */
    protected function _getStore()
    {
        $storeId = (int) $this->getRequest()->getParam('store', 0);
        return $this->_storeManager->getStore($storeId);
    }

    
    protected function _prepareCollection()
    {
        $store = $this->_getStore();
        $collection = $this->productFactory->create()->getCollection()->addAttributeToSelect(
            'sku'
        )->addAttributeToSelect(
            'name'
        )->addAttributeToSelect(
            'attribute_set_id'
        )->addAttributeToSelect(
            'type_id'
        )->setStore(
            $store
        );

        if ($this->moduleManager->isEnabled('Magento_CatalogInventory')) {
            $collection->joinField(
                'qty',
                'cataloginventory_stock_item',
                'qty',
                'product_id=entity_id',
                '{{table}}.stock_id=1',
                'left'
            );
        }
        if ($store->getId()) {
            $collection->setStoreId($store->getId());
            $collection->addStoreFilter($store);
            $collection->joinAttribute(
                'name',
                'catalog_product/name',
                'entity_id',
                null,
                'inner',
                Store::DEFAULT_STORE_ID
            );
            $collection->joinAttribute(
                'status',
                'catalog_product/status',
                'entity_id',
                null,
                'inner',
                $store->getId()
            );
            $collection->joinAttribute(
                'visibility',
                'catalog_product/visibility',
                'entity_id',
                null,
                'inner',
                $store->getId()
            );
            $collection->joinAttribute('price', 'catalog_product/price', 'entity_id', null, 'left', $store->getId());
        } else {
            $collection->addAttributeToSelect('price');
            $collection->joinAttribute('status', 'catalog_product/status', 'entity_id', null, 'inner');
            $collection->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', null, 'inner');
        }
        $this->setCollection($collection);
        return parent::_prepareCollection();
    }

    
    protected function _addColumnFilterToCollection($column)
    {
        if ($column->getId() == 'in_products') {
            $productIds = $this->_getSelectedProducts();
            if (empty($productIds)) {
                $productIds = 0;
            }
            if ($column->getFilter()->getValue()) {
                $this->getCollection()->addFieldToFilter('entity_id', ['in' => $productIds]);
            } else {
                if ($productIds) {
                    $this->getCollection()->addFieldToFilter('entity_id', ['nin' => $productIds]);
                }
            }
        } else {
            parent::_addColumnFilterToCollection($column);
        }
        return $this;
    }

    /**
     * @return Extended
     */
    protected function _prepareColumns()
    {
        $this->addColumn(
            'in_products',
            [
                'type' => 'checkbox',
                'html_name' => 'products_id',
                'required' => true,
                'values' => $this->_getSelectedProducts(),
                'align' => 'center',
                'index' => 'entity_id',
            ]
        );

        $this->addColumn(
            'entity_id',
            [
                'header' => __('ID'),
                'width' => '50px',
                'index' => 'entity_id',
                'type' => 'number',
            ]
        );
        $this->addColumn(
            'name',
            [
                'header' => __('Name'),
                'index' => 'name',
                'header_css_class' => 'col-type',
                'column_css_class' => 'col-type',
            ]
        );
        $this->addColumn(
            'sku',
            [
                'header' => __('SKU'),
                'index' => 'sku',
                'header_css_class' => 'col-sku',
                'column_css_class' => 'col-sku',
            ]
        );
        $store = $this->_getStore();
        $this->addColumn(
            'price',
            [
                'header' => __('Price'),
                'type' => 'price',
                'currency_code' => $store->getBaseCurrency()->getCode(),
                'index' => 'price',
                'header_css_class' => 'col-price',
                'column_css_class' => 'col-price',
            ]
        );
        $this->addColumn(
            'position',
            [
                'header' => __('Position'),
                'name' => 'position',
                'width' => 60,
                'type' => 'number',
                'validate_class' => 'validate-number',
                'index' => 'position',
                'editable' => true,
                'edit_only' => true,
            ]
        );
        return parent::_prepareColumns();
    }

    /**
     * @return string
     */
    public function getGridUrl()
    {
        return $this->getUrl('galaxymedia/index/grids', ['_current' => true]);
    }

    /**
     * @return array
     */
    protected function _getSelectedProducts()
    {
        $products = array_keys($this->getSelectedProducts());
        //$products = array_values($this->getSelectedProducts());
          /*echo "<pre>";
        print_r($products);
        die;*/
        return $products;
    }

    /**
     * @return array
     */
    public function getSelectedProducts()
    {
        
        $id = $this->getRequest()->getParam('id');
        $model = $this->_socialform->create()->load($id);
        
        $model = $model->getRelatedProducts();
        $model = json_decode($model);
        
        
        $grids = [];
        if(!empty($model)){
        foreach ($model as $key => $value) {
             $grids[] = $key;
        }
        }
        

        
        
        return $grids;
    }
}

Grids.php

<?php
/**
 * Created By : RH
 */
namespace Social\Media\Controller\Adminhtml\Index;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\Result\RawFactory;
use Magento\Framework\View\LayoutFactory;

class Grids extends \Magento\Backend\App\Action
{

     /**
     * @var \Magento\Framework\Controller\Result\RawFactory
     */
    protected $resultRawFactory;

    /**
     * @var \Magento\Framework\View\LayoutFactory
     */
    protected $layoutFactory;

    /**
     * @param Context       $context
     * @param Rawfactory    $resultRawFactory
     * @param LayoutFactory $layoutFactory
     */
    public function __construct(
        Context $context,
        Rawfactory $resultRawFactory,
        LayoutFactory $layoutFactory
    ) {
        parent::__construct($context);
        $this->resultRawFactory = $resultRawFactory;
        $this->layoutFactory = $layoutFactory;
    }

    /**
     * @return \Magento\Framework\Controller\Result\Raw
     */
    public function execute()
    {
        $resultRaw = $this->resultRawFactory->create();
        return $resultRaw->setContents(
            $this->layoutFactory->create()->createBlock(
                'Social\Media\Block\Adminhtml\Tab\Productgrid',
                'rh.custom.tab.productgrid'
            )->toHtml()
        );
    }
}

assign-products.js

 /**
     * Created by : RH
     */
    
    /* global $, $H */
    
    define([
        'mage/adminhtml/grid'
    ], function () {
        'use strict';
    
        return function (config) {
            var selectedProducts = config.selectedProducts,
                categoryProducts = $H(selectedProducts),
                gridJsObject = window[config.gridJsObjectName],
                tabIndex = 1000;
            /**
             * Show selected product when edit form in associated product grid
             */
            $('rh_products').value = Object.toJSON(categoryProducts);
            /**
             * Register Category Product
             *
             * @param {Object} grid
             * @param {Object} element
             * @param {Boolean} checked
             */
            function registerCategoryProduct(grid, element, checked) {
                if (checked) {
                    if (element.positionElement) {
                        element.positionElement.disabled = false;
                        categoryProducts.set(element.value, element.positionElement.value);
                    }
                } else {
                    if (element.positionElement) {
                        element.positionElement.disabled = true;
                    }
                    categoryProducts.unset(element.value);
                }
                $('rh_products').value = Object.toJSON(categoryProducts);
                grid.reloadParams = {
                    'selected_products[]': categoryProducts.keys()
                };
            }
    
            /**
             * Click on product row
             *
             * @param {Object} grid
             * @param {String} event
             */
            function categoryProductRowClick(grid, event) {
                var trElement = Event.findElement(event, 'tr'),
                    isInput = Event.element(event).tagName === 'INPUT',
                    checked = false,
                    checkbox = null;
    
                if (trElement) {
                    checkbox = Element.getElementsBySelector(trElement, 'input');
    
                    if (checkbox[0]) {
                        checked = isInput ? checkbox[0].checked : !checkbox[0].checked;
                        gridJsObject.setCheckboxChecked(checkbox[0], checked);
                    }
                }
            }
    
            /**
             * Change product position
             *
             * @param {String} event
             */
            function positionChange(event) {
                var element = Event.element(event);
    
                if (element && element.checkboxElement && element.checkboxElement.checked) {
                    categoryProducts.set(element.checkboxElement.value, element.value);
                    $('rh_products').value = Object.toJSON(categoryProducts);
                }
            }
    
            /**
             * Initialize category product row
             *
             * @param {Object} grid
             * @param {String} row
             */
            function categoryProductRowInit(grid, row) {
                var checkbox = $(row).getElementsByClassName('checkbox')[0],
                    position = $(row).getElementsByClassName('input-text')[0];
    
                if (checkbox && position) {
                    checkbox.positionElement = position;
                    position.checkboxElement = checkbox;
                    position.disabled = !checkbox.checked;
                    position.tabIndex = tabIndex++;
                    Event.observe(position, 'keyup', positionChange);
                }
            }
    
            gridJsObject.rowClickCallback = categoryProductRowClick;
            gridJsObject.initRowCallback = categoryProductRowInit;
            gridJsObject.checkboxCheckCallback = registerCategoryProduct;
    
            if (gridJsObject.rows) {
                gridJsObject.rows.each(function (row) {
                    categoryProductRowInit(gridJsObject, row);
                });
            }
        };
    });

Data is saving properly in this format - {"1":"","2":"","3":""} but when edit item then hidden input values show blank because of this old checkbox selected not saving in table but it is saving new checkbox selected.

Source - https://www.rohanhapani.com/magento-2-how-to-add-product-grid-in-ui-form-using-uicomponent/

Does anyone know how to solve this?

4
  • Are you saving the related products ids in separate table? IF yes, in what format? – Abdul Samad Abbasi Mar 2 at 4:19
  • @AbdulSamadAbbasi no related_products it is just name – sumeet bajaj Mar 2 at 5:34
  • @AbdulSamadAbbasi i saving values like {"1":"","2":"","3":"","4":"","5":""} like assign product to category. – sumeet bajaj Mar 2 at 5:46
  • Look, if you want to assign products to your custom grid, like same in magento category grid, then you need to make a new table with foreign key between your main grid table and product_id. This way you could assign as many products on checking box. and one more thing, it should be saved and loaded in form of array. not JSON. – Abdul Samad Abbasi Mar 2 at 5:53
1

ResourceModel

In you afterSave():

protected function _afterSave(\Magento\Framework\Model\AbstractModel $object)
    {
        if ($object->getData('related_products')) {
            $newProducts = json_decode($object->getData('related_products'), true);
            $newProducts= array_keys($newProducts);
            try {
                $oldProducts = (array) $this->getProducts($object->getId());
                $insert = array_diff($newProducts, $oldProducts);
                $delete = array_diff($oldProducts, $newProducts);
                $connection = $this->getConnection();
                $table = $connection->getTableName('maintable_producttable_relation');
               
                if ($delete) {
                    $where = ['maintable_id = ?' => $object->getId(), 'product_id IN (?)' => $delete];
                    $connection->delete($table, $where);
                }
                if ($insert) {
                    $data = [];
                    foreach ($insert as $product_id) {
                        $data[] = ['maintable_id' => $object->getId(), 'product_id' => (int)$product_id];
                    }
                    $connection->insertMultiple($table, $data);
                }
            } catch (\Exception $e) {
                $this->messageManager->addException($e, __('Something went wrong while saving the Faq.'));
            }
        }
        return parent::_afterSave($object);
    }

In your afterLoad():

protected function _afterLoad(\Magento\Framework\Model\AbstractModel $object)
    {
        $oldProducts = (array) $this->getProducts($object->getId());
        $object->setData('related_products', $oldProducts);
        return parent::_afterLoad($object);
    }

and finally, getProducts():

public function getProducts($id)
    {        
        $select = $this->getConnection()->select()->from(
            $this->getTable('maintable_producttable_relation'),
            ['product_id']
        )->where(
            'maintable_id = ?',
            (int)$id
        );
        return $this->getConnection()->fetchCol($select);
    }

NOTE: If you want to save it in single table, just skip everything and do json_decode before save and store array keys and in afterLoad() set array of product ids like in above code. Hope it helps!

5
  • like category is also saving in array format ? – sumeet bajaj Mar 2 at 8:37
  • The values(product_ids) you're saving are in json form as you told in comments. You need to store them in form of array or if you don't want to do that, convert them to array in afterLoad() because edit form expects it to be in form or array to pre-select products you saved as checked. – Abdul Samad Abbasi Mar 2 at 10:15
  • i add my full code so that easily understand, the checkbox is selected but when saving then new values are saving but not old values – sumeet bajaj Mar 2 at 11:15
  • If it's selecting and saving then only problem is that you're not saving it in the right format. The code I mentioned in answer is absolutely working. You just need to understand the code in afterSave() and afterLoad(). The insertion and deletion is based on the selected values you're getting from product grid. Just implement this same code according to your need and I'm sure it's gonna work. Still, you can ask anything I'd be glad to help. – Abdul Samad Abbasi Mar 3 at 4:26
  • for your answer i upvote but i save values in json and getting via json problem solved thanks for your help – sumeet bajaj Mar 3 at 15:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.