Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Understanding and Customizing the DataProvider Class

Understanding and Customizing the DataProvider Class

~8 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026

The DataProvider class is the bridge between the collection (chapter 2) and the grid: it supplies the raw data, applies filtering and sorting from the request, and ultimately returns an array that gets serialized to JSON.

The minimal implementation

For most grids, a very thin class that only injects the collection is enough - the actual filtering, sorting, and pagination logic is already fully handled by the AbstractDataProvider base class:

app/code/Mironsoft/Announcement/Ui/DataProvider/Listing/AnnouncementDataProvider.php
<?php

declare(strict_types=1);

namespace Mironsoft\Announcement\Ui\DataProvider\Listing;

use Magento\Framework\Api\Filter;
use Magento\Ui\DataProvider\AbstractDataProvider;
use Mironsoft\Announcement\Model\ResourceModel\Announcement\CollectionFactory;

/**
 * Supplies announcement rows to the admin grid.
 */
class AnnouncementDataProvider extends AbstractDataProvider
{
    /**
     * @param string $name Component name, injected by the UI Component framework.
     * @param string $primaryFieldName Primary key column of the underlying collection.
     * @param string $requestFieldName Request parameter name carrying the current ID.
     * @param CollectionFactory $collectionFactory Factory building the announcement collection.
     * @param array<string, mixed> $meta Additional UI Component meta configuration.
     * @param array<string, mixed> $data Additional UI Component data configuration.
     */
    public function __construct(
        string $name,
        string $primaryFieldName,
        string $requestFieldName,
        CollectionFactory $collectionFactory,
        array $meta = [],
        array $data = [],
    ) {
        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
        $this->collection = $collectionFactory->create();
    }
}

Magento generates CollectionFactory automatically (factory pattern convention: {Collection}Factory) - you don't need to write a factory class for it.

Applying filters correctly: addFieldToFilter()

As soon as custom filter logic is needed - for example a default filter that hides inactive entries, or a customization of getData() - a fixed PHPStan-level-5 rule applies in this project: addFieldToFilter() is always called in the array form ['eq' => $value] for integer values, never as a bare scalar.

// Wrong (fails PHPStan level 5):
$collection->addFieldToFilter('is_active', 1);

// Correct:
$collection->addFieldToFilter('is_active', ['eq' => 1]);

The reason: addFieldToFilter()'s signature accepts array|string as its second parameter - a bare int is tolerated by Magento at runtime (implicit type coercion), but isn't allowed according to the type declaration and gets flagged by PHPStan at level 5. The array form is also explicit and more readable - eq, neq, gt, lt, in, nin, like are all available as operators.

Overriding getData()

For grid-wide extra logic - for instance loading a computed column that doesn't come directly from the table - you override getData():

/**
 * Returns grid data, augmented with a computed excerpt of the message.
 *
 * @return array<string, mixed>
 */
public function getData(): array
{
    if (isset($this->loadedData)) {
        return $this->loadedData;
    }

    $items = parent::getData();
    foreach ($items['items'] as &$item) {
        $item['excerpt'] = mb_substr((string) $item['message'], 0, 80);
    }

    $this->loadedData = $items;

    return $this->loadedData;
}

Achtung: getData() is called freshly on every grid request - without caching the result in an instance variable (like $this->loadedData above), expensive extra logic (database queries, computations) runs unnecessarily multiple times per request, since both the grid and possible export calls invoke the same method.

Where your own data logic belongs

The DataProvider itself is deliberately kept thin - more complex business logic (for example "only show announcements from the last 30 days") belongs in a separate, injectable service object that the DataProvider calls from its constructor, instead of writing the logic directly into the class. That keeps the DataProvider class testable and the filter logic reusable - for example from a CLI command as well.