Magento 2.1 Adminhtml Grid Error

adminadminhtmlgridmagento-2.1uicomponent

Having trouble getting an Adminhtml grid setup for a basic entity. Im receiving this error and I'm not sure why? I have debugged all the way through but can't seem to figure it out.

Fatal error: Method Magento\Ui\TemplateEngine\Xhtml\Result::__toString() must not throw an exception, caught TypeError: Argument 1 passed to Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider::searchResultToOutput() must implement interface Magento\Framework\Api\Search\SearchResultInterface, instance of Lewis\Roundel\Model\ResourceModel\Roundel\Collection given, called in /var/www/mage2playground.dev/htdocs/vendor/magento/framework/View/Element/UiComponent/DataProvider/DataProvider.php on line 284

Here is my table setup:

class InstallSchema implements InstallSchemaInterface
{
    public function install(
        SchemaSetupInterface $setup,
        ModuleContextInterface $context
    ) {
        $installer = $setup;
        $installer->startSetup();

        $table = $installer->getConnection()->newTable(
            $installer->getTable('lewis_roundel')
        )->addColumn(
            'entity_id', Table::TYPE_SMALLINT, null, array(
            'identity' => true,
            'nullable' => null,
            'primary'  => true,
        ), 'Unique Record Id'
        )->addColumn(
            'roundel_name', Table::TYPE_TEXT, 255, array(
            'nullable' => false,
        ), 'Roundel Name'
        )->addColumn(
            'type', Table::TYPE_TEXT, 255, array(
            'nullable' => false,
        ), 'Roundel Type (Image/Handmade)'
        )->addColumn(
            'options', Table::TYPE_TEXT, null, array(
            'nullable' => false,
        ), 'Roundel Options'
        )->addColumn(
            'position', Table::TYPE_TEXT, 255, array(
            'nullable' => false,
        ), 'Position'
        )->addColumn(
            'status', Table::TYPE_INTEGER, null, array(
            'nullable' => false,
        ), 'Status'
        )->addColumn(
            'created_at', Table::TYPE_TIMESTAMP, null, array(
            'nullable' => true,
        ), 'Created At'
        )->addColumn(
            'updated_at', Table::TYPE_TIMESTAMP, null, array(
            'nullable' => true,
        ), 'Updated At'
        )->setComment('Lewis Roundel Manager Table');
        $installer->getConnection()->createTable($table);
        $installer->endSetup();
    }
}

My Models

<?php

namespace Lewis\Roundel\Model;

use Lewis\Roundel\Api\Data\RoundelInterface;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\Model\AbstractModel;
use Lewis\Roundel\Model\ResourceModel\Roundel as ResourceRoundel;

class Roundel extends AbstractModel implements IdentityInterface, RoundelInterface
{
    const CACHE_TAG = 'lewis_roundel_roundel';

    protected $_cacheTag = 'lewis_roundel_roundel';

    protected $_eventPrefix = 'lewis_roundel_roundel';

    protected function _construct()
    {
        $this->_init(ResourceRoundel::class);
    }

    public function getIdentities()
    {
        return [self::CACHE_TAG . '_' . $this->getId()];
    }
}

Resource Model

namespace Lewis\Roundel\Model\ResourceModel;

use Magento\Framework\Model\AbstractModel;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;

class Roundel extends AbstractDb
{
    protected function _construct()
    {
        $this->_init('lewis_roundel', 'entity_id');
    }
}

Collection Resource Model

namespace Lewis\Roundel\Model\ResourceModel\Roundel;

use Lewis\Roundel\Model\Roundel;
use Lewis\Roundel\Model\ResourceModel\Roundel as ResourceRoundel;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;

class Collection extends AbstractCollection
{

    protected function _construct()
    {
        $this->_init(Roundel::class, ResourceRoundel::class);
    }
}

My 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">
    <virtualType name="RoundelGridDataProvider" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
        <arguments>
            <argument name="collection" xsi:type="object" shared="false">Lewis\Roundel\Model\ResourceModel\Roundel\Collection</argument>
        </arguments>
    </virtualType>
    <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
        <arguments>
            <argument name="collections" xsi:type="array">
                <item name="roundel_listing_data_source" xsi:type="string">Lewis\Roundel\Model\ResourceModel\Roundel\Collection</item>
            </argument>
        </arguments>
    </type>
</config>

And my ui component

<?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">roundel_listing.roundel_listing_data_source</item>
            <item name="deps" xsi:type="string">roundel_listing.roundel_listing_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">roundel_listing</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 Roundel</item>
                <item name="class" xsi:type="string">primary</item>
                <item name="url" xsi:type="string">*/*/new</item>
            </item>
        </item>
    </argument>
    <dataSource name="roundel_listing_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">RoundelGridDataProvider</argument>
            <argument name="name" xsi:type="string">roundel_listing_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">entity_id</argument>
            <argument name="requestFieldName" xsi:type="string">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>
    <columns name="roundel_listing_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">roundel_listing.roundel_listing.listing_top.bookmarks</item>
                    <item name="namespace" xsi:type="string">current</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>
            </argument>
        </column>
        <column name="roundel_name">
            <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">Name</item>
                </item>
            </argument>
        </column>
        <column name="type">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">Lewis\Roundel\Ui\Component\Roundel\Types</item>
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">select</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
                    <item name="dataType" xsi:type="string">select</item>
                    <item name="label" xsi:type="string" translate="true">Type</item>
                </item>
            </argument>
        </column>
        <column name="render">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string" translate="true">Roundel Quick View</item>
                </item>
            </argument>
        </column>
        <column name="position">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">Lewis\Roundel\Ui\Component\Roundel\Status</item>
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">select</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
                    <item name="dataType" xsi:type="string">select</item>
                    <item name="label" xsi:type="string" translate="true">Position</item>
                </item>
            </argument>
        </column>
        <column name="status">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">Lewis\Roundel\Ui\Component\Roundel\Positions</item>
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">select</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
                    <item name="dataType" xsi:type="string">select</item>
                    <item name="label" xsi:type="string" translate="true">Status</item>
                </item>
            </argument>
        </column>
        <column name="updated_at" class="Magento\Ui\Component\Listing\Columns\Date">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">dateRange</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
                    <item name="dataType" xsi:type="string">date</item>
                    <item name="label" xsi:type="string" translate="true">Updated At</item>
                    <item name="visible" xsi:type="boolean">false</item>
                </item>
            </argument>
        </column>
        <actionsColumn name="actions" class="Lewis\Roundel\Ui\Component\Listing\Column\RoundelActions">
            <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">107</item>
                    <item name="indexField" xsi:type="string">entity_id</item>
                </item>
            </argument>
        </actionsColumn>
    </columns>
</listing>

Where am i going wrong 🙁 ?

I tried to implement the SearchResultsInterface on my collection but this ended up with an error in this piece of core code

Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider.php

protected function searchResultToOutput(SearchResultInterface $searchResult)
    {
        $arrItems = [];

        $arrItems['items'] = [];
        foreach ($searchResult->getItems() as $item) {
            $itemData = [];
            foreach ($item->getCustomAttributes() as $attribute) {
                $itemData[$attribute->getAttributeCode()] = $attribute->getValue();
            }
            $arrItems['items'][] = $itemData;
        }

        $arrItems['totalRecords'] = $searchResult->getTotalCount();

        return $arrItems;
    }

Basically The Item is an Object of my Roundel Model – but it doesn't have any custom attributes – I dont see why it needs them or even if it did, where to put them – any explanation around this would also help.

Thanks

Best Answer

Ok i have solved using a mixed of these two resources -

The first link simply describes how to use each type of API interfaces and the necessary concrete implementations.

The second is a Sample module that helped me get around using deprecated methods and the repository pattern etc.