Magento 2: Simplest UI Component

原文地址

今天我们将深入 Magento 2 的 ui components ,并尝试从头创建一个。现阶段的 Maggento 2 创建一个 ui component ,需要用一些不同寻常的,对生产环境来说不安全的方法,但是要想真正了解一个系统,有时候就是要从“地基”开始。

和这个系列的其他教程一样,请确保 Magento 2 是 developer 模式。为了防止下面的步骤不管用,我们在 github 上放了完整的模块代码。还有,以下基于 Magento 2.1.1 ,但是文中涉及的概念是适用所有版本的。

用 Pestle 创建基本的模块

首先,我们用 pestle来创建一个带后台 menu 的模块。如果你不清楚下面的命令在干啥,那你可能要看看 Magento 2 for PHP MVC Developers 系列文章

首先取得 pestle.phar

curl -LO http://pestle.pulsestorm.net/pestle.phar

然后

php pestle.phar generate_module Pulsestorm SimpleUiComponent 0.0.1

php pestle.phar generate_acl Pulsestorm_SimpleUiComponent Pulsestorm_SimpleUiComponent::top,Pulsestorm_SimpleUiComponent::menu_1

php pestle.phar generate_menu Pulsestorm_SimpleUiComponent Magento_Backend::system_other_settings Pulsestorm_SimpleUiComponent::a_menu_item Pulsestorm_SimpleUiComponent::menu_1 "Hello Simple Ui Component" pulsestorm_simpleuicomponent/index/index 1

php pestle.phar generate_route Pulsestorm_SimpleUiComponent adminhtml pulsestorm_simpleuicomponent

php pestle.phar generate_view Pulsestorm_SimpleUiComponent adminhtml pulsestorm_simpleuicomponent_index_index Main content.phtml 1column

php bin/magento module:enable Pulsestorm_SimpleUiComponent

php bin/magento setup:upgrade

运行上述命令后,登陆后台,应该就能通过 System -> Other Settings -> Hello Simple Ui Component 进入到刚刚创建的页面中了。

配置 UI component

点击 System -> Other Settings -> Hello Simple Ui Component 进入到刚刚创建的页面,你看到的是这样子的:

simple-ui-component

首先我们要做的是给我们的 layout handle xml 文件加 <uiComponent>

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/adminhtml/layout/pulsestorm_simpleuicomponent_index_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">
    <referenceBlock name="content">
        <block template="content.phtml" class="Pulsestorm\SimpleUiComponent\Block\Adminhtml\Main" name="pulsestorm_simpleuicomponent_block_main" />

        <!-- START: our new ui component -->
        <uiComponent name="pulsestorm_simple"/>
        <!-- END:   our new ui component -->
    </referenceBlock>
</page>

上面的代码,我们告诉 Magento 我们要加一个 pulsestorm_simple UI component 到页面的 content block 中。然后没咱们清空缓存,刷新页面,我们会看到如下的错误:

1 exception(s):
Exception #0 (Magento\Framework\Exception\LocalizedException): Object 
DOMDocument should be created.

Exception #0 (Magento\Framework\Exception\LocalizedException): Object 
DOMDocument should be created.
#0 /path/to/magento/
vendor/magento/framework/View/Element/UiComponent/Config/Reader.php(95):
Magento\Framework\View\Element\UiComponent\Config\DomMerger->getDom()

错误原因是我们配置一个叫 pulsestorm_simple 的 ui component ,但是 Magento 又找不到它的定义文件。Object DOMDocument should be created 错误来自于 php 代码尝试载入一个不存在的 xml object。

任何 ui component 都需要一个 ui_component/[...].xml 定义文件。下面我们修复这个问题。

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/adminhtml/ui_component/pulsestorm_simple.xml -->
<pulsestorm_simple xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
</pulsestorm_simple>

ui component 的名字(pulsestorm_simple)和它的 xml 文件名(pulsestorm_simple.xml)需要一致。所有 ui component 的文件都在 area/view/ui_component 目录下。尽管不限制使用 ui component 在 frontend area 中,但是前台不一定会像预期的那样管用,因为 Magento 核心团队大多数情况下(只在?)后台 layout 中使用 ui components 。

清除缓存,刷新页面后,我们会得到一个新的错误:

1 exception(s):
Exception #0 (Magento\Framework\Exception\LocalizedException): Element
'pulsestorm_simple': No matching global declaration available for the
validation root.
Line: 1

这里,我们的顶层节点名字叫 pulsestorm_simple 。回顾上篇 Magento 2 的 UI Components 介绍(翻译), ui component 文件是 domain specific language (DSL),由它控制嵌套的 php 对象的实例化。ui_component 文件中的每个节点对应着下面这个文件中的节点。

vendor/magento/module-ui/view/base/ui_component/etc/definition.xml

所以,问题是我们的节点 pulsestorm_simple 不在 definition.xml 中,Magento 的 UI component DSL 当然也就不知道遇到这个节点的时候,用哪个 php 类去实例化。多亏了 Magento 会合并配置文件再载入,我们就可以通过添加下面的文件来改变合并后的 definition.xml必须搁在 base 下面才管用

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/base/ui_component/etc/definition.xml -->
<components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_definition.xsd">
    <pulsestorm_simple class="Pulsestorm\SimpleUiComponent\Component\Simple"/>
</components>

通过上面的配置,我们等于告诉 Magento

遇到 pulsestorm_simple ui component 节点,请实例化 Pulsestorm\SimpleUiComponent\Component\Simple

命名要注意,因为我们的文件会和核心 definition.xml 进行合并,如果你的命名和核心中的重名了,那么可能就改变系统原来的行为了。推荐加上 vendor namespace 作为前缀(pulsestorm_ )

清空缓存,刷新页面,我们会得到一个新的错误:

1 exception(s):
Exception #0 (Magento\Framework\Exception\LocalizedException): Element
'pulsestorm_simple': This element is not expected. Expected is one of
( range, tab, dataSource, paging, massaction, listing, form, fieldset,
field, filters ).

Magento 正在合并我们的 definition.xml,但必须通过 schema validation 。具体来说,Magento 要求最终的 definition.xml 要符合下面这个文件中定义结构要求:

vendor/magento/module-ui/etc/ui_definition.xsd

很不幸的是,Magento 2 没有提供方法让我们加入规则。如果你知道查看哪里(Magento\Framework\Config\Dom::validateDomDocument),可以使用 object manager 的 preference 来注入一些自定义的行为,这样我们是可以让 Magento 跳过 XSD 验证的。但是,很不幸,这么做可能跟其他也这么做的插件冲突。所以如果你打算发布代码的话,这么做可不太合适。使用 Magento 的 plugin 方式(更安全稳定些)也是不行的,因为 validateDomDocument 虽然是 public 但它是 static 方法,Magento 的 plugin system 不支持 static 方法。

到这里,我们想创建一个全新的、顶层的 ui_component 节点是行不通的。这预示着,Magento 的 UI component 系统是为官方核心开发保留的,也可能说是 UI component 还不是功能齐全的。

跳过验证

当然咯,上面我们说行不通,是说,用安全稳定的方法做不到,但是我们还是可以通过可能不那么稳定的 class preference 来做到嘛。

Magento 的开发者通过 calss preference 定义 interfaces 对应的具体类,object manager 遇到这个 interface 的时候,就会去实例化对应的具体类。

Class preference 还可以用来替换具体的类,和 Magento 1 中 class rewrites 有非常相似的功能(缺点也是一样的)。

下面我们将用 class preference 方式来跳过 XML 的 XSD 验证。在生产环境中,或是要发布的系统中,这么做是不合适的,但是这里只是为了让教程继续下去。

创建 di.xml 文件:

<!-- File: app/code/Pulsestorm/SimpleUiComponent/etc/di.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">                                                                -->
    <preference for="Magento\Framework\App\Arguments\ValidationState" type="Pulsestorm\SimpleUiComponent\Model\ValidationState"/>

</config>

然后添加下面的文件:

#File: app/code/Pulsestorm/SimpleUiComponent/Model/ValidationState.php
<?php
namespace Pulsestorm\SimpleUiComponent\Model;
class ValidationState extends \Magento\Framework\App\Arguments\ValidationState
{
    public function isValidationRequired()
    {
        return false;
    }
}

关于为什么这么做就可以跳过 XSD 验证,本篇不介绍,有兴趣的读者可以自己研究下,不过这篇可能对你有帮助。

清理缓存后刷新页面,这次看到一个新的错误:

1 exception(s):
Exception #0 (ReflectionException): Class
Pulsestorm\SimpleUiComponent\Component\Simple does not exist

XSD 验证跳过了,我们可以继续探索了。

UPDTE: Hello, from late 2017! When Magento release version 2.2, they broke this tutorial. If you’re using Magento 2.2, in addition to disabling XSD validation, you’ll also need to add a definition.map.xml file with a name=”puslestorm_simple” node. Why? We have no idea and Magento haven’t really explained what this file is for. Open source doesn’t always mean open intent. If you’re using Magento 2.2 just copy the file from GitHub to your module and you should take take of any errors about undefined children keys.

UI Component Rendering Class

在我们遇到 schema validation 的坑之前,我们在 definition.xml 中添加了以下设置:

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/base/ui_component/etc/definition.xml -->
<components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_definition.xsd">
    <pulsestorm_simple class="Pulsestorm\SimpleUiComponent\Component\Simple"/>
</components>

etc/definition.xml 定义了默认的属性和节点,当 Magento 在 ui_component/[somefile].xml 遇到特定的节点的时候,就会用 definition.xml 中的值。以本例来说,当 Magento 遇到 pulsestorm_simple 时,就会使用 Pulsestorm\SimpleUiComponent\Component\Simple 类。就是说,下面我们使用 pulsestorm_simple

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/adminhtml/ui_component/pulsestorm_simple.xml -->
<pulsestorm_simple xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
</pulsestorm_simple>

Magento 会尝试用 Pulsestorm\SimpleUiComponent\Component\Simple 实例化对象,用这个对象去 render UI component。所以错误就是 Pulsestorm\SimpleUiComponent\Component\Simple 不存在。下面创建它。

#File: app/code/Pulsestorm/SimpleUiComponent/Component/Simple.php
<?php
namespace Pulsestorm\SimpleUiComponent\Component;
class Simple extends \Magento\Ui\Component\AbstractComponent
{
    const NAME = 'pulsestorm_simple';
    public function getComponentName()
    {
        return static::NAME;
    }
}

Magento 2 的 UI component 类必须继承 Magento\Ui\Component\AbstractComponent 类,必须定义一个 getComponentName 方法。ui component 的 name 是否必须和 UI component node 的 name 一样,还是要和 ui_componont/[filename].xml (pulsestorm_simple) 一样不太清楚。但是最好是跟 Magento 的核心代码的做法保持一致。所以,我们也给我们的 component 一个 NAME constant 。

清空缓存,刷新页面后我们得到了一个新的错误:

1 exception(s):
Exception #0 (Magento\Framework\Exception\LocalizedException): Object
DOMDocument should be created.

Exception #0 (Magento\Framework\Exception\LocalizedException): Object
DOMDocument should be created.

这次 Magento 又报错说 XML 文件缺少了。ui component object (继承自 Magento\Ui\Component\AbstractComponent)负责 render XHML templates 。

我们告诉 Magento ,我需要 render Pulsestorm\SimpleUiComponent\Component\Simple 对象,但是我们没告诉它 Pulsestorm\SimpleUiComponent\Component\Simple 这个对象应该用哪个 template 。下面我们修改 definition.xml

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/base/ui_component/etc/definition.xml -->
<components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_definition.xsd">
    <pulsestorm_simple class="Pulsestorm\SimpleUiComponent\Component\Simple">
        <argument name="data" xsi:type="array">
            <item name="template" xsi:type="string">templates/our-template</item>
        </argument>
    </pulsestorm_simple>
</components>

下面创建模板文件:

#File: app/code/Pulsestorm/SimpleUiComponent/view/adminhtml/ui_component/templates/our-template.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<div>
    <h1>Hello World</h1>
</div>

UI component 将会在模块的 view/[area]/ui_component 目录下找 template。definition.xml 文件中的值会加上 .xhtml 后缀转成文件路径。请注意,虽然文件看起来很像 HTML ,但是他们是有 XML 头的。他们是 XHTML 文件,是需要格式良好的 XML 。

下面清楚缓存,刷新页面,这次我们又看到一个新的错误,不过这次已经接近目标了。

( ! ) Fatal error: Method Magento\Ui\TemplateEngine\Xhtml\Result::__toString() must not throw an
 exception, caught Error: Call to a member function getConfigData() on null
 in /path/to/magento/
 vendor/magento/module-ui/Component/Wrapper/UiComponent.php on line 0

UI components 系统除了 render XHTML templates 外,还需要将特定的 XHTML template 与 data provider class 匹配起来。UI component 设计本意是从服务端获取数据,data provider 是 component 获得数据的正式方法。

这意味着,创建一个最简单的 UI component 的最后一步就是配置一个 data provider class 。这个改动放在 pulsestorm_simple.xml 中,因为理论上每个 component 实例都 render 不同的 UI Component 。下面我们给 pulsestorm_simple.xml 添加 dataSource 节点。

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/adminhtml/ui_component/pulsestorm_simple.xml -->
<pulsestorm_simple xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">

    <dataSource name="pulsestorm_simple_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <!-- the PHP class that implements a data provider -->
            <argument name="class" xsi:type="string">Pulsestorm\SimpleUiComponent\Model\DataProvider</argument>

            <!-- redundant with the `dataSource` name -->
            <argument name="name" xsi:type="string">pulsestorm_simple_data_source</argument>

            <!-- required: means ui components are meant to work with models -->
            <argument name="primaryFieldName" xsi:type="string">entity_id</argument>

            <!-- required: means ui components are meant to work with URL passing -->
            <argument name="requestFieldName" xsi:type="string">id</argument>
        </argument>

    </dataSource>

</pulsestorm_simple>

<dataSource/> 树中有一些冗余的样板命名约定需要注意。首先是 name 属性

<dataSource name="pulsestorm_simple_data_source">...</dataSource>

它是 UI component 名称(pulsestorm_simple)加上 _data_source,同样的还有下面的参数节点:

<argument name="name" xsi:type="string">pulsestorm_simple_data_source</argument>

这个参数节点是必要的,虽然它是冗余的。

下面两个节点也是必要的:

<argument name="primaryFieldName" xsi:type="string">entity_id</argument>
<argument name="requestFieldName" xsi:type="string">id</argument>

最后是 class

<argument name="class" xsi:type="string">Pulsestorm\SimpleUiComponent\Model\DataProvider</argument>

这里我们告诉我们的 component 使用哪个 PHP data provider class 去进行实例化。所以我们下面创建它。

#File: app/code/Pulsestorm/SimpleUiComponent/Model/DataProvider.php
<?php
namespace Pulsestorm\SimpleUiComponent\Model;
class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
{
}

DataProvider 类必须继承自 Magento\Ui\DataProvider\AbstractDataProvider —— 尽管这个类没有 abstract methods 需要定义。

下面清空缓存,刷新页面。

我们终于把 XHTML 模板给 render 出来啦。

幕后发生的事情

在我们讨论可以用这个渲染的 XHTML 模板来做点什么事情之前,让我们先来谈谈幕后发生了什么。当 Magento 的 layout rendering code 遇到 UI component 标签的时候,一堆等同于下面的伪代码的代码就会执行。

配置 ui_component 的整个过程是选择实例化的类,并在该类上设置数据属性。在本例中实例化过程就像这样:

$data = functionThatLoadsArgumentNodesFromXmlFiles();
$ui_component = new Pulsestorm\SimpleUiComponent\Component\Simple(
    //...
    [
        'template'=>'templates/our-template'
    ],
);
echo $ui_component->render();
$data = functionThatLoadsArgumentNodesFromXmlFiles();
$ui_component = new Pulsestorm\SimpleUiComponent\Component\Simple(
    //...
    $data,
);
echo $ui_component->render();

好的 DSL 通常会让你忘记像这样的实现细节——但是如果你从来没有遇到过 dsl, 这种事情看起来很奇怪, 也很陌生。每当你被一些 UI 组件的配置缠住时, 请记住您正在为 Magento 准备转换为 PHP 代码的值。这些值并不是简单的数据属性。

Raw Template Source

这是我们从浏览器中看到的样子,但是加载的源代码是什么样子的呢。我们查看网页源代码,不是 browser debugger 中。

< div >
    <h1 > Hello World < /h1>
    <script type = "text/x-magento-init" >
    {
        "*":
        {
            "Magento_Ui/js/core/app":
            {
                "types":
                {
                    "dataSource": [],
                    "pulsestorm_simple":
                    {
                        "extends": "pulsestorm_simple"
                    },
                    "html_content":
                    {
                        "component": "Magento_Ui\/js\/form\/components\/html",
                        "extends": "pulsestorm_simple"
                    }
                },
                "components":
                {
                    "pulsestorm_simple":
                    {
                        "children":
                        {
                            "pulsestorm_simple":
                            {
                                "type": "pulsestorm_simple",
                                "name": "pulsestorm_simple"
                            },
                            "pulsestorm_simple_data_source":
                            {
                                "type": "dataSource",
                                "name": "pulsestorm_simple_data_source",
                                "dataScope": "pulsestorm_simple",
                                "config":
                                {
                                    "params":
                                    {
                                        "namespace": "pulsestorm_simple"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    < /script>
</div >

Magento 从 XHTML template 中不仅仅是加载了 <div><h1> 标签,他还载入了一个 text/x-magento-init script 。这是我们今天要讨论的关于 UI component system 的最后一个方面。UI component 不仅加载 XHTML template,也不仅仅是将 template 和 data provider 对象绑定起来,他还加载一个 JSON 对象,通过 x-magento-init 的方式,用这个 JSON 对象来实例化 Magento_Ui/js/core/app RequireJS app/module。

现在,我们知道 UI component 做的大致事情了,下面我们来看看这个 template/rendering engine 的一些功能。

XHTML Template Tags

phtml 模板类似 —— 你可以在 XHTML 模板中对底层的 UI Component 类进行 “调用”。你可以使用特殊的模板指令 {{...}}。下面我们看个例子,我们给 component 类添加一个 getEvenMoreData 方法。

#File: app/code/Pulsestorm/SimpleUiComponent/Component/Simple.php
<?php
namespace Pulsestorm\SimpleUiComponent\Component;
class Simple extends \Magento\Ui\Component\AbstractComponent
{
    const NAME = 'pulsestorm_simple';
    public function getComponentName()
    {
        return static::NAME;
    }

    //added this method
    public function getEvenMoreData()
    {
        return 'Even More Data!';
    }
}

我们可以在 xhtml template 中调用 {{...}}

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/adminhtml/ui_component/templates/our-template.xhtml -->
<?xml version="1.0" encoding="UTF-8"?>
<div>
    <h1>Hello World</h1>

    <p>
        {{getComponentName()}}
    </p>

    <p>
        {{getEvenMoreData()}}
    </p>
</div>

上面的代码保存后,清空缓存刷新页面。你就会看到类的方法/属性中的数据传递到模板上来了。

除了调用对象中的方法,我们应该还能通过 data 配置节点来获得 data 属性值,就像下面这样。

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/base/ui_component/etc/definition.xml -->
<argument name="data" xsi:type="array">
    <item name="template" xsi:type="string">templates/our-template</item>

    <!-- NEW NODE HERE -->
    <item name="message" xsi:type="string">Hello World</item>
</argument>

然后再 xhtml 中调用它。

<?xml version="1.0" encoding="UTF-8"?>
<div>
    <!-- ... -->
    {{message}}
</div>

但是,这里有个 bug,除非这个 data variable 在 tag 的属性中,否则加载的还是 {{message}} 而不是它的值。

<?xml version="1.0" encoding="UTF-8"?>
<div class="{{message}}">
    <!-- ... -->
    {{message}}
</div>

上面这一段 render 出来的是:

<?xml version="1.0" encoding="UTF-8"?>
<div class="Hello World">
    <!-- ... -->
    {{message}}
</div>

真是很烦人,再次表明 UI Component system 还不成熟。

理解 UI Component 继承

definition.xml 中放置顶级节点,意味着你创建了一个可重用的 UI 组件标签。其他程序员可以在 XML 文件中使用 <uiComponent> 使用你的组件。

definition.xml 文件还可以设置组件的默认值,不过这些值是可以被覆盖的。

举例来说,我们设置了默认的模板:

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/base/ui_component/etc/definition.xml -->
<components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_definition.xsd">
    <pulsestorm_simple class="Pulsestorm\SimpleUiComponent\Component\Simple">
        <argument name="data" xsi:type="array">
            <item name="template" xsi:type="string">templates/our-template</item>
        </argument>
    </pulsestorm_simple>
</components>

假设我们需要一个 pulsestorm_simple 组件,但是我们要换一个 template。那么我们可以按照相同的结构重写一下要用的 template。

比如,我们需要下面的模板:

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/adminhtml/ui_component/templates/different.xhtml -->
<?xml version="1.0" encoding="UTF-8"?>
<div>
    <h1>Hello Brave New World</h1>
</div>

我们需要做的就是增加下面 template 部分

<!-- File: app/code/Pulsestorm/SimpleUiComponent/view/adminhtml/ui_component/pulsestorm_simple.xml -->
<pulsestorm_simple 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="template" xsi:type="string">templates/different</item>
    </argument>
    <!-- ... -->
</pulsestorm_simple>

这个功能通常不作用于 template,它常用在配置参数的重写上,并且对于使用至关重要。在实际开发中,你不会向 definition.xml 中加东西,但是 debug grid listing 的参数时,还是需要参考 definition.xml 的。

添加数据

今天我们要说的最后一件时 UI Component 的 data 。UI Component 的 data 类似于 grid listing 的行,或是 form 的默认值。Behind the scenes, the UI Component system can render this backend data for you in the frontend as a javascript array/object.

我们需要做的就是在 component 类中定义 getDataSourceData 方法。

#File: app/code/Pulsestorm/SimpleUiComponent/Component/Simple.php    
<?php
namespace Pulsestorm\SimpleUiComponent\Component;
class Simple extends \Magento\Ui\Component\AbstractComponent
{
    <!-- ... -->
    public function getDataSourceData()
    {
        return ['data' => ['foo'=>'bar']];
    }
}

清除缓存并刷新页面后,查看源代码,我们会发现刚刚的 foo => bar 已经在 JSON 中了。

"pulsestorm_simple_data_source": {
    //...
    "config": {
        "data": {
            "foo": "bar"
        }
    //...
    }
}

你可能会感到很困惑,如果 data 来自于 component 类的 getDataSourceData 方法,那么为什么还要配置 Pulsestorm\SimpleUiComponent\Model\DataProvider 呢?

Alan 也没有好的答案。基于核心代码,看起来正确的使用方式是,在 component 类中获得 data provider 对象,然后调用它的 getData 方法。

#File: app/code/Pulsestorm/SimpleUiComponent/Component/Simple.php
public function getDataSourceData()
{
    return ['data' => $this->getContext()->getDataProvider()->getData()];
}

data provider 的 getData 方法返回实际的数据

#File: app/code/Pulsestorm/SimpleUiComponent/Model/DataProvider.php
<?php
namespace Pulsestorm\SimpleUiComponent\Model;
class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
{
    public function getData()
    {
        return [['foo'=>'baz']];
    }
}

乍一看,UI Component 系统似乎是一个面向对象的领域专用语言(domain specific language),用来创建用户界面的组件,可能已经朝着这个目的去做了,但是,深入了解后发现,他还没有完成,里面有各种边缘清空,bugs, 奇怪的遗漏。

后续步骤

简而言之, 这是 UI Component system 的 PHP 部分。在一天结束时, 所有这些复杂性可以归结为渲染一个 xhtml 模板并将其绑定到数据源上。

在我们的后续文章中,我们将更深入地了解 Magento 的 js 系统(RequireJS 和 knockout.js)是如何与 UI component 系统交互的。这些交互正是 Magento 的 grid listings 和后端 forms 渲染的主要工作,理解这些系统对于自定义后端 UI 是至关重要的。

发表评论

电子邮件地址不会被公开。 必填项已用*标注