PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

UI Components – siatka danych, DataProvider, własne kolumny, konfiguracja XML

by Henryk Tews / wtorek, 13 lipca 2021 / Opublikowano w Magento 2

UI Components to jeden z bardziej zagadkowych elementów Magento 2. Siatki danych w panelu admina, formularze edycji, filtry – wszystko to jest zbudowane na UI Components. Dokumentacja Magento jest tu wyjątkowo skąpa, a debugowanie bywa frustrujące. Pokazuję jak to działa od środka i jak napisać własną siatkę danych z filtrowaniem.

Czym są UI Components?

UI Components to system renderowania interfejsu oparty na knockout.js po stronie frontendu i konfiguracji XML po stronie PHP. Każdy komponent ma trzy warstwy:

  • XML – deklaracja struktury i konfiguracji komponentu
  • PHP DataProvider – dostarcza dane do komponentu
  • JavaScript ViewModel – logika po stronie klienta (knockout.js)

Magento ładuje konfigurację XML, buduje na jej podstawie JSON z konfiguracją komponentu i przekazuje do knockout.js który renderuje widok. Stąd charakterystyczny fragment w źródle strony:

// To co widzisz w źródle admina - konfiguracja UI Components jako JSON
<script type="text/x-magento-init">
    {
        "*": {
            "Magento_Ui/js/core/app": {
                "components": {
                    "vendor_module_listing": {
                        "children": { ... }
                    }
                }
            }
        }
    }
</script>

Własna siatka danych – struktura plików

Vendor/Module/
  Ui/
    Component/
      Listing/
        Column/
          Status.php       <- własna kolumna z renderowaniem
    DataProvider/
      SubscriptionDataProvider.php
  view/
    adminhtml/
      layout/
        vendor_module_index.xml   <- layout strony
      ui_component/
        vendor_module_listing.xml <- konfiguracja siatki

Layout strony admina

<!-- view/adminhtml/layout/vendor_module_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_listing"/>
        </referenceContainer>
    </body>
</page>

Konfiguracja siatki – ui_component XML

<!-- view/adminhtml/ui_component/vendor_module_listing.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">vendor_module_listing.vendor_module_listing_data_source</item>
        </item>
    </argument>

    <settings>
        <buttons>
            <button name="add">
                <url path="*/*/new"/>
                <class>primary</class>
                <label translate="true">Dodaj nowy</label>
            </button>
        </buttons>
        <spinner>vendor_module_listing_columns</spinner>
        <deps>
            <dep>vendor_module_listing.vendor_module_listing_data_source</dep>
        </deps>
    </settings>

    <!-- DataSource - połączenie z DataProviderem PHP -->
    <dataSource name="vendor_module_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_listing_data_source">
            <settings>
                <requestFieldName>id</requestFieldName>
                <primaryFieldName>subscription_id</primaryFieldName>
            </settings>
        </dataProvider>
    </dataSource>

    <!-- Pasek narzędzi z filtrowaniem i paginacją -->
    <listingToolbar name="listing_top">
        <settings>
            <sticky>true</sticky>
        </settings>
        <bookmark name="bookmarks"/>
        <columnsControls name="columns_controls"/>
        <filterSearch name="fulltext"/>
        <filters name="listing_filters"/>
        <paging name="listing_paging"/>
    </listingToolbar>

    <!-- Definicja kolumn -->
    <columns name="vendor_module_listing_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>

        <!-- Własna kolumna z customowym renderowaniem -->
        <column name="status" class="Vendor\Module\Ui\Component\Listing\Column\Status">
            <settings>
                <filter>select</filter>
                <label translate="true">Status</label>
            </settings>
        </column>

        <column name="created_at" class="Magento\Ui\Component\Listing\Columns\Date">
            <settings>
                <filter>dateRange</filter>
                <label translate="true">Data utworzenia</label>
            </settings>
        </column>

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

</listing>

DataProvider – PHP

<?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 = []
    ) {
        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
        $this->collection = $collectionFactory->create();
    }

    public function getData(): array
    {
        if (!$this->getCollection()->isLoaded()) {
            $this->getCollection()->load();
        }

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

        return [
            'totalRecords' => $this->getCollection()->getSize(),
            'items'        => $items,
        ];
    }
}

Własna kolumna z renderowaniem

<?php

declare(strict_types=1);

namespace Vendor\Module\Ui\Component\Listing\Column;

use Magento\Ui\Component\Listing\Columns\Column;

class Status extends Column
{
    private array $statusLabels = [
        1 => '<span class="grid-severity-notice">Aktywna</span>',
        0 => '<span class="grid-severity-critical">Nieaktywna</span>',
    ];

    public function prepareDataSource(array $dataSource): array
    {
        if (!isset($dataSource['data']['items'])) {
            return $dataSource;
        }

        foreach ($dataSource['data']['items'] as &$item) {
            $status = (int) ($item[$this->getData('name')] ?? 0);
            $item[$this->getData('name')] = $this->statusLabels[$status]
                ?? '<span class="grid-severity-minor">Nieznany</span>';
        }

        return $dataSource;
    }
}

Podsumowanie

UI Components mają stromą krzywą uczenia i kiepską dokumentację, ale gdy już rozumiesz strukturę XML - DataProvider - knockout.js, pisanie kolejnych siatek i formularzy idzie sprawnie przez kopiowanie i modyfikowanie istniejących konfiguracji. Warto przejrzeć kod natywnych modułów Magento jak Magento_Customer czy Magento_Sales - to najlepszy wzorzec do naśladowania.

About Henryk Tews

Co możesz przeczytać następne

Message Queue Framework z RabbitMQ – publisher, consumer, DDEV config
Wzorzec Proxy – lazy loading, kontrola dostępu, caching, Proxy w Magento 2
Service Contracts – Data Interfaces i Repository Pattern
  • Publikacje
  • O autorze
  • Kontakt

© 2026 Created by

GÓRA
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 Zawsze aktywne
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.
  • Zarządzaj opcjami
  • Zarządzaj serwisami
  • Zarządzaj {vendor_count} dostawcami
  • Przeczytaj więcej o tych celach
Zobacz preferencje
  • {title}
  • {title}
  • {title}