Magento 2 – Fatal Error: Call to Undefined Method addField()

gridlistingmagento2uicomponent

I'm trying to populate a grid with data from DB. I've looked at other examples, tried appending _listing to the xml that renders the grid, tried just about everything I can think of and still can't get it to work.

The full error I get is:

Fatal error: Call to undefined method Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider::addField() in Magento2/vendor/magento/module-ui/Component/Listing/Columns/Column.php on line 116

Magento\Ui\Component\Listing\Columns\Column->addFieldToSelect( ) calls addField() which is not defined. Even if I define it (by extending AbstractDataProvider if I recall right) $this->getCollection()->addFieldToSelect($field, $alias); throws an error. Because it can't get the collection. It's null. But digging and tinkering through the source code is of no use. I must've missed something and I have no idea what it might be.

I've seen modules with custom DataProvider classes, i.e. di.xml has nothing to do with the dataSource defined in the listing XML. I've tried that as well but without luck.

This is my grid(or listing, if you prefer) – so_grid.xml:

<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
    <item name="js_config" xsi:type="array">
        <item name="provider" xsi:type="string">so_grid.so_grid_data_source</item>
        <item name="deps" xsi:type="string">so_grid.so_grid_data_source</item>
    </item>
    <item name="spinner" xsi:type="string">listing_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 Item</item>
            <item name="class" xsi:type="string">primary</item>
            <item name="url" xsi:type="string">*/*/new</item>
        </item>
    </item>
</argument>
<!-- Data Source -->
<dataSource name="so_grid_data_source">
    <argument name="dataProvider" xsi:type="configurableObject">
        <argument name="class" xsi:type="string">TestDataProvider</argument>
        <argument name="name" xsi:type="string">so_grid_data_source</argument>
        <argument name="primaryFieldName" xsi:type="string">entity_id</argument>
        <argument name="requestFieldName" xsi:type="string">request_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>
<listingToolbar name="listing_top">
    <!-- Filters -->
    <filters name="listing_filters">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="columnsProvider" xsi:type="string">so_grid.so_grid.listing_columns</item>
                <item name="storageConfig" xsi:type="array">
                    <item name="provider" xsi:type="string">so_grid.so_grid.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">so_grid.so_grid.listing_top.listing_filters</item>
                    <item name="imports" xsi:type="array">
                        <item name="visible" xsi:type="string">so_grid.so_grid.listing_columns.${ $.index }:visible</item>
                    </item>
                </item>
            </item>
            <item name="observers" xsi:type="array">
                <item name="column" xsi:type="string">column</item>
            </item>
        </argument>
    </filters>
    <!-- Paging -->
    <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">so_grid.so_grid.listing_top.bookmarks</item>
                    <item name="namespace" xsi:type="string">current.paging</item>
                </item>
                <item name="selectProvider" xsi:type="string">so_grid.so_grid.listing_columns.ids</item>
            </item>
        </argument>
    </paging>
</listingToolbar>
<!-- Columns -->
<columns name="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">so_grid.so_grid.listing_top.bookmarks</item>
                <item name="namespace" xsi:type="string">current</item>
            </item>
            <item name="childDefaults" xsi:type="array">
                <item name="storageConfig" xsi:type="array">
                    <item name="provider" xsi:type="string">so_grid.so_grid.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 name="fieldAction" xsi:type="array">
                    <item name="provider" xsi:type="string">so_grid.so_grid.listing_columns.actions</item>
                    <item name="target" xsi:type="string">applyAction</item>
                    <item name="params" xsi:type="array">
                        <item name="0" xsi:type="string">edit</item>
                        <item name="1" xsi:type="string">${ $.$data.rowIndex }</item>
                    </item>
                </item>
            </item>
        </item>
    </argument>
    <column name="name">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="filter" xsi:type="string">text</item>
                <item name="add_field" xsi:type="boolean">true</item>
                <item name="label" xsi:type="string" translate="true">Name</item>
                <item name="sortOrder" xsi:type="number">30</item>
            </item>
        </argument>
    </column>
</columns>

I have a working routing; the InstallSchema ran fine; there's a model, resourceModel and a collection. Let me know if I you need me to provide the code for any of it.

Best Answer

I got it working! It turned out I was missing a lot of things(__constructors, di config, xml nodes for the grid/listing). Have a look at the code below.

Model:

<?php

namespace Back\Hand\Model;

class TestItem extends \Magento\Framework\Model\AbstractModel
{
    protected function _construct()
    {
        $this->_init('Back\Hand\Model\ResourceModel\TestItem');
    }
}

ResourceModel:

<?php

namespace Back\Hand\Model\ResourceModel;

class TestItem extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
    protected function _construct()
    {
        $this->_init('test_entity', 'entity_id');
    }
}

Collection:

<?php

namespace Back\Hand\Model\ResourceModel\TestItem;

class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
    protected $_idFieldName = 'id';

    protected $_storeManager;

    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\DB\Adapter\AdapterInterface $connection = null,
        \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
    ) {
        parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource);
        $this->_storeManager = $storeManager;
    }

    protected function _construct()
    {
        $this->_init('Back\Hand\Model\TestItem', 'Back\Hand\Model\ResourceModel\TestItem');
        $this->_map['fields']['entity_id'] = 'main_table.id';
    }

}

Grid:

<?php

namespace Back\Hand\Model\ResourceModel\TestItem\Grid;

use Magento\Framework\Api\Search\SearchResultInterface as SearchInterface;
use Magento\Framework\Search\AggregationInterface;
use Back\Hand\Model\ResourceModel\TestItem\Collection as TestItemCollection;

class Collection extends TestItemCollection implements SearchInterface
{
    public function __construct(
        \Magento\Framework\Data\Collection\EntityFactoryInterface $entity,
        \Psr\Log\LoggerInterface $logger,
        \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetch,
        \Magento\Framework\Event\ManagerInterface $event,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        $mainTable,
        $eventPrefix,
        $eventObject,
        $resourceModel,
        $model = 'Magento\Framework\View\Element\UiComponent\DataProvider\Document',
        $connection = null,
        \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
    ) {
        parent::__construct(
            $entity,
            $logger,
            $fetch,
            $event,
            $storeManager,
            $connection,
            $resource
        );
        $this->_eventPrefix = $eventPrefix;
        $this->_eventObject = $eventObject;
        $this->_init($model, $resourceModel);
        $this->setMainTable($mainTable);
    }

    public function getAggregations()
    {
        return $this->_aggregations;
    }

    public function setAggregations($aggregations)
    {
        $this->_aggregations = $aggregations;
    }

    public function getAllIds($limitCount = null, $offset = null)
    {
        return $this->getConnection()->fetchCol($this->_getAllIdsSelect($limitCount, $offset), $this->_bindParams);
    }

    public function getSearchCriteria()
    {
        return null;
    }

    public function setSearchCriteria(\Magento\Framework\Api\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;
    }
}

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">
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
    <arguments>
        <argument name="collections" xsi:type="array">
            <item name="so_grid_data_source" xsi:type="string">Back\Hand\Model\ResourceModel\TestItem\Grid\Collection</item>
        </argument>
    </arguments>
</type>

<type name="Back\Hand\Model\ResourceModel\TestItem\Grid\Collection">
    <arguments>
        <argument name="mainTable" xsi:type="string">test_entity</argument>
        <argument name="eventPrefix" xsi:type="string">testItem_grid_collection</argument>
        <argument name="eventObject" xsi:type="string">testItem_grid_collection</argument>
        <argument name="resourceModel" xsi:type="string">Back\Hand\Model\ResourceModel\TestItem</argument>
    </arguments>
</type>
</config>

so_grid.xml:

<?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">so_grid.so_grid_data_source</item>
        <item name="deps" xsi:type="string">so_grid.so_grid_data_source</item>
    </item>
    <item name="spinner" xsi:type="string">listing_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 Page</item>
            <item name="class" xsi:type="string">primary</item>
            <item name="url" xsi:type="string">*/*/new</item>
        </item>
    </item>
</argument>
<dataSource name="so_grid_data_source">
    <argument name="dataProvider" xsi:type="configurableObject">
        <argument name="class" xsi:type="string">Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider</argument>
        <argument name="name" xsi:type="string">so_grid_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 name="storageConfig" xsi:type="array">
                    <item name="indexField" xsi:type="string">entity_id</item>
                </item>
            </item>
        </argument>
    </argument>
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
        </item>
    </argument>
</dataSource>

<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 name="stickyTmpl" xsi:type="string">ui/grid/sticky/toolbar</item>
        </item>
    </argument>
    <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">so_grid</item>
                </item>
            </item>
        </argument>
    </bookmark>
    <component name="columns_controls">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="columnsData" xsi:type="array">
                    <item name="provider" xsi:type="string">so_grid.so_grid.listing_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>
    <filters name="listing_filters">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="columnsProvider" xsi:type="string">so_grid.so_grid.listing_columns</item>
                <item name="storageConfig" xsi:type="array">
                    <item name="provider" xsi:type="string">so_grid.so_grid.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">so_grid.so_grid.listing_top.listing_filters</item>
                    <item name="imports" xsi:type="array">
                        <item name="visible" xsi:type="string">so_grid.so_grid.listing_columns.${ $.index }:visible</item>
                    </item>
                </item>
            </item>
            <item name="observers" xsi:type="array">
                <item name="column" xsi:type="string">column</item>
            </item>
        </argument>
    </filters>
    <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">so_grid.so_grid.listing_top.bookmarks</item>
                    <item name="namespace" xsi:type="string">current.paging</item>
                </item>
                <item name="selectProvider" xsi:type="string">so_grid.so_grid.listing_columns.images</item>
            </item>
        </argument>
    </paging>
</container>
<columns name="listing_columns">
    <column name="entity_id">
        <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">Id</item>
                <item name="sortOrder" xsi:type="number">4</item>
            </item>
        </argument>
    </column>
    <column name="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 name="sortOrder" xsi:type="number">5</item>
            </item>
        </argument>
    </column>
</columns>

Probably I don't need all the code above to get the grid going. But still... I find all this relentless. So much stuff going around just to render a grid with data.