Reading list Switch to dark mode

    Magento 2 Development 17: More about Admin Grid

    Updated 7 March 2024

    In the previous blog, we created a very basic admin grid. Here we will modify it a little bit.

    Class of a Column

    In the previous grid, we have not shown the content of the blog. That is because the blog content will be large and it will not be practical to show the whole content. But we can show 20 characters just like we did on front-end.

    Let’s see how we can do that. We are adding a new column in the grid, so we need to change the content of the uiComponent file view/adminhtml/ui_component/blogmanager_blog_listing.xml

    Updated code for view/adminhtml/ui_component/blogmanager_blog_listing.xml file

    <?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">blogmanager_blog_listing.blogmanager_blog_listing_data_source</item>
                <item name="deps" xsi:type="string">blogmanager_blog_listing.blogmanager_blog_listing_data_source</item>
            </item>
            <item name="spinner" xsi:type="string">blogmanager_blog_columns</item>
        </argument>
        <dataSource name="blogmanager_blog_listing_data_source">
            <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">blogmanager_blog_listing_data_source</argument>
                <argument name="primaryFieldName" xsi:type="string">entity_id</argument>
                <argument name="requestFieldName" xsi:type="string">entity_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 name="storageConfig" xsi:type="array">
                        <item name="indexField" xsi:type="string">entity_id</item>
                    </item>
                    </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>
        <listingToolbar name="listing_top">
            <bookmark name="bookmarks"/>
            <columnsControls name="columns_controls"/>
            <filters name="listing_filters">
                    <argument name="data" xsi:type="array">
                        <item name="config" xsi:type="array">
                            <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>
                    </argument>
            </filters>
            <paging name="listing_paging"/>
        </listingToolbar>
        <columns name="blogmanager_blog_columns">
            <column name="entity_id">
                <settings>
                    <filter>textRange</filter>
                    <label translate="true">ID</label>
                    <resizeDefaultWidth>25</resizeDefaultWidth>
                </settings>
            </column>
            <column name="user_id">
                <settings>
                    <filter>text</filter>
                    <label translate="true">User</label>
                </settings>
            </column>
            <column name="title">
                <settings>
                    <filter>text</filter>
                    <label translate="true">Title</label>
                </settings>
            </column>
            <column name="content" class="Webkul\BlogManager\Ui\Component\Listing\Columns\Content">
                <settings>
                    <filter>false</filter>
                    <sortable>false</sortable>
                    <label translate="true">Content</label>
                </settings>
            </column>
            <column name="status">
                <settings>
                    <filter>text</filter>
                    <sortable>false</sortable>
                    <label translate="true">Status</label>
                </settings>
            </column>
            <column name="updated_at" component="Magento_Ui/js/grid/columns/date">
                <settings>
                    <filter>dateRange</filter>
                    <dataType>date</dataType>
                    <label translate="true">Updated</label>
                </settings>
            </column>
            <column name="created_at" component="Magento_Ui/js/grid/columns/date">
                <settings>
                    <filter>dateRange</filter>
                    <dataType>date</dataType>
                    <label translate="true">Created</label>
                </settings>
            </column>
        </columns>
    </listing>

    Please use the online diff checker to easily see what’s different from the last version of this file. The only thing different here is that we have added a new column tag for content. In that tag, we have used class=”Webkul\BlogManager\Ui\Component\Listing\Columns\Content”. By adding the class we are telling Magento that we will provide the data for this column with this class. All the UI component-related php files are created under Ui/Component folder.

    Searching for an experienced
    Magento 2 Company ?
    Find out More

    Now let’s create the Content class as Ui/Component/Listing/Columns/Content.php

    Code for Ui/Component/Listing/Columns/Content.php file

    <?php
    namespace Webkul\BlogManager\Ui\Component\Listing\Columns;
    
    class Content extends \Magento\Ui\Component\Listing\Columns\Column
    {
        /**
         * Prepare Data Source
         *
         * @param array $dataSource
         * @return array
         */
        public function prepareDataSource(array $dataSource)
        {
            if (isset($dataSource['data']['items'])) {
                $fieldName = 'content';
                foreach ($dataSource['data']['items'] as &$item) {
                    $item[$fieldName] = substr($item[$fieldName], 0, 20).'...';
                }
            }
            return $dataSource;
        }
    }

    So what we are doing here is that we are looping through all the rows and updating the content column to show only 20 characters. For updating the data, we are using the prepareDataSource method.

    We use this technique when we need to perform some operations before showing the data or when the data is not coming from our table.

    Now if you browse, you will see the content column,

    2021-03-05_11-03


    Select Option type Column

    We will be implementing a feature in the next few blogs with which the admin will be able to manage the status of the blog. That’s why we have added the status column, which will be 0 or 1. In the status column, we are showing 0 or 1 instead it will be better if we show text labels such as Disabled or Enabled.

    So for that as you may have guessed we need to make changes in view/adminhtml/ui_component/blogmanager_blog_listing.xml

    Updated code for view/adminhtml/ui_component/blogmanager_blog_listing.xml file

    <?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">blogmanager_blog_listing.blogmanager_blog_listing_data_source</item>
                <item name="deps" xsi:type="string">blogmanager_blog_listing.blogmanager_blog_listing_data_source</item>
            </item>
            <item name="spinner" xsi:type="string">blogmanager_blog_columns</item>
        </argument>
        <dataSource name="blogmanager_blog_listing_data_source">
            <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">blogmanager_blog_listing_data_source</argument>
                <argument name="primaryFieldName" xsi:type="string">entity_id</argument>
                <argument name="requestFieldName" xsi:type="string">entity_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 name="storageConfig" xsi:type="array">
                        <item name="indexField" xsi:type="string">entity_id</item>
                    </item>
                    </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>
        <listingToolbar name="listing_top">
            <bookmark name="bookmarks"/>
            <columnsControls name="columns_controls"/>
            <filters name="listing_filters">
                    <argument name="data" xsi:type="array">
                        <item name="config" xsi:type="array">
                            <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>
                    </argument>
            </filters>
            <paging name="listing_paging"/>
        </listingToolbar>
        <columns name="blogmanager_blog_columns">
            <column name="entity_id">
                <settings>
                    <filter>textRange</filter>
                    <label translate="true">ID</label>
                    <resizeDefaultWidth>25</resizeDefaultWidth>
                </settings>
            </column>
            <column name="user_id">
                <settings>
                    <filter>text</filter>
                    <label translate="true">User</label>
                </settings>
            </column>
            <column name="title">
                <settings>
                    <filter>text</filter>
                    <label translate="true">Title</label>
                </settings>
            </column>
            <column name="content" class="Webkul\BlogManager\Ui\Component\Listing\Columns\Content">
                <settings>
                    <filter>false</filter>
                    <sortable>false</sortable>
                    <label translate="true">Content</label>
                </settings>
            </column>
            <column name="status" component="Magento_Ui/js/grid/columns/select">
                <settings>
                    <options class="Webkul\BlogManager\Model\Blog\Status"/>
                    <dataType>select</dataType>
                    <filter>select</filter>
                    <sortable>false</sortable>
                    <label translate="true">Status</label>
                </settings>
            </column>
            <column name="updated_at" component="Magento_Ui/js/grid/columns/date">
                <settings>
                    <filter>dateRange</filter>
                    <dataType>date</dataType>
                    <label translate="true">Updated</label>
                </settings>
            </column>
            <column name="created_at" component="Magento_Ui/js/grid/columns/date">
                <settings>
                    <filter>dateRange</filter>
                    <dataType>date</dataType>
                    <label translate="true">Created</label>
                </settings>
            </column>
        </columns>
    </listing>

    Only changes you will find here are in the column tag. We are using a select component that will replace the value with the label. We will provide key-value pair with the options class Webkul\BlogManager\Model\Blog\Status.

    In an earlier section, I mentioned that all the PHP files related to the ui component should be in Ui/Component folder but this class is not limited to ui component, we can use it front-end too. So I will create it in the model folder (it’s a general convention).

    In the filter tag, we have mentioned that it is a select type so in the filter section you will find a multi-select dropdown for this column.

    Now let’s see the option class Model/Blog/Status.php

    Code for Model/Blog/Status.php file

    <?php
    namespace Webkul\BlogManager\Model\Blog;
    
    use Magento\Framework\Data\OptionSourceInterface;
    
    class Status implements OptionSourceInterface
    {
        /**
         * Converts to option array
         *
         * @return array
         */
        public function toOptionArray()
        {
            $options = [];
            $options[] = ['label' => 'Disabled', 'value' => 0];
            $options[] = ['label' => 'Enabled', 'value' => 1];
            return $options;
        }
    }

    Here we are creating an associative array and returning it in the toOptionArray method.

    Whenever we have any such column which has options, like Yes/No or Bad/Neutral/Good, etc. we use this technique.

    Now if you check the Manage Blog page, you will see the changes,

    2021-03-05_12-16


    _renderFiltersBefore

    In the grid, we are showing the customer id in the User column which is not very helpful. Instead of the id, we should show the customer’s name. We can do this by creating a class for the column but then we will not be able to perform sorting and filtering on that column. This is because the column data get modified later. What I mean by this is that at first the original SQL query gets executed (in our virtual class) then we loop through the rows that we get as a result of the SQL query.

    So if we want sorting and filtering to work we need to modify the SQL, for that, we need to create the actual data provider class instead of using the virtual class.

    The customer data are stored in the “customer_grid_flat” table. We can join our table with the customer table to get the name.

    Now let’s see this in code. First, we will create the data provider class as Model/ResourceModel/Blog/Grid/Collection.php this is the general convention to create the data provider or grid collection of a table alongside the collection class of that table.

    Code for Model/ResourceModel/Blog/Grid/Collection.php file

    <?php
    namespace Webkul\BlogManager\Model\ResourceModel\Blog\Grid;
    
    use Magento\Framework\Api\Search\SearchResultInterface;
    use Magento\Framework\Search\AggregationInterface;
    use Webkul\BlogManager\Model\ResourceModel\Blog\Collection as BlogCollection;
    use Magento\Framework\Data\Collection\EntityFactoryInterface;
    use Psr\Log\LoggerInterface;
    use Magento\Framework\Data\Collection\Db\FetchStrategyInterface;
    use Magento\Framework\Event\ManagerInterface;
    use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
    
    class Collection extends BlogCollection implements SearchResultInterface
    {
        /**
         * Dependency Initilization
         *
         * @param EntityFactoryInterface $entityFactory
         * @param LoggerInterface $logger
         * @param FetchStrategyInterface $fetchStrategy
         * @param ManagerInterface $eventManager
         * @param [type] $mainTable
         * @param [type] $resourceModel
         * @param [type] $model
         * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection
         * @param AbstractDb|null $resource
         */
        public function __construct(
            EntityFactoryInterface $entityFactory,
            LoggerInterface $logger,
            FetchStrategyInterface $fetchStrategy,
            ManagerInterface $eventManager,
            $mainTable,
            $resourceModel,
            $model = \Magento\Framework\View\Element\UiComponent\DataProvider\Document::class,
            \Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
            AbstractDb $resource = null
        ) {
            parent::__construct(
                $entityFactory,
                $logger,
                $fetchStrategy,
                $eventManager,
                $connection,
                $resource
            );
            $this->_init($model, $resourceModel);
            $this->setMainTable($mainTable);
        }
    
        /**
         * Get Agrigations
         *
         * @return \Magento\Framework\Api\Search\AggregationInterface
         */
        public function getAggregations()
        {
            return $this->aggregations;
        }
    
        /**
         * Set Agrigations
         *
         * @param \Magento\Framework\Api\Search\AggregationInterface $aggregations $aggregations
         * @return \Magento\Framework\Api\Search\SearchResultInterface
         */
        public function setAggregations($aggregations)
        {
            $this->aggregations = $aggregations;
        }
    
        /**
         * Get Search Criteria
         *
         * @return null
         */
        public function getSearchCriteria()
        {
            return null;
        }
        
        /**
         * Set Search Criteria
         *
         * @param \Magento\Framework\Api\SearchCriteriaInterface|null $searchCriteria
         * @return this
         */
        public function setSearchCriteria(
            \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria = null
        ) {
            return $this;
        }
    
        /**
         * Returns the total count of the bolg
         *
         * @return int
         */
        public function getTotalCount()
        {
            return $this->getSize();
        }
    
        /**
         * Sets the total count of the bolg
         *
         * @param int $totalCount
         * @return this
         */
        public function setTotalCount($totalCount)
        {
            return $this;
        }
    
        /**
         * Set Item
         *
         * @param array|null $items
         * @return int
         */
        public function setItems(array $items = null)
        {
            return $this;
        }
    
        /**
         * Render Filter before
         *
         * @return void
         */
        protected function _renderFiltersBefore()
        {
            $cgfTable = $this->getTable('customer_grid_flat');
            $this->getSelect()->joinLeft(
                $cgfTable.' as cgf',
                'main_table.user_id = cgf.entity_id',
                [
                    'user_name'=>'cgf.name'
                ]
            );
            parent::_renderFiltersBefore();
        }
        
        /**
         * Initilize Select
         *
         * @return void
         */
        protected function _initSelect()
        {
            $this->addFilterToMap('user_name', 'cgf.name');
            parent::_initSelect();
        }
    }

    You don’t have to worry about all the methods. We have to understand only _renderFiltersBefore and _initSelect methods.

    $this->getTable(‘customer_grid_flat’) This will give the table name by adding the table prefix if any.

    We already know about the getSelect, $this->getSelect() will give access to the SQL query of this class. We are performing left join with the joinLeft method. Then we are joining the customer_grid_flat table on ‘main_table.user_id = cgf.entity_id’, where cgf is an alias for customer_grid_flat. And we are getting the customer name in the user_name column.

    The actual SQL query will look like this,

    SELECT
       `main_table`.*,
       `cgf`.`name` AS `user_name` 
    FROM
       `blogmanager_blog` AS `main_table` 
       LEFT JOIN
          `customer_grid_flat` AS `cgf` 
          ON main_table.user_id = cgf.entity_id

    We also need to map this column alias in the _initSelect method otherwise the filter will not work.


    Now we need to modify the etc/di.xml because the data provider is not a virtual class anymore,

    Updated code for etc/di.xml file

    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
        <type name="Webkul\BlogManager\Model\ResourceModel\Blog\Grid\Collection">
            <arguments>
                <argument name="mainTable" xsi:type="string">blogmanager_blog</argument>
                <argument name="resourceModel" xsi:type="string">Webkul\BlogManager\Model\ResourceModel\Blog</argument>
            </arguments>
        </type>
        <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
            <arguments>
                <argument name="collections" xsi:type="array">
                    <item name="blogmanager_blog_listing_data_source" xsi:type="string">Webkul\BlogManager\Model\ResourceModel\Blog\Grid\Collection</item>
                </argument>
            </arguments>
        </type>
    </config>

    Here we have changed the tag virtualType to type. And we are providing the arguments as before. If you check the grid class you will find these arguments in the __construct method.


    We also need to change the ui component file view/adminhtml/ui_component/blogmanager_blog_listing.xml because the new column name is user_name

    Updated code for view/adminhtml/ui_component/blogmanager_blog_listing.xml file

    <?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">blogmanager_blog_listing.blogmanager_blog_listing_data_source</item>
                <item name="deps" xsi:type="string">blogmanager_blog_listing.blogmanager_blog_listing_data_source</item>
            </item>
            <item name="spinner" xsi:type="string">blogmanager_blog_columns</item>
        </argument>
        <dataSource name="blogmanager_blog_listing_data_source">
            <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">blogmanager_blog_listing_data_source</argument>
                <argument name="primaryFieldName" xsi:type="string">entity_id</argument>
                <argument name="requestFieldName" xsi:type="string">entity_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 name="storageConfig" xsi:type="array">
                        <item name="indexField" xsi:type="string">entity_id</item>
                    </item>
                    </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>
        <listingToolbar name="listing_top">
            <bookmark name="bookmarks"/>
            <columnsControls name="columns_controls"/>
            <filters name="listing_filters">
                    <argument name="data" xsi:type="array">
                        <item name="config" xsi:type="array">
                            <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>
                    </argument>
            </filters>
            <paging name="listing_paging"/>
        </listingToolbar>
        <columns name="blogmanager_blog_columns">
            <column name="entity_id">
                <settings>
                    <filter>textRange</filter>
                    <label translate="true">ID</label>
                    <resizeDefaultWidth>25</resizeDefaultWidth>
                </settings>
            </column>
            <column name="user_name">
                <settings>
                    <filter>text</filter>
                    <label translate="true">User</label>
                </settings>
            </column>
            <column name="title">
                <settings>
                    <filter>text</filter>
                    <label translate="true">Title</label>
                </settings>
            </column>
            <column name="content" class="Webkul\BlogManager\Ui\Component\Listing\Columns\Content">
                <settings>
                    <filter>false</filter>
                    <sortable>false</sortable>
                    <label translate="true">Content</label>
                </settings>
            </column>
            <column name="status" component="Magento_Ui/js/grid/columns/select">
                <settings>
                    <options class="Webkul\BlogManager\Model\Blog\Status"/>
                    <dataType>select</dataType>
                    <filter>select</filter>
                    <sortable>false</sortable>
                    <label translate="true">Status</label>
                </settings>
            </column>
            <column name="updated_at" component="Magento_Ui/js/grid/columns/date">
                <settings>
                    <filter>dateRange</filter>
                    <dataType>date</dataType>
                    <label translate="true">Updated</label>
                </settings>
            </column>
            <column name="created_at" component="Magento_Ui/js/grid/columns/date">
                <settings>
                    <filter>dateRange</filter>
                    <dataType>date</dataType>
                    <label translate="true">Created</label>
                </settings>
            </column>
        </columns>
    </listing>

    Now you should see something like,

    2021-03-05_13-47

    Please check out these blogs to learn more about join in SQL,

    https://webkul.com/blog/magento2-mysql-join-explained/
    https://webkul.com/blog/how-to-apply-join-in-collection-in-uicomponent-grid-in-magento2/

    Folder Structure,

    2021-03-05_14-22


    Next Blog -> Magento 2 Development 18: MassAction

    Previous Blog -> Magento 2 Development 16: uiComponent and di.xml

    . . .

    Leave a Comment

    Your email address will not be published. Required fields are marked*


    Be the first to comment.

    Back to Top

    Message Sent!

    If you have more details or questions, you can reply to the received confirmation email.

    Back to Home