Magento 2 admin grid custom module

Magento 2 admin grid

Magento 2 admin grid custom module fully deals with the admin part work in magento 2 admin module development. Before moving to this article, you should be aware of the basics of module creation in Magento 2. To learn about the full module creation click here. The first step of the article is to have menu, yes we need to define the menu before moving to grid page. Create a new file menu.xml in the directory app/code/Explorer/Test/etc/adminhtml/.

Menu in Magento 2 admin grid

To do magento 2 add menu item use the below xml format.

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
    <menu>
        <add id="Explorer_Test::content_elements" title="Test Extension" module="Magento_Backend" sortOrder="10" parent="Magento_Backend::content"
             resource="Magento_Backend::content_elements"/>
        <add id="Explorer_Test::explorer_test" title="Manage Items" module="Explorer_Test" sortOrder="10" parent="Explorer_Test::content_elements" action="test/test"
             resource="Explorer_Test::test"/> 
    </menu>
</config>

Explorer_Test::content_elements defines the parent menu and Explorer_Test::explorer_test id defines submenu for the parent Explorer_Test::content_elements using parent attribute tag. It is important to have action attributes which routes the menu to the required controller. Here we are routing to test controller in test module.

Test.php

Next step create the abstract test class controller file in the directory Explorer\Test\Controller\Adminhtml. The initpage function sets the active menu in the page. The isAllowed function used to check the admin user allowed or not allowed for the page to see the magento 2 admin grid.

<?php

namespace Explorer\Test\Controller\Adminhtml;

abstract class Test extends \Magento\Backend\App\Action
{
    
    protected $_coreRegistry = null;

    public function __construct(\Magento\Backend\App\Action\Context $context, \Magento\Framework\Registry $coreRegistry)
    {
        $this->_coreRegistry = $coreRegistry;
        parent::__construct($context);
    }

    protected function initPage($resultPage)
    {
        $resultPage->setActiveMenu('Explorer_Test::explorer_test')
            ->addBreadcrumb(__('Test'), __('Test'))
            ->addBreadcrumb(__('Items'), __(''));
        return $resultPage;
    }

    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('Explorer_Test::test_menu');
    }
}

Index.php

Next the Index controller create the corresponding page for the menu. As you already know from the previous articles. In magento2 every controller requires a execute function to render as the page. As so on this Index controller also require a execute function to render admin page. The $resultpagefactory object do the same thing of $this->loadlayout and $this->renderlayout function in magento 1 it renders the layout.

<?php
namespace Explorer\Test\Controller\Adminhtml\Test;

class Index extends \Explorer\Test\Controller\Adminhtml\Test
{
    protected $resultPageFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\Registry $coreRegistry,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory
    ) {
        $this->resultPageFactory = $resultPageFactory;
        parent::__construct($context, $coreRegistry);
    }

    public function execute()
    {
        $resultPage = $this->resultPageFactory->create();
        $this->initPage($resultPage)->getConfig()->getTitle()->prepend(__('Items'));
        return $resultPage;
    }
}

Routes.xml

Magento 2 admin route do the same thing of routes element in config.xml file of magento 1. In magento 2 it is separated as routes.xml file in the directory app/code/Explorer/Test/etc/adminhtml/.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
        <route id="test" frontName="test">
            <module name="Explorer_Test" before="Magento_Backend" />
        </route>
    </router>
</config>

Keep the route id and frontname as same. The important thing to note here is which defines this router for admin part.

Collection to get data

Collection.php

To display the module data in the Magento 2 admin grid or somewhere else we need to have collection class relates to the resource model. So create the Collection.php in the directory Explorer\Test\Model\ResourceModel\Test\Grid.

	<?php

namespace Explorer\Test\Model\ResourceModel\Test\Grid;

use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\Search\AggregationInterface;
use Explorer\Test\Model\ResourceModel\Test\Collection as TestCollection;

class Collection extends TestCollection implements SearchResultInterface
{
    
    protected $aggregations;


    public function __construct(
        \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory,
        \Psr\Log\LoggerInterface $logger,
        \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        $mainTable,
        $eventPrefix,
        $eventObject,
        $resourceModel,
        $model = 'Magento\Framework\View\Element\UiComponent\DataProvider\Document',
        $connection = null,
        \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
    ) {
        parent::__construct(
            $entityFactory,
            $logger,
            $fetchStrategy,
            $eventManager,
            $storeManager,
            $connection,
            $resource
        );
        $this->_eventPrefix = $eventPrefix;
        $this->_eventObject = $eventObject;
        $this->_init($model, $resourceModel);
        $this->setMainTable($mainTable);
    }

    
    public function getAggregations()
    {
        return $this->aggregations;
    }

    
    public function setAggregations($aggregations)
    {
        $this->aggregations = $aggregations;
    }


    
    public function getAllIds($limit = null, $offset = null)
    {
        return $this->getConnection()->fetchCol($this->_getAllIdsSelect($limit, $offset), $this->_bindParams);
    }

    public function getSearchCriteria()
    {
        return null;
    }

    
    public function setSearchCriteria(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria = null)
    {
        return $this;
    }

    
    public function getTotalCount()
    {
        return $this->getSize();
    }

    
    public function setTotalCount($totalCount)
    {
        return $this;
    }

    public function setItems(array $items = null)
    {
        return $this;
    }
}

This class needed for the proper functioning of all ui components as well as it contains the functions of search and paginations etc.

test_index_index.xml

To view the controller action as a page we need to have the layout section. Create a file test_index_index.xml in the directory app/code/Explorer/Test/view/adminhtml/layout/. To use the default magento2 admin grid components define the uicomponent in the content tag of layout section.

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <uiComponent name="test_test_listing"/>
        </referenceContainer>
    </body>
</page>
[/source]

<h3>Admin grid view ui components</h3>

<h4>test_test_listing.xml</h4>
Magento 2 ui component grid make us easy to create the admin grid componenets with extended feature of searches and displaying column in the custom manner. We need to have several files to implement this properly. The most important file is test_test_listing.xml file in the directory app/code/Explorer/Test/view/adminhtml/ui_component. 


1
  <?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">test_test_listing.test_test_listing_data_source</item>
            <item name="deps" xsi:type="string">test_test_listing.test_test_listing_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">test_test_columns</item>
        <item name="buttons" xsi:type="array">
            <item name="add" xsi:type="array">
                <item name="name" xsi:type="string">add</item>
                <item name="label" xsi:type="string" translate="true">Add New Item</item>
                <item name="class" xsi:type="string">primary</item>
                <item name="url" xsi:type="string">*/*/new</item>
            </item>
        </item>
    </argument>
    <dataSource name="test_test_listing_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">TestGridDataProvider</argument>
            <argument name="name" xsi:type="string">test_test_listing_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">test_id</argument>
            <argument name="requestFieldName" xsi:type="string">id</argument>
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="update_url" xsi:type="url" path="mui/index/render"/>
                </item>
            </argument>
        </argument>
        <argument name="data" xsi:type="array">
            <item name="js_config" xsi:type="array">
                <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
            </item>
        </argument>
    </dataSource>
    <container name="listing_top">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="template" xsi:type="string">ui/grid/toolbar</item>
            </item>
        </argument>
        <bookmark name="bookmarks">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="storageConfig" xsi:type="array">
                        <item name="namespace" xsi:type="string">test_test_listing</item>
                    </item>
                </item>
            </argument>
        </bookmark>
        <component name="columns_controls">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="columnsData" xsi:type="array">
                        <item name="provider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns</item>
                    </item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/controls/columns</item>
                    <item name="displayArea" xsi:type="string">dataGridActions</item>
                </item>
            </argument>
        </component>
        <filterSearch name="fulltext">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="provider" xsi:type="string">test_test_listing.test_test_listing_data_source</item>
                    <item name="chipsProvider" xsi:type="string">test_test_listing.test_test_listing.listing_top.listing_filters_chips</item>
                    <item name="storageConfig" xsi:type="array">
                        <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
                        <item name="namespace" xsi:type="string">current.search</item>
                    </item>
                </item>
            </argument>
        </filterSearch>
        <filters name="listing_filters">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="columnsProvider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns
                    </item>
                    <item name="storageConfig" xsi:type="array">
                        <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
                        <item name="namespace" xsi:type="string">current.filters</item>
                    </item>
                    <item name="templates" xsi:type="array">
                        <item name="filters" xsi:type="array">
                            <item name="select" xsi:type="array">
                                <item name="component" xsi:type="string">Magento_Ui/js/form/element/ui-select</item>
                                <item name="template" xsi:type="string">ui/grid/filters/elements/ui-select</item>
                            </item>
                        </item>
                    </item>
                    <item name="childDefaults" xsi:type="array">
                        <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.listing_filters</item>
                        <item name="imports" xsi:type="array">
                            <item name="visible" xsi:type="string">test_test_listing.test_test_listing.test_test_columns.${ $.index }:visible</item>
                        </item>
                    </item>
                </item>
            </argument>
        </filters>
        <massaction name="listing_massaction">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="selectProvider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns.ids</item>
                    <item name="indexField" xsi:type="string">test_id</item>
                </item>
            </argument>
            <action name="delete">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="type" xsi:type="string">delete</item>
                        <item name="label" xsi:type="string" translate="true">Delete</item>
                        <item name="url" xsi:type="url" path="test/test/massDelete"/>
                        <item name="confirm" xsi:type="array">
                            <item name="title" xsi:type="string" translate="true">Delete items</item>
                            <item name="message" xsi:type="string" translate="true">Are you sure you wan't to delete selected items?</item>
                        </item>
                    </item>
                </argument>
            </action>
            <action name="disable">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="type" xsi:type="string">disable</item>
                        <item name="label" xsi:type="string" translate="true">Disable</item>
                        <item name="url" xsi:type="url" path="test/test/massDisable"/>
                    </item>
                </argument>
            </action>
            <action name="enable">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="type" xsi:type="string">enable</item>
                        <item name="label" xsi:type="string" translate="true">Enable</item>
                        <item name="url" xsi:type="url" path="test/test/massEnable"/>
                    </item>
                </argument>
            </action>
        </massaction>
        <paging name="listing_paging">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="storageConfig" xsi:type="array">
                            <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
                            <item name="namespace" xsi:type="string">current.paging</item>
                        </item>
                        <item name="selectProvider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns.ids</item>
                    </item>
                </argument>
        </paging>
    </container>
    <columns name="test_test_columns">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="storageConfig" xsi:type="array">
                    <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
                    <item name="namespace" xsi:type="string">current</item>
                </item>
            </item>
            <item name="childDefaults" xsi:type="array">
                <item name="fieldAction" xsi:type="array">
                    <item name="provider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns_editor</item>
                    <item name="target" xsi:type="string">startEdit</item>
                    <item name="params" xsi:type="array">
                        <item name="0" xsi:type="string">${ $.$data.rowIndex }</item>
                        <item name="1" xsi:type="boolean">true</item>
                    </item>
                </item>
                <item name="storageConfig" xsi:type="array">
                    <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
                    <item name="root" xsi:type="string">columns.${ $.index }</item>
                    <item name="namespace" xsi:type="string">current.${ $.storageConfig.root }</item>
                </item>
            </item>
        </argument>
        <selectionsColumn name="ids">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="indexField" xsi:type="string">test_id</item>
                </item>
            </argument>
        </selectionsColumn>
        <column name="test_id">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="filter" xsi:type="string">textRange</item>
                        <item name="sorting" xsi:type="string">asc</item>
                        <item name="label" xsi:type="string" translate="true">ID</item>
                    </item>
                </argument>
        </column>
        <column name="full_name">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="editor" xsi:type="array">
                        <item name="editorType" xsi:type="string">text</item>
                        <item name="validation" xsi:type="array">
                            <item name="required-entry" xsi:type="boolean">true</item>
                        </item>
                    </item>
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Name</item>
                </item>
            </argument>
        </column>
        <column name="email">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="editor" xsi:type="array">
                        <item name="editorType" xsi:type="string">text</item>
                        <item name="validation" xsi:type="array">
                            <item name="required-entry" xsi:type="boolean">true</item>
                        </item>
                    </item>
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Email</item>
                </item>
            </argument>
        </column>
        <column name="created_at" class="Magento\Ui\Component\Listing\Columns\Date">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">dateRange</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
                    <item name="dataType" xsi:type="string">date</item>
                    <item name="label" xsi:type="string" translate="true">Created</item>
                </item>
            </argument>
        </column>
        <column name="updated_at" class="Magento\Ui\Component\Listing\Columns\Date">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">dateRange</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
                    <item name="dataType" xsi:type="string">date</item>
                    <item name="label" xsi:type="string" translate="true">Modified</item>
                </item>
            </argument>
        </column>
        <column name="is_active">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="array">
                    <item name="disable" xsi:type="array">
                        <item name="value" xsi:type="string">0</item>
                        <item name="label" xsi:type="string" translate="true">Disabled</item>
                    </item>
                    <item name="enable" xsi:type="array">
                        <item name="value" xsi:type="string">1</item>
                        <item name="label" xsi:type="string" translate="true">Enabled</item>
                    </item>
                </item>
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">select</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
                    <item name="editor" xsi:type="string">select</item>
                    <item name="dataType" xsi:type="string">select</item>
                    <item name="label" xsi:type="string" translate="true">Status</item>
                </item>
            </argument>
        </column>
        <actionsColumn name="actions" class="Explorer\Test\Ui\Component\Listing\Column\TestActions">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="indexField" xsi:type="string">test_id</item>
                </item>
            </argument>
        </actionsColumn>
    </columns>
</listing>
  • Column tag name can be added through
  • Buttons can be added through the tag
  • Mass delete can be performed through
  • Mass enable and mass disable action can be performed through and
  • Searching can be added through the tag and tag implements paging and scalability.

Testactions.php

To function the action column mentioned in the grid defining xml we need a class for it. As described in we need to create a file called TestActions.php

	<?php
namespace Explorer\Test\Ui\Component\Listing\Column;

use Magento\Framework\UrlInterface;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;


class TestActions extends Column
{
    
    const URL_PATH_EDIT = 'test/test/edit';
    const URL_PATH_DELETE = 'test/test/delete';

    
    protected $urlBuilder;

    
    public function __construct(
        ContextInterface $context,
        UiComponentFactory $uiComponentFactory,
        UrlInterface $urlBuilder,
        array $components = [],
        array $data = []
    ) {
        $this->urlBuilder = $urlBuilder;
        parent::__construct($context, $uiComponentFactory, $components, $data);
    }


    public function prepareDataSource(array $dataSource)
    {
        if (isset($dataSource['data']['items'])) {
            foreach ($dataSource['data']['items'] as & $item) {
                if (isset($item['test_id'])) {
                    $item[$this->getData('name')] = [
                        'edit' => [
                            'href' => $this->urlBuilder->getUrl(
                                static::URL_PATH_EDIT,
                                [
                                    'test_id' => $item['test_id']
                                ]
                            ),
                            'label' => __('Edit')
                        ],
                        'delete' => [
                            'href' => $this->urlBuilder->getUrl(
                                static::URL_PATH_DELETE,
                                [
                                    'test_id' => $item['test_id']
                                ]
                            ),
                            'label' => __('Delete'),
                            'confirm' => [
                                'title' => __('Delete "${ $.$data.name }"'),
                                'message' => __('Are you sure you wan\'t to delete a "${ $.$data.name }" record?')
                            ]
                        ]
                    ];
                }
            }
        }

        return $dataSource;
    }
}

This class create the array in the necessary format required for mass action.

Mass enable

In the next step as we all know to perform the required action in specific we need a controller to act it. The mass enable and mass disable can be performed through the controller massenable and massdisable having the execute function.

<?php
namespace Explorer\Test\Controller\Adminhtml\Test;

use Magento\Framework\Controller\ResultFactory;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Explorer\Test\Model\ResourceModel\Test\CollectionFactory;


class MassEnable extends \Magento\Backend\App\Action
{
    
    protected $filter;

    protected $collectionFactory;

    public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
    {
        $this->filter = $filter;
        $this->collectionFactory = $collectionFactory;
        parent::__construct($context);
    }


    public function execute()
    {
        $collection = $this->filter->getCollection($this->collectionFactory->create());

        foreach ($collection as $item) {
            $item->setIsActive(true);
            $item->save();
        }

        $this->messageManager->addSuccess(__('A total of %1 record(s) have been enabled.', $collection->getSize()));

        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
        return $resultRedirect->setPath('*/*/');
    }
}

Mass Disable

<?php

namespace Explorer\Test\Controller\Adminhtml\Test;

use Magento\Framework\Controller\ResultFactory;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Explorer\Test\Model\ResourceModel\Test\CollectionFactory;

class MassDisable extends \Magento\Backend\App\Action
{
    
    protected $filter;

    protected $collectionFactory;

    public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
    {
        $this->filter = $filter;
        $this->collectionFactory = $collectionFactory;
        parent::__construct($context);
    }

    public function execute()
    {
        $collection = $this->filter->getCollection($this->collectionFactory->create());

        foreach ($collection as $item) {
            $item->setIsActive(false);
            $item->save();
        }

        $this->messageManager->addSuccess(__('A total of %1 record(s) have been disabled.', $collection->getSize()));

        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
        return $resultRedirect->setPath('*/*/');
    }
}

di.xml

For the proper functioning of admin grid we need to define xml di.xml in the app/code/Explorer/Test/etc/.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
    <preference for="Magento\Theme\Block\Html\Title" type="Explorer\Test\Block\HelloTitle" />
    <type name="Magento\Catalog\Block\Product\View">
        <plugin name="inroduct-custom-module" type="Explorer\Test\Model\Plugin\Product" sortOrder="1"/>
    </type>
    <type name="Magento\Catalog\Model\Product">
        <plugin name="getname-test-module" type="Explorer\Test\Model\Plugin\Product" sortOrder="10"/>
    </type>
    <type name="Magento\Catalog\Controller\Product\View">
        <plugin name="product-cont-test-module" type="Explorer\Test\Model\Plugin\Product" sortOrder="10"/>
    </type>
    <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
        <arguments>
            <argument name="collections" xsi:type="array">
                <item name="test_test_listing_data_source" xsi:type="string">Explorer\Test\Model\ResourceModel\Test\Grid\Collection</item>
            </argument>
        </arguments>
    </type>
    <type name="Explorer\Test\Model\ResourceModel\Test\Grid\Collection">
        <arguments>
            <argument name="mainTable" xsi:type="string">explorer_test</argument>
            <argument name="eventPrefix" xsi:type="string">explorer_test_grid_collection</argument>
            <argument name="eventObject" xsi:type="string">test_grid_collection</argument>
            <argument name="resourceModel" xsi:type="string">Explorer\Test\Model\ResourceModel\Test</argument>
        </arguments>
    </type>
    <virtualType name="TestGirdFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool">
        <arguments>
            <argument name="appliers" xsi:type="array">
                <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item>
                <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item>
            </argument>
        </arguments>
    </virtualType>
    <virtualType name="TestGridDataProvider" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
        <arguments>
            <argument name="collection" xsi:type="object" shared="false">Explorer\Test\Model\ResourceModel\Test\Collection</argument>
            <argument name="filterPool" xsi:type="object" shared="false">TestGirdFilterPool</argument>
        </arguments>
    </virtualType>
</config>

Test.php

We need to create the class Test.php located in the Explorer/Test/Block/Adminhtml/ for the admin blocks.

<?php

namespace Explorer\Test\Block\Adminhtml;

class Test extends \Magento\Backend\Block\Widget\Grid\Container
{
    protected function _construct()
    {
        $this->_blockGroup = 'Explorer_Test';
        $this->_controller = 'adminhtml_test';
        $this->_headerText = __('Items');
        $this->_addButtonLabel = __('Add New Item');
        parent::_construct();
    }
}

Edit.php

The title of the admin page can add or remove buttons to it besides the basic button.

<?php
namespace Explorer\Test\Block\Adminhtml\Test;

class Edit extends \Magento\Backend\Block\Widget\Form\Container
{
    protected $_coreRegistry = null;

    public function __construct(
        \Magento\Backend\Block\Widget\Context $context,
        \Magento\Framework\Registry $registry,
        array $data = []
    ) {
        $this->_coreRegistry = $registry;
        parent::__construct($context, $data);
    }

    protected function _construct()
    {
        $this->_objectId = 'test_id';
        $this->_blockGroup = 'Explorer_Test';
        $this->_controller = 'adminhtml_test';

        parent::_construct();

        $this->buttonList->update('save', 'label', __('Save Item'));
        $this->buttonList->update('delete', 'label', __('Delete Item'));

        $this->buttonList->add(
            'saveandcontinue',
            [
                'label' => __('Save and Continue Edit'),
                'class' => 'save',
                'data_attribute' => [
                    'mage-init' => ['button' => ['event' => 'saveAndContinueEdit', 'target' => '#edit_form']],
                ]
            ],
            -100
        );

    }


    public function getHeaderText()
    {
        if ($this->_coreRegistry->registry('test_item')->getId()) {
            return __("Edit Block '%1'", $this->escapeHtml($this->_coreRegistry->registry('test_item')->getName()));
        } else {
            return __('New Item');
        }
    }
}

If we want to use any editor we need to place in that construct function. The title value of the admin page is defined by the getheadertext()

function in the class.

Form.php

<?php

namespace Explorer\Test\Block\Adminhtml\Test\Edit;

class Form extends \Magento\Backend\Block\Widget\Form\Generic
{
    
    protected $_wysiwygConfig;

    protected $_systemStore;

    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Framework\Registry $registry,
        \Magento\Framework\Data\FormFactory $formFactory,
        \Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig,
        \Magento\Store\Model\System\Store $systemStore,
        array $data = []
    ) {
        $this->_wysiwygConfig = $wysiwygConfig;
        $this->_systemStore = $systemStore;
        parent::__construct($context, $registry, $formFactory, $data);
    }

    protected function _construct()
    {
        parent::_construct();
        $this->setId('test_form');
        $this->setTitle(__('Item Information'));
    }


    protected function _prepareForm()
    {
        $model = $this->_coreRegistry->registry('test_item');

        $form = $this->_formFactory->create(
            ['data' => ['id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post']]
        );

        $form->setHtmlIdPrefix('item_');

        $fieldset = $form->addFieldset(
            'base_fieldset',
            ['legend' => __('General Information'), 'class' => 'fieldset-wide']
        );

        if ($model->getId()) {
            $fieldset->addField('test_id', 'hidden', ['name' => 'test_id']);
        }

        $fieldset->addField(
            'full_name',
            'text',
            [
                'name' => 'full_name',
                'label' => __('Name'),
                'title' => __('Name'),
                'required' => true
            ]
        );

        $fieldset->addField(
            'email',
            'text',
            [
                'name' => 'email',
                'label' => __('Email'),
                'title' => __('Email'),
                'required' => true,
                'class' => 'validate-email'
            ]
        );

        $fieldset->addField(
            'is_active',
            'select',
            [
                'label' => __('Status'),
                'title' => __('Status'),
                'name' => 'is_active',
                'required' => true,
                'options' => ['1' => __('Enabled'), '0' => __('Disabled')]
            ]
        );
        if (!$model->getId()) {
            $model->setData('is_active', '1');
        }

        $form->setValues($model->getData());
        $form->setUseContainer(true);
        $this->setForm($form);

        return parent::_prepareForm();
    }
}

To add the fields in the magento 2 admin form, add the fields in the prepareform function as similar as magento 1.

Edit.php

  <?php
namespace Explorer\Test\Block\Adminhtml\Test;

class Edit extends \Magento\Backend\Block\Widget\Form\Container
{
    protected $_coreRegistry = null;

    public function __construct(
        \Magento\Backend\Block\Widget\Context $context,
        \Magento\Framework\Registry $registry,
        array $data = []
    ) {
        $this->_coreRegistry = $registry;
        parent::__construct($context, $data);
    }

    protected function _construct()
    {
        $this->_objectId = 'test_id';
        $this->_blockGroup = 'Explorer_Test';
        $this->_controller = 'adminhtml_test';

        parent::_construct();

        $this->buttonList->update('save', 'label', __('Save Item'));
        $this->buttonList->update('delete', 'label', __('Delete Item'));

        $this->buttonList->add(
            'saveandcontinue',
            [
                'label' => __('Save and Continue Edit'),
                'class' => 'save',
                'data_attribute' => [
                    'mage-init' => ['button' => ['event' => 'saveAndContinueEdit', 'target' => '#edit_form']],
                ]
            ],
            -100
        );

    }


    public function getHeaderText()
    {
        if ($this->_coreRegistry->registry('test_item')->getId()) {
            return __("Edit Block '%1'", $this->escapeHtml($this->_coreRegistry->registry('test_item')->getName()));
        } else {
            return __('New Item');
        }
    }
}

Create controllers and layout

New Action.php

Create the appropriate controller class to implement form action page of magento 2 admin grid. Use the below code

<?php
namespace Explorer\Test\Controller\Adminhtml\Test;

class NewAction extends \Explorer\Test\Controller\Adminhtml\Test
{
    protected $resultForwardFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\Registry $coreRegistry,
        \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory
    ) {
        $this->resultForwardFactory = $resultForwardFactory;
        parent::__construct($context, $coreRegistry);
    }

    public function execute()
    {
        $resultForward = $this->resultForwardFactory->create();
        return $resultForward->forward('edit');
    }
}

This class implments basically to create a new element, in the execute function of the class it redirects to the edit controller.

Edit.php

Create the Edit.php controller in the directory Explorer\Test\Controller\Adminhtml\Test. In Magento 2 admin grid, to execute edit action in the controller check the id in the model. If the model contains the id, will load the corresponding model in the register else it will returns the error message. If there is no id in the url will act as a new page.

	<?php

namespace Explorer\Test\Controller\Adminhtml\Test;

class Edit extends \Explorer\Test\Controller\Adminhtml\Test
{
    protected $resultPageFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\Registry $coreRegistry,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory
    ) {
        $this->resultPageFactory = $resultPageFactory;
        parent::__construct($context, $coreRegistry);
    }


    public function execute()
    {
        $id = $this->getRequest()->getParam('test_id');
        $model = $this->_objectManager->create('Explorer\Test\Model\Test');

        if ($id) {
            $model->load($id);
            if (!$model->getId()) {
                $this->messageManager->addError(__('This item no longer exists.'));
                $resultRedirect = $this->resultRedirectFactory->create();
                return $resultRedirect->setPath('*/*/');
            }
        }
    
        $data = $this->_objectManager->get('Magento\Backend\Model\Session')->getFormData(true);
        if (!empty($data)) {
            $model->setData($data);
        }

        $this->_coreRegistry->register('test_item', $model);

        $resultPage = $this->resultPageFactory->create();

        $this->initPage($resultPage)->addBreadcrumb(
            $id ? __('Edit Item') : __('New Item'),
            $id ? __('Edit Item') : __('New Item')
        );
        $resultPage->getConfig()->getTitle()->prepend(__('Items'));
        $resultPage->getConfig()->getTitle()->prepend($model->getId() ? $model->getName() : __('New Item'));
        return $resultPage;
    }
}

test_test_edit.xml

As a next step the edit action controller makes a query of the test_id parameter. To execute the controller we need to have layout file called test_test_edit.xml. Magento 2 admin grid edit action layout file should be located in Explorer/Test/view/adminhtml/layout/ directory.

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="editor"/>
    <body>
        <referenceContainer name="content">
            <block class="Explorer\Test\Block\Adminhtml\Test\Edit" name="test_test_edit"/>
        </referenceContainer>
    </body>
</page>

Save.php

To perform the save action in Magento 2 admin grid create the Save.php controller in the Explorer\Test\Controller\Adminhtml\Test directory with model save action in execute function.

<?php

namespace Explorer\Test\Controller\Adminhtml\Test;

class Save extends \Explorer\Test\Controller\Adminhtml\Test
{
    
    public function execute()
    {
        $resultRedirect = $this->resultRedirectFactory->create();
        $data = $this->getRequest()->getPostValue();
        if ($data) {
            $id = $this->getRequest()->getParam('test_id');
            $model = $this->_objectManager->create('Explorer\Test\Model\Test')->load($id);
            if (!$model->getId() && $id) {
                $this->messageManager->addError(__('This item no longer exists.'));
                return $resultRedirect->setPath('*/*/');
            }


            $model->setData($data);

            try {
                $model->save();
                $this->messageManager->addSuccess(__('You saved the item.'));
                $this->_objectManager->get('Magento\Backend\Model\Session')->setFormData(false);

                if ($this->getRequest()->getParam('back')) {
                    return $resultRedirect->setPath('*/*/edit', ['test_id' => $model->getId()]);
                }
                return $resultRedirect->setPath('*/*/');
            } catch (\Exception $e) {
                $this->messageManager->addError($e->getMessage());
                $this->_objectManager->get('Magento\Backend\Model\Session')->setFormData($data);
                return $resultRedirect->setPath('*/*/edit', ['test_id' => $this->getRequest()->getParam('test_id')]);
            }
        }
        return $resultRedirect->setPath('*/*/');
    }
}

Delete.php

To perform the Delete action in Magento 2 admin grid create the Delete.php controller in the Explorer\Test\Controller\Adminhtml\Test directory with model delete function in execute function.

<?php

namespace Explorer\Test\Controller\Adminhtml\Test;

class Delete extends \Explorer\Test\Controller\Adminhtml\Test
{
    public function execute()
    {
        $resultRedirect = $this->resultRedirectFactory->create();
        $id = $this->getRequest()->getParam('test_id');
        if ($id) {
            try {
                
                $model = $this->_objectManager->create('Explorer\Test\Model\Test');
                $model->load($id);
                $model->delete();
                
                $this->messageManager->addSuccess(__('You deleted the item.'));
                return $resultRedirect->setPath('*/*/');
            } catch (\Exception $e) {
                $this->messageManager->addError($e->getMessage());
                return $resultRedirect->setPath('*/*/edit', ['test_id' => $id]);
            }
        }
        $this->messageManager->addError(__('We can\'t find the item to delete.'));
        return $resultRedirect->setPath('*/*/');
    }
}

You’re done now. Have a great day 🙂

  • Fatal error: Class ‘Explorer\Test\Model\ResourceModel\Test\Collection’ not found
    in C:\xampp\htdocs\magento\magento21\app\code\Explorer\Test\Model\ResourceModel
    \Test\Grid\Collection.php on line 9

    One Collection class is missing in above Post