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

form.xml Grundgerüst: Fieldsets, Felder, DataProvider

form.xml Grundgerüst: Fieldsets, Felder, DataProvider

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

Formulare folgen demselben Grundprinzip wie Grids: eine XML-Deklaration unter view/adminhtml/ui_component/, eine PHP-DataProvider-Klasse - diesmal für genau einen Datensatz statt einer Liste - und ein Controller, der beides über die Layout-XML verbindet.

Edit-Controller und Layout

app/code/Mironsoft/Announcement/Controller/Adminhtml/Announcement/Edit.php
<?php

declare(strict_types=1);

namespace Mironsoft\Announcement\Controller\Adminhtml\Announcement;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\View\Result\Page;
use Magento\Framework\View\Result\PageFactory;

/**
 * Renders the announcement edit form page.
 */
class Edit extends Action
{
    public const ADMIN_RESOURCE = 'Mironsoft_Announcement::announcement';

    /**
     * @param Context $context Backend action context.
     * @param PageFactory $resultPageFactory Factory for the page result.
     */
    public function __construct(
        Context $context,
        private readonly PageFactory $resultPageFactory,
    ) {
        parent::__construct($context);
    }

    /**
     * Builds the edit form page result.
     *
     * @return Page
     */
    public function execute(): Page
    {
        $id = (int) $this->getRequest()->getParam('id');

        $resultPage = $this->resultPageFactory->create();
        $resultPage->setActiveMenu('Mironsoft_Announcement::announcement');
        $resultPage->getConfig()->getTitle()->prepend(
            $id ? __('Edit Announcement') : __('New Announcement')
        );

        return $resultPage;
    }
}
app/code/Mironsoft/Announcement/view/adminhtml/layout/mironsoft_announcement_announcement_edit.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="mironsoft_announcement_form"/>
        </referenceContainer>
    </body>
</page>

Das form.xml-Skelett

app/code/Mironsoft/Announcement/view/adminhtml/ui_component/mironsoft_announcement_form.xml
<?xml version="1.0"?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_components.xsd">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">mironsoft_announcement_form.announcement_form_data_source</item>
        </item>
    </argument>

    <settings>
        <buttons>
            <button name="back" class="Mironsoft\Announcement\Block\Adminhtml\Announcement\Edit\BackButton"/>
            <button name="save" class="Mironsoft\Announcement\Block\Adminhtml\Announcement\Edit\SaveButton"/>
        </buttons>
        <namespace>mironsoft_announcement_form</namespace>
        <dataScope>data</dataScope>
        <deps>
            <dep>mironsoft_announcement_form.announcement_form_data_source</dep>
        </deps>
    </settings>

    <dataSource name="announcement_form_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">Mironsoft\Announcement\Ui\DataProvider\Form\AnnouncementDataProvider</argument>
            <argument name="name" xsi:type="string">announcement_form_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">announcement_id</argument>
            <argument name="requestFieldName" xsi:type="string">id</argument>
        </argument>
        <argument name="data" xsi:type="array">
            <item name="js_config" xsi:type="array">
                <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
            </item>
        </argument>
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="submit_url" xsi:type="url" path="mironsoft_announcement/announcement/save"/>
            </item>
        </argument>
    </dataSource>

    <fieldset name="general">
        <settings>
            <label translate="true">General Information</label>
        </settings>
        <field name="title" formElement="input">
            <settings>
                <dataType>text</dataType>
                <label translate="true">Title</label>
            </settings>
        </field>
    </fieldset>
</form>

Der submit_url-Eintrag in der dataSource legt fest, wohin der Speichern-Button per POST sendet - ohne diesen Eintrag würde das Formular versuchen, sich selbst aufzurufen, was fast immer zu einem 404 führt.

Der Form-DataProvider

Anders als beim Grid liefert dieser DataProvider genau einen Datensatz, identifiziert über requestFieldName (hier id aus der URL). Die Basisklasse ist dieselbe wie beim Grid:

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

declare(strict_types=1);

namespace Mironsoft\Announcement\Ui\DataProvider\Form;

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

/**
 * Supplies a single announcement record to the edit form.
 */
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();
    }
}

Tipp: AbstractDataProvider::getData() filtert die Collection automatisch auf die ID aus requestFieldName und liefert das Ergebnis in der Struktur [$id => [...Feldwerte]] zurück - genau das Format, das die JavaScript-Formularseite erwartet. Für einen "Neu anlegen"-Aufruf (keine ID in der URL) bleibt das Array einfach leer, und das Formular zeigt leere Felder.

Save- und Back-Buttons

Die in <buttons> referenzierten Klassen implementieren ButtonProviderInterface und liefern nur Konfigurationsdaten (Label, Klasse, Sortierung) für die Toolbar - keine eigene Logik. Kapitel 22 vertieft eigene Toolbar-Buttons; für den Start reicht ein minimaler Save-Button:

class SaveButton implements ButtonProviderInterface
{
    public function getButtonData(): array
    {
        return [
            'label' => __('Save'),
            'class' => 'save primary',
            'data_attribute' => ['mage-init' => ['button' => ['event' => 'save']]],
            'sort_order' => 90,
        ];
    }
}

Kapitel 10 füllt das Fieldset mit weiteren Feldtypen, Kapitel 12 implementiert den save-Controller, auf den submit_url zeigt.