Add custom grid serialization

we will create a custom tab with custom table allow serialization grid and custom column get/post value like selected product, qty in grid.
1. Add “htmlContent” in your form ui. You need to add tabs and template.
2. In block Lights\OrderCombine\Block\Adminhtml\Edit\Tab\Missing . Implement tabs interface.

namespace Lights\OrderCombine\Block\Adminhtml\Edit\Tab;

use Magento\Ui\Component\Layout\Tabs\TabInterface;
class Missing extends \Magento\Backend\Block\Template implements TabInterface
{
    protected $blockGrid;
    /**
     * Core registry
     *
     * @var \Magento\Framework\Registry
     */
    protected $_coreRegistry;
    /**
     * @var \Lights\OrderCombine\Model\CombineDetailsFactory
     */
    protected $detailsFactory;

    /**
     * Orders constructor.
     * @param \Magento\Backend\Block\Template\Context $context
     * @param \Magento\Framework\Registry $registry
     * @param \Lights\OrderCombine\Model\CombineDetailsFactory $detailsFactory
     * @param array $data
     */
    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Framework\Registry $registry,
        \Lights\OrderCombine\Model\CombineDetailsFactory $detailsFactory,
        array $data = []
    ) {
        $this->_coreRegistry = $registry;
        $this->detailsFactory = $detailsFactory;
        parent::__construct($context, $data);
    }

    /**
     * @return \Magento\Framework\Phrase
     */
    public function getTabLabel()
    {
        return __('Missing Products');
    }

    /**
     * @return \Magento\Framework\Phrase
     */
    public function getTabTitle()
    {
        return __('Missing Products');
    }

    /**
     * @return bool
     */
    public function canShowTab()
    {
        if ($this->canShow()) {
            return true;
        }
        return false;
    }

    /**
     * @return bool
     */
    public function isHidden()
    {
        if ($this->canShow()) {
            return false;
        }
        return true;
    }

    /**
     * Tab class getter
     *
     * @return string
     */
    public function getTabClass()
    {
        return '';
    }

    /**
     * Return URL link to Tab content
     *
     * @return string
     */
    public function getTabUrl()
    {
        return '';
    }

    /**
     * Tab should be loaded trough Ajax call
     *
     * @return bool
     */
    public function isAjaxLoaded()
    {
        return false;
    }

    /**
     * @return bool
     */
    public function canShow()
    {
        $id = $this->getRequest()->getParam('id');
        if($id != null && $id !='')
            return true;
        return false;
    }

    /**
     * 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(
                \Lights\OrderCombine\Block\Adminhtml\Edit\Tab\MissingProductgrid::class,
                'product.missing.grid'
            );
        }
        return $this->blockGrid;
    }

    /**
     * @return string
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function getGridHtml()
    {
        return $this->getBlockGrid()->toHtml();
    }

    /**
     * product ids and qtys
     * @return string
     */
    public function getProductsJson()
    {
        $selected = [];
        $orderCombineDetail = $this->detailsFactory->create()->load($this->getRequest()->getParam('id'));
        if($orderCombineDetail){
            if($orderCombineDetail->getData('missing_tab_data') != null){
                $selected = $orderCombineDetail->getData('missing_tab_data');
                return $selected;
            }
        }

        return \Zend_Json::encode($selected);
    }

}
– Model \Lights\OrderCombine\Model\CombineDetailsFactory to get your data selected on grid.
– Function getProductsJson() will specify selected product show on grid. It return array ids with json_encode.
– Function getBlockGrid() will call grid show in your tabs.

3. Implement block include of grid data: \Lights\OrderCombine\Block\Adminhtml\Edit\Tab\MissingProductgrid.php . I will use product grid in here(You can use your custom table)

namespace Lights\OrderCombine\Block\Adminhtml\Edit\Tab;

use Magento\Backend\Block\Widget\Grid\Column;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Catalog\Model\Product\Visibility;
use Magento\Framework\App\ObjectManager;
class MissingProductgrid extends \Magento\Backend\Block\Widget\Grid\Extended
{
    protected $blockGrid;
    /**
     * Core registry
     *
     * @var \Magento\Framework\Registry
     */
    protected $_coreRegistry = null;

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

    /**
     * @var Status
     */
    private $status;

    /**
     * @var Visibility
     */
    private $visibility;
    /**
     * @var \Lights\OrderCombine\Model\CombineDetailsFactory
     */
    protected $combineDetailsFactory;

    /**
     * LiquorProductgrid constructor.
     * @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 array $data
     * @param Visibility|null $visibility
     * @param Status|null $status
     * @param \Lights\OrderCombine\Model\CombineDetailsFactory $combineDetailsFactory
     */
    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Backend\Helper\Data $backendHelper,
        \Magento\Catalog\Model\ProductFactory $productFactory,
        \Magento\Framework\Registry $coreRegistry,
        array $data = [],
        Visibility $visibility = null,
        Status $status = null,
        \Lights\OrderCombine\Model\CombineDetailsFactory $combineDetailsFactory
    ) {
        $this->combineDetailsFactory = $combineDetailsFactory;
        $this->_productFactory = $productFactory;
        $this->_coreRegistry = $coreRegistry;
        $this->visibility = $visibility ?: ObjectManager::getInstance()->get(Visibility::class);
        $this->status = $status ?: ObjectManager::getInstance()->get(Status::class);
        parent::__construct($context, $backendHelper, $data);
    }

    /**
     * @return void
     */
    protected function _construct()
    {
        parent::_construct();
        $this->setId('catalog_missing_products');
        $this->setDefaultSort('entity_id');
        $this->setUseAjax(true);
    }


    /**
     * @param Column $column
     * @return $this|\Magento\Backend\Block\Widget\Grid\Extended
     * @throws \Magento\Framework\Exception\LocalizedException
     * @throws \Zend_Json_Exception
     */
    protected function _addColumnFilterToCollection($column)
    {
        // Set custom filter for in category flag
        if ($column->getId() == 'in_category') {
            $productIds = $this->_getSelectedProducts();
            if (empty($productIds)) {
                $productIds = 0;
            }
            if ($column->getFilter()->getValue()) {
                $this->getCollection()->addFieldToFilter('entity_id', ['in' => $productIds]);
            } elseif (!empty($productIds)) {
                $this->getCollection()->addFieldToFilter('entity_id', ['nin' => $productIds]);
            }
        } else {
            parent::_addColumnFilterToCollection($column);
        }
        return $this;
    }

    /**
     * @return \Magento\Backend\Block\Widget\Grid\Extended
     */
    protected function _prepareCollection()
    {

        $collection = $this->_productFactory->create()->getCollection()->addAttributeToSelect(
            'name'
        )->addAttributeToSelect(
            'sku'
        )->addAttributeToSelect(
            'visibility'
        )->addAttributeToSelect(
            'status'
        )->addAttributeToSelect(
            'price'
        );
        $storeId = (int)$this->getRequest()->getParam('store', 0);
        if ($storeId > 0) {
            $collection->addStoreFilter($storeId);
        }
        $this->setCollection($collection);

        return parent::_prepareCollection();
    }

    /**
     * @return \Magento\Backend\Block\Widget\Grid\Extended
     * @throws \Exception
     */
    protected function _prepareColumns()
    {
        $this->addColumn(
            'in_category',
            [
                'type' => 'checkbox',
                'name' => 'in_category',
                'values' => $this->_getSelectedProducts(),
                'index' => 'entity_id',
                'header_css_class' => 'col-select col-massaction',
                'column_css_class' => 'col-select col-massaction'
            ]
        );
        $this->addColumn(
            'entity_id',
            [
                'header' => __('ID'),
                'sortable' => true,
                'index' => 'entity_id',
                'header_css_class' => 'col-id',
                'column_css_class' => 'col-id'
            ]
        );
        $this->addColumn('name', ['header' => __('Name'), 'index' => 'name']);
        $this->addColumn('sku', ['header' => __('SKU'), 'index' => 'sku']);
        $this->addColumn(
            'visibility',
            [
                'header' => __('Visibility'),
                'index' => 'visibility',
                'type' => 'options',
                'options' => $this->visibility->getOptionArray(),
                'header_css_class' => 'col-visibility',
                'column_css_class' => 'col-visibility'
            ]
        );

        $this->addColumn(
            'status',
            [
                'header' => __('Status'),
                'index' => 'status',
                'type' => 'options',
                'options' => $this->status->getOptionArray()
            ]
        );

        $this->addColumn(
            'price',
            [
                'header' => __('Price'),
                'type' => 'currency',
                'currency_code' => (string)$this->_scopeConfig->getValue(
                    \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE,
                    \Magento\Store\Model\ScopeInterface::SCOPE_STORE
                ),
                'index' => 'price'
            ]
        );
        $this->addColumn(
            'position',
            [
                'header' => __('Qty'),
                'name' => 'position',
                'type' => 'number',
                'validate_class' => 'validate-number',
                'index' => 'position',
                'editable' => true,
                'edit_only' => true,
                'header_css_class' => 'col-position',
                'column_css_class' => 'col-position'
            ]
        );

        return parent::_prepareColumns();
    }

    /**
     * url grid when processing filter
     * @return string
     */
    public function getGridUrl()
    {
        return $this->getUrl('ordercombine/*/missing', ['_current' => true]);
    }

    /**
     * @return array|mixed
     * @throws \Zend_Json_Exception
     */
    protected function _getSelectedProducts()
    {
        $selected = [];
        $orderCombineDetail = $this->combineDetailsFactory->create()->load($this->getRequest()->getParam('id'));
        if($orderCombineDetail){
            if($orderCombineDetail->getData('missing_tab_data') != null){
                $selected = \Zend_Json::decode($orderCombineDetail->getData('missing_tab_data'));
            }
        }

        return array_keys($selected);
    }

}

– Function getGridUrl() will use url callback when you have action on grid.
– Function _getSelectedProducts() will return an array key to filter and selected value. – Function _prepareColumns() will allow add your column in grid.

4. File templates:Lights/OrderCombine/view/adminhtml/templates/tabs/missingproducts.phtml
– Input will post data name= “ordercombine[missing_tab_data]”. When you select or add qty it will add to this field. – data-form-part=”ordercombine_index_edit”: It allow your input can post to ui_form.

5. File js process grid and data when adjustment.
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/* global $, $H */

define([
    'mage/adminhtml/grid'
], function () {
    'use strict';

    return function (config) {
        var selectedProducts = config.selectedProducts,
            categoryProducts = $H(selectedProducts),
            gridJsObject = window[config.gridJsObjectName],
            tab_post_id = config.tab_post_id,
            tabIndex = 1000;
        $(tab_post_id).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);
            }
            $(tab_post_id).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);
                $(tab_post_id).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],
                positionValue = checkbox.value;

            if (checkbox && position) {
                if(selectedProducts[positionValue] != undefined){
                    position.value = selectedProducts[positionValue];
                }
                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);
            });
        }
    };
});


6. Finally, Create controller of this grid.

namespace Lights\OrderCombine\Controller\Adminhtml\Index;

use Magento\Backend\App\Action;
class Missing  extends Action
{
    /**
     * @var \Magento\Framework\Controller\Result\RawFactory
     */
    protected $resultRawFactory;

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

    /**
     * @param Action\Context $context
     * @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory
     * @param \Magento\Framework\View\LayoutFactory $layoutFactory
     */
    public function __construct(
        Action\Context $context,
        \Magento\Framework\Controller\Result\RawFactory $resultRawFactory,
        \Magento\Framework\View\LayoutFactory $layoutFactory
    ) {
        parent::__construct($context);
        $this->resultRawFactory = $resultRawFactory;
        $this->layoutFactory = $layoutFactory;
    }

    /**
     * Grid Action
     * Display list of products related to current category
     *
     * @return \Magento\Framework\Controller\Result\Raw
     */
    public function execute()
    {

        /** @var \Magento\Framework\Controller\Result\Raw $resultRaw */
        $resultRaw = $this->resultRawFactory->create();
        return $resultRaw->setContents(
            $this->layoutFactory->create()->createBlock(
                \Lights\OrderCombine\Block\Adminhtml\Edit\Tab\LiquorProductgrid::class,
                'product.missing.grid'
            )->toHtml()
        );
    }

}
Now, you can use grid sezialiler on your tabs and post data on your form.
1. Add “htmlContent” in your form ui. You need to add tabs and template. 2. Create controller file:
use Magento\Backend\App\Action\Context;
use Magento\Catalog\Controller\Adminhtml\Product\Builder;
use Magento\Framework\View\Result\LayoutFactory;
use Magento\Catalog\Controller\Adminhtml\Product;
 
class Productgrid extends Product
{
 
protected $resultLayoutFactory;
 
 
public function __construct(
       Context$context,
Builder $productBuilder,
LayoutFactory$resultLayoutFactory
)
    {
parent::__construct($context, $productBuilder);
$this->resultLayoutFactory= $resultLayoutFactory;
}
 
 
public function execute()
    {
$this->productBuilder->build($this->getRequest());
$resultLayout= $this->resultLayoutFactory->create();
$resultLayout->getLayout()->getBlock('company.module.edit.tab.productgrid')
            ->setProducts($this->getRequest()->getPost('products', null));
return $resultLayout;
}
}


3. Block file:
use Magento\Backend\Block\Template\Context;
 
use Magento\Backend\Helper\Data;
 
use Magento\Catalog\Model\ProductFactory;
 
use Vendor\Extension\Model\OptionFactory;
 
use Magento\Framework\Registry;
 
 
 
class Missing extends \Magento\Backend\Block\Widget\Grid\Extended
 
{
 
protected $coreRegistry= null;
 
protected $productFactory;
 
 
 
public function __construct(
 
        Context $context,
 
Data $backendHelper,
 
ProductFactory$productFactory,
 
OptionFactory$optionFactory,
 
Registry $coreRegistry,
 
array $data = []
 
    )
 
    {
 
$this->productFactory= $productFactory;
 
$this->optionFactory= $optionFactory;
 
$this->coreRegistry= $coreRegistry;
 
parent::__construct($context, $backendHelper, $data);
 
}
 
 
 
protected function _construct()
 
    {
 
parent::_construct();
 
$this->setId('product_grid');
 
$this->setDefaultSort('entity_id');
 
$this->setUseAjax(true);
 
}
 
 
 
protected function _addColumnFilterToCollection($column)
 
    {
 
if ($column->getId() == '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;
 
}
 
 
 
 
 
protected function _prepareCollection()
 
    {
 
$collection = $this->productFactory->create()->getCollection()->addAttributeToSelect(
 
'*'
 
);
 
$this->setCollection($collection);
 
return parent::_prepareCollection();
 
}
 
 
 
protected function _prepareColumns()
 
    {
 
 
 
$this->addColumn(
 
'products',
 
[
 
'type' =>'checkbox',
 
'field_name' =>'products_id[]',
 
'required' =>true,
 
'values' =>$this->_getSelectedProducts(),
 
'align' =>'center',
 
'index' =>'entity_id',
 
'header_css_class' =>'col-select',
 
'column_css_class' =>'col-select'
 
]
 
        );
 
 
 
 
 
$this->addColumn(
 
'name',
 
[
 
'header' => __('Name'),
 
'index' =>'name',
 
'header_css_class' =>'col-name',
 
'column_css_class' =>'col-name'
 
]
 
        );
 
 
 
 
 
$this->addColumn(
 
'sku',
 
[
 
'header' => __('SKU'),
 
'index' =>'sku',
 
'header_css_class' =>'col-sku',
 
'column_css_class' =>'col-sku'
 
]
 
        );
 
 
 
 
 
return parent::_prepareColumns();
 
}
 
 
 
 
 
public function getGridUrl()
 
    {
 
return $this->_getData(
 
'grid_url'
 
) ? $this->_getData(
 
'grid_url'
 
) : $this->getUrl(
 
'*/*/productgrid',
 
['_current' =>true]
 
        );
 
}
 
 
 
protected function _getSelectedProducts()
 
    {
 
$products = array_keys($this->getSelectedProducts());
 
return $products;
 
}
 
 
 
public function getSelectedProducts()
 
    {
 
$tm_id= $this->getRequest()->getParam('id');
 
if (!isset($tm_id)) {
 
$tm_id= 0;
 
}
 
 
 
$collection = $this->optionFactory->create()->load($tm_id);
 
$data = $collection->getProducts();
 
$products = explode(',', $data);
 
 
 
$proIds= array();
 
 
 
foreach($products as $product) {
 
$proIds[$product] = array('id' =>$product);
 
}
 
return $proIds;
 
}
 
}

Leave a Reply