How to Create EAV Module in Magento 2: Step-by-Step Tutorial

In today’s post, we’re going to teach you guys how to create an EAV Module in Magento 2.

Before we directly get started with the tutorial, let’s quickly go over the basics first.

What is EAV Model in Magento 2?

EAV, for those who don’t know, stands for Entity Attribute Value, which a data model used in Magento 2.

Entity: it’s responsible for storing data.

Attribute: It’s a separate property of an entity like name, gender, email address, etc.

Value: It’s basically the value of an entity and attribute.

Together, the EAV structure helps to get flexibility for your data through flexible attributes that are added dynamically.

That being said, let’s look at the main advantage of the EAV model structure.

Advantages of Using EAV Model Structure

  • It’s quick to implement.
  • It greatly enhances flexibility to store, access, and retrieve data and attributes.
  • You can easily add attributes to any entity without having to modify your core database structure.

Based on these advantages, if you’re seriously considering to create and use an EAV module in your Magento 2 project, then you must know that the EAV also has some disadvantages.

Disadvantages of Using EAV Model Structure

  • It’s a quite complex structure compared to the flat table structure.
  • The EAV flexibility ultimately slows down the SQL Queries’ execution.

So, now that you know all good and bad things about EAV Model Structure, let’s quickly move forward and see how you can create an EAV module by yourself.

How to Create Custom EAV Module in Magento 2?

Let’s look at the step by step process to create an EAV module in Magento 2.

Step - 1

First of all, we need to create app/code/MD/HelloWorld/etc/module.xml file to declare module:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="MD_HelloWorld" setup_version="1.0.0" >
    </module>
</config>

Step - 2

The next step is to create app/code/MD/HelloWorld/registration.php file to register the module:

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'MD_HelloWorld',
    __DIR__
);

Step - 3

Now, we need to create app/code/MD/HelloWorld/etc/acl.xml file to setup admin acl file:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="MD_HelloWorld::helloworld" title="Hello World" sortOrder="10" />
            </resource>
        </resources>
    </acl>
</config>

Step - 4

After that, we also need to create app/code/MD/HelloWorld/etc/di.xml file to create and setup collection files for UI component grid and config of collection:

<?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="Magento\Eav\Model\Entity\AttributeCache">
        <arguments>
            <argument name="unsupportedTypes" xsi:type="array">
               <item name="md_helloworld" xsi:type="string">md_helloworld</item>
            </argument>
        </arguments>
    </type>
    <type name="MD\HelloWorld\UI\Component\Listing\DataProvider">
        <arguments>
            <argument name="collection" xsi:type="object" shared="false">MD\HelloWorld\Model\ResourceModel\HelloWorld\Collection</argument>
            <argument name="filterPool" xsi:type="object" shared="false">RefGridFilterPool</argument>
        </arguments>
    </type>
    <virtualType name="RefGridFilterPool" 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>
    <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
        <arguments>
            <argument name="collections" xsi:type="array">
                <item name="md_helloworld_helloworld_listing_data_source" xsi:type="string">MD\HelloWorld\Model\ResourceModel\HelloWorld\Grid\Collection</item>
            </argument>
        </arguments>
    </type>
    <type name="MD\HelloWorld\Model\ResourceModel\HelloWorld\Grid\Collection">
        <arguments>
            <argument name="eventPrefix" xsi:type="string">md_helloworld_helloworld_grid_collection</argument>
            <argument name="eventObject" xsi:type="string">md_helloworld_helloworld_grid_collection</argument>
            <argument name="resourceModel" xsi:type="string">MD\HelloWorld\Model\ResourceModel\HelloWorld</argument>
        </arguments>
    </type>
</config>

Step - 5

Now, we will create app/code/MD/HelloWorld/etc/adminhtml/routes.xml file to setup admin panel router:

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

Step - 6

The next step is to create app/code/MD/HelloWorld/etc/adminhtml/menu.xml file to set admin menu:

<?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="MD_HelloWorld::helloworld" title="Hello World" module="MD_HelloWorld" sortOrder="50" parent="Magento_Backend::content" action="md_helloworld/helloworld" resource="MD_HelloWorld::helloworld"/>
    </menu>
</config>

Step - 7

Once it’s done, it’s time to create app/code/MD/HelloWorld/Block/Adminhtml/HelloWorld/Edit/BackButton.php file to add back button in UI Form:

<?php
namespace MD\HelloWorld\Block\Adminhtml\HelloWorld\Edit;
use Magento\Backend\Block\Widget\Context;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class BackButton implements ButtonProviderInterface
{
     * [__construct desription]
     * @param Context $context [description]
     */
    public function __construct(Context $context)
    { 
 $this->urlBuilder = $context->getUrlBuilder();
    }
    public function getButtonData()
    {
   return 
[
       'label' => __('Back'),
            'on_click' => sprintf("location.href = '%s';", $this->getBackUrl()),
            'class' => 'back',
            'sort_order' => 10
        ];
    }
    public function getBackUrl()
    {
        return $this->urlBuilder->getUrl('*/*/');
    }

Step - 8

Now, we will create app/code/MD/HelloWorld/Block/Adminhtml/HelloWorld/Edit/DeleteButton.php file to add delete records button in the UI Form:

<?php
namespace MD\HelloWorld\Block\Adminhtml\HelloWorld\Edit;
use Magento\Backend\Block\Widget\Context;
use Magento\Framework\Registry;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class DeleteButton implements ButtonProviderInterface {
    public function __construct(
        Context $context,
        Registry $registry
    ) {
        $this->urlBuilder = $context->getUrlBuilder();
        $this->registry = $registry;
    }
     public function getButtonData() {
        if ($this->registry->registry('entity_id') > 0) {
            $data = [
                'label' => __('Delete'),
                'class' => 'delete',
                'id' => 'helloworld-edit-delete-button',
                'data_attribute' => [
                    'url' => $this->getDeleteUrl(),
                ],
                'on_click' =>
                'deleteConfirm(\'' . __("Are you sure you want to do this?") . '\', \'' . $this->getDeleteUrl() . '\')',
                'sort_order' => 20,
            ];
          return $data;
        }
    }
    public function getDeleteUrl() {
        return $this->urlBuilder->getUrl('*/*/delete', ['entity_id' => $this->registry->registry('entity_id')]);
    }
}

Step - 9

The next step is to create app/code/MD/HelloWorld/Block/Adminhtml/HelloWorld/Edit/ResetButton.php file to add reset records button in UI Form:

<?php
namespace MD\HelloWorld\Block\Adminhtml\HelloWorld\Edit;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class ResetButton implements ButtonProviderInterface {
    public function getButtonData() {
        return [
            'label' => __('Reset'),
            'class' => 'reset',
            'on_click' => 'location.reload();',
            'sort_order' => 20,
        ];
    }
}

Step - 10

Now, the next step is to create app/code/MD/HelloWorld/Block/Adminhtml/HelloWorld/Edit/SaveAndContinueButton.php file to add “save and continue” button in UI Form :

<?php
namespace MD\HelloWorld\Block\Adminhtml\HelloWorld\Edit;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class SaveAndContinueButton implements ButtonProviderInterface
{
    public function getButtonData()
    {
        $data = [
            'label' => __('Save and Continue Edit'),
            'class' => 'save',
            'data_attribute' => [
                'mage-init' => [
                    'button' => ['event' => 'saveAndContinueEdit'],
                ],
            ],
            'sort_order' => 50,
        ];
        return $data;
    }
}

Step - 11

The next step is to create app/code/MD/HelloWorld/Block/Adminhtml/HelloWorld/Edit/SaveButton.php file to add save button in UI Form :

<?php
namespace MD\HelloWorld\Block\Adminhtml\HelloWorld\Edit;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class SaveButton implements ButtonProviderInterface
{
    public function getButtonData()
    {
        $data = [
            'label' => __('Save'),
            'class' => 'save primary',
            'data_attribute' => [
                'mage-init' => ['button' => ['event' => 'save']],
                'form-role' => 'save',
            ],
            'sort_order' => 60,
        ];
        return $data;
    }
}

Step - 12

Next, we will create app/code/MD/HelloWorld/Controller/Adminhtml/HelloWorld/Add.php file to add new record UI Form:

<?php
namespace MD\HelloWorld\Controller\Adminhtml\HelloWorld;
use Magento\Framework\Controller\ResultFactory;
class Add extends \Magento\Backend\App\Action
{
 public function execute()
    {
        $resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE);
        $resultPage->getConfig()->getTitle()->prepend(__('Add New Record'));
        return $resultPage;
    }
}

Step - 13

Now, we need to create app/code/MD/HelloWorld/Controller/Adminhtml/HelloWorld/Delete.php file to delete record UI Form:

<?php
namespace MD\HelloWorld\Controller\Adminhtml\HelloWorld;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use MD\HelloWorld\Model\HelloWorldFactory;
class Delete extends Action
{
    protected $helloworldFactory;
    public function __construct(
        Context $context,
        HelloWorldFactory $helloworldFactory
    ) {
        $this->helloworldFactory = $helloworldFactory;
        parent::__construct($context);
    }
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('MD_HelloWorld::helloworld');
    }
    public function execute()
    {
        $resultRedirect = $this->resultRedirectFactory->create();
        $id = $this->getRequest()->getParam('entity_id', null);
        try {
            $helloWorldData = $this->helloworldFactory->create()->load($id);
            if ($helloworldData->getId()) {
                $helloworldData->delete();
                $this->messageManager->addSuccessMessage(__('You deleted the record.'));
            } else {

                $this->messageManager->addErrorMessage(__('Record does not exist.'));
            }
        } catch (\Exception $exception) {
            $this->messageManager->addErrorMessage($exception->getMessage());
        }
        return $resultRedirect->setPath('*/*');
    }
}

Step - 14

Now, the next step is to create app/code/MD/HelloWorld/Controller/Adminhtml/HelloWorld/Edit.php file to edit  existing record in UI Form:

<?php
namespace MD\HelloWorld\Controller\Adminhtml\HelloWorld;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Registry;
use Magento\Framework\View\Result\PageFactory;
use MD\HelloWorld\Model\HelloWorldFactory;
class Edit extends Action
{
protected $_coreRegistry = null;
   protected $resultPageFactory;
    protected $helloworldFactory;
    public function __construct(
        Context $context,
        PageFactory $resultPageFactory,
        Registry $registry,
        HelloWorldFactory $helloworldFactory
    ) {
        $this->resultPageFactory = $resultPageFactory;
        $this->_coreRegistry = $registry;
        $this->helloworldFactory = $helloworldFactory;
        parent::__construct($context);
    }
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('MD_HelloWorld::helloworld');
    }
    public function execute()
    {
        $id = $this->getRequest()->getParam('entity_id');
        $helloworldData = $this->helloworldFactory->create();
        if ($id) {
            $helloworldData->load($id);
            if (!$helloworldData->getId()) {
                $this->messageManager->addErrorMessage(__('This record no longer exists.'));
                /** \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
                $resultRedirect = $this->resultRedirectFactory->create();
                return $resultRedirect->setPath('*/*/');
            }
        }
        $data = $this->_session->getFormData(true);
        if (!empty($data)) {
            $helloworldData->addData($data);
        }
        $this->_coreRegistry->register('entity_id', $id);
        /** @var \Magento\Backend\Model\View\Result\Page $resultPage */
        $resultPage = $this->resultPageFactory->create();
        $resultPage->setActiveMenu('MD_HelloWorld::helloworld');
        $resultPage->getConfig()->getTitle()->prepend(__('Edit Record'));
        return $resultPage;
    }
}

Step - 15

Now, let’s create app/code/MD/HelloWorld/Controller/Adminhtml/HelloWorld/Index.php file to create the admin UI grid page:

<?php
namespace MD\HelloWorld\Controller\Adminhtml\HelloWorld;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
class Index extends Action
{
    protected $resultPageFactory;
    public function __construct(
        Context $context,
        PageFactory $resultPageFactory
    ) {
        parent::__construct($context);
        $this->resultPageFactory = $resultPageFactory;
    }
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('MD_HelloWorld::helloworld');
    }
    public function execute()
    {
        /** @var \Magento\Backend\Model\View\Result\Page $resultPage */
        $resultPage = $this->resultPageFactory->create();
        $resultPage->setActiveMenu('MD_HelloWorld::helloworld');
        $resultPage->getConfig()->getTitle()->prepend(__('Hello World'));
        return $resultPage;
    }
}

Step - 16

The next step is to create app/code/MD/HelloWorld/Controller/Adminhtml/HelloWorld/InlineEdit.php file for inline edit record UI grid:

<?php
namespace MD\HelloWorld\Controller\Adminhtml\HelloWorld;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\Result\JsonFactory;
use MD\HelloWorld\Model\ResourceModel\HelloWorld\Collection;
class InlineEdit extends Action
{
    protected $helloworldCollection;
    public function __construct(
        Context $context,
        Collection $helloworldCollection,
        JsonFactory $jsonFactory
    ) {
        parent::__construct($context);
        $this->jsonFactory = $jsonFactory;
        $this->helloworldCollection = $helloworldCollection;
    }
    public function execute()
    {
        /** @var \Magento\Framework\Controller\Result\Json $resultJson */
        $resultJson = $this->jsonFactory->create();
        $error = false;
        $messages = [];
        $post_items = $this->getRequest()->getParam('items', []);
        if (!($this->getRequest()->getParam('isAjax') && count($post_items))) {
            return $resultJson->setData([
                'messages' => [__('Please correct the data sent.')],
                'error' => true,
            ]);
        }
        try {
            $this->helloworldCollection
                ->setStoreId($this->getRequest()->getParam('store', 0))
                ->addFieldToFilter('entity_id', ['in' => array_keys($post_items)])
                ->walk('saveCollection', [$post_items]);
        } catch (\Exception $e) {
            $messages[] = __('There was an error saving the data: ') . $e->getMessage();
            $error = true;
        }
        return $resultJson->setData([
            'messages' => $messages,
            'error' => $error
 ]);
    }
}

Step - 17

After that, we need to create app/code/MD/HelloWorld/Controller/Adminhtml/HelloWorld/MassDelete.php file for mass delete records :

<?php
namespace MD\HelloWorld\Controller\Adminhtml\HelloWorld;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Magento\Framework\Controller\ResultFactory;
use MD\HelloWorld\Model\ResourceModel\HelloWorld\Collection;
class MassDelete extends Action
{    protected $filter;
    protected $helloworldCollection;
    public function __construct(Context $context, Filter $filter, Collection $helloworldCollection)
    {
        $this->filter = $filter;
        $this->helloworldCollection = $helloworldCollection;
        parent::__construct($context); }
    public function execute()
    {
        $collection = $this->filter->getCollection($this->helloworldCollection);
        $collectionSize = $collection->getSize();
        $collection->walk('delete');
        $this->messageManager->addSuccessMessage(__('A total of %1 record(s) have been deleted.', $collectionSize));
        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
        return $resultRedirect->setPath('*/*/');
    }
}

Step - 18

Next, we will create app/code/MD/HelloWorld/Controller/Adminhtml/HelloWorld/Save.php file to save records :

<?php
namespace MD\HelloWorld\Controller\Adminhtml\HelloWorld;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use MD\HelloWorld\Model\HelloWorldFactory;
class Save extends Action
{
    protected $helloworldFactory;
    public function __construct(
        Context $context,
        HelloWorldFactory $helloworldFactory
    ) {
        $this->helloworldFactory = $helloworldFactory;
        parent::__construct($context);
    }
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('MD_HelloWorld::helloworld');
    }
    public function execute()
    {
        $storeId = (int)$this->getRequest()->getParam('store_id');
        $data = $this->getRequest()->getParams();
        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultRedirectFactory->create();
        if ($data) {
            $params = [];
            $helloworldData = $this->helloworldFactory->create();
            $helloworldData->setStoreId($storeId);
            $params['store'] = $storeId;
            if (empty($data['entity_id'])) {
                $data['entity_id'] = null;
            } else {
                $helloworldData->load($data['entity_id']);
                $params['entity_id'] = $data['entity_id'];
           }
            $helloworldData->addData($data);
            $this->_eventManager->dispatch(
               'md_helloworld_helloworld_prepare_save',
                ['object' => $this->helloworldFactory, 'request' => $this->getRequest()]
            );
            try {
                $helloworldData->save();
                $this->messageManager->addSuccessMessage(__('You saved this record.'));
                $this->_getSession()->setFormData(false);
                if ($this->getRequest()->getParam('back')) {
                    $params['entity_id'] = $helloworldData->getId();
                    $params['_current'] = true;
                    return $resultRedirect->setPath('*/*/edit', $params);
                }
                return $resultRedirect->setPath('*/*/');
            } catch (\Exception $e) {
                $this->messageManager->addErrorMessage($e->getMessage());
                $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the record.'));
            }
            $this->_getSession()->setFormData($this->getRequest()->getPostValue());
            return $resultRedirect->setPath('*/*/edit', $params);
        }
        return $resultRedirect->setPath('*/*/');
    }
}

Step - 19

After that, we need to create app/code/MD/HelloWorld/Controller/Adminhtml/HelloWorld/Validate.php file to check data validation on the submit form:

<?php
namespace MD\HelloWorld\Controler\Adminhtml\HelloWorld;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\Result\JsonFactory;
class Validate extends Action {
    protected $jsonFactory;
    protected $response;
    public function __construct(
        Context $context,
        JsonFactory $jsonFactory
    ) {
        parent::__construct($context);
        $this->jsonFactory = $jsonFactory;
        $this->response = new \Magento\Framework\DataObject();
    }
    public function validateRequireEntries(array $data) {
        $requiredFields = [
           'identifier' => __('Hello World Identifier'),
       ];
        foreach ($data as $field => $value) {
            if (in_array($field, array_keys($requiredFields)) && $value == '') {
                $this->_addErrorMessage(
                    __('To apply changes you should fill in required "%1" field', $requiredFields[$field])
                );
            }
       }
    }
    protected function _addErrorMessage($message) {
        $this->response->setError(true);
        if (!is_array($this->response->getMessages())) {
            $this->response->setMessages([]);
       }
        $messages = $this->response->getMessages();
       $messages[] = $message;
        $this->response->setMessages($messages);
    }
    public function execute() {
        $this->response->setError(0);
        $this->validateRequireEntries($this->getRequest()->getParams());
        $resultJson = $this->jsonFactory->create()->setData($this->response);
        return $resultJson;
    }
}

Step - 20

The next step is to create Main model file: app/code/MD/HelloWorld/Model/HelloWorld.php

<?php
namespace MD\HelloWorld\Model;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\Model\AbstractModel;
class HelloWorld extends AbstractModel implements IdentityInterface
{
const CACHE_TAG = 'md_helloworld_helloworld';
const KEY_ENTITY_TYPE_ID = 'entity_type_id';
const KEY_ATTR_TYPE_ID = 'attribute_set_id';
protected $_cacheTag = 'md_helloworld_helloworld';
protected $_eventPrefix = 'md_helloworld_helloworld';
protected function _construct()
{
parent::_construct();

$this->_init(\MD\HelloWorld\Model\ResourceModel\HelloWorld::class);
}
public function getIdentities()
{
return [self::CACHE_TAG . '_' . $this->getId()];
}
public function saveCollection(array $data)
{
if (isset($data[$this->getId()])) {
$this->addData($data[$this->getId()]);
$this->getResource()->save($this);
}
return $this;
}
public function setEntityTypeId($entityTypeId)
{
return $this->setData(self::KEY_ENTITY_TYPE_ID, $entityTypeId);
}
public function getEntityTypeId()
{
return $this->getData(self::KEY_ENTITY_TYPE_ID);
}
public function setAttributeSetId($attrSetId)
{
return $this->setData(self::KEY_ATTR_TYPE_ID, $attrSetId);
}
public function getAttributeSetId()
{
return $this->getData(self::KEY_ATTR_TYPE_ID);
}
protected function _getResource()
{
return parent::_getResource();
}
/**
* Retrieve default attribute set id
*
* @return int
*/
public function getDefaultAttributeSetId()
{
return $this->getResource()->getEntityType()->getDefaultAttributeSetId();
}
}

Step - 21
Next, we need to create Resource model file:  app/code/MD/HelloWorld/Model/ResourceModel/HelloWorld.php

<?php
namespace MD\HelloWorld\Model\ResourceModel;
use Magento\Eav\Model\Entity\AbstractEntity;
use Magento\Eav\Model\Entity\Context;
use Magento\Framework\DataObject;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;
use MD\HelloWorld\Setup\HelloWorldSetup;
class HelloWorld extends AbstractEntity
{
protected $_storeId = null;
protected $entityManager;
public function __construct(
Context $context,
StoreManagerInterface $storeManager,
array $data = []
) {
parent::__construct($context, $data);

$this->setType(HelloWorldSetup::ENTITY_TYPE_CODE);

$this->setConnection(HelloWorldSetup::ENTITY_TYPE_CODE . '_read', HelloWorldSetup::ENTITY_TYPE_CODE . '_write');

$this->_storeManager = $storeManager;

}
protected function _beforeSave(\Magento\Framework\DataObject $object)
{
$object->setAttributeSetId($object->getAttributeSetId() ?: $this->getEntityType()->getDefaultAttributeSetId());
$object->setEntityTypeId($object->getEntityTypeId() ?: $this->getEntityType()->getEntityTypeId());
return parent::_beforeSave($object);
}
public function getEntityType()
{
if (empty($this->_type)) {
$this->setType(HelloWorldSetup::ENTITY_TYPE_CODE);
}
return parent::getEntityType();
}
protected function _getDefaultAttributes()
{
return [
'attribute_set_id',
'entity_type_id',
'created_at',
'updated_at',
];
}
public function setStoreId($storeId)
{
$this->_storeId = $storeId;
return $this;
}
public function getStoreId()
{
if ($this->_storeId === null) {
return $this->_storeManager->getStore()->getId();
}
return $this->_storeId;
}
protected function _saveAttribute($object, $attribute, $value)
{
$table = $attribute->getBackend()->getTable();
if (!isset($this->_attributeValuesToSave[$table])) {
$this->_attributeValuesToSave[$table] = [];
}
$entityIdField = $attribute->getBackend()->getEntityIdField();
$storeId = $object->getStoreId() ?: Store::DEFAULT_STORE_ID;
$data = [
$entityIdField => $object->getId(),
'entity_type_id' => $object->getEntityTypeId(),
'attribute_id' => $attribute->getId(),
'value' => $this->_prepareValueForSave($value, $attribute),
'store_id' => $storeId,
];
if (!$this->getEntityTable() || $this->getEntityTable() == \Magento\Eav\Model\Entity::DEFAULT_ENTITY_TABLE) {
$data['entity_type_id'] = $object->getEntityTypeId();
}
if ($attribute->isScopeStore()) {
$this->_attributeValuesToSave[$table][] = $data;
} elseif ($attribute->isScopeWebsite() && $storeId != Store::DEFAULT_STORE_ID) {
$storeIds = $this->_storeManager->getStore($storeId)->getWebsite()->getStoreIds(true);
foreach ($storeIds as $storeId) {
$data['store_id'] = (int) $storeId;
$this->_attributeValuesToSave[$table][] = $data;
}
} else {
$data['store_id'] = Store::DEFAULT_STORE_ID;
$this->_attributeValuesToSave[$table][] = $data;
}
return $this;
}
}

Step-22

After that, we need to create Hello world attributes file : app/code/MD/HelloWorld/Model/ResourceModel/Attribute.php

<?php
namespace MD\HelloWorld\Model\ResourceModel;
use Magento\Eav\Model\Config;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
use Magento\Eav\Model\ResourceModel\Entity\Attribute as EavAttribute;
use Magento\Eav\Model\ResourceModel\Entity\Type;
use Magento\Framework\Model\AbstractModel;
use Magento\Framework\Model\ResourceModel\Db\Context;
use Magento\Store\Model\StoreManagerInterface;
use MD\HelloWorld\Setup\HelloWorldSetup;
class Attribute extends EavAttribute {
    protected $_eavConfig;
    public function __construct(
        Context $context,
        StoreManagerInterface $storeManager,
        Type $eavEntityType,
        Config $eavConfig,
        $connectionName = null
    ) {
        $this->_eavConfig = $eavConfig;
        parent::__construct($context, $storeManager, $eavEntityType, $connectionName);
    }
    protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) {
        $applyTo = $object->getApplyTo();
        if (is_array($applyTo)) {
            $object->setApplyTo(implode(',', $applyTo));
        }
        return parent::_beforeSave($object);
    }
    protected function _afterSave(AbstractModel $object) {
        $this->_clearUselessAttributeValues($object);
        return parent::_afterSave($object);
    }
    protected function _clearUselessAttributeValues(AbstractModel $object) {
        $origData = $object->getOrigData();
        if ($object->isScopeGlobal() && isset(
            $origData['is_global']
        ) && ScopedAttributeInterface::SCOPE_GLOBAL != $origData['is_global']
        ) {
            $attributeStoreIds = array_keys($this->_storeManager->getStores());
            if (!empty($attributeStoreIds)) {
                $delCondition = [
                   'attribute_id = ?' => $object->getId(),
                    'store_id IN(?)' => $attributeStoreIds,
               ];
                $this->getConnection()->delete($object->getBackendTable(), $delCondition);
            }
        }
        return $this;
    }
    public function deleteEntity(AbstractModel $object) {
        if (!$object->getEntityAttributeId()) {
            return $this;
        }
        $select = $this->getConnection()->select()->from(
            $this->getTable('eav_entity_attribute')
        )->where(
            'entity_attribute_id = ?',
            (int) $object->getEntityAttributeId()
        );
        $result = $this->getConnection()->fetchRow($select);
        if ($result) {
            $attribute = $this->_eavConfig->getAttribute(
                HelloWorldSetup::ENTITY_TYPE_CODE,
                $result['attribute_id']
            );
            $backendTable = $attribute->getBackend()->getTable();
            if ($backendTable) {
                $select = $this->getConnection()->select()->from(
                    $attribute->getEntity()->getEntityTable(),
                    'entity_id'
                )->where(
                    'attribute_set_id = ?',
                    $result['attribute_set_id']
                );
                $clearCondition = [
                    'attribute_id =?' => $attribute->getId(),
                    'entity_id IN (?)' => $select,
                ];
                $this->getConnection()->delete($backendTable, $clearCondition);
            }
        }
        $condition = ['entity_attribute_id = ?' => $object->getEntityAttributeId()];
        $this->getConnection()->delete($this->getTable('eav_entity_attribute'), $condition);
        return $this;
    }
}

Step - 23
Next, create a Collection file: app/code/MD/HelloWorld/Model/ResourceModel/HelloWorld/Collection.php

<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
/**
* Code standard by : MD
*/
namespace MD\HelloWorld\Model\ResourceModel\HelloWorld;
use Magento\Eav\Model\Config;
use Magento\Eav\Model\EntityFactory as EavEntityFactory;
use Magento\Eav\Model\Entity\Collection\AbstractCollection;
use Magento\Eav\Model\ResourceModel\Helper;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Data\Collection\Db\FetchStrategyInterface;
use Magento\Framework\Data\Collection\EntityFactory;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Event\ManagerInterface;
use Magento\Framework\Validator\UniversalFactory;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;
/**
* Class Collection
* @package MD\HelloWorld\Model\ResourceModel\HelloWorld
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Collection extends AbstractCollection
{
/**
* @var string
*/
protected $_idFieldName = 'entity_id';
/**
* @var $_storeId
*/
protected $_storeId;
/**
* @var StoreManagerInterface
*/
protected $_storeManager;
/**
* [__construct description]
* @param EntityFactory          $entityFactory    [description]
* @param LoggerInterface        $logger           [description]
* @param FetchStrategyInterface $fetchStrategy    [description]
* @param ManagerInterface       $eventManager     [description]
* @param Config                 $eavConfig        [description]
* @param ResourceConnection     $resource         [description]
* @param EavEntityFactory       $eavEntityFactory [description]
* @param Helper                 $resourceHelper   [description]
* @param UniversalFactory       $universalFactory [description]
* @param StoreManagerInterface  $storeManager     [description]
* @param AdapterInterface|null  $connection       [description]
*/
public function __construct(
EntityFactory $entityFactory,
LoggerInterface $logger,
FetchStrategyInterface $fetchStrategy,
ManagerInterface $eventManager,
Config $eavConfig,
ResourceConnection $resource,
EavEntityFactory $eavEntityFactory,
Helper $resourceHelper,
UniversalFactory $universalFactory,
StoreManagerInterface $storeManager,
AdapterInterface $connection = null
) {
$this->_storeManager = $storeManager;
parent::__construct(
$entityFactory,
$logger,
$fetchStrategy,
$eventManager,
$eavConfig,
$resource,
$eavEntityFactory,
$resourceHelper,
$universalFactory,
$connection
);
}
/**
* Define resource model
*
* @return void
*/
protected function _construct()
{
$this->_init(\MD\HelloWorld\Model\HelloWorld::class, \MD\HelloWorld\Model\ResourceModel\HelloWorld::class);
}
/**
* Set store scope
*
* @param int|string|\Magento\Store\Model\Store $store
* @return $this
*/
public function setStore($store)
{
$this->setStoreId($this->_storeManager->getStore($store)->getId());
return $this;
}
/**
* Set store scope
*
* @param int|string|\Magento\Store\Api\Data\StoreInterface $storeId
* @return $this
*/
public function setStoreId($storeId)
{
if ($storeId instanceof \Magento\Store\Api\Data\StoreInterface) {
$storeId = $storeId->getId();
}
$this->_storeId = (int) $storeId;
return $this;
}
/**
* Return current store id
*
* @return int
*/
public function getStoreId()
{
if ($this->_storeId === null) {
$this->setStoreId($this->_storeManager->getStore()->getId());
}
return $this->_storeId;
}
/**
* Retrieve default store id
*
* @return int
*/
public function getDefaultStoreId()
{
return \Magento\Store\Model\Store::DEFAULT_STORE_ID;
}
/**
* Retrieve attributes load select
*
* @param string $table
* @param array|int $attributeIds
* @return \Magento\Eav\Model\Entity\Collection\AbstractCollection
*/
protected function _getLoadAttributesSelect($table, $attributeIds = [])
{
if (empty($attributeIds)) {
$attributeIds = $this->_selectAttributes;
}
$storeId = $this->getStoreId();
$connection = $this->getConnection();
$entityTable = $this->getEntity()->getEntityTable();
$indexList = $connection->getIndexList($entityTable);
$entityIdField = $indexList[$connection->getPrimaryKeyName($entityTable)]['COLUMNS_LIST'][0];
if ($storeId) {
$joinCondition = [
't_s.attribute_id = t_d.attribute_id',
"t_s.{$entityIdField} = t_d.{$entityIdField}",
$connection->quoteInto('t_s.store_id = ?', $storeId),
];
$select = $connection->select()->from(
['t_d' => $table],
['attribute_id']
)->join(
['e' => $entityTable],
"e.{$entityIdField} = t_d.{$entityIdField}",
['e.entity_id']
)->where(
"e.entity_id IN (?)",
array_keys($this->_itemsById)
)->where(
't_d.attribute_id IN (?)',
$attributeIds
)->joinLeft(
['t_s' => $table],
implode(' AND ', $joinCondition),
[]
)->where(
't_d.store_id = ?',
$connection->getIfNullSql('t_s.store_id', \Magento\Store\Model\Store::DEFAULT_STORE_ID)
);
} else {
$select = $connection->select()->from(
['t_d' => $table],
['attribute_id']
)->join(
['e' => $entityTable],
"e.{$entityIdField} = t_d.{$entityIdField}",
['e.entity_id']
)->where(
"e.entity_id IN (?)",
array_keys($this->_itemsById)
)->where(
'attribute_id IN (?)',
$attributeIds
)->where(
'store_id = ?',
$this->getDefaultStoreId()
);
}
return $select;
}
/**
* @param \Magento\Framework\DB\Select $select
* @param string $table
* @param string $type
* @return \Magento\Framework\DB\Select
*/
protected function _addLoadAttributesSelectValues($select, $table, $type)
{
$storeId = $this->getStoreId();
if ($storeId) {
$connection = $this->getConnection();
$valueExpr = $connection->getCheckSql('t_s.value_id IS NULL', 't_d.value', 't_s.value');
$select->columns(
['default_value' => 't_d.value', 'store_value' => 't_s.value', 'value' => $valueExpr]
);
} else {
$select = parent::_addLoadAttributesSelectValues($select, $table, $type);
}
return $select;
}
/**
* Adding join statement to collection select instance
*
* @param string $method
* @param object $attribute
* @param string $tableAlias
* @param array $condition
* @param string $fieldCode
* @param string $fieldAlias
* @return \Magento\Eav\Model\Entity\Collection\AbstractCollection
*/
protected function _joinAttributeToSelect($method, $attribute, $tableAlias, $condition, $fieldCode, $fieldAlias)
{
if (isset($this->_joinAttributes[$fieldCode]['store_id'])) {
$storeId = $this->_joinAttributes[$fieldCode]['store_id'];
} else {
$storeId = $this->getStoreId();
}
$connection = $this->getConnection();
if ($storeId != $this->getDefaultStoreId() && !$attribute->isScopeGlobal()) {
/**
* Add joining default value for not default store
* if value for store is null - we use default value
*/
$defCondition = '(' . implode(') AND (', $condition) . ')';
$defAlias = $tableAlias . '_default';
$defAlias = $this->getConnection()->getTableName($defAlias);
$defFieldAlias = str_replace($tableAlias, $defAlias, $fieldAlias);
$tableAlias = $this->getConnection()->getTableName($tableAlias);
$defCondition = str_replace($tableAlias, $defAlias, $defCondition);
$defCondition .= $connection->quoteInto(
" AND " . $connection->quoteColumnAs("{$defAlias}.store_id", null) . " = ?",
$this->getDefaultStoreId()
);
$this->getSelect()->{$method}(
[$defAlias => $attribute->getBackend()->getTable()],
$defCondition,
[]
);
$method = 'joinLeft';
$fieldAlias = $this->getConnection()->getCheckSql(
"{$tableAlias}.value_id > 0",
$fieldAlias,
$defFieldAlias
);
$this->_joinAttributes[$fieldCode]['condition_alias'] = $fieldAlias;
$this->_joinAttributes[$fieldCode]['attribute'] = $attribute;
} else {
$storeId = $this->getDefaultStoreId();
}
$condition[] = $connection->quoteInto(
$connection->quoteColumnAs("{$tableAlias}.store_id", null) . ' = ?',
$storeId
);
return parent::_joinAttributeToSelect($method, $attribute, $tableAlias, $condition, $fieldCode, $fieldAlias);
}
}

Step - 24

Create a Grid collection file: app/code/MD/HelloWorld/Model/ResourceModel/HelloWorld/Grid/Collection.php

<?php
namespace MD\HelloWorld\Model\ResourceModel\HelloWorld\Grid;
use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use MD\HelloWorld\Model\ResourceModel\HelloWorld\Collection as HelloWorldCollection;
use Magento\Eav\Model\Config;
use Magento\Eav\Model\EntityFactory as EavEntityFactory;
use Magento\Eav\Model\ResourceModel\Helper;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Data\Collection\Db\FetchStrategyInterface;
use Magento\Framework\Data\Collection\EntityFactory;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Event\ManagerInterface;
use Magento\Framework\Validator\UniversalFactory;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;
class Collection extends HelloWorldCollection implements SearchResultInterface
{
protected $aggregations;
public function __construct(
EntityFactory $entityFactory,
LoggerInterface $logger,
FetchStrategyInterface $fetchStrategy,
ManagerInterface $eventManager,
Config $eavConfig,
ResourceConnection $resource,
EavEntityFactory $eavEntityFactory,
Helper $resourceHelper,
UniversalFactory $universalFactory,
StoreManagerInterface $storeManager,
$eventPrefix,
$eventObject,
$resourceModel,
$model = 'MD\HelloWorld\Ui\Component\Listing\DataProvider\Document',
AdapterInterface $connection = null
) {
parent::__construct(
$entityFactory,
$logger,
$fetchStrategy,
$eventManager,
$eavConfig,
$resource,
$eavEntityFactory,
$resourceHelper,
$universalFactory,
$storeManager,
$connection
);
$this->_eventPrefix = $eventPrefix;
$this->_eventObject = $eventObject;
$this->_init($model, $resourceModel);
}
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(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;
}
}

Step - 25

Now, we will create app/code/MD/HelloWorld/Model/ResourceModel/Eav/Attribute.php file for attribute EAV resource model file:

<?php
namespace MD\HelloWorld\Model\ResourceModel\Eav;
use MD\HelloWorld\Setup\HelloWorldSetup;
use Magento\Eav\Model\Entity\Attribute as EavAttribute;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
class Attribute extends EavAttribute implements ScopedAttributeInterface
{
const MODULE_NAME = 'MD_HelloWorld';
const KEY_IS_GLOBAL = 'is_global';
const KEY_IS_STATIC = 'static';
protected $_eventObject = 'attribute';
protected static $_labels = null;
protected $_eventPrefix = HelloWorldSetup::ENTITY_TYPE_CODE . '_attribute';
protected function _construct()
{
$this->_init(\MD\HelloWorld\Model\ResourceModel\Attribute::class);
}
public function beforeSave()
{
$this->setData('modulePrefix', self::MODULE_NAME);
if (isset($this->_origData[self::KEY_IS_GLOBAL])) {
if (!isset($this->_data[self::KEY_IS_GLOBAL])) {
$this->_data[self::KEY_IS_GLOBAL] = self::SCOPE_GLOBAL;
}
}
return parent::beforeSave();

}
public function afterSave()
{
$this->_eavConfig->clear();
return parent::afterSave();
}
public function getIsGlobal()
{
if ($this->getBackendType() === self::KEY_IS_STATIC) {
return true;
}
return $this->_getData(self::KEY_IS_GLOBAL);
}
public function isScopeGlobal()
{
return $this->getIsGlobal() == self::SCOPE_GLOBAL;
}
public function isScopeWebsite()
{
return $this->getIsGlobal() == self::SCOPE_WEBRHE;
}
public function isScopeStore()
{
return !$this->isScopeGlobal() && !$this->isScopeWebsite();
}
public function getStoreId()
{
$dataObject = $this->getDataObject();
if ($dataObject) {
return $dataObject->getStoreId();
}
return $this->getData('store_id');
}
public function getSourceModel()
{
$model = $this->getData('source_model');
if (empty($model)) {
if ($this->getBackendType() == 'int' && $this->getFrontendInput() == 'select') {
return $this->_getDefaultSourceModel();
}
}
return $model;
}
public function _getDefaultSourceModel()
{
return 'Magento\Eav\Model\Entity\Attribute\Source\Table';
}
public function afterDelete()
{
$this->_eavConfig->clear();
return parent::afterDelete();
}
}

Step - 26

Now, we will create app/code/MD/HelloWorld/Model/ResourceModel/Attribute/Collection.php file for attribute collection:

<?php
namespace MD\HelloWorld\Model\ResourceModel\Attribute;
use Magento\Eav\Model\Config;
use Magento\Eav\Model\EntityFactory as EavEntityFactory;
use Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection as EavCollection;
use Magento\Framework\Data\Collection\Db\FetchStrategyInterface;
use Magento\Framework\Data\Collection\EntityFactory;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Event\ManagerInterface
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
use Psr\Log\LoggerInterface;
use MD\HelloWorld\Setup\HelloWorldSetup;
lass Collection extends EavCollection
{
protected $_eavEntityFactory;
public function __construct(
EntityFactory $entityFactory,
LoggerInterface $logger,
FetchStrategyInterface $fetchStrategy,
ManagerInterface $eventManager,
Config $eavConfig,
EavEntityFactory $eavEntityFactory,
AdapterInterface $connection = null,
AbstractDb $resource = null
) {
$this->_eavEntityFactory = $eavEntityFactory;
parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $eavConfig, $connection, $resource);
}
protected function _initSelect(
{
$this->getSelect()->from(
['main_table' => $this->getResource()->getMainTable()]
)->where(
'main_table.entity_type_id=?',
$this->_eavEntityFactory->create()->setType(HelloWorldSetup::ENTITY_TYPE_CODE)->getTypeId()
)->join(
['additional_table' => $this->getTable(HelloWorldSetup::EAV_ENTITY_TYPE_CODE . '_eav_attribute')],
'additional_table.attribute_id = main_table.attribute_id'
);
return $this;
}
public function getFilterAttributesOnly(
{
$this->getSelect()->where('additional_table.is_filterable', 1);
return $this;
}
public function addVisibilityFilter($status = 1)
{
$this->getSelect()->where('additional_table.is_visible', $status);
return $this;
}
public function setEntityTypeFilter($typeId)
{
return $this;
}
}

Step - 27

Next, we will need to create app/code/MD/HelloWorld/Setup/EavTablesSetup.php file to install eav tables :

<?php
namespace MD\HelloWorld\Setup;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\SchemaSetupInterface;
class EavTablesSetup {
protected $setup;
public function __construct(SchemaSetupInterface $setup) {
$this->setup = $setup;
}
public function createEavTables($entityCode) {
$this->createEAVMainTable($entityCode);
$this->createEntityTable($entityCode, 'datetime', Table::TYPE_DATETIME);
$this->createEntityTable($entityCode, 'decimal', Table::TYPE_DECIMAL, '12,4');
$this->createEntityTable($entityCode, 'int', Table::TYPE_INTEGER);
$this->createEntityTable($entityCode, 'text', Table::TYPE_TEXT, '64k');
$this->createEntityTable($entityCode, 'varchar', Table::TYPE_TEXT, 255);
}
protected function createEAVMainTable($entityCode)
{$tableName = $entityCode . '_eav_attribute';
$tableName = 'md_helloworld_eav_attribute';
$table = $this->setup->getConnection()->newTable(
$this->setup->getTable($tableName)
)->addColumn(
'attribute_id',
Table::TYPE_SMALLINT,
null,
['identity' => false, 'unsigned' => true, 'nullable' => false, 'primary' => true],
'Attribute Id'
)->addColumn(
'is_global',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '1'],
'Is Global'
)->addColumn(
'is_filterable',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Is Filterable'
)->addColumn(
'is_visible',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '1'],
'Is Visible'
)
->addColumn(
'is_wysiwyg_enabled',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Attribute uses WYSIWYG'
)->addColumn(
'validate_rules',
Table::TYPE_TEXT,
'64k',
[],
'Validate Rules'
)->addColumn(
'is_system',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Is System'
)->addColumn(
'sort_order',
Table::TYPE_INTEGER,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Sort Order'
)->addColumn(
'data_model',
Table::TYPE_TEXT,
255,
[],
'Data Model'
)->addForeignKey(
$this->setup->getFkName($tableName, 'attribute_id', 'eav_attribute', 'attribute_id'),
'attribute_id',
$this->setup->getTable('eav_attribute'),
'attribute_id',
Table::ACTION_CASCADE
)->setComment(
'MD HelloWorld Eav Attribute'
);
$this->setup->getConnection()->createTable($table);
}
protected function createEntityTable($entityCode, $type, $valueType, $valueLength = null) {
$tableName = $entityCode . '_' . $type;
$table = $this->setup->getConnection()
->newTable($this->setup->getTable($tableName))
->addColumn(
'value_id',
Table::TYPE_INTEGER,
null,
['identity' => true, 'nullable' => false, 'primary' => true],
'Value ID'
)
->addColumn(
'entity_type_id',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Entity Type ID'
)
->addColumn(
'attribute_id',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Attribute ID'
)
->addColumn(
'store_id',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Store ID'
)
->addColumn(
'entity_id',
Table::TYPE_INTEGER,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Entity ID'
)
->addColumn(
'value',
$valueType,
$valueLength,
[],
'Value'
)
->addIndex(
$this->setup->getIdxName(
$tableName,
['entity_id', 'attribute_id', 'store_id', 'entity_type_id'],
AdapterInterface::INDEX_TYPE_UNIQUE
),
['entity_id', 'attribute_id', 'store_id', 'entity_type_id'],
['type' => AdapterInterface::INDEX_TYPE_UNIQUE]
)
->addIndex(
$this->setup->getIdxName($tableName, ['entity_id']),
['entity_id']
)
->addIndex(
$this->setup->getIdxName($tableName, ['attribute_id']),
['attribute_id']
)
->addIndex(
$this->setup->getIdxName($tableName, ['store_id']),
['store_id']
)
->addIndex(
$this->setup->getIdxName($tableName, ['entity_type_id']),
['entity_type_id']
)
->addForeignKey(
$this->setup->getFkName(
$tableName,
'attribute_id',
'eav_attribute',
'attribute_id'
),
'attribute_id',
$this->setup->getTable('eav_attribute'),
'attribute_id',
Table::ACTION_CASCADE
)
->addForeignKey(
$this->setup->getFkName(
$tableName,
'entity_id',
$entityCode,
'entity_id'
),
'entity_id',
$this->setup->getTable($entityCode),
'entity_id',
Table::ACTION_CASCADE
)
->addForeignKey(
$this->setup->getFkName($tableName, 'store_id', 'store', 'store_id'),
'store_id',
$this->setup->getTable('store'),
'store_id',
Table::ACTION_CASCADE
)
->addForeignKey(
$this->setup->getFkName($tableName, 'entity_type_id', 'eav_entity_type', 'entity_type_id'),
'entity_type_id',
$this->setup->getTable('eav_entity_type'),
'entity_type_id',
Table::ACTION_CASCADE
)
->setComment($entityCode . ' ' . $type . 'Attribute Backend Table');
$this->setup->getConnection()->createTable($table);
}
}

Step - 28

Now, let’s create app/code/MD/HelloWorld/Setup/InstallData.php file to install entities:

<?php
namespace MD\HelloWorld\Setup;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use MD\HelloWorld\Setup\HelloWorldSetupFactory;
class InstallData implements InstallDataInterface
{
protected $helloworldSetupFactory;
public function __construct(HelloWorldSetupFactory $helloworldSetupFactory)
{
$this->helloworldSetupFactory = $helloworldSetupFactory;
}
public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
$helloworldSetup = $this->helloworldSetupFactory->create(['setup' => $setup]);
$setup->startSetup();
$helloworldSetup->installEntities();
$entities = $helloworldSetup->getDefaultEntities();
foreach ($entities as $entityName => $entity) {
$helloworldSetup->addEntityType($entityName, $entity);
}
$setup->endSetup();
}
}

Step - 29

Now, create app/code/MD/HelloWorld/Setup/InstallSchema.php file to install schema:

<?php
namespace MD\HelloWorld\Setup;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use MD\HelloWorld\Setup\EavTablesSetupFactory;
use MD\HelloWorld\Setup\HelloWorldSetup;
class InstallSchema implements InstallSchemaInterface
{
protected $eavTablesSetupFactory;
public function __construct(EavTablesSetupFactory $eavTablesSetupFactory)
{
$this->eavTablesSetupFactory = $eavTablesSetupFactory
}
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
$tableName = HelloWorldSetup::ENTITY_TYPE_CODE;
$table = $setup->getConnection()
->newTable($setup->getTable($tableName))
->addColumn(
'entity_id',
Table::TYPE_INTEGER,
null,
['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
'Entity ID'
)->setComment('Entity Table');
$table->addColumn(
'entity_type_id',
Table::TYPE_SMALLINT,
null,
[
'unsigned' => true,
'nullable' => false,
'default' => '0',
],
'Entity Type ID'
)->addIndex(
$setup->getIdxName($tableName, ['entity_type_id']),
['entity_type_id']
)->addForeignKey(
$setup->getFkName(
'md_helloworld_helloworld',
'entity_type_id',
'eav_entity_type',
'entity_type_id'
),
'entity_type_id',
$setup->getTable('eav_entity_type'),
'entity_type_id',
\Magento\Framework\DB\Ddl\Table::ACTION_CASCADE,
\Magento\Framework\DB\Ddl\Table::ACTION_CASCADE
);
$table->addColumn(
'attribute_set_id',
Table::TYPE_SMALLINT,
null,
[
'unsigned' => true,
'nullable' => false,
'default' => '0',
],
'Attribute Set ID'
)->addIndex(
$setup->getIdxName($tableName, ['attribute_set_id']),
['attribute_set_id']
)->addForeignKey(
$setup->getFkName(
'md_helloworld_helloworld',
'attribute_set_id',
'eav_attribute_set',
'attribute_set_id'
),
'attribute_set_id',
$setup->getTable('eav_attribute_set'),
'attribute_set_id',
\Magento\Framework\DB\Ddl\Table::ACTION_CASCADE,
\Magento\Framework\DB\Ddl\Table::ACTION_CASCADE
);
// Add more static attributes here...
$table->addColumn(
'created_at',
Table::TYPE_TIMESTAMP,
null,
['nullable' => false, 'default' => Table::TIMESTAMP_INIT],
'Creation Time'
)->addColumn(
'updated_at',
Table::TYPE_TIMESTAMP,
null,
['nullable' => false, 'default' => Table::TIMESTAMP_INIT_UPDATE],
'Update Time'
);
$setup->getConnection()->createTable($table);
/** @var \MD\HelloWorld\Setup\EavTablesSetup $eavTablesSetup */
$eavTablesSetup = $this->eavTablesSetupFactory->create(['setup' => $setup]);
$eavTablesSetup->createEavTables(HelloWorldSetup::ENTITY_TYPE_CODE);
$setup->endSetup();
}
}

Step - 30

The next step is to create app/code/MD/HelloWorld/Setup/HelloWorldSetup.php file for eav attribute list file:

<?php
namespace MD\HelloWorld\Setup;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
use Magento\Eav\Setup\EavSetup;
class HelloWorldSetup extends EavSetup {
const ENTITY_TYPE_CODE = 'md_helloworld_helloworld';
protected function getAttributes() {
$attributes = [];
$attributes['main_title'] = [
'group' => 'General',
'type' => 'varchar',
'label' => 'Main Title',
'input' => 'text',
'global' => ScopedAttributeInterface::SCOPE_STORE,
'required' => '1',
'user_defined' => false,
'default' => '',
'unique' => false,
'position' => '10',
'note' => '',
'visible' => '1',
'wysiwyg_enabled' => '0',
];
// Add your more entity attributes here...
return $attributes;
}
public function getDefaultEntities() {
$entities = [
self::ENTITY_TYPE_CODE => [
'entity_model' => 'MD\HelloWorld\Model\ResourceModel\HelloWorld',
'attribute_model' => 'MD\HelloWorld\Model\ResourceModel\Eav\Attribute',
'table' => self::ENTITY_TYPE_CODE,
'increment_model' => null,
'additional_attribute_table' => 'md_helloworld_eav_attribute',
'entity_attribute_collection' => 'MD\HelloWorld\Model\ResourceModel\Attribute\Collection',
'attributes' => $this->getAttributes(),
],
];
return $entities;
}
}

Step - 31

After that, we need to create app/code/MD/HelloWorld/Ui/Component/Form/HelloWorld/DataProvider.php file for data provider file for UI Form:

<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
/**
* Code standard by : MD
*/
namespace MD\HelloWorld\Ui\Component\Form\HelloWorld;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool;
use Magento\Ui\DataProvider\AbstractDataProvider;
use MD\HelloWorld\Model\ResourceModel\HelloWorld\Collection;
class DataProvider extends AbstractDataProvider {
/**
* @var Collection
*/
protected $collection;
/**
* @var FilterPool
*/
protected $filterPool;
/**
* @var array
*/
protected $loadedData;
/**
* @var RequestInterface
*/
protected $request;
/**
* @param string           $name             [description]
* @param string           $primaryFieldName [description]
* @param string           $requestFieldName [description]
* @param Collection       $collection       [description]
* @param FilterPool       $filterPool       [description]
* @param RequestInterface $request          [description]
* @param array            $meta             [description]
* @param array            $data             [description]
*/
public function __construct(
$name,
$primaryFieldName,
$requestFieldName,
Collection $collection,
FilterPool $filterPool,
RequestInterface $request,
array $meta = [],
array $data = []
) {
parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
$this->collection = $collection;
$this->filterPool = $filterPool;
$this->request = $request;
}
/**
* Get data
*
* @return array
*/
public function getData() {
if (!$this->loadedData) {
$storeId = (int) $this->request->getParam('store');
$this->collection->setStoreId($storeId)->addAttributeToSelect('*');
$items = $this->collection->getItems();
foreach ($items as $item) {
$item->setStoreId($storeId);
$this->loadedData[$item->getEntityId()] = $item->getData();
break;
}
}
return $this->loadedData;
}
}

Step - 32

Next, we will be creating app/code/MD/HelloWorld/Ui/Component/Listing/Column/Bookmark.php file to display different columns in UI grid:

<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
/**
* Code standard by : MD
*/
namespace MD\HelloWorld\Ui\Component\Listing\Column;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Ui\Api\BookmarkManagementInterface;
use Magento\Ui\Api\BookmarkRepositoryInterface;
class Bookmark extends \Magento\Ui\Component\Bookmark
{
/**
* @var \MD\HelloWorld\Model\HelloWorld
*/
protected $helloworld;
/**
* [__construct description]
* @param  ContextInterface                $context            [description]
* @param  \MD\HelloWorld\Model\HelloWorld $helloworld         [description]
* @param  BookmarkRepositoryInterface     $bookmarkRepository [description]
* @param  BookmarkManagementInterface     $bookmarkManagement [description]
* @param  {Array}  array                  $components         [description]
* @param  {Array}  array                  $data               [description]
*/
public function __construct(
ContextInterface $context,
\MD\HelloWorld\Model\HelloWorld $helloworld,
BookmarkRepositoryInterface $bookmarkRepository,
BookmarkManagementInterface $bookmarkManagement,
array $components = [],
array $data = []
) {
parent::__construct($context, $bookmarkRepository, $bookmarkManagement, $components, $data);
$this->helloworld = $helloworld;
}
/**
* Register component
*
* @return void
*
public function prepare()
{
$namespace = $this->getContext()->getRequestParam('namespace', $this->getContext()->getNamespace());
$config = [];
if (!empty($namespace)) {
$storeId = $this->getContext()->getRequestParam('store');
if (empty($storeId)) {
$storeId = $this->getContext()->getFilterParam('store_id');
}
$bookmarks = $this->bookmarkManagement->loadByNamespace($namespace);
/** @var \Magento\Ui\Api\Data\BookmarkInterface $bookmark */
foreach ($bookmarks->getItems() as $bookmark) {
if ($bookmark->isCurrent()) {
$config['activeIndex'] = $bookmark->getIdentifier();
}
$config = array_merge_recursive($config, $bookmark->getConfig());
if (!empty($storeId)) {
$config['current']['filters']['applied']['store_id'] = $storeId;
}
}
}
$this->setData('config', array_replace_recursive($config, $this->getConfiguration($this)));
parent::prepare();
$jsConfig = $this->getConfiguration($this);
$this->getContext()->addComponentDefinition($this->getComponentName(), $jsConfig);
}
}

Step - 33

After that, we will create app/code/MD/HelloWorld/Ui/Component/Listing/Column/HelloWorldActions.php file for mass action column in UI grid:

<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
/**
* Code standard by : MD
*/
namespace MD\HelloWorld\Ui\Component\Listing\Column;
use Magento\Framework\UrlInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Ui\Component\Listing\Columns\Column;
class HelloWorldActions extends Column {
/**
* Url path
*/
const URL_PATH_EDIT = 'md_helloworld/helloworld/edit';
const URL_PATH_DELETE = 'md_helloworld/helloworld/delete';
/**
* @var UrlInterface
*/
protected $urlBuilder;
/**
* [__construct description]
* @param ContextInterface   $context            [description]
* @param UiComponentFactory $uiComponentFactory [description]
* @param UrlInterface       $urlBuilder         [description]
* @param array              $components         [description]
* @param array              $data               [description]
*/
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
UrlInterface $urlBuilder,
array $components = [],
array $data = []
) {
$this->urlBuilder = $urlBuilder;
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* Prepare Data Source
*
* @param array $dataSource
* @return array
*/
public function prepareDataSource(array $dataSource) {
if (isset($dataSource['data']['items'])) {
$storeId = $this->context->getFilterParam('store_id');
foreach ($dataSource['data']['items'] as &$item) {
if (isset($item['entity_id'])) {
$item[$this->getData('name')]['edit'] = [
'href' => $this->urlBuilder->getUrl(
self::URL_PATH_EDIT,
['entity_id' => $item['entity_id'], 'store' => $storeId]
),
'label' => __('Edit'),
'hidden' => false,
];
$item[$this->getData('name')]['delete'] = [
'href' => $this->urlBuilder->getUrl(
self::URL_PATH_DELETE,
['entity_id' => $item['entity_id'], 'store' => $storeId]
),
'label' => __('Delete'),
'confirm' => [
'title' => __('Delete ' . $item['main_title']),
'message' => __('Are you sure you wan\'t to delete a ' . $item['main_title'] . ' record?'),
],
'hidden' => false,
];
}
}
}
return $dataSource;
}
}

Step - 34

Now, we need to create app/code/MD/HelloWorld/Ui/Component/Listing/DataProvider/Document.php file for primary id field name:

<?php
namespace MD\HelloWorld\Ui\Component\Listing\DataProvider;
class Document extends \Magento\Framework\View\Element\UiComponent\DataProvider\Document
{
protected $_idFieldName = 'entity_id';
public function getIdFieldName()
{
return $this->_idFieldName;
}
}

Step - 35

Next, we will create app/code/MD/HelloWorld/Ui/Component/Listing/DataProvider.php file for UI Grid data provider file:

<?php
namespace MD\HelloWorld\Ui\Component\Listing;
use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider as UiDataProvider;
class DataProvider extends UiDataProvider
{
protected function searchResultToOutput(SearchResultInterface $searchResult)
{
$searchResult->setStoreId($this->request->getParam('store', 0))->addAttributeToSelect('*'); // Add here needed EAV attributes to display on grid
return parent::searchResultToOutput($searchResult);
}
protected function prepareUpdateUrl()
{
$storeId = $this->request->getParam('store', 0);
if ($storeId) {
$this->data['config']['update_url'] = sprintf(
'%s%s/%s',
$this->data['config']['update_url'],
'store',
$storeId
);
}
return parent::prepareUpdateUrl();
}
}

Step - 36

After that, we need to create app/code/MD/HelloWorld/view/adminhtml/layout/md_helloworld_helloworld_add.php file for set UI Form layout for new record:

<?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="styles"/>
<body>
<referenceContainer name="content">
<uiComponent name="md_helloworld_helloworld_form"/>
</referenceContainer>
</body>
</page>

Step - 37

Now, create app/code/MD/HelloWorld/view/adminhtml/layout/md_helloworld_helloworld_edit.php file to set UI Form layout for edit record:

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-1column"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="page.main.actions">
<block class="Magento\Backend\Block\Store\Switcher" name="md_helloworld_helloworld.store.switcher" template="Magento_Backend::store/switcher.phtml" >
<action method="setUseConfirm">
<argument name="params" xsi:type="string">1</argument>
</action>
</block>
</referenceContainer>
<referenceContainer name="content">
<uiComponent name="md_helloworld_helloworld_form"/>
</referenceContainer>
</body>
</page>

Step - 38

Next step is to create app/code/MD/HelloWorld/view/adminhtml/layout/md_helloworld_helloworld_index.php file to set UI grid layout:

<?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="styles"/>
<body>
<referenceContainer name="page.main.actions">
<block class="Magento\Backend\Block\Store\Switcher" name="adminhtml.report.grid.store.switcher" template="Magento_Backend::store/switcher.phtml">
<arguments>
<argument name="use_confirm" xsi:type="string">0</argument>
<argument name="switch_websites" xsi:type="string">0</argument>
<argument name="switch_store_groups" xsi:type="string">0</argument>
<argument name="switch_store_views" xsi:type="string">1</argument>
</arguments>
</block>
</referenceContainer>
<referenceContainer name="content">
<uiComponent name="md_helloworld_helloworld_listing"/>
</referenceContainer>
</body>
</page>

Step - 39

After that, you need to create app/code/MD/HelloWorld/view/adminhtml/ui_component/md_helloworld_helloworld_form.php file For UI Form layout:

<?xml version="1.0"?>
<form 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">md_helloworld_helloworld_form.md_helloworld_helloworld_form_data_source</item>
<item name="deps" xsi:type="string">md_helloworld_helloworld_form.md_helloworld_helloworld_form_data_source</item>
</item>
<item name="config" xsi:type="array">
<item name="dataScope" xsi:type="string">data</item>
</item>
<item name="template" xsi:type="string">templates/form/collapsible</item>
<item name="label" xsi:type="string" translate="true">Hello World Form</item>
<item name="buttons" xsi:type="array">
<item name="back" xsi:type="string">MD\HelloWorld\Block\Adminhtml\HelloWorld\Edit\BackButton</item>
<item name="reset" xsi:type="string">MD\HelloWorld\Block\Adminhtml\HelloWorld\Edit\ResetButton</item>
<item name="delete" xsi:type="string">MD\HelloWorld\Block\Adminhtml\HelloWorld\Edit\DeleteButton</item>
<item name="save" xsi:type="string">MD\HelloWorld\Block\Adminhtml\HelloWorld\Edit\SaveButton</item>
<item name="save_and_continue" xsi:type="string">MD\HelloWorld\Block\Adminhtml\HelloWorld\Edit\SaveAndContinueButton</item>
</item>
</argument>
<dataSource name="md_helloworld_helloworld_form_data_source">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">MD\HelloWorld\Ui\Component\Form\HelloWorld\DataProvider</argument>
<argument name="name" xsi:type="string">md_helloworld_helloworld_form_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="submit_url" xsi:type="url" path="*/*/save"/>
<item name="validate_url" xsi:type="url" path="*/*/validate"/>
</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/form/provider</item>
</item>
</argument>
</dataSource>
<fieldset name="main_fieldset">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="label" xsi:type="string" translate="true">Main Information</item>
</item>
</argument>
<field name="main_title">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="dataType" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Main Title</item>
<item name="formElement" xsi:type="string">input</item>
<item name="source" xsi:type="string">main_title</item>
<item name="dataScope" xsi:type="string">main_title</item>
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">true</item>
</item>
</item>
</argument>
</field>
<!-- Add more field here -->
</fieldset>
</form>

Step - 40

Lastly, create app/code/MD/HelloWorld/view/adminhtml/ui_component/md_helloworld_helloworld_listing.php file for UI Grid layout:

<?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">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing_data_source</item>
<item name="deps" xsi:type="string">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing_data_source</item>
</item>
<item name="spinner" xsi:type="string">md_helloworld_helloworld_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 Record</item>
<item name="class" xsi:type="string">primary</item>
<item name="url" xsi:type="string">*/*/add</item>
</item>
</item>
</argument>
<dataSource name="md_helloworld_helloworld_listing_data_source">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">MD\HelloWorld\Ui\Component\Listing\DataProvider</argument>
<argument name="name" xsi:type="string">md_helloworld_helloworld_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>
</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" class="\MD\HelloWorld\Ui\Component\Listing\Column\Bookmark">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/controls/bookmarks/bookmarks</item>
<item name="storageConfig" xsi:type="array">
<item name="saveUrl" xsi:type="url" path="mui/bookmark/save"/>
<item name="deleteUrl" xsi:type="url" path="mui/bookmark/delete"/>
<item name="namespace" xsi:type="string">md_helloworld_helloworld_listing</item>
</item>
</item>
</argument>
</bookmark>
<columnsControls name="columns_controls"/>
<!-- <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">md_helloworld_helloworld_listing</item>
</item>
</item>
</argument>
</bookmark> -->
<component name="columns_controls">
<argument name="daa" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsData" xsi:type="array">
<item name="provider" xsi:type="string">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.md_helloworld_helloworld_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">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing_data_source</item>
<item name="chipsProvider" xsi:type="string">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.listing_top.listing_filters_chips</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">md_helloworld_helloworld_listing.md_helloworld_helloworld_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">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.md_helloworld_helloworld_columns</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">md_helloworld_helloworld_listing.md_helloworld_helloworld_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">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.listing_top.listing_filters</item>
<item name="imports" xsi:type="array">
<item name="visible" xsi:type="string">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.md_helloworld_helloworld_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">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.md_helloworld_helloworld_columns.ids</item>
<item name="indexField" xsi:type="string">entity_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="*/*/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="edit">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">edit</item>
<item name="label" xsi:type="string" translate="true">Edit</item>
<item name="callback" xsi:type="array">
<item name="provider" xsi:type="string">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.md_helloworld_helloworld_columns_editor</item>
<item name="target" xsi:type="string">editSelected</item>
</item>
</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">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.paging</item>
</item>
<item name="selectProvider" xsi:type="string">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.md_helloworld_helloworld_columns.ids</item>
</item>
</argument>
</paging>
</container>
<columns name="md_helloworld_helloworld_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">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current</item>
</item>
<item name="editorConfig" xsi:type="array">
<item name="selectProvider" xsi:type="string">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.md_helloworld_helloworld_columns.ids</item>
<item name="enabled" xsi:type="boolean">false</item>
<item name="indexField" xsi:type="string">entity_id</item>
<item name="clientConfig" xsi:type="array">
<item name="saveUrl" xsi:type="url" path="*/*/inlineEdit"/>
<item name="validateBeforeSave" xsi:type="boolean">false</item>
</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">md_helloworld_helloworld_listing.md_helloworld_helloworld_listing.md_helloworld_helloworld_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">md_helloworld_helloworld_listing.md_helloworld_helloworld_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>
</item>
</argument>
<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">entity_id</item>
</item>
</argument>
</selectionsColumn>
<column name="entity_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 name="sortOrder" xsi:type="number">20</item>
</item>
</argument>
</column>
<column name="main_title">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Main Title</item>
<item name="sortOrder" xsi:type="number">30</item>
</item>
</argument>
</column>
<!-- Add more columns here -->
<actionsColumn name="actions" class="MD\HelloWorld\Ui\Component\Listing\Column\HelloWorldActions">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="indexField" xsi:type="string">entity_id</item>
<item name="sortOrder" xsi:type="number">60</item>
</item>
</argument>
</actionsColumn>
</columns>
</listing>

Testing the Module

Now, let’s test the EAV module by executing the below commands:

php bin/magento s:up
php bin/magento s:s:d -f
php bin/magento c:c

Conclusion…

With that, we hope you have now finally learned exactly how to create EAV module in Magento 2.

If you found this tutorial useful, don’t forget to share it with others. And if you’ve any queries or questions, feel free to contact us for help.