Magento2 How To Create Custom Admin Grid

Admin grids are used to represent, filter and sort various data in the Magento backend. Magento 2 Grid is a kind of table which provides listing of data items present in your database table and provides you some features like: sort, filter, delete, update item, etc.

Magento 2 provide two ways to create Admin Grid: using layout and using component. We will find out the detail for both of them. I will use the sample module Magemeta_Grid with some demo data:

To Create Admin Grid:

Step 1: Create database schema

Database: We will use a simple database

app/code/Magemeta/Grid/Setup/InstallSchema.php

Let put this content for this file to create above table:

<?php

namespace Magemeta/Grid/Setup;

class InstallSchema implements /Magento/Framework/Setup/InstallSchemaInterface
{

	public function install(/Magento/Framework/Setup/SchemaSetupInterface $setup, /Magento/Framework/Setup/ModuleContextInterface $context)
	{
		$installer = $setup;
		$installer->startSetup();
		if (!$installer->tableExists('magemeta_grid_post')) {
			$table = $installer->getConnection()->newTable(
				$installer->getTable('magemeta_grid_post')
			)
				->addColumn(
					'post_id',
					/Magento/Framework/DB/Ddl/Table::TYPE_INTEGER,
					null,
					[
						'identity' => true,
						'nullable' => false,
						'primary'  => true,
						'unsigned' => true,
					],
					'Post ID'
				)
				->addColumn(
					'name',
					/Magento/Framework/DB/Ddl\Table::TYPE_TEXT,
					255,
					['nullable => false'],
					'Post Name'
				)
				->addColumn(
					'url_key',
					/Magento/Framework/DB/Ddl/Table::TYPE_TEXT,
					255,
					[],
					'Post URL Key'
				)
				->addColumn(
					'post_content',
					/Magento/Framework/DB/Ddl/Table::TYPE_TEXT,
					'64k',
					[],
					'Post Post Content'
				)
				->addColumn(
					'tags',
					/Magento/Framework/DB/Ddl/Table::TYPE_TEXT,
					255,
					[],
					'Post Tags'
				)
				->addColumn(
					'status',
					/Magento/Framework/DB/Ddl/Table::TYPE_INTEGER,
					1,
					[],
					'Post Status'
				)
				->addColumn(
					'featured_image',
					/Magento/Framework/DB/Ddl/Table::TYPE_TEXT,
					255,
					[],
					'Post Featured Image'
				)
				->addColumn(
					'created_at',
					/Magento/Framework/DB\Ddl/Table::TYPE_TIMESTAMP,
					null,
					['nullable' => false, 'default' => /Magento/Framework/DB/Ddl/Table::TIMESTAMP_INIT],
					'Created At'
				)->addColumn(
					'updated_at',
					/Magento/Framework/DB/Ddl/Table::TYPE_TIMESTAMP,
					null,
					['nullable' => false, 'default' => /Magento/Framework/DB/Ddl/Table::TIMESTAMP_INIT_UPDATE],
					'Updated At')
				->setComment('Post Table');
			$installer->getConnection()->createTable($table);

			$installer->getConnection()->addIndex(
				$installer->getTable('mageplaza_helloworld_post'),
				$setup->getIdxName(
					$installer->getTable('mage_helloworld_post'),
					['name', 'url_key', 'post_content', 'tags', 'featured_image'],
					/Magento/Framework/DB/Adapter/AdapterInterface::INDEX_TYPE_FULLTEXT
				),
				['name', 'url_key', 'post_content', 'tags', 'featured_image'],
				/Magento/Framework/DB/Adapter/AdapterInterface::INDEX_TYPE_FULLTEXT
			);
		}
		$installer->endSetup();
	}
}

Step 2: Create routes admin:

File: app/code/Magemeta/Grid/etc/adminhtml/routes.xml

<?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="magemeta_grid" frontName="magemeta_grid">
            <module name="Magemeta_Grid"/>
        </route>
    </router>
</config>

Step 3: Create admin menu

Admin menu/Route: we will use the route mage_helloworld for our admin page and the menu link to:

magemeta_grid/post/index

Step 4: Create Controller

Create controller file called index.php

app/code/Magemeta/Grid/Controller/Adminhtml/Post/Index.php

<?php

namespace Magemeta/Grid/Controller/Adminhtml/Post;

class Index extends /Magento/Backend/App/Action
{
	protected $resultPageFactory = false;

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

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

		return $resultPage;
	}


}

Step 5:

Method #1 – Create Admin Grid using Component

Step 5.1: Declare resource

Now we will create di.xml file which will connect to the Model to get the data for our grid.

File: app/code/Magemeta/Grid/etc/di.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
    <type name="Magento/Framework/View/Element/UiComponent/DataProvider/CollectionFactory">
        <arguments>
            <argument name="collections" xsi:type="array">
                <item name="magemeta_grid_post_listing_data_source" xsi:type="string">Magemeta/Grid/Model/ResourceModel/Post/Grid/Collection</item>
            </argument>
        </arguments>
    </type>
    <virtualType name="Magemeta/Grid/Model/ResourceModel/Post/Grid/Collection" type="Magento/Framework/View/Element/UiComponent/DataProvider/SearchResult">
        <arguments>
            <argument name="mainTable" xsi:type="string">magemeta_grid_post</argument>
            <argument name="resourceModel" xsi:type="string">Magemeta/Grid/Model/ResourceModel/Post</argument>
        </arguments>
    </virtualType>
</config>

The above code will declare the post collection class, table and resourceModel for the table. This source will be called in the layout file to get data for grid.

Step 5.2: Create layout file

For the action magemeta_grid/post/index, It will create layout file name magemeta_grid_post_index.xml

File: app/code/Magemeta/Grid/view/adminhtml/layout/magemeta_grid_post_index.xml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <update handle="styles"/>
    <body>
        <referenceContainer name="content">
            <uiComponent name="magemeta_grid_post_listing"/>
        </referenceContainer>
    </body>
</page>

In above layout file, we declare an uiComponent for the content of this page.

Step 5.3: Create component layout file

As declaration in layout file, we will create a component file magemeta_grid_post_listing.xml

File: app/code/Magemeta/Grid/view/adminhtml/ui_component/magemeta_grid_post_listing.xml

<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">magemeta_grid_post_listing.magemeta_grid_post_listing_data_source</item>
            <item name="deps" xsi:type="string">magemeta_grid_post_listing.magemeta_grid_post_listing_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">spinner_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 Post</item>
                <item name="class" xsi:type="string">primary</item>
                <item name="url" xsi:type="string">*/*/new</item>
            </item>
        </item>
    </argument>
    <dataSource name="nameOfDataSource">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">Magento/Framework/View/Element/UiComponent/DataProvider/DataProvider</argument>
            <argument name="name" xsi:type="string">magemeta_grid_post_listing_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">post_id</argument>
            <argument name="requestFieldName" xsi:type="string">id</argument>
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
                    <item name="update_url" xsi:type="url" path="mui/index/render"/>
                    <item name="storageConfig" xsi:type="array">
                        <item name="indexField" xsi:type="string">post_id</item>
                    </item>
                </item>
            </argument>
        </argument>
    </dataSource>
    <columns name="spinner_columns">
        <selectionsColumn name="ids">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="resizeEnabled" xsi:type="boolean">false</item>
                    <item name="resizeDefaultWidth" xsi:type="string">55</item>
                    <item name="indexField" xsi:type="string">post_id</item>
                </item>
            </argument>
        </selectionsColumn>
        <column name="post_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="name">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">text</item>
                    <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="label" xsi:type="string" translate="true">Name</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>
    </columns>
</listing>


Step 5.4: Create a listing toolbar

As we saw on the top of this page, the Magento 2 Grid will support some actions to interact with grid like: sort, filter, action delete/update etc. The sort feature is a default action for the grid.

Prepare for this, we will create a listing toolbar element under the parent listing in the component layout file:

File: app/code/Magemeta/Grid/view/adminhtml/ui_component/magemeta_grid_post_listing.xml

<?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">
    <!-- ... other block of code -->
    <listingToolbar name="listing_top">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="sticky" xsi:type="boolean">true</item>
            </item>
        </argument>
    </listingToolbar>
    <!-- other blocks of code -->
</listing>

Step 5.5: Create a Bookmark

This argument is used to define the template Magento/Ui/view/base/web/templates/grid/toolbar.html which will be loaded to define the knockout.js for handling all ajax update action in this grid. You can place this container element before or after the columns element to define the position of the toolbar (above or below the columns). Let’s see the detail for each action: Bookmark

File: app/code/Magemeta/Grid/view/adminhtml/ui_component/magemeta_grid_post_listing.xml

<?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">
    <!-- ... other block of code -->
    <listingToolbar name="listing_top">
	<!-- ... other block of code -->    
	<bookmark name="bookmarks"/>
    </listingToolbar>
    <!-- ... other block of code -->
</listing>

This will add the bookmark feature which allows admin setup difference state of the grid. Each state may have a difference columns list. So with each admin user, they can choose to show the information which are relevant to them.

Step 5.6: Column controls

This node will add a columns list box which allow the admin user can choose which columns can be shown up on grid. After changing list, admin can save that state as a bookmark which allow to access this state quickly.

File: app/code/Magemeta/Grid/view/adminhtml/ui_component/magemeta_grid_post_listing.xml

<?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">
    <!-- ... other block of code -->
    <listingToolbar name="listing_top">
	<!-- ... other block of code -->    
	<columnsControls name="columns_controls"/>
    </listingToolbar>
    <!-- ... other block of code -->
</listing>

Full text search node will add search box at the top of Grid. We can use this to search all the data in the table.

File: app/code/Magemeta/Grid/view/adminhtml/ui_component/magemeta_grid_post_listing.xml

<?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">
    <!-- ... other block of code -->
    <listingToolbar name="listing_top">
	<!-- ... other block of code -->    
	<filterSearch name="fulltext"/>
    </listingToolbar>
    <!-- ... other block of code -->
</listing>

Step 5.8: Filter

Filter node define the filter box for each column. we can see this by click to the Filter button at the top of the grid.

File: app/code/Magemeta/Grid/view/adminhtml/ui_component/magemeta_grid_post_listing.xml

<?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">
    <!-- ... other block of code -->
    <listingToolbar name="listing_top">
	<!-- ... other block of code -->    
	<filters name="listing_filters" />
    </listingToolbar>
    <!-- ... other block of code -->
</listing>

Step 5.9: Mass actions

This node will add the mass action select to the grid. The Admin can use this action to take some action quickly on multiple items.

File: app/code/Magemeta/Grid/view/adminhtml/ui_component/magemeta_grid_post_listing.xml

<?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">
    <!-- ... other block of code -->
    <listingToolbar name="listing_top">
	<!-- ... other block of code -->    
        <massaction name="listing_massaction">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/tree-massactions</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="magemeta_grid/post/massDelete"/>
                        <item name="confirm" xsi:type="array">
                            <item name="title" xsi:type="string" translate="true">Delete Post</item>
                            <item name="message" xsi:type="string" translate="true">Are you sure you want to delete selected items?</item>
                        </item>
                    </item>
                </argument>
            </action>
        </massaction>
    </listingToolbar>
    <!-- ... other block of code -->
</listing>

Step 5.10: Paging

This node will add the pagination for the grid. This is useful if you have a large of data in the table.its show the page no.

File: app/code/Magemeta/Grid/view/adminhtml/ui_component/magemeta_grid_post_listing.xml

<?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">
    <!-- ... other block of code -->
    <listingToolbar name="listing_top">
	<!-- ... other block of code -->    
	<paging name="listing_paging"/>
    </listingToolbar>
    <!-- ... other block of code -->
</listing>

Step 5.11: Export button

This node will add an export button which you can export the data of the grid.

<?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">
    <!-- ... other block of code -->
    <listingToolbar name="listing_top">
	<!-- ... other block of code -->    
	<exportButton name="export_button"/>
    </listingToolbar>
    <!-- ... other block of code -->
</listing>

Leave a Reply