Our Work

Magento 2 Admin Grid

Updated today

Magento 2 creation of admin grid is quite different from Magento 1. In this article we will see how we can create Magento 2 admin grid. Admin Grids. As you are aware Magento 2 Grids are kind of table which lists the items of your database table and provide you some common features like sort, filter, delete, update item, etc.
The below diagram show roughly the activities involved in creating admin grid. Further in the Article these all are described in detail.

Activities in creation of admin grid

For the purpose of this article we will create simple Ticketing system admin grid which shows the support tickets created by customers (The next article covers where logged in customer can raise a ticket, and also see his previous created tickets in List)

The full source code can be downloaded from this Github link

https://github.com/KtreeOpenSource/Magento2Examples/tree/master/TicketingSystem

The full source code can be downloaded from this Github link

To create backend Admin grid we need to do the following activities

  1. Create Module
  2. Define Admin routes
  3. Create entries in menu.xml to Display your custom menus in Admin Panel
  4. Create Controller
  5. Define Database schema through Installer
  6. Create Model
  7. Use admin Grid Components to Create Grid UI.

1.Module

1.1 Create Module

Please refer this article for Module Creation.
https://ktree.com/create-magento-2-module-custom.html

1.2 Folder structure
app
    code
Ktree(Vendor)
    TicketingSystem(Custom Module Name)
        Block
    Adminhtml/Module/Grid/Renderer/Action/UrlBuilder.php
    Adminhtml/Tickets/Edit/Form.php
    Adminhtml/Tickets/Add.php
       Controller
    Adminhtml/Tickets/Add.php
            Adminhtml/Tickets/Index.php
            Adminhtml/Tickets/Save.php
       etc
             adminhtml/menu.xml
             adminhtml/routes.xml
    di.xml
    module.xml
       Model
     ResourceModel/Tickets/Collection.php
    ResourceModel/Tickets.php
    Source/Category.php
Source/Priority.php
Source/Status.php
Tickets.php
       Setup
    InstallSchema.php
       Ui
    Component/Listing/Column/Actions.php
    Component/Listing/Column/Update.php
       view
adminhtml/layout/ticketingsystem_tickets_add.xml
adminhtml/layout/ticketingsystem_tickets_index.xml
adminhtml/ui_component/ticketingsystem_tickets_listing.xml
       registration.php

2.Admin Routes

Through Admin Routes we can define, how our modules can be accessed. This can be done in two different ways, one as \admin another more specific name based on your module. Here we will go with the second approach. To do this we will 

  • Create routes.xml file inside etc/adminhtml Folder
  • Admin URL has the following  structure 

Since we want to  have specific url,  we will use specific route. We will create frontName as “ticketingsystem” for our module

File Location: app/code/Ktree/TicketingSystem/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="ticketingsystem" frontName="ticketingsystem">
            <module name="Ktree_TicketingSystem" />
        </route>
    </router>
</config>

In node attributes  id - type of the router and admin is for backend
node attributes  id - unique identifier 
node attributes name - _
FrontName -  our module backend route name and its defines the first segment of admin url(it should unique).

3.Admin menu

Next step would be to add to Magento 2 Backend Menu. To add admin menu we need to define this in menu.xml file inside etc/admin.html

Below we are creating a “Ticketing System” parent menu and under it we are adding menu item “Manage Tickets
File Location: app/code/Ktree/TicketingSystem/etc/adminhtml/menu.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
    <menu>
        <add id="Ktree_TicketingSystem::ticketingsystem" title="Ticketing System" module="Ktree_TicketingSystem" sortOrder="51" resource="Ktree_TicketingSystem::ticketingsystem" />
        <add id="Ktree_TicketingSystem::manage" title="Manage Tickets" module="Ktree_TicketingSystem" sortOrder="10" action="ticketingsystem/tickets/index" resource="Ktree_TicketingSystem::manage" parent="Ktree_TicketingSystem::ticketingsystem" />
    </menu>
</config>

    The node 
id - Unique identifier and format should be {Vendor_ModuleName}::
{menu_description}
title - Admin menu bar text
module - which this menu is belong to
sortOrder - position of the menu
action - url and format should be {router_name}{controller_folder}{action_name}
resource - Using this we can define ACL rules.
parent - Id of parent menu

4.Create Controller
In this step we will create Controller and Action for “Manage Tickets” which we have defined in menu.xml, Step 3 (action="ticketingsystem/tickets/index"). Magento 2 Controller contains one or more files in Controller folder of module, it includes class of action type which contain execute() method. If we put in another way every URL in Magento 2 corresponds to a single controller file, and each controller file has a single execute method.

For our example we created Tickets controller and Index action inside Controller/Admin.html folder of module.

File Location:
app/code/Ktree/TicketingSystem/Controller/Adminhtml/Tickets/Index.php

<?php
namespace Ktree\TicketingSystem\Controller\Adminhtml\Tickets;
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->setActiveMenu('Ktree_TicketingSystem::tickets_manage');
        $resultPage->getConfig()->getTitle()->prepend((__('Tickets')));
        return $resultPage;
    }
}

Our controller is rendering the page in execute method, this is how it happens. 

We injected a “page factory” object via automatic constructor dependency injection in the __construct method.

\Magento\Backend\App\Action\Context $context,

\Magento\Framework\View\Result\PageFactory $resultPageFactory

We Use that page factory object to create a page and Return the created page.

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

return $resultPage;

Since our module is running in the backend due to the automatic DI our resultPage returns result of type MagentoBackendModelViewResultPage

5.Database Schema

This step is similar to Magento 1, where we define Database schema or table structure.  We create a file  InstallSchema.php inside Setup folder of module.

  • This file executes only one time when install the module and first install method will be called.
  • Below we are creating 'ktree_ticketing_system' table with require field

File Location: app/code/Ktree/TicketingSystem/Setup/InstallSchema.php

<?php
namespace Ktree\TicketingSystem\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('ktree_ticketing_system')) {
            $table = $installer->getConnection()->newTable($installer->getTable('ktree_ticketing_system'))->addColumn('id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, null, ['identity' => true, 'nullable' => false, 'primary' => true, 'unsigned' => true, ], 'Id')->addColumn('ticket_id', \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 255, [], 'Ticket Id')
            // Other Columns Download from Github link
            ->setComment('Ktree Ticketing System Table');
            $installer->getConnection()->createTable($table);
        }
        $installer->endSetup();
    }
}

After completing above step:
Run command “php bin/magento setup:upgrade” to create table. Validate if table is created or not in the DB

Note: If table is not created remove module entry from “setup_module” table and re-run “php bin/magento setup:upgrade”. 

6.Model

Now our Database table is created, our next step is to create model and other requisites for  CRUD

6.1. Create base Model

Here we create our base model, in other words our ‘Tickets’ object is instantiated using this class.

  • We create model file (Model/Tickets.php) inside Model Folder
  • We can also define any other methods here in this class through which we can interact with our object.
  • Whenever a object is instantiated the constructor is _construct() is  called which call the _init method with Models  resource model name. The resource model is created in the next sub step.

File Location: app/code/Ktree/TicketingSystem/Model/Tickets.php

<?php
namespace Ktree\TicketingSystem\Model;
class Tickets extends \Magento\Framework\Model\AbstractModel implements \Magento\Framework\DataObject\IdentityInterface {
    const CACHE_TAG = 'ktree_ticketingsystem_ticket';
    protected $_cacheTag = 'ktree_ticketingsystem_ticket';
    protected $_eventPrefix = 'ktree_ticketingsystem_ticket';
    protected function _construct() {
        $this->_init('Ktree\TicketingSystem\Model\ResourceModel\Tickets');
    }
    public function getIdentities() {
        return [self::CACHE_TAG . '_' . $this->getId() ];
    }
    public function getDefaultValues() {
        $values = [];
        return $values;
    }
}

$_eventPrefix - a prefix for events to be triggered
$_eventObject - a object name when access in event
$_cacheTag - a unique identifier for usage in caching

6.2 Create Resource Model

Next step is a resource model class, which actually  contains the methods that will fetch the information from the database. Each CRUD model in Magento 2 has a corresponding resource model class.

  • We create Resource Model file (Model/ResourceModel/Tickets.php) inside Model/ResourceModel Folder
  • In the _init method we define the  name of the database table which created in Step 5 ('ktree_ticketing_system') , and the primary key for this table ('id').

File Location:app/code/Ktree/TicketingSystem/Model/ResourceModel/Tickets.php

<?php
namespace Ktree\TicketingSystem\Model\ResourceModel;
class Tickets extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb {
    protected function _construct() {
        $this->_init('ktree_ticketing_system', 'id');
    }
    
6.3 Create Collection

Collections are also type of ResourceModel, but instead of single object they get multiple objects. A collection collects individual Models.

  • To create Resource Model Collection we need to create Collection.php file in Model/ResourceModel/ResourceModelClassName Folder
  • A collection resource model’s _init method accepts two arguments, the First one is ('Ktree\TicketingSystem\Model\Tickets')  -  model that this collection collects and the second argument ('Ktree\TicketingSystem\Model\ResourceModel\Tickets') -  collected model’s resource model.

File Location:
app/code/Ktree/TicketingSystem/Model/ResourceModel/Tickets/Collection.php

<?php
namespace Ktree\TicketingSystem\Model\ResourceModel\Tickets;
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection {
        protected $_idFieldName = 'id';
        protected $_eventPrefix = 'ktree_ticketingsystem_ticket_collection';
        protected $_eventObject = 'ticket_collection';
        
    /**
          * Define resource model
          * @return void
         
     */
        protectedfunction _construct()      {
                 $this->_init('Ktree\TicketingSystem\Model\Tickets', 'Ktree\TicketingSystem\Model\ResourceModel\Tickets');
            
    }
}
6.4 Factory Object

Factory design pattern is where instead of creating the object using ‘New’ Keyword, we create it using our ‘Own Class’, the main reason is being to avoid hard dependency and also to localize the logic for complex objects. In Magento 2 for each CRUD model there is corresponding factory class, with ‘Factory’ appended to CRUD Model name. 

  • In Magento 2 for each CRUD model there is corresponding factory class, with ‘Factory’ appended to CRUD Model name. 
  • It will automatically generate the Factory class in the var/generation folder if the class does not already exist (File Location: var/generation///Model/ClassFactory.php)
  • For our module file will be
    /var/generation/Ktree/TicketingSystem/Model/TicketsFactory.php

7.Create Components (Admin Grids)

A new concept in the Magento 2 is UI Components, which helps in reusability of components, the basic idea behind is to allow us build more complex UI components reusing smaller components

7.1 Configure Data Source UI Component for data provider in dependency injection(di.xml) file 

In this step we will define the data provider which is the Ticket collection class, table and resourceModel for the table. This source is used in our next step Layout file to get data for grid

  • We create di.xml in etc/folder.
  • In the below code we have defined
    • Database table “ktree_ticketing_system”
    • Collection "Ktree\TicketingSystem\Model\ResourceModel\Tickets\Collection"
    • Resource Model Ktree\TicketingSystem\Model\ResourceModel\Tickets

File Location : app/code/Ktree/TicketingSystem/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="ktree_ticketingsystem_tickets_listing_data_source"
xsi:type="string">Ktree\TicketingSystem\Model\ResourceModel
\Tickets\Collection
</item>
</argument>
</arguments>
</type>
<virtualType name="Ktree\TicketingSystem\Model\ResourceModel\Tickets\Collection"
type="Magento\Framework\View\Element\UiComponent
\DataProvider\SearchResult">
<arguments>
<argument name="mainTable" xsi:type="string">ktree_ticketing_system</argument>
<argument name="resourceModel"
xsi:type="string">Ktree\TicketingSystem\Model\ResourceModel
\Tickets</argument>
</arguments>
</virtualType>
</config>
7.2 Create action layout file for admin grid

Here we create layout file for our admin grid, we define we use the Listing UI Component  "ticketingsystem_tickets_listing" which we are going to create in next step 7.3

  • Layout xml file is created inside view/adminhtml/layout folder.
  • File name should be formatted as __.xml 

File Location :
app/code/Ktree/TicketingSystem/view/adminhtml/layout/ticketingsystem_tickets_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="ticketingsystem_tickets_listing" />
        </referenceContainer>
    </body>
</page>
7.3 Configure Listing(Grid) UI Component

Listing is a basic UI component responsible for rendering grids, lists and tiles, providing filtering, pagination, sorting. In this step we will configure it.

  • We create UI component  in view/adminhtml/ui_component/ folder, File name should same as uiComponent name.
  • To enable the ToolBar the node , For Filters For Full text search  filterSearch name="fulltext"/> For Save column templates node
<listingToolbar name="listing_top">
   <filters name="listing_filters" />
   <columnsControls name="columns_controls"/>
   <filterSearch name="fulltext"/>
   <paging name="listing_paging"/>
   <exportButton name="export_button"/>
   <argument name="data" xsi:type="array">
      <item name="config" xsi:type="array">
         <item name="sticky" xsi:type="boolean">true</item>
      </item>
   </argument>
</listingToolbar>

File Location :
app/code/Ktree/TicketingSystem/view/adminhtml/ui_component/ticketingsystem_tickets_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">ticketingsystem_tickets_listing.ktree_ticketingsystem_tickets_listing_data_source</item>
            <item name="deps" xsi:type="string">ticketingsystem_tickets_listing.ktree_ticketingsystem_tickets_listing_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">spinner_columns</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">ktree_ticketingsystem_tickets_listing_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">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">id</item>
                    </item>
                </item>
            </argument>
        </argument>
    </dataSource>
    <listingToolbar name="listing_top">
        <filters name="listing_filters" />
        <columnsControls name="columns_controls" />
        <filterSearch name="fulltext" />
        <paging name="listing_paging" />
        <exportButton name="export_button" />
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="sticky" xsi:type="boolean">true</item>
            </item>
        </argument>
    </listingToolbar>
    <columns name="spinner_columns">
        <column name="ticket_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">Ticket Id</item>
                </item>
            </argument>
        </column>
        <column name="custmer_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">Custmer Id</item>
                </item>
            </argument>
        </column>
        <column name="category">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">Ktree\TicketingSystem\Model\Source\Category</item>
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">select</item>
                    <item name="dataType" xsi:type="string">multiselect</item>
                    <item name="sorting" xsi:type="string">asc</item>
                    <item name="label" xsi:type="string" translate="true">Category</item>
                </item>
            </argument>
        </column>
// More columns download source code from github
</columns>
</listing>

Clear cache by running  “php bin/magento cache:clean”  to check result.

You should see empty grid, since we did not added any records

For testing purposes, add records manually from database and see if those are appearing in the grid or not.

7.4 Add action column to Grid and Edit form

To enable edit and delete of the records in the admin grid we create actions.

  • To add action column to grid we need add below code to ui component xml file “app/code/Ktree/TicketingSystem/view/adminhtml/ui_component/ticketingsystem_tickets_listing.xml”  inside columns node.
  • This will take table primary key as input and outputs the URL(URL path will be specified in actionsColumn node class file). 
<actionsColumn name="actions" class="Ktree\TicketingSystem\Ui\Component\Listing\Column\Actions">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="indexField" xsi:type="string">id</item>
        </item>
    </argument>
</actionsColumn>
  • Create a action class which we specified in actionsColumn the is Ktree\TicketingSystem\Ui\Component\Listing\Column\Actions
  • In Actions Class We can declare action columns like edit,delete and any custom action.
  • Below we are declaring only edit column.

File Location :
Ktree\TicketingSystem\Ui\Component\Listing\Column\Actions.php

<?php
namespace Ktree\TicketingSystem\Ui\Component\Listing\Column;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;
use Ktree\TicketingSystem\Block\Adminhtml\Module\Grid\Renderer\Action\UrlBuilder;
use Magento\Framework\UrlInterface;
class Actions extends Column {
    /** Url path */
    const URL_PATH_EDIT = 'ticketingsystem/tickets/add';
    /** @var UrlBuilder */
    protected $actionUrlBuilder;
    /** @var UrlInterface */
    protected $urlBuilder;
    /**
     * @param ContextInterface $context
     * @param UiComponentFactory $uiComponentFactory
     * @param UrlBuilder $actionUrlBuilder
     * @param UrlInterface $urlBuilder
     * @param array $components
     * @param array $data
     */
    public function __construct(ContextInterface $context, UiComponentFactory $uiComponentFactory, UrlBuilder $actionUrlBuilder, UrlInterface $urlBuilder, array $components = [], array $data = []) {
        $this->urlBuilder = $urlBuilder;
        $this->actionUrlBuilder = $actionUrlBuilder;
        parent::__construct($context, $uiComponentFactory, $components, $data);
    }
    /**
     * Prepare Data Source
     *
     * @param array $dataSource
     * @return array
     */
    public function prepareDataSource(array $dataSource) {
        if (isset($dataSource['data']['items'])) {
            foreach ($dataSource['data']['items'] as & $item) {
                $name = $this->getData('name');
                if (isset($item['id'])) {
                    $item[$name]['edit'] = ['href' => $this->urlBuilder->getUrl(self::URL_PATH_EDIT, ['id' => $item['id']]), 'label' => __('Edit') ];
                }
            }
        }
        return $dataSource;
    }
}
  • Here we Create Url builder class to build specified action/URL.

File Location :
app/code/Ktree/TicketingSystem/Block/Adminhtml/Module/Grid/Renderer/Action/UrlBuilder.php

<?php
namespace Ktree\TicketingSystem\Block\Adminhtml\Module\Grid\Renderer\Action;
class UrlBuilder {
    /**
     * @var \Magento\Framework\UrlInterface
     */
    protected $frontendUrlBuilder;
    /**
     * @param \Magento\Framework\UrlInterface $frontendUrlBuilder
     */
    public function __construct(\Magento\Framework\UrlInterface $frontendUrlBuilder) {
        $this->frontendUrlBuilder = $frontendUrlBuilder;
    }
    /**
     * Get action url
     *
     * @param string $routePath
     * @param string $scope
     * @param string $store
     * @return string
     */
    public function getUrl($routePath, $scope, $store) {
        $this->frontendUrlBuilder->setScope($scope);
        $href = $this->frontendUrlBuilder->getUrl($routePath, ['_current' => false, '_query' => '___store=' . $store]);
        return $href;
    }
}

If you refresh admin grid we will able to grid with edit option.

Now we need to add action to edit column.

 As we mentioned URL_PATH_EDIT in Action.php to active link we need to create Controller, layout, block files.

  • Create admin controller file to edit records
  • Here we will load the respective records based on primary key
  • This data will be sent to edit form. 

File Location:
app/code/Ktree/TicketingSystem/Controller/Adminhtml/Tickets/Add.php

<?php
namespace Ktree\TicketingSystem\Controller\Adminhtml\Tickets;
use Magento\Framework\Controller\ResultFactory;
class Add extends \Magento\Backend\App\Action {
        
    /**
          * @var \Magento\Framework\Registry
         
     */
        private $coreRegistry;
        protected $_ticketsFactory;
        
    /**
          * @param \Magento\Backend\App\Action\Context $context
          * @param \Magento\Framework\Registry $coreRegistry,
         
     */
        publicfunction __construct(\Magento\Backend\App\Action\Context $context, \Magento\Framework\Registry $coreRegistry, \Ktree\TicketingSystem\Model\TicketsFactory $ticketsFactory)      {
                parent::__construct($context);
                 $this->coreRegistry     = $coreRegistry;
                 $this->_ticketsFactory = $ticketsFactory;
            
    }
        
    /**
          * Mapped Grid List page.
          * @return \Magento\Backend\Model\View\Result\Page
         
     */
        publicfunction execute()      {
                 $rowId = (int)$this->getRequest()->getParam('id');
                
        /** @var \Magento\Backend\Model\View\Result\Page $resultPage */
                if($rowId) {
                         $rowData = $this->_ticketsFactory->create()->load($rowId);
                        if(!$rowData->getId()) {
                                 $this->messageManager->addError(__('row data no longer exist.'));
                                 $this->_redirect('ticketingsystem/tickets');
                                return;
                            
            }
                    
        }
                 $this->coreRegistry->register('row_data', $rowData);
                 $resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE);
                 $title       = $rowId ? __('Edit Row Data ') : __('Add Row Data');
                 $resultPage->getConfig()->getTitle()->prepend($title);
                return $resultPage;
            
    }
        protectedfunction _isAllowed()      {
                return $this->_authorization->isAllowed('Ktree_TicketingSystem::add');
            
    }
}

Create admin layout file

  • To create action layout we need to create layout xml file insdie view/adminhtml/layout folder.
  • File name should be formatted as __.xml 
  • Here we will declare the block name.

 File Location:
app/code/Ktree/TicketingSystem/view/adminhtml/layout/ticketingsystem_tickets_add.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">
            <block class="Ktree\TicketingSystem\Block\Adminhtml\Tickets\Add" name="add" />
        </referenceContainer>
    </body>
</page>

In node   class - path of the respective block class
Name - block name

Create admin block file

  • To create block file we need to create block files inside Block/Adminhtml folder(We have created a Add.php block)
  • Here we declare edit form name, action URl and saving URL.

File Location:
app/code/Ktree/TicketingSystem/Block/Adminhtml/Tickets/Add.php

ERROR -----------------------------------------------------------------
The document cannot be processed.
If you think this request should have succeeded please send your feedback.
Email:[email protected]
XmlException ----------------------------------------------------------
Unexpected end of file while parsing PI has occurred. Line 73, position 2.
XML Document ----------------------------------------------------------
<?php
namespace Ktree\TicketingSystem\Block\Adminhtml\Tickets;
class Add extends \Magento\Backend\Block\Widget\Form\Container {
    /**
     * Core registry.
     *
     * @var \Magento\Framework\Registry
     */
    protected $_coreRegistry = null;
    /**
     * @param \Magento\Backend\Block\Widget\Context $context
     * @param \Magento\Framework\Registry $registry
     * @param array $data
     */
    public function __construct(\Magento\Backend\Block\Widget\Context $context, \Magento\Framework\Registry $registry, array $data = []) {
        $this->_coreRegistry = $registry;
        parent::__construct($context, $data);
    }
    /**
     * Initialize Imagegallery Images Edit Block.
     */
    protected function _construct() {
        $this->_objectId = 'row_id';
        $this->_blockGroup = 'Ktree_TicketingSystem';
        $this->_controller = 'adminhtml_tickets';
        parent::_construct();
        if ($this->_isAllowedAction('Ktree_TicketingSystem::add')) {
            $this->buttonList->update('save', 'label', __('Save'));
        } else {
            $this->buttonList->remove('save');
        }
        $this->buttonList->remove('reset');
    }
    /**
     * Retrieve text for header element depending on loaded image.
     *
     * @return \Magento\Framework\Phrase
     */
    public function getHeaderText() {
        return __('Add Tickets');
    }
    /**
     * Check permission for passed action.
     *
     * @param string $resourceId
     *
     * @return bool
     */
    protected function _isAllowedAction($resourceId) {
        return $this->_authorization->isAllowed($resourceId);
    }
    /**
     * Get form action URL.
     *
     * @return string
     */
    public function getFormActionUrl() {
        if ($this->hasFormActionUrl()) {
            return $this->getData('form_action_url');
        }
        return $this->getUrl('*/*/save');
    }
}

Create admin edit block form file

  • To create block file we need to create form file inside Block/Adminhtml/Tickets/Edit folder
  • Here we declare edit form fields(which we want to show in from)

File Location:
app/code/Ktree/TicketingSystem/Block/Adminhtml/Tickets/Edit/Form.php

<?php
namespace Ktree\TicketingSystem\Block\Adminhtml\Tickets\Edit;
/**
* Adminhtml Add New Row Form.
*/
class Form extends \Magento\Backend\Block\Widget\Form\Generic {
    /**
     * @var \Magento\Store\Model\System\Store
     */
    protected $_systemStore;
    protected $_status;
    protected $_priority;
    protected $_category;
    /**
     * @param \Magento\Backend\Block\Template\Context $context
     * @param \Magento\Framework\Registry $registry
     * @param \Magento\Framework\Data\FormFactory $formFactory
     * @param array $data
     */
    public function __construct(\Magento\Backend\Block\Template\Context $context, \Magento\Framework\Registry $registry, \Magento\Framework\Data\FormFactory $formFactory, \Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig, \Ktree\TicketingSystem\Model\Source\Status $status, \Ktree\TicketingSystem\Model\Source\Priority $priority, \Ktree\TicketingSystem\Model\Source\Category $category, array $data = []) {
        $this->_status = $status;
        $this->_priority = $priority;
        $this->_category = $category;
        $this->_wysiwygConfig = $wysiwygConfig;
        parent::__construct($context, $registry, $formFactory, $data);
    }
    /**
     * Prepare form.
     *
     * @return $this
     */
    protected function _prepareForm() {
        $dateFormat = $this->_localeDate->getDateFormat(\IntlDateFormatter::SHORT);
        $model = $this->_coreRegistry->registry('row_data');
        $form = $this->_formFactory->create(['data' => ['id' => 'edit_form', 'enctype' => 'multipart/form-data', 'action' => $this->getData('action'), 'method' => 'post']]);
        $form->setHtmlIdPrefix('tickets_');
        if ($model->getId()) {
            $fieldset = $form->addFieldset('base_fieldset', ['legend' => __('Edit Row Data'), 'class' => 'fieldset-wide']);
            $fieldset->addField('id', 'hidden', ['name' => 'id']);
        } else {
            $fieldset = $form->addFieldset('base_fieldset', ['legend' => __('Add Row Data'), 'class' => 'fieldset-wide']);
        }
        $fieldset->addField('ticket_id', 'text', ['name' => 'ticket_id', 'label' => __('Ticket Id'), 'id' => 'ticket_id', 'title' => __('Ticket Id'), 'class' => 'required-entry', 'required' => true, 'disabled' => true, ]);
        $fieldset->addField('custmer_id', 'text', ['name' => 'custmer_id', 'label' => __('Custmer Id'), 'id' => 'custmer_id', 'title' => __('Custmer Id'), 'class' => 'required-entry', 'required' => true, 'disabled' => true, ]);
        // Other Columns get code from github
        );
        $form->setValues($model->getData());
        $form->setUseContainer(true);
        $this->setForm($form);
        return parent::_prepareForm();
    }
}

After creating above files and if you click on edit action you will able to see the form as below

In the form we have disabled all field except Status field.  To save Form changes we need to create action.

Create save action,

File Location:
app/code/Ktree/TicketingSystem/Controller/Adminhtml/Tickets/Save.php

<?php
namespace Ktree\TicketingSystem\Controller\Adminhtml\Tickets;
class Save extends \Magento\Backend\App\Action
{
    protected $_ticketsFactory;
    public function __construct(\Magento\Backend\App\Action\Context $context, \Ktree\TicketingSystem\Model\TicketsFactory $ticketsFactory)
    {
        $this->_ticketsFactory = $ticketsFactory;
        parent::__construct($context);
    }
    /**
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     * @SuppressWarnings(PHPMD.NPathComplexity)
     */
    public function execute()
    {
        $data = $this->getRequest()->getPostValue();
        if (!$data) {
            $this->_redirect('ticketingsystem/tickets/add');
            return;
        }
        try {
            $rowData = $this->_ticketsFactory->create()->load($data['id']);
            if (!$rowData->getId()) {
                $this->messageManager->addError(__('row data no longer exist.'));
                $this->_redirect('ticketingsystem/tickets');
                return;
            }
            $rowData->setData($data);
            $rowData->save();
            $this->messageManager->addSuccess(__('Row data has been successfully saved.'));
        }
        catch (\Exception $e) {
            $this->messageManager->addError(__($e->getMessage()));
        }
        $this->_redirect('ticketingsystem/tickets');
    }
    /**
     * @return bool
     */
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('Ktree_TicketingSystem::save');
    }
}

After completing above step and click on Save button, changes will be stored to and success message will be displayed.

Note: We updated status from New -> Open

8. Known issues while development

  1. when we are creating table through installer sometime table won’t be created.

    • Delete module entry from table setup_module
    • Running command from magento root directory “php bin/magento setup:upgrade”
  2. When we update any changes our custom module admin/front css and js file will not load

    • rm -rf var/*

    • rm -rf pub/static/*
    • php bin/magento setup:static-content:deploy
    • chmod -R 777 pub/static
    • chmod -R 777 var
  3. When we are trying to create admin grid it will display only heading.

    • Check admin layout file name
    • Check UI Component node 
    • Check admin route name
  4. Sometimes grid keeps on loading 

    • Mainly Check UI Component node 
    • Check admin route name properly give or not to layout files
    • Check file names are given properly or not.
  5. We may get Magento 2 Class *Factory does not exist  exception when we trying to create object for our customly created factory object ex: $this->_ticketsFactory->create()

    • Remove var/generation
    • Then run php bin/magento setup:di:compile  from magento root directory and give permission to var/generation directory.

Looking for Magento Developer?

Please Contact us if you have any Magento Implementation requirements. Hire dedicated Magento developers or Magento Development services from KTree. KTree is Best offshore Magento E-commerce development company with extensive experience in E-commerce development and Magento Plugins.

Request For Quote