Skip to content

Magento 2 Custom Admin Grid with UI Component Not Rendering Data from Custom SQL Query

I’m working on creating a custom admin grid in Magento 2 using ui_component, and fetching data from a custom SQL query. However, the grid is not rendering the data in the UI, even though I can see the data is fetched correctly in the logs. I suspect there might be an issue with my di.xml, dataSource configuration, or the DataProvider. Here’s the relevant code:

app/code/Vendor/TargetedList/etc/di.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <!-- Define the collection factory to be injected into the DataProvider -->
    <type name="VendorTargetedListUiComponentDataProvider">
        <arguments>
            <argument name="collectionFactory" xsi:type="object">VendorTargetedListModelResourceModelCustomCollectionFactory</argument>
        </arguments>
    </type>
</config>

app/code/Vendor/TargetedList/view/adminhtml/ui_component/custom_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">vendor_targetedlist_custom.custom_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">custom_columns</item>
    </argument>
    
    <dataSource name="custom_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">VendorTargetedListUiComponentDataProvider</argument>
            <argument name="name" xsi:type="string">custom_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">entity_id</argument>
            <argument name="requestFieldName" xsi:type="string">entity_id</argument>
        </argument>
        <settings>
            <updateUrl path="mui/index/render"/>
        </settings>
    </dataSource>

    <columns name="custom_columns">
        <column name="entity_id">
            <settings>
                <filter>textRange</filter>
                <label translate="true">Order ID</label>
            </settings>
        </column>
    </columns>
</listing>

app/code/Vendor/TargetedList/Model/ResourceModel/Custom/Collection.php

<?php
namespace VendorTargetedListModelResourceModelCustom;

use MagentoFrameworkDataCollection as DataCollection;

class Collection extends DataCollection
{
    protected function _construct()
    {
        // No need to define a table since you're writing a custom SQL query.
    }

    public function getData()
    {
        if (!$this->_loadData) {
            $this->_renderFilters();
        }

        $data = [];
        foreach ($this as $item) {
            $data[] = $item;
        }

        return $data;
    }

    public function _initSelect()
    {
        // Add your custom SQL query here
        $this->getSelect()
            ->from(['so' => $this->getTable('sales_order')], ['entity_id', 'base_grand_total', 'created_at'])
            ->joinLeft(
                ['ce' => $this->getTable('customer_entity')],
                'so.customer_id = ce.entity_id',
                ['email', 'firstname', 'lastname']
            )
            ->where('so.base_grand_total > ?', 100);

        return $this;
    }
}

app/code/Vendor/TargetedList/Model/ResourceModel/Custom/CollectionFactory.php

<?php
namespace VendorTargetedListModelResourceModelCustom;

use MagentoFrameworkObjectManagerInterface;

class CollectionFactory
{
    protected $objectManager = null;
    protected $instanceName = null;

    public function __construct(
        ObjectManagerInterface $objectManager,
        $instanceName = '\Vendor\TargetedList\Model\ResourceModel\Custom\Collection'
    ) {
        $this->objectManager = $objectManager;
        $this->instanceName = $instanceName;
    }

    public function create(array $data = [])
    {
        return $this->objectManager->create($this->instanceName, $data);
    }
}

app/code/Vendor/TargetedList/Ui/Component/DataProvider.php

<?php
namespace VendorTargetedListUiComponent;

use MagentoUiDataProviderAbstractDataProvider;
use VendorTargetedListModelResourceModelCustomCollectionFactory;

class DataProvider extends AbstractDataProvider
{
    protected $_resource;

    public function __construct(
        MagentoFrameworkAppResourceConnection $resource,
        $name,
        $primaryFieldName,
        $requestFieldName,
        CollectionFactory $collectionFactory,
        array $meta = [],
        array $data = []
    ) {
        $this->collection = $collectionFactory->create();
        $this->_resource = $resource;
        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
    }

    public function getData()
    {
        $connection = $this->_resource->getConnection();
        $select = "SELECT so.entity_id, so.base_grand_total, so.created_at, ce.email, ce.firstname, ce.lastname 
                   FROM sales_order AS so 
                   LEFT JOIN customer_entity AS ce ON so.customer_id = ce.entity_id 
                   WHERE so.base_grand_total > 100;";

        $items = $connection->fetchAll($select);

        MagentoFrameworkAppObjectManager::getInstance()->get('PsrLogLoggerInterface')->debug(print_r($items, true));

        return [
            'totalRecords' => count($items),
            'items' => $items,
        ];
    }
}

Issue:
The custom query seems to be working fine because I can see the expected data in the logs:

[2024-09-09 11:07:23] main.DEBUG: Array
(
    [0] => Array
        (
            [entity_id] => 2535305
            [base_grand_total] => 222.8000
            [created_at] => 2024-05-03 11:28:14
            [email] => [email protected]
            [firstname] => Name
            [lastname] => Test
        )
    ...
) [] []

However, the data does not show up in the frontend admin grid UI. I have ensured that the column names in custom_grid.xml match the fields returned from the query (entity_id, base_grand_total, created_at, email, firstname, lastname).

Could the issue be with my dataSource, di.xml, or the way the CollectionFactory is configured? Any help or insights would be greatly appreciated!