PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

UI Components – data grid, DataProvider, custom columns, XML configuration

by Henryk Tews / Tuesday, 13 July 2021 / Published in Magento 2

Magento 2 UI Components are the backbone of the admin panel – every grid, form and mass-action uses them. They are also one of the most complex parts of the platform. I show how the data grid works, how to write a DataProvider, add custom columns, and configure everything through XML without touching the JavaScript layer.

UI Component architecture

A UI Component in the admin consists of three layers:

  • Layout XML – declares which component to render on the page
  • Component XML – configures columns, filters, mass actions, DataProvider
  • DataProvider PHP – provides data to the grid

Layout XML – place the grid on the page

<!-- view/adminhtml/layout/vendor_module_subscription_index.xml -->
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <uiComponent name="vendor_module_subscription_listing"/>
        </referenceContainer>
    </body>
</page>

Component XML – grid configuration

<!-- view/adminhtml/ui_component/vendor_module_subscription_listing.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_module_subscription_listing.vendor_module_subscription_listing_data_source
            </item>
        </item>
    </argument>

    <!-- Data source -->
    <dataSource name="vendor_module_subscription_listing_data_source"
                component="Magento_Ui/js/grid/provider">
        <settings>
            <storageConfig>
                <param name="indexField" xsi:type="string">subscription_id</param>
            </storageConfig>
            <updateUrl path="mui/index/render"/>
        </settings>
        <aclResource>Vendor_Module::subscriptions</aclResource>
        <dataProvider class="Vendor\Module\Ui\DataProvider\SubscriptionDataProvider"
                      name="vendor_module_subscription_listing_data_source">
            <settings>
                <requestFieldName>id</requestFieldName>
                <primaryFieldName>subscription_id</primaryFieldName>
            </settings>
        </dataProvider>
    </dataSource>

    <!-- Toolbar with search and filters -->
    <listingToolbar name="listing_top">
        <settings>
            <sticky>true</sticky>
        </settings>
        <bookmark name="bookmarks"/>
        <columnsControls name="columns_controls"/>
        <filterSearch name="fulltext"/>
        <filters name="listing_filters">
            <filterSelect name="status" provider="${ $.parentName }">
                <settings>
                    <caption translate="true">-- All Statuses --</caption>
                    <dataScope>status</dataScope>
                    <label translate="true">Status</label>
                    <options class="Vendor\Module\Model\Source\SubscriptionStatus"/>
                </settings>
            </filterSelect>
        </filters>
        <massaction name="listing_massaction">
            <action name="delete">
                <settings>
                    <confirm>
                        <message translate="true">Delete selected items?</message>
                        <title translate="true">Delete items</title>
                    </confirm>
                    <url path="vendor_module/subscription/massDelete"/>
                    <type>delete</type>
                    <label translate="true">Delete</label>
                </settings>
            </action>
        </massaction>
        <paging name="listing_paging"/>
    </listingToolbar>

    <!-- Columns -->
    <columns name="vendor_module_subscription_columns">
        <selectionsColumn name="ids">
            <settings><indexField>subscription_id</indexField></settings>
        </selectionsColumn>

        <column name="subscription_id">
            <settings>
                <filter>textRange</filter>
                <label translate="true">ID</label>
                <sorting>asc</sorting>
            </settings>
        </column>

        <column name="email">
            <settings>
                <filter>text</filter>
                <label translate="true">Email</label>
            </settings>
        </column>

        <column name="status" component="Magento_Ui/js/grid/columns/select">
            <settings>
                <options class="Vendor\Module\Model\Source\SubscriptionStatus"/>
                <filter>select</filter>
                <dataType>select</dataType>
                <label translate="true">Status</label>
            </settings>
        </column>

        <column name="created_at" class="Magento\Ui\Component\Listing\Columns\Date"
                component="Magento_Ui/js/grid/columns/date">
            <settings>
                <filter>dateRange</filter>
                <dataType>date</dataType>
                <label translate="true">Created At</label>
            </settings>
        </column>

        <actionsColumn name="actions" class="Vendor\Module\Ui\Component\Listing\Column\SubscriptionActions">
            <settings>
                <indexField>subscription_id</indexField>
                <label translate="true">Actions</label>
            </settings>
        </actionsColumn>
    </columns>
</listing>

DataProvider PHP class

<?php

declare(strict_types=1);

namespace Vendor\Module\Ui\DataProvider;

use Magento\Ui\DataProvider\AbstractDataProvider;
use Vendor\Module\Model\ResourceModel\Subscription\CollectionFactory;

class SubscriptionDataProvider extends AbstractDataProvider
{
    public function __construct(
        string $name,
        string $primaryFieldName,
        string $requestFieldName,
        CollectionFactory $collectionFactory,
        array $meta = [],
        array $data = []
    ) {
        $this->collection = $collectionFactory->create();
        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
    }

    public function getData(): array
    {
        if (isset($this->loadedData)) {
            return $this->loadedData;
        }

        $items = $this->collection->getItems();
        $this->loadedData = [];

        foreach ($items as $item) {
            $this->loadedData[$item->getId()] = $item->getData();
        }

        return $this->loadedData;
    }
}

Summary

UI Components have a steep XML learning curve but are consistent once you understand the pattern. DataProvider drives the data, the XML component file drives the configuration, and the layout XML places everything on the page. For custom columns – extend AbstractColumn and override prepareDataSource(). For actions column – define the URL and label in your custom column class.

About Henryk Tews

What you can read next

Xdebug – configuration, PHPStorm, debugging Magento plugins
Strategy pattern in PHP – and how Magento 2 uses it in pricing

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}