Magento – Magento 2: store selector for custom module in admin form

adminformcustommagento2modulemultistore

Stuck in a new issue regarding adding store selector for my custom module in admin form –
I need to add a store selector and to add the values of a store in a different table as it is working with existing cms_block and cms_page modules

enter image description here

EDIT

I added a custom form in admin section without using ui_component and now added a store selector in form, now a new issue in my front is to save this store data in my custom modules store's table , my custom table is cms_job and my custom module's store table is cms_job_store

now further i want to save data in both these tables and to retrive data for Grid section which is generated using ui_component

please help me on this,

Thanks

Best Answer

First you need to create table in update script.

namespace Namespace\Module\Setup;
use Magento\Framework\Setup\UpgradeSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\DB\Ddl\Table;

class UpgradeSchema implements UpgradeSchemaInterface{
 public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context){
    $setup->startSetup();
        if (version_compare($context->getVersion(), '1.1.1') < 0) {
        $table = $setup->getConnection()->newTable(
            $setup->getTable('yourmodel_store')
        )->addColumn('yourmodel_id',Table::TYPE_INTEGER,null,
            ['unsigned' => true,'nullable' => false, 'primary' => true],
            'yourmodel ID'
        )->addColumn('store_id',Table::TYPE_SMALLINT,null,
            ['unsigned' => true, 'nullable' => false, 'primary' => true],
            'Store ID'
        )->addIndex($setup->getIdxName('brand_store', ['store_id']),
            ['store_id']
        )->addForeignKey(
            $setup->getFkName('yourmodel_store', 'yourmodel_id', 'yourmodel', 'model_id'),'yourmodel_id',
            $setup->getTable('yourmodel'),'yourmodel_id', Table::ACTION_CASCADE
        )->addForeignKey(
            $setup->getFkName('yourmodel_store', 'store_id', 'store', 'store_id'),'store_id',
            $setup->getTable('store'),'store_id', Table::ACTION_CASCADE
        )->setComment(
            'yourmodel To Store Linkage Table'
        );
        $setup->getConnection()->createTable($table);
    }    
    $setup->endSetup();
}

}

Now add 2 files at Namespace\Module\Model\ResourceModel\Modelname\Relation\Store ReadHandler and SaveHandler.

ReadHandler.php

namespace Namespace\Module\Model\ResourceModel\Modelname\Relation\Store;
use Namespace\Module\Model\ResourceModel\Modelname;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\EntityManager\Operation\ExtensionInterface;

class ReadHandler implements ExtensionInterface{
protected $metadataPool;
protected $resourcePage;
public function __construct(
    MetadataPool $metadataPool,
    Modelname $resourcePage
) {
    $this->metadataPool = $metadataPool;
    $this->resourcePage = $resourcePage;
}
public function execute($entity, $arguments = []){
    if ($entity->getId()) {
        $stores = $this->resourcePage->lookupStoreIds((int)$entity->getId());
        $entity->setData('store_id', $stores);
    }
    return $entity;
 }
}

SaveHandler.php

namespace Namespace\Module\Model\ResourceModel\Modelname\Relation\Store;

use Magento\Framework\EntityManager\Operation\ExtensionInterface;
use Namespace\Module\Api\Data\ModelnameInterface;
use Namespace\Module\Model\ResourceModel\Modelname;
use Magento\Framework\EntityManager\MetadataPool;

class SaveHandler implements ExtensionInterface{
protected $metadataPool;
protected $resourcePage;

public function __construct(
    MetadataPool $metadataPool,
    Modelname $resourcePage
) {
    $this->metadataPool = $metadataPool;
    $this->resourcePage = $resourcePage;
}
public function execute($entity, $arguments = [])
{
    $entityMetadata = $this->metadataPool->getMetadata(ModelnameInterface::class);
    $linkField = $entityMetadata->getLinkField();
    $connection = $entityMetadata->getEntityConnection();
    $oldStores = $this->resourcePage->lookupStoreIds((int)$entity->getId());
    $newStores = (array)$entity->getStores();
    if (empty($newStores)) {
        $newStores = (array)$entity->getStoreId();
    }
    $table = $this->resourcePage->getTable('table_store');
    $delete = array_diff($oldStores, $newStores);
    if ($delete) {
        $where = [
            $linkField . ' = ?' => (int)$entity->getData($linkField),
            'store_id IN (?)' => $delete,
        ];
        $connection->delete($table, $where);
    }
    $insert = array_diff($newStores, $oldStores);
    if ($insert) {
        $data = [];
        foreach ($insert as $storeId) {
            $data[] = [
                $linkField => (int)$entity->getData($linkField),
                'store_id' => (int)$storeId
            ];
        }
        $connection->insertMultiple($table, $data);
    }
    return $entity;
 }
}

Now add Interface at Namespace\Module\Api\Data\YourmodelInterface.php and add all getter and setter of your model with annotation.

namespace Namespace\Module\Api\Data;

/**
 * Yourmodel interface.
 * @api
 */
interface YourmodelInterface
{
    /**#@+
     * Constants for keys of data array. Identical to the name of the getter in snake case
     */
    const TABLE_ID          = 'table_id';
    const TITLE             = 'title';
 /**
 * Get ID
 *
 * @return int|null
 */
public function getId();
/**
 * Get Title
 *
 * @return string|null
 */
public function getTitle();
/**
 * Set ID
 *
 * @param int $id
 * @return \Namespace\Module\Api\Data\YourmodelInterface
 */
public function setId($id);
/**
 * Set Title
 *
 * @param string $title
 * @return \Namespace\Module\Api\Data\YourmodelInterface
 */
public function setTitle($title);
}

now add RepositoryInterface of your model at Namespace\Module\Api\YourmodelRepositoryInterface.php

namespace Namespace\Module\Api;

use Namespace\Module\Api\Data\YourmodelInterface;
use Magento\Framework\Api\SearchCriteriaInterface;

interface YourmodelRepositoryInterface{
public function save(YourmodelInterface $page);
public function getById($id);
public function getList(SearchCriteriaInterface $criteria);
public function delete(YourmodelInterface $page);
public function deleteById($id);
}

Now add model repository at Namespace\Module\Model\YourmodelRepository.php

namespace Namespace\Module\Model;

use Namespace\Module\Api\YourmodelRepositoryInterface;
use Namespace\Module\Api\Data\YourmodelInterface;
use Namespace\Module\Model\YourmodelFactory;
use Namespace\Module\Model\ResourceModel\Yourmodel\CollectionFactory;
use Magento\Framework\Api\DataObjectHelper;
use Magento\Framework\Reflection\DataObjectProcessor;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\Api\SearchResultsInterfaceFactory;
class YourmodelRepository implements YourmodelRepositoryInterface
{
    protected $objectFactory;
    protected $dataPageFactory;
    protected $dataObjectHelper;
    protected $dataObjectProcessor;
    protected $collectionFactory;
    public function __construct(
        YourmodelFactory $objectFactory,
        CollectionFactory $collectionFactory,
        DataObjectHelper $dataObjectHelper,   
        DataObjectProcessor $dataObjectProcessor,    
        \Namespace\Module\Api\Data\YourmodelInterfaceFactory $dataPageFactory,
        SearchResultsInterfaceFactory $searchResultsFactory       
    )
    {
        $this->objectFactory        = $objectFactory;
        $this->dataObjectHelper = $dataObjectHelper;
        $this->dataPageFactory = $dataPageFactory;
        $this->dataObjectProcessor = $dataObjectProcessor;
        $this->collectionFactory    = $collectionFactory;
        $this->searchResultsFactory = $searchResultsFactory;
    }

public function save(YourmodelInterface $object)
{
    if (empty($object->getStoreId())) {
        $storeId = $this->storeManager->getStore()->getId();
        $object->setStoreId($storeId);
    }
    try {
        $this->resource->save($object);
    } catch (\Exception $e) {
        throw new CouldNotSaveException(__(
            'Could not save the brand: %1',
            $e->getMessage()
        ));
    }
    return $object;
}
public function getById($id)
{
    $object = $this->objectFactory->create();
    $object->load($id);
    if (!$object->getId()) {
        throw new NoSuchEntityException(__('Object with id "%1" does not exist.', $id));
    }
    return $object;        
}
public function delete(YourmodelInterface $object){
    try {
        $object->delete();
    } catch (\Exception $e) {
        throw new CouldNotDeleteException(__($e->getMessage()));
    }
    return true;    
}    
public function deleteById($id){
    return $this->delete($this->getById($id)); }    

public function getList(SearchCriteriaInterface $criteria){
    $searchResults = $this->searchResultsFactory->create();
    $searchResults->setSearchCriteria($criteria);
    $collection = $this->collectionFactory->create();
    foreach ($criteria->getFilterGroups() as $filterGroup) {
        foreach ($filterGroup->getFilters() as $filter) {
            if ($filter->getField() === 'store_id') {
                $collection->addStoreFilter($filter->getValue(), false);
                continue;
            }
            $condition = $filter->getConditionType() ?: 'eq';
            $collection->addFieldToFilter($filter->getField(), [$condition => $filter->getValue()]);
        }
    }
    $searchResults->setTotalCount($collection->getSize());
    $sortOrders = $criteria->getSortOrders();
    if ($sortOrders) {
        /** @var SortOrder $sortOrder */
        foreach ($sortOrders as $sortOrder) {
            $collection->addOrder(
                $sortOrder->getField(),
                ($sortOrder->getDirection() == SortOrder::SORT_ASC) ? 'ASC' : 'DESC'
            );
        }
    }
    $collection->setCurPage($criteria->getCurrentPage());
    $collection->setPageSize($criteria->getPageSize());
    $pages = [];
    foreach ($collection as $pageModel) {
        $pageData = $this->dataPageFactory->create();
        $this->dataObjectHelper->populateWithArray(
            $pageData,
            $pageModel->getData(),
            'Namespace\Module\Api\Data\YourmodelInterface'
        );
        $pages[] = $this->dataObjectProcessor->buildOutputDataArray(
            $pageData,
            'Namespace\Module\Api\Data\YourmodelInterface'
        );
    }
    $searchResults->setItems($pages);
    return $searchResults;
 }
}

Now add Resourcemodel at Namespace\Module\Model\ResourceModel\Yourmodel.php;

namespace Namespace\Module\Model\ResourceModel;

use Magento\Framework\Model\ResourceModel\Db\Context;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\Stdlib\DateTime;
use Magento\Framework\EntityManager\EntityManager;
use Magento\Framework\EntityManager\MetadataPool;
use Namespace\Module\Api\Data\ModelnameInterface;
use Magento\Framework\Model\AbstractModel;
use Magento\Framework\DB\Select;

class Yourmodel extends   \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
    protected $_store = null;
    protected $_storeManager;
    protected $dateTime;
    protected $entityManager;
    protected $metadataPool;

public function __construct(
    Context $context,
    StoreManagerInterface $storeManager,
    DateTime $dateTime,
    EntityManager $entityManager,
    MetadataPool $metadataPool,
    $connectionName = null
) {
    parent::__construct($context, $connectionName);
    $this->_storeManager = $storeManager;
    $this->dateTime = $dateTime;
    $this->entityManager = $entityManager;
    $this->metadataPool = $metadataPool;
}

protected function _construct()
{
    $this->_init('yourtable','table_id');
}

public function getConnection()
{
    return $this->metadataPool->getMetadata(ModelnameInterface::class)->getEntityConnection();
}

private function getModelnameId(AbstractModel $object, $value, $field = null)
{
    $entityMetadata = $this->metadataPool->getMetadata(ModelnameInterface::class);

    if (!is_numeric($value) && $field === null) {
        $field = 'table_id';
    } elseif (!$field) {
        $field = $entityMetadata->getIdentifierField();
    }

    $pageId = $value;
    if ($field != $entityMetadata->getIdentifierField() || $object->getStoreId()) {
        $select = $this->_getLoadSelect($field, $value, $object);
        $select->reset(Select::COLUMNS)
            ->columns($this->getMainTable() . '.' . $entityMetadata->getIdentifierField())
            ->limit(1);
        $result = $this->getConnection()->fetchCol($select);
        $pageId = count($result) ? $result[0] : false;
    }
    return $pageId;
}

public function load(AbstractModel $object, $value, $field = null)
{
    $pageId = $this->getModelnameId($object, $value, $field);
    if ($pageId) {
        $this->entityManager->load($object, $pageId);
    }
    return $this;
}

protected function _getLoadSelect($field, $value, $object)
{
    $entityMetadata = $this->metadataPool->getMetadata(ModelnameInterface::class);
    $linkField = $entityMetadata->getLinkField();

    $select = parent::_getLoadSelect($field, $value, $object);

    if ($object->getStoreId()) {
        $storeIds = [
            Store::DEFAULT_STORE_ID,
            (int)$object->getStoreId(),
        ];
        $select->join(
            ['yourmodel_store' => $this->getTable('yourmodel_store')],
            $this->getMainTable() . '.' . $linkField . ' = yourmodel_store.' . $linkField,
            []
        )
            ->where('is_active = ?', 1)
            ->where('yourmodel_store.store_id IN (?)', $storeIds)
            ->order('yourmodel_store.store_id DESC')
            ->limit(1);
    }

    return $select;
}
public function lookupStoreIds($pageId)
{
    $connection = $this->getConnection();

    $entityMetadata = $this->metadataPool->getMetadata(ModulenameInterface::class);
    $linkField = $entityMetadata->getLinkField();

    $select = $connection->select()
        ->from(['cps' => $this->getTable('yourmodel_store')], 'store_id')
        ->join(
            ['cp' => $this->getMainTable()],
            'cps.' . $linkField . ' = cp.' . $linkField,
            []
        )
        ->where('cp.' . $entityMetadata->getIdentifierField() . ' = :yourmodel_id');

    return $connection->fetchCol($select, ['yourmodel_id' => (int)$pageId]);
}

public function setStore($store)
{
    $this->_store = $store;
    return $this;
}

public function getStore()
{
    return $this->_storeManager->getStore($this->_store);
}

public function save(AbstractModel $object)
{
    $this->entityManager->save($object);
    return $this;
}

public function delete(AbstractModel $object)
{
    $this->entityManager->delete($object);
    return $this;
 }
}

now add Collection.php at Namespace\Module\Model\ResourceModel\Yourmodel\Collection.php

namespace Namespace\Module\Model\ResourceModel\Yourmodel;

use Namespace\Module\Api\Data\YourmodelInterface;
use Namespace\Module\Model\ResourceModel\AbstractCollection;

class Collection extends AbstractCollection
{
protected $_idFieldName = 'yourmodel_id';
protected $_previewFlag;

protected function _construct()
{
    $this->_init('Namespace\Module\Model\Yourmodel','Namespace\Module\Model\ResourceModel\Yourmodel');
    $this->_map['fields']['yourmodel_id'] = 'main_table.yourmodel_id';
    $this->_map['fields']['store'] = 'store_table.store_id';
}

public function addStoreFilter($store, $withAdmin = true)
{
    if (!$this->getFlag('store_filter_added')) {
        $this->performAddStoreFilter($store, $withAdmin);
    }
    return $this;
}

public function setFirstStoreFlag($flag = false)
{
    $this->_previewFlag = $flag;
    return $this;
}

protected function _afterLoad()
{
    $entityMetadata = $this->metadataPool->getMetadata(YourmodelInterface::class);
    $this->performAfterLoad('yourmodel_store', $entityMetadata->getLinkField());
    $this->_previewFlag = false;

    return parent::_afterLoad();
}

protected function _renderFiltersBefore()
{
    $entityMetadata = $this->metadataPool->getMetadata(YourmodelInterface::class);
    $this->joinStoreRelationTable('yourmodel_store', $entityMetadata->getLinkField());
 }
}

Now add Abstract collection at Namespace\Module\Model\ResourceModel\AbstractCollection.php

namespace Namespace\Module\Model\ResourceModel;

use Magento\Store\Model\Store;

abstract class AbstractCollection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{

protected $storeManager;

protected $metadataPool;

public function __construct(
    \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory,
    \Psr\Log\LoggerInterface $logger,
    \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
    \Magento\Framework\Event\ManagerInterface $eventManager,
    \Magento\Store\Model\StoreManagerInterface $storeManager,
    \Magento\Framework\EntityManager\MetadataPool $metadataPool,
    \Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
    \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
) {
    $this->storeManager = $storeManager;
    $this->metadataPool = $metadataPool;
    parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource);
}

protected function performAfterLoad($tableName, $linkField)
{
    $linkedIds = $this->getColumnValues($linkField);
    if (count($linkedIds)) {
        $connection = $this->getConnection();
        $select = $connection->select()->from(['yourmodel_store' => $this->getTable($tableName)])
            ->where('yourmodel_store.' . $linkField . ' IN (?)', $linkedIds);
        $result = $connection->fetchAll($select);
        if ($result) {
            $storesData = [];
            foreach ($result as $storeData) {
                $storesData[$storeData[$linkField]][] = $storeData['store_id'];
            }

            foreach ($this as $item) {
                $linkedId = $item->getData($linkField);
                if (!isset($storesData[$linkedId])) {
                    continue;
                }
                $storeIdKey = array_search(Store::DEFAULT_STORE_ID, $storesData[$linkedId], true);
                if ($storeIdKey !== false) {
                    $stores = $this->storeManager->getStores(false, true);
                    $storeId = current($stores)->getId();
                    $storeCode = key($stores);
                } else {
                    $storeId = current($storesData[$linkedId]);
                    $storeCode = $this->storeManager->getStore($storeId)->getCode();
                }
                $item->setData('_first_store_id', $storeId);
                $item->setData('store_code', $storeCode);
                $item->setData('store_id', $storesData[$linkedId]);
            }
        }
    }
}

public function addFieldToFilter($field, $condition = null)
{
    if ($field === 'store_id') {
        return $this->addStoreFilter($condition, false);
    }

    return parent::addFieldToFilter($field, $condition);
}

abstract public function addStoreFilter($store, $withAdmin = true);

protected function performAddStoreFilter($store, $withAdmin = true)
{
    if ($store instanceof Store) {
        $store = [$store->getId()];
    }

    if (!is_array($store)) {
        $store = [$store];
    }

    if ($withAdmin) {
        $store[] = Store::DEFAULT_STORE_ID;
    }

    $this->addFilter('store', ['in' => $store], 'public');
}

protected function joinStoreRelationTable($tableName, $linkField)
{
    if ($this->getFilter('store')) {
        $this->getSelect()->join(
            ['store_table' => $this->getTable($tableName)],
            'main_table.' . $linkField . ' = store_table.' . $linkField,
            []
        )->group(
            'main_table.' . $linkField
        );
    }
    parent::_renderFiltersBefore();
}
public function getSelectCountSql()
{
    $countSelect = parent::getSelectCountSql();
    $countSelect->reset(\Magento\Framework\DB\Select::GROUP);
    return $countSelect;
 }
}

Now add Model class at Namespace\Module\Model\Yourmodel.php and add all your get and set which are defined in Yourmoduleinterface.

namespace Namespace\Module\Model;
use Namespace\Module\Api\Data\YourmodelInterface as Bi;
class Yourmodel extends \Magento\Framework\Model\AbstractModel implements  Bi, \Magento\Framework\DataObject\IdentityInterface
{
const CACHE_TAG = 'modelname';
protected function _construct()
{
    $this->_init('Namespace\Module\Model\ResourceModel\Yourmodel');
}
public function getIdentities()
{
    return [$this->getId()];
}
public function getStores()
{
    return $this->hasData('stores') ? $this->getData('stores') : (array)$this->getData('store_id');
}
public function getId()
{
    return parent::getData(self::TABLE_ID);
}
public function getTitle()
{
    return $this->getData(self::TITLE);
}
public function setId($id)
{
    return $this->setData(self::TABLE_ID, $id);
}
public function setTitle($title)
{
    return $this->setData(self::TITLE, $title);
 }
 }

now Add Data provider at Namespace\Module\Model\DataProvider.php

namespace Namespace\Module\Model;

use Namespace\Module\Model\ResourceModel\Yourmodel\CollectionFactory;
use Magento\Framework\App\Request\DataPersistorInterface;

class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider {

protected $collection;
protected $dataPersistor;
protected $loadedData;

public function __construct(
    $name,
    $primaryFieldName,
    $requestFieldName,
    CollectionFactory $pageCollectionFactory,
    DataPersistorInterface $dataPersistor,
    array $meta = [],
    array $data = []
) {
    $this->collection = $pageCollectionFactory->create();
    $this->dataPersistor = $dataPersistor;
    parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
    $this->meta = $this->prepareMeta($this->meta);
}
 public function prepareMeta(array $meta)
{
    return $meta;
}
public function getData() {
    if (isset($this->loadedData)) {
        return $this->loadedData;
    }
    $items = $this->collection->getItems();
    foreach ($items as $customRow) {
        $brand = $customRow->getData();
        $this->loadedData[$customRow->getId()] = $brand;
    }
    return $this->loadedData;
}
}

Now add di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Namespace\Module\Api\YourmodelRepositoryInterface" type="Namespace\Module\Model\YourmodelRepository" />
<preference for="Namespace\Module\Api\Data\YourmodelInterface" type="Namespace\Module\Model\Yourmodel" />

<virtualType name="Namespace\Module\Model\Resource\Test\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult">
    <arguments>
        <argument name="mainTable" xsi:type="string">yourtabel</argument>
        <argument name="resourceModel" xsi:type="string">Namespace\Module\Model\ResourceModel\Yourmodel</argument>
    </arguments>
</virtualType>

<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
    <arguments>
        <argument name="collections" xsi:type="array">
            <item name="name_module_grid_data_source" xsi:type="string">Namespace\Module\Model\ResourceModel\Yourmodel\Grid\Collection</item>
        </argument>
    </arguments>
</type>
<virtualType name="CustomGridFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool">
 <arguments>
    <argument name="appliers" xsi:type="array">
        <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item>
        <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item>
    </argument>
 </arguments>
</virtualType> 

<virtualType name="Namespace\Module\Model\DataProvi" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
 <arguments>
    <argument name="collection" xsi:type="object" shared="false">Namespace\Module\Model\ResourceModel\Yourmodel\Collection</argument>
    <argument name="filterPool" xsi:type="object" shared="false">CustomGridFilterPool</argument>
 </arguments>
</virtualType>
 <type name="Namespace\Module\Model\ResourceModel\Yourmodel\Grid\Collection">
    <arguments>
        <argument name="mainTable" xsi:type="string">yourtable</argument>
        <argument name="eventPrefix" xsi:type="string">name_module_grid_collection</argument>
        <argument name="eventObject" xsi:type="string">model_grid_collection</argument>
        <argument name="resourceModel" xsi:type="string">Namespace\Module\Model\ResourceModel\Yourmodel</argument>
    </arguments>
</type>

<type name="Magento\Framework\Model\Entity\RepositoryFactory">
    <arguments>
        <argument name="entities" xsi:type="array">
            <item name="Namespace\Module\Api\Data\YourmodelInterface" xsi:type="string">Namespace\Module\Api\YourmodelRepositoryInterface</item>
        </argument>
    </arguments>
</type> 
<type name="Magento\Framework\EntityManager\MetadataPool">
    <arguments>
        <argument name="metadata" xsi:type="array">
            <item name="Namespace\Module\Api\Data\YourmodelInterface" xsi:type="array">
                <item name="entityTableName" xsi:type="string">yourtabel</item>
                <item name="identifierField" xsi:type="string">tabel_id</item>
            </item>
        </argument>
    </arguments>
</type>
<type name="Magento\Framework\EntityManager\Operation\ExtensionPool">
    <arguments>
        <argument name="extensionActions" xsi:type="array">
            <item name="Namespace\Module\Api\Data\YourmodelInterface" xsi:type="array">
                <item name="read" xsi:type="array">
                    <item name="storeReader" xsi:type="string">Namespace\Module\Model\ResourceModel\Yourmodel\Relation\Store\ReadHandler</item>
                </item>
                <item name="create" xsi:type="array">
                    <item name="storeCreator" xsi:type="string">Namespace\Module\Model\ResourceModel\Yourmodel\Relation\Store\SaveHandler</item>
                </item>
                <item name="update" xsi:type="array">
                    <item name="storeUpdater" xsi:type="string">Namespace\Module\Model\ResourceModel\Yourmodel\Relation\Store\SaveHandler</item>
                </item>
            </item>
       </argument>
    </arguments>
</type>
 <type name="Magento\Framework\EntityManager\HydratorPool">
    <arguments>
        <argument name="hydrators" xsi:type="array">
            <item name="Namespace\Module\Api\Data\YourmodelInterface" xsi:type="string">Magento\Framework\EntityManager\AbstractModelHydrator</item>
        </argument>
    </arguments>
</type>
 </config>

add collection.php at Namespace\Module\Model\ResourceModel\Yourmodel\Grid\Collection.php copy file from Magento\Cms\Model\ResourceModel\Page\Grid\Collection.php;

Related Topic