6

Im trying to build a custom admin form (using ui components) for a custom entity. We can assume the entity is very simple(e.g. id,name)

One thing that i have hard time with, is populating the form with data.

For this purpose i wrote a custom data provider which looks something like this:

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

    $items = $this->collection->getItems();
    /** @var Customer $customer */
    foreach ($items as $faq) {
        $this->loadedData[$faq->getId()] = $faq->getData();
    }

    $data = $this->getSession()->getFormData();
    if (!empty($data)) {
        $faqId = isset($data) ? $data : null;
        $this->loadedData[$faqId] = $data;
        $this->getSession()->unsFormData();
    }

    return $this->loadedData;
}

So the actual data are right under the id of the entity.

The ui component has a single fieldset with one field:

<fieldset name="general">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="label" xsi:type="string" translate="true">Conditions</item>
        </item>
    </argument>
    <field name="name">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="dataType" xsi:type="string">text</item>
                <item name="label" xsi:type="string" translate="true">Label</item>
                <item name="formElement" xsi:type="string">input</item>
                <item name="dataScope" xsi:type="string">name</item>
                <item name="source" xsi:type="string">general</item>
                <item name="validation" xsi:type="array">
                    <item name="required-entry" xsi:type="boolean">true</item>
                </item>
            </item>
        </argument>
    </field>
</fieldset>

The problem is, that the field never gets populated. Only way at the moment is to change stracture of the array returned by data provider:

$this->loadedData[$faq->getId()]['general'] = $faq->getData();

So the structure of the array has the same structure as fieldset -> field in the xml.

This seems to make some sense, however when i look how CMS module is defined for pages and blocks, it doesn't use the fieldset -> field hierarchy.

The data providers don't contain the fieldset key anywhere(or at least not from the getData) method, but the xml uses magical source 'block'.

For example for block

\Magento\Cms\Model\Block\DataProvider

public function getData()
{
    if (isset($this->loadedData)) {
        return $this->loadedData;
    }
    $items = $this->collection->getItems();
    /** @var \Magento\Cms\Model\Block $block */
    foreach ($items as $block) {
        $this->loadedData[$block->getId()] = $block->getData();
    }

    $data = $this->dataPersistor->get('cms_block');
    if (!empty($data)) {
        $block = $this->collection->getNewEmptyItem();
        $block->setData($data);
        $this->loadedData[$block->getId()] = $block->getData();
        $this->dataPersistor->clear('cms_block');
    }

    return $this->loadedData;
}

\Magento\Cms\view\ui_component\cms_block_form.xml

<fieldset name="general">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="label" xsi:type="string"/>
        </item>
    </argument>
    <field name="is_active">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="dataType" xsi:type="string">boolean</item>
                <item name="label" xsi:type="string" translate="true">Enable Block</item>
                <item name="formElement" xsi:type="string">checkbox</item>
                <item name="source" xsi:type="string">block</item>
                <item name="sortOrder" xsi:type="number">10</item>
                <item name="dataScope" xsi:type="string">is_active</item>
                <item name="prefer" xsi:type="string">toggle</item>
                <item name="valueMap" xsi:type="array">
                    <item name="true" xsi:type="number">1</item>
                    <item name="false" xsi:type="number">0</item>
                </item>
                <item name="default" xsi:type="number">1</item>
            </item>
        </argument>
    </field>

Where/How is this 'block' source created?

Do i really need to have the fieldset keys in data returned from the data provider?

Any help would be greatly appreciated.

0

If you see the the customer edit form and get a look over the array returned by getdata() method . It sends out a array as [id][fieldset_name][data]. So here the key is mentioned during edit action.I guess this is the standard way of loading a UI form with data during edit.

  • Yes some of the forms use this and in case of tabbed form like customer it makes sense. However for very simple form this is way too complicated. The question I really have is, how the cms module doest this? Because the fieldset name is not used there. – Langley Aug 24 '16 at 5:51
  • @Langley did you find out much more about this? In the same predicament now. Thanks – Tom Burman Mar 9 '17 at 12:49
  • Not that much, but ended up making it work somehow. Cant remember. Looking into the original question i think it may be wrong dataScope in the form configuration (not the fields) – Langley Mar 9 '17 at 16:55
  • Each form does contain a fieldset so format of [id][fieldset_name][data] if definitely correct. – Aditya Yadav Jun 25 '18 at 8:03
0

Using dataPersistor to get Form data, thought you're using getSession

$data = $this->getSession()->getFormData();

Save action

<?php


namespace Vendor\Module\Controller\Adminhtml\Scheduler;

use Magento\Framework\Exception\LocalizedException;

class Save extends \Magento\Backend\App\Action
{

    protected $dataPersistor;

    /**
     * @param \Magento\Backend\App\Action\Context $context
     * @param \Magento\Framework\App\Request\DataPersistorInterface $dataPersistor
     */
    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\App\Request\DataPersistorInterface $dataPersistor
    ) {
        $this->dataPersistor = $dataPersistor;
        parent::__construct($context);
    }

    /**
     * Save actionSave action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultRedirectFactory->create();
        $data = $this->getRequest()->getPostValue();
        if ($data) {
            $id = $this->getRequest()->getParam('scheduler_id');

            $model = $this->_objectManager->create('Vendor\Module\Model\Scheduler')->load($id);
            if (!$model->getId() && $id) {
                $this->messageManager->addErrorMessage(__('This Scheduler no longer exists.'));
                return $resultRedirect->setPath('*/*/');
            }

            $model->setData($data);

            try {

                $model->save();
                $this->messageManager->addSuccessMessage(__('You saved the Scheduler.'));
                $this->dataPersistor->clear('engraving_scheduler');

                if ($this->getRequest()->getParam('back')) {
                    return $resultRedirect->setPath('*/*/edit', ['scheduler_id' => $model->getId()]);
                }
                return $resultRedirect->setPath('*/*/');
            } catch (LocalizedException $e) {
                $this->messageManager->addErrorMessage($e->getMessage());
            } catch (\Exception $e) {
                $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the Scheduler.'));
            }

            $this->dataPersistor->set('engraving_scheduler', $data);
            return $resultRedirect->setPath('*/*/edit', ['scheduler_id' => $this->getRequest()->getParam('scheduler_id')]);
        }
        return $resultRedirect->setPath('*/*/');
    }
}

DataProvider

<?php


namespace Vendor\Module\Model\Scheduler;

use Vendor\Module\Model\ResourceModel\Scheduler\CollectionFactory;
use Magento\Framework\App\Request\DataPersistorInterface;

class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
{

    protected $dataPersistor;

    protected $loadedData;
    protected $collection;


    /**
     * Constructor
     *
     * @param string $name
     * @param string $primaryFieldName
     * @param string $requestFieldName
     * @param CollectionFactory $collectionFactory
     * @param DataPersistorInterface $dataPersistor
     * @param array $meta
     * @param array $data
     */
    public function __construct(
        $name,
        $primaryFieldName,
        $requestFieldName,
        CollectionFactory $collectionFactory,
        DataPersistorInterface $dataPersistor,
        array $meta = [],
        array $data = []
    ) {
        $this->collection = $collectionFactory->create();
        $this->dataPersistor = $dataPersistor;
        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
    }

    /**
     * Get data
     *
     * @return array
     */
    public function getData()
    {
        if (isset($this->loadedData)) {
            return $this->loadedData;
        }
        $items = $this->collection->getItems();
        foreach ($items as $model) {
            $this->loadedData[$model->getId()] = $model->getData();
        }
        $data = $this->dataPersistor->get('engraving_scheduler');

        if (!empty($data)) {
            $model = $this->collection->getNewEmptyItem();
            $model->setData($data);
            $this->loadedData[$model->getId()] = $model->getData();
            $this->dataPersistor->clear('engraving_scheduler');
        }

        return $this->loadedData;
    }
}

ui 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_configuration.xsd">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">engraving_scheduler_form.scheduler_form_data_source</item>
            <item name="deps" xsi:type="string">engraving_scheduler_form.scheduler_form_data_source</item>
        </item>
        <item name="label" translate="true" xsi:type="string">General Information</item>
        <item name="config" xsi:type="array">
            <item name="dataScope" xsi:type="string">data</item>
            <item name="namespace" xsi:type="string">engraving_scheduler_form</item>
        </item>
        <item name="template" xsi:type="string">templates/form/collapsible</item>
        <item name="buttons" xsi:type="array">
            <item name="back" xsi:type="string">Vendor\Module\Block\Adminhtml\Scheduler\Edit\BackButton</item>
            <item name="delete" xsi:type="string">Vendor\Module\Block\Adminhtml\Scheduler\Edit\DeleteButton</item>
            <item name="save" xsi:type="string">Vendor\Module\Block\Adminhtml\Scheduler\Edit\SaveButton</item>
            <item name="save_and_continue" xsi:type="string">Vendor\Module\Block\Adminhtml\Scheduler\Edit\SaveAndContinueButton</item>
        </item>
    </argument>
    <dataSource name="scheduler_form_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">Vendor\Module\Model\Scheduler\DataProvider</argument>
            <argument name="name" xsi:type="string">scheduler_form_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">scheduler_id</argument>
            <argument name="requestFieldName" xsi:type="string">scheduler_id</argument>
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="submit_url" path="*/*/save" xsi:type="url"/>
                </item>
            </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>
    </dataSource>
    <fieldset name="General">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="label" xsi:type="string"/>
            </item>
        </argument>
        <field name="label">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="label" translate="true" xsi:type="string">Title</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">Scheduler</item>
                    <item name="sortOrder" xsi:type="number">10</item>
                    <item name="dataScope" xsi:type="string">label</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">true</item>
                    </item>
                </item>
            </argument>
        </field>
    </fieldset>
</form>
  • Yes DataPersistor is a better way of doing this (the question is quite dated), but following up with ObjectManager use is not so good. – Langley Nov 15 '18 at 8:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.