题外话
目前 Magento 2 官网上关于 UI components 的介绍文档 依然不够实用,如果要实践还是要依靠社区内容,以及自己参考 Magento 2 自带的模块。
之前发布了两篇关于 UI components 的文章,一篇是 Alan Storm 的 Magento 2 的 UI Components 介绍(翻译),另一篇是 How to Create Admin Grid in Magento 2,但这篇文章只用到了 Listing component ,没有继续下去,完成完整的增删改查。此外,该文章末尾的参考文档,其实是很有参考价值的,博主深挖到了文档对应 github 地址。
文档:How to Create Admin Grid in Magento 2
代码:mageplaza/magento-2-sample-module
上面的模块基于 Magento 2.1 ,使用了 ui component 的 listng component ,但是 form 部分是用的是 layout 方式而不是 component 方式。ui components 的 xml 写法 2.1 和 2.2 有一些差别,但是 2.2 兼容 2.1 的写法。
比如 2.1.7 中 vendor\magento\module-customer\view\base\ui_component\customer_form.xml
中的写法:
<dataSource name="customer_form_data_source">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">Magento\Customer\Model\Customer\DataProvider</argument>
<argument name="name" xsi:type="string">customer_form_data_source</argument>
<argument name="primaryFieldName" xsi:type="string">entity_id</argument>
<argument name="requestFieldName" xsi:type="string">id</argument>
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="submit_url" xsi:type="url" path="customer/index/save"/>
<item name="validate_url" xsi:type="url" path="customer/index/validate"/>
</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>
在 2.2.0 版本中是这样的:
<dataSource name="customer_form_data_source">
<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>
<settings>
<validateUrl path="customer/index/validate"/>
<submitUrl path="customer/index/save"/>
</settings>
<dataProvider class="Magento\Customer\Model\Customer\DataProvider" name="customer_form_data_source">
<settings>
<requestFieldName>id</requestFieldName>
<primaryFieldName>entity_id</primaryFieldName>
</settings>
</dataProvider>
</dataSource>
该模块在 2.2.0 版本上 di:compile 的时候会报错,主要是因为 2.2.0 中类的变动,后面还会提到,但不影响执行和研究。总的来说,是一个很好的例子,有兴趣的童鞋可以继续研究。
本文中项目的出发点是使用 UI components 完成一个完整的增删改查。和参考文档不同的是,form 部分用的是 form component 。
具体的功能点如下:
- ui listing 的展示、筛选
- 单个记录的增加(form component)
- 单个记录的修改(form component)
- 单个记录的删除
- 批量删除
- ui listing 的行内批量修改
Magento_Ui
模块中的 readme.md
是这样介绍的:
The Magento\Ui module introduces a set of common UI components, which could be used and configured via layout XML files.
他是为了统一 UI ,希望可以通过 layout xml 来进行配置和使用。但目前看来模块还够不稳定。
实践
创建一个模块
创建模块 ThankIT_UIcomponents
ThankIT\UIcomponents\registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'ThankIT_UIcomponents',
__DIR__
);
ThankIT\UIcomponents\etc\module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="ThankIT_UIcomponents" setup_version="1.0.0" />
</config>
创建数据表和 model 等
ThankIT\UIcomponents\Setup\InstallSchema.php
<?php
namespace ThankIT\UIcomponents\Setup;
class InstallSchema implements \Magento\Framework\Setup\InstallSchemaInterface
{
/**
* install tables
*
* @param \Magento\Framework\Setup\SchemaSetupInterface $setup
* @param \Magento\Framework\Setup\ModuleContextInterface $context
* @return void
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function install(
\Magento\Framework\Setup\SchemaSetupInterface $setup,
\Magento\Framework\Setup\ModuleContextInterface $context
) {
$installer = $setup;
$installer->startSetup();
if (!$installer->tableExists('thankit_uicomponents_post')) {
$table = $installer->getConnection()->newTable(
$installer->getTable('thankit_uicomponents_post')
)
->addColumn(
'post_id',
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
null,
[
'identity' => true,
'nullable' => false,
'primary' => true,
'unsigned' => true,
],
'Post ID'
)
->addColumn(
'name',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
255,
['nullable => false'],
'Post Name'
)
->addColumn(
'url_key',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
255,
[],
'Post URL Key'
)
->addColumn(
'post_content',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
'64k',
[],
'Post Post Content'
)
->addColumn(
'tags',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
255,
[],
'Post Tags'
)
->addColumn(
'status',
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
1,
[],
'Post Status'
)
->addColumn(
'featured_image',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
255,
[],
'Post Featured Image'
)
->addColumn(
'sample_country_selection',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
3,
[],
'Post Sample Country Selection'
)
->addColumn(
'sample_upload_file',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
255,
[],
'Post Sample File'
)
->addColumn(
'sample_multiselect',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
'64k',
[],
'Post Sample Multiselect'
)
->addColumn(
'created_at',
\Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
null,
[],
'Post Created At'
)
->addColumn(
'updated_at',
\Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
null,
[],
'Post Updated At'
)
->setComment('Post Table');
$installer->getConnection()->createTable($table);
$installer->getConnection()->addIndex(
$installer->getTable('thankit_uicomponents_post'),
$setup->getIdxName(
$installer->getTable('thankit_uicomponents_post'),
['name', 'url_key', 'post_content', 'tags', 'featured_image', 'sample_upload_file'],
\Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_FULLTEXT
),
['name', 'url_key', 'post_content', 'tags', 'featured_image', 'sample_upload_file'],
\Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_FULLTEXT
);
}
$installer->endSetup();
}
}
ThankIT\UIcomponents\Model\Post.php
<?php
namespace ThankIT\UIcomponents\Model;
class Post extends \Magento\Framework\Model\AbstractModel
{
/**
* Initialize resource model
*
* @return void
*/
protected function _construct()
{
$this->_init('ThankIT\UIcomponents\Model\ResourceModel\Post');
}
}
ThankIT\UIcomponents\Model\ResourceModel\Post.php
<?php
namespace ThankIT\UIcomponents\Model\ResourceModel;
class Post extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
/**
* Date model
*
* @var \Magento\Framework\Stdlib\DateTime\DateTime
*/
protected $_date;
/**
* constructor
*
* @param \Magento\Framework\Stdlib\DateTime\DateTime $date
* @param \Magento\Framework\Model\ResourceModel\Db\Context $context
*/
public function __construct(
\Magento\Framework\Stdlib\DateTime\DateTime $date,
\Magento\Framework\Model\ResourceModel\Db\Context $context
) {
$this->_date = $date;
parent::__construct($context);
}
/**
* Initialize resource model
*
* @return void
*/
protected function _construct()
{
$this->_init('thankit_uicomponents_post', 'post_id');
}
/**
* before save callback
*
* @param \Magento\Framework\Model\AbstractModel|\Mageplaza\HelloWorld\Model\Post $object
* @return $this
*/
protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object)
{
$object->setUpdatedAt($this->_date->date());
if ($object->isObjectNew()) {
$object->setCreatedAt($this->_date->date());
}
return parent::_beforeSave($object);
}
}
ThankIT\UIcomponents\Model\ResourceModel\Post\Collection.php
<?php
namespace ThankIT\UIcomponents\Model\ResourceModel\Post;
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
// 这个很有意思,本来应该取到 Model\ResourceModel\Post 中设置的值
// 如果这里不设置,那么 massDelete 中 filter 中 getIdFieldName 为空
// 所以这里加上了,但是为啥呢?
protected $_idFieldName = 'post_id';
protected function _construct()
{
$this->_init('ThankIT\UIcomponents\Model\Post', 'ThankIT\UIcomponents\Model\ResourceModel\Post');
}
}
后台入口
ThankIT\UIcomponents\etc\adminhtml\menu.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
<menu>
<add id="ThankIT_UIcomponents::posts" title="ThankIT" module="ThankIT_UIcomponents" sortOrder="51" resource="ThankIT_UIcomponents::posts"/>
<add id="ThankIT_UIcomponents::post" title="Posts" module="ThankIT_UIcomponents" sortOrder="10" action="thankit_uicomponent/post/index" resource="ThankIT_UIcomponents::post" parent="ThankIT_UIcomponents::posts"/>
</menu>
</config>
ThankIT\UIcomponents\etc\adminntml\routes.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/App/etc/routes.xsd">
<router id="admin">
<route id="thankit_uicomponent" frontName="thankit_uicomponent">
<module name="ThankIT_UIcomponents" after="Magento_Ui"/>
</route>
</router>
</config>
ThankIT\UIcomponents\etc\acl.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Backend::admin">
<resource id="ThankIT_UIcomponents::posts" title="ThankIT" sortOrder="51">
<resource id="ThankIT_UIcomponents::post" title="Posts" sortOrder="10"/>
</resource>
</resource>
</resources>
</acl>
</config>
grid 的实现
ThankIT\UIcomponents\Controller\Adminhtml\Post\Index.php
<?php
namespace ThankIT\UIcomponents\Controller\Adminhtml\Post;
class Index extends \Magento\Backend\App\Action
{
const ADMIN_RESOURCE = 'ThankIT_UIcomponents::post';
protected $resultPageFactory;
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
parent::__construct($context);
$this->resultPageFactory = $resultPageFactory;
}
public function execute()
{
$page = $this->resultPageFactory->create();
// 如果没有下面这句,title 是 Magento Admin ,active menu 正常
// 如果下面这句的 id 不存在,则 active menu 不正常 title 还是 Magento Admin
$page->setActiveMenu('ThankIT_UIcomponents::post');
// title 替换成 Posts
$page->getConfig()->getTitle()->prepend((__('Posts')));
// class: Magento\Backend\Model\View\Result\Page
// addBreadcrumb($label, $title, $link = null)
$page->addBreadcrumb(__('ThankIT'), __('ThankIT'));
$page->addBreadcrumb(__('Hello World'), __('Manage Blogs'));
return $page;
}
protected function _isAllowed()
{
return $this->_authorization->isAllowed(static::ADMIN_RESOURCE);
}
}
ThankIT/UIcomponents/view/adminhtml/layout/thankit_uicomponent_post_index.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="content">
<uiComponent name="thankit_uicomponents_post_listing"/>
</referenceContainer>
</body>
</page>
ThankIT/UIcomponents/view/adminhtml/ui_component/thankit_uicomponents_post_listing.xml
<?xml version="1.0"?>
<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">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing_data_source</item>
<item name="deps" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing_data_source</item>
</item>
<item name="spinner" xsi:type="string">thankit_uicomponents_post_columns</item>
<item name="buttons" xsi:type="array">
<item name="add" xsi:type="array">
<item name="name" xsi:type="string">add</item>
<item name="label" xsi:type="string" translate="true">Add New Post</item>
<item name="class" xsi:type="string">primary</item>
<item name="url" xsi:type="string">thankit_uicomponent/post/new</item>
</item>
</item>
</argument>
<dataSource name="thankit_uicomponents_post_listing_data_source">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider</argument>
<argument name="name" xsi:type="string">thankit_uicomponents_post_listing_data_source</argument>
<argument name="primaryFieldName" xsi:type="string">post_id</argument>
<argument name="requestFieldName" xsi:type="string">post_id</argument>
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="update_url" xsi:type="url" path="mui/index/render"/>
</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/grid/provider</item>
</item>
</argument>
</dataSource>
<container name="listing_top">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="template" xsi:type="string">ui/grid/toolbar</item>
<item name="stickyTmpl" xsi:type="string">ui/grid/sticky/toolbar</item>
</item>
</argument>
<bookmark name="bookmarks">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="namespace" xsi:type="string">thankit_uicomponents_post_listing</item>
</item>
</item>
</argument>
</bookmark>
<component name="columns_controls">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsData" xsi:type="array">
<item name="provider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns</item>
</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/controls/columns</item>
<item name="displayArea" xsi:type="string">dataGridActions</item>
</item>
</argument>
</component>
<exportButton name="export_button">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="selectProvider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns.ids</item>
</item>
</argument>
</exportButton>
<filterSearch name="fulltext">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="provider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing_data_source</item>
<item name="chipsProvider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.listing_top.listing_filters_chips</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.search</item>
</item>
</item>
</argument>
</filterSearch>
<filters name="listing_filters">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsProvider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.filters</item>
</item>
<item name="templates" xsi:type="array">
<item name="filters" xsi:type="array">
<item name="select" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/form/element/ui-select</item>
<item name="template" xsi:type="string">ui/grid/filters/elements/ui-select</item>
</item>
</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="provider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.listing_top.listing_filters</item>
<item name="imports" xsi:type="array">
<item name="visible" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns.${ $.index }:visible</item>
</item>
</item>
</item>
<item name="observers" xsi:type="array">
<item name="column" xsi:type="string">column</item>
</item>
</argument>
</filters>
<massaction name="listing_massaction">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="selectProvider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns.ids</item>
<item name="indexField" xsi:type="string">post_id</item>
</item>
</argument>
<action name="delete">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">delete</item>
<item name="label" xsi:type="string" translate="true">Delete</item>
<item name="url" xsi:type="url" path="thankit_uicomponent/post/massDelete"/>
<item name="confirm" xsi:type="array">
<item name="title" xsi:type="string" translate="true">Delete Posts</item>
<item name="message" xsi:type="string" translate="true">Are you sure you wan't to delete selected Posts?</item>
</item>
</item>
</argument>
</action>
<action name="edit">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">edit</item>
<item name="label" xsi:type="string" translate="true">Edit</item>
<item name="callback" xsi:type="array">
<item name="provider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns_editor</item>
<item name="target" xsi:type="string">editSelected</item>
</item>
</item>
</argument>
</action>
</massaction>
<paging name="listing_paging">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.paging</item>
</item>
<item name="selectProvider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns.ids</item>
</item>
</argument>
</paging>
</container>
<columns name="thankit_uicomponents_post_columns">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="editorConfig" xsi:type="array">
<item name="selectProvider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns.ids</item>
<item name="enabled" xsi:type="boolean">true</item>
<item name="indexField" xsi:type="string">post_id</item>
<item name="clientConfig" xsi:type="array">
<item name="saveUrl" xsi:type="url" path="thankit_uicomponent/post/inlineEdit"/>
<item name="validateBeforeSave" xsi:type="boolean">false</item>
</item>
</item>
<!-- column clickable -->
<item name="childDefaults" xsi:type="array">
<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns_editor</item>
<item name="target" xsi:type="string">startEdit</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">${ $.$data.rowIndex }</item>
<item name="1" xsi:type="boolean">true</item>
</item>
</item>
</item>
</item>
</argument>
<selectionsColumn name="ids">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="resizeEnabled" xsi:type="boolean">true</item>
<item name="resizeDefaultWidth" xsi:type="string">55</item>
<item name="indexField" xsi:type="string">post_id</item>
</item>
</argument>
</selectionsColumn>
<column name="post_id">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">textRange</item>
<item name="sorting" xsi:type="string">asc</item>
<item name="label" xsi:type="string" translate="true">ID</item>
</item>
</argument>
</column>
<column name="name">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="editor" xsi:type="array">
<item name="editorType" xsi:type="string">text</item>
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">true</item>
</item>
</item>
<item name="label" xsi:type="string" translate="true">Name</item>
</item>
</argument>
</column>
<column name="url_key">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="visible" xsi:type="boolean">false</item>
<item name="label" xsi:type="string" translate="true">URL Key</item>
<item name="dataType" xsi:type="string">text</item>
</item>
</argument>
</column>
<column name="tags">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="visible" xsi:type="boolean">false</item>
<item name="label" xsi:type="string" translate="true">Tags</item>
<item name="dataType" xsi:type="string">text</item>
</item>
</argument>
</column>
<column name="status">
<argument name="data" xsi:type="array">
<item name="options" xsi:type="object">Magento\Config\Model\Config\Source\Yesno</item>
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">select</item>
<item name="label" xsi:type="string" translate="true">Status</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
<item name="dataType" xsi:type="string">select</item>
</item>
</argument>
</column>
<column name="sample_country_selection">
<argument name="data" xsi:type="array">
<item name="options" xsi:type="object">Magento\Config\Model\Config\Source\Locale\Country</item>
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">select</item>
<item name="visible" xsi:type="boolean">false</item>
<item name="label" xsi:type="string" translate="true">Sample Country Selection</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
<item name="dataType" xsi:type="string">select</item>
</item>
</argument>
</column>
<column name="created_at" class="Magento\Ui\Component\Listing\Columns\Date">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">dateRange</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
<item name="dataType" xsi:type="string">date</item>
<item name="label" xsi:type="string" translate="true">Created</item>
</item>
</argument>
</column>
<column name="updated_at" class="Magento\Ui\Component\Listing\Columns\Date">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">dateRange</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
<item name="dataType" xsi:type="string">date</item>
<item name="label" xsi:type="string" translate="true">Modified</item>
</item>
</argument>
</column>
<actionsColumn name="actions" class="ThankIT\UIcomponents\Ui\Component\Listing\Column\PostActions">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="resizeEnabled" xsi:type="boolean">false</item>
<item name="resizeDefaultWidth" xsi:type="string">107</item>
<item name="indexField" xsi:type="string">post_id</item>
</item>
</argument>
</actionsColumn>
</columns>
<container name="sticky">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/sticky/sticky</item>
<item name="toolbarProvider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.listing_top</item>
<item name="listingProvider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns</item>
</item>
</argument>
</container>
</listing>
这个文件是关键。
这里用的 2.1 的写法,和 How to Create Admin Grid in Magento 2 是一样的。
这里声明了一个 dataSource thankit_uicomponents_post_listing_data_source
我们需要在 di.xml 创建它
ThankIT\UIcomponents\etc\di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="thankit_uicomponents_post_listing_data_source" xsi:type="string">ThankIT\UIcomponents\Model\ResourceModel\Post\Grid\Collection</item>
</argument>
</arguments>
</type>
<type name="ThankIT\UIcomponents\Model\ResourceModel\Post\Grid\Collection">
<arguments>
<argument name="mainTable" xsi:type="string">thankit_uicomponents_post</argument>
<argument name="resourceModel" xsi:type="string">ThankIT\UIcomponents\Model\ResourceModel\Post</argument>
</arguments>
</type>
</config>
这里和之前文章中的相比,有一些简化。
ThankIT\UIcomponents\Model\ResourceModel\Post\Grid\Collection.php
<?php
namespace ThankIT\UIcomponents\Model\ResourceModel\Post\Grid;
use Magento\Framework\Data\Collection\Db\FetchStrategyInterface as FetchStrategy;
use Magento\Framework\Data\Collection\EntityFactoryInterface as EntityFactory;
use Magento\Framework\Event\ManagerInterface as EventManager;
use Psr\Log\LoggerInterface as Logger;
class Collection extends \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult
{
public function __construct(
EntityFactory $entityFactory,
Logger $logger,
FetchStrategy $fetchStrategy,
EventManager $eventManager,
$mainTable,
$resourceModel
) {
parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $mainTable, $resourceModel);
}
}
这个 collection 和之前不一样,继承的是不同的父类,这也是之前代码在 2.2 上 di:compile 会报错的原因。
add new
在 ThankIT/UIcomponents/view/adminhtml/layout/thankit_uicomponent_post_index.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="buttons" xsi:type="array">
<item name="add" xsi:type="array">
<item name="name" xsi:type="string">add</item>
<item name="label" xsi:type="string" translate="true">Add New Post</item>
<item name="class" xsi:type="string">primary</item>
<item name="url" xsi:type="string">thankit_uicomponent/post/new</item>
</item>
</item>
</argument>
这里我们给了 url thankit_uicomponent/post/new
下面创建它的 controller 和对应的 layout
ThankIT\UIcomponents\Controller\Adminhtml\Post\NewAction.php
<?php
namespace ThankIT\UIcomponents\Controller\Adminhtml\Post;
class NewAction extends \Magento\Backend\App\Action
{
const ADMIN_RESOURCE = 'ThankIT_UIcomponents::post';
protected $resultPageFactory;
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
parent::__construct($context);
$this->resultPageFactory = $resultPageFactory;
}
public function execute()
{
$page = $this->resultPageFactory->create();
$page->setActiveMenu('ThankIT_UIcomponents::post');
$page->getConfig()->getTitle()->prepend((__('New Post')));
$page->addBreadcrumb(__('ThankIT'), __('ThankIT'));
$page->addBreadcrumb(__('Hello World'), __('Manage Blogs'));
return $page;
}
}
这里要用 NewAction.php
作为名字。
ThankIT/UIcomponents/view/adminhtml/layout/thankit_uicomponent_post_new.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="content">
<uiComponent name="thankit_uicomponents_post_form"/>
</referenceContainer>
</body>
</page>
关键的地方来了,这里用的是 2.2 的写法,参考了 vendor\magento\module-catalog\view\adminhtml\ui_component\category_form.xml
的用法。
ThankIT\UIcomponents\view\adminhtml\ui_component\thankit_uicomponents_post_form.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<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">thankit_uicomponents_post_form.post_form_data_source</item>
<item name="deps" xsi:type="string">thankit_uicomponents_post_form.post_form_data_source</item>
</item>
<item name="layout" xsi:type="array">
<item name="type" xsi:type="string">tabs</item>
</item>
<item name="buttons" xsi:type="array">
<item name="save" xsi:type="array">
<item name="name" xsi:type="string">save</item>
<item name="label" xsi:type="string" translate="true">Save Post</item>
<item name="class" xsi:type="string">primary</item>
<item name="url" xsi:type="string">thankit_uicomponent/post/save</item>
</item>
<item name="back" xsi:type="array">
<item name="name" xsi:type="string">back</item>
<item name="label" xsi:type="string" translate="true">Back</item>
<item name="class" xsi:type="string">back</item>
<item name="url" xsi:type="string">thankit_uicomponent/post/index</item>
</item>
</item>
</argument>
<dataSource name="post_form_data_source">
<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>
<settings>
<submitUrl path="thankit_uicomponent/post/save"/>
</settings>
<dataProvider class="ThankIT\UIcomponents\Model\Post\DataProvider" name="post_form_data_source">
<settings>
<requestFieldName>post_id</requestFieldName>
<primaryFieldName>post_id</primaryFieldName>
</settings>
</dataProvider>
</dataSource>
<fieldset name="post_form">
<settings>
<label translate="true">Post Information</label>
<collapsible>false</collapsible>
</settings>
<field name="post_id" formElement="hidden">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">post</item>
<item name="label" xsi:type="string" translate="true">Post ID</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
</settings>
</field>
<field name="name" formElement="input">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">post</item>
<item name="label" xsi:type="string" translate="true">Name</item>
</item>
</argument>
<settings>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
</validation>
<dataType>text</dataType>
</settings>
</field>
<field name="url_key" formElement="input">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">post</item>
<item name="label" xsi:type="string" translate="true">url key</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
</settings>
</field>
<field name="post_content" formElement="wysiwyg" template="ui/form/field">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">post</item>
<item name="label" xsi:type="string" translate="true">post content</item>
</item>
</argument>
<formElements>
<wysiwyg>
<settings>
<rows>8</rows>
<wysiwyg>true</wysiwyg>
</settings>
</wysiwyg>
</formElements>
</field>
<field name="featured_image" formElement="fileUploader">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">post</item>
</item>
</argument>
<settings>
<elementTmpl>ui/form/element/uploader/uploader</elementTmpl>
<dataType>string</dataType>
<label translate="true">Featured Image</label>
<visible>true</visible>
<required>false</required>
</settings>
<formElements>
<fileUploader>
<settings>
<required>false</required>
<uploaderConfig>
<param xsi:type="url" name="url" path="catalog/category_image/upload"/>
</uploaderConfig>
<previewTmpl>Magento_Catalog/image-preview</previewTmpl>
</settings>
</fileUploader>
</formElements>
</field>
<field name="tags" formElement="input">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">post</item>
<item name="label" xsi:type="string" translate="true">tags</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
</settings>
</field>
<field name="status" formElement="select">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">post</item>
<item name="label" xsi:type="string" translate="true">status</item>
</item>
</argument>
<settings>
<dataType>number</dataType>
</settings>
<formElements>
<select>
<settings>
<options class="Magento\Config\Model\Config\Source\Yesno"/>
</settings>
</select>
</formElements>
</field>
<field name="sample_country_selection" component="Magento_Ui/js/form/element/country" formElement="select">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">post</item>
<item name="label" xsi:type="string" translate="true">sample country selection</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
</settings>
<formElements>
<select>
<settings>
<options class="\Magento\Config\Model\Config\Source\Locale\Country"/>
</settings>
</select>
</formElements>
</field>
</fieldset>
</form>
这里面声明了
<dataProvider class="ThankIT\UIcomponents\Model\Post\DataProvider" name="post_form_data_source">
<settings>
<requestFieldName>post_id</requestFieldName>
<primaryFieldName>post_id</primaryFieldName>
</settings>
</dataProvider>
ThankIT\UIcomponents\Model\Post\DataProvider.php
<?php
namespace ThankIT\UIcomponents\Model\Post;
use ThankIT\UIcomponents\Model\ResourceModel\Post\CollectionFactory as postCollectionFactory;
class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
{
protected $request;
protected $loadedData;
public function __construct(
postCollectionFactory $postCollectionFactory,
\Magento\Framework\App\RequestInterface $request,
\ThankIT\UIcomponents\Model\PostFactory $postFactory,
$name,
$primaryFieldName,
$requestFieldName,
array $meta = [],
array $data = []
) {
$this->collection = $postCollectionFactory->create();
$this->postFactory = $postFactory;
$this->request = $request;
parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
}
public function getData()
{
$requestId = $this->request->getParam($this->requestFieldName);
if (isset($this->loadedData)) {
return $this->loadedData;
}
$post = $this->postFactory->create();
// 如果是 update 操作
if ($requestId) {
$post->load($requestId);
if (!$post->getId()) {
throw NoSuchEntityException::singleField('id', $requestId);
}
$postData = $post->getData();
$this->loadedData[$requestId]['post_form'] = $postData;
} else {
// new 操作
$this->loadedData = [];
}
return $this->loadedData;
}
}
这里遇到过的错误类似:
PHP Fatal error: Method Magento\Ui\TemplateEngine\Xhtml\Result::__toString() must not throw an exception, caught Error: Call to a member function addFieldToFilter() on null in /data/sites/project_name/vendor/magento/module-ui/Component/Wrapper/UiComponent.php on line 0
通过:$this->collection = $postCollectionFactory->create();
解决。
getData()
可以先不要管,返回 []
也行。
保存
ThankIT\UIcomponents\view\adminhtml\ui_component\thankit_uicomponents_post_form.xml 中的保存按钮路径是 thankit_uicomponent/post/save
ThankIT\UIcomponents\Controller\Adminhtml\Post\Save.php
<?php
namespace ThankIT\UIcomponents\Controller\Adminhtml\Post;
class Save extends \Magento\Backend\App\Action
{
const ADMIN_RESOURCE = 'ThankIT_UIcomponents::post';
protected $postFactory;
public function __construct(
\Magento\Backend\App\Action\Context $context,
\ThankIT\UIcomponents\Model\PostFactory $postFactory
) {
parent::__construct($context);
$this->postFactory = $postFactory;
}
public function execute()
{
$data = $this->getRequest()->getParam('post_form');
// var_dump($data);
// return;
$post = $this->postFactory->create();
$postId = $data['post_id'];
unset($data['post_id']);
// 如果是 update
if ($postId) {
$post->load($postId);
// setData 的用法有点怪 在他前面的都被 overwrite 了
// 如果没有 post_id 则会新增
$post->setData($data);
$post->setData('post_id', $postId);
} else {
$post->setData($data);
}
// todo 处理图片
if (array_key_exists('featured_image', $data)) {
$featuredImage = $data['featured_image'];
unset($data['featured_image']);
// $post->setFeaturedImage($featuredImage[0]['url']);
}
$post->save();
$this->_redirect('*/*/index');
}
}
这个仅是参考,所写的考虑也不周全,尤其是图片处理部分,根本就没有处理。
update
其实 update 和新增使用的是相同的 form ,相同的 save
ThankIT/UIcomponents/view/adminhtml/ui_component/thankit_uicomponents_post_listing.xml 中
<actionsColumn name="actions" class="ThankIT\UIcomponents\Ui\Component\Listing\Column\PostActions">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="resizeEnabled" xsi:type="boolean">false</item>
<item name="resizeDefaultWidth" xsi:type="string">107</item>
<item name="indexField" xsi:type="string">post_id</item>
</item>
</argument>
</actionsColumn>
ThankIT\UIcomponents\Ui\Component\Listing\Column\PostActions.php
<?php
namespace ThankIT\UIcomponents\Ui\Component\Listing\Column;
class PostActions extends \Magento\Ui\Component\Listing\Columns\Column
{
/**
* Url path to edit
*
* @var string
*/
const URL_PATH_EDIT = 'thankit_uicomponent/post/edit';
/**
* Url path to delete
*
* @var string
*/
const URL_PATH_DELETE = 'thankit_uicomponent/post/delete';
/**
* URL builder
*
* @var \Magento\Framework\UrlInterface
*/
protected $_urlBuilder;
/**
* constructor
*
* @param \Magento\Framework\UrlInterface $urlBuilder
* @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context
* @param \Magento\Framework\View\Element\UiComponentFactory $uiComponentFactory
* @param array $components
* @param array $data
*/
public function __construct(
\Magento\Framework\UrlInterface $urlBuilder,
\Magento\Framework\View\Element\UiComponent\ContextInterface $context,
\Magento\Framework\View\Element\UiComponentFactory $uiComponentFactory,
array $components = [],
array $data = []
) {
$this->_urlBuilder = $urlBuilder;
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* Prepare Data Source
*
* @param array $dataSource
* @return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as &$item) {
if (isset($item['post_id'])) {
$item[$this->getData('name')] = [
'edit' => [
'href' => $this->_urlBuilder->getUrl(
static::URL_PATH_EDIT,
[
'post_id' => $item['post_id'],
]
),
'label' => __('Edit'),
],
'delete' => [
'href' => $this->_urlBuilder->getUrl(
static::URL_PATH_DELETE,
[
'post_id' => $item['post_id'],
]
),
'label' => __('Delete'),
'confirm' => [
'title' => __('Delete "${ $.$data.name }"'),
'message' => __('Are you sure you wan\'t to delete the Post "${ $.$data.name }" ?'),
],
],
];
}
}
}
return $dataSource;
}
}
所以我们的 edit 对应的 url 是 thankit_uicomponent/post/edit
delete 对应的 url 是 thankit_uicomponent/post/delete
并且他们都会将 post_id
传递过去。
ThankIT\UIcomponents\Controller\Adminhtml\Post\Edit.php
<?php
namespace ThankIT\UIcomponents\Controller\Adminhtml\Post;
class Edit extends \Magento\Backend\App\Action
{
const ADMIN_RESOURCE = 'ThankIT_UIcomponents::post';
protected $postFactory;
public function __construct(
\Magento\Backend\App\Action\Context $context,
\ThankIT\UIcomponents\Model\PostFactory $postFactory,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
parent::__construct($context);
$this->postFactory = $postFactory;
$this->resultPageFactory = $resultPageFactory;
}
public function execute()
{
// $data = $this->getRequest()->getParams();
$page = $this->resultPageFactory->create();
$page->setActiveMenu('ThankIT_UIcomponents::post');
// title 替换成 Posts
$page->getConfig()->getTitle()->prepend((__('Edit Post')));
return $page;
}
}
ThankIT/UIcomponents/view/adminhtml/layout/thankit_uicomponent_post_edit.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="content">
<uiComponent name="thankit_uicomponents_post_form"/>
</referenceContainer>
</body>
</page>
而 thankit_uicomponents_post_form
我们之前已经声明过了。
delete
ThankIT\UIcomponents\Controller\Adminhtml\Post\Delete.php
<?php
namespace ThankIT\UIcomponents\Controller\Adminhtml\Post;
class Delete extends \Magento\Backend\App\Action
{
const ADMIN_RESOURCE = 'ThankIT_UIcomponents::post';
protected $postFactory;
public function __construct(
\Magento\Backend\App\Action\Context $context,
\ThankIT\UIcomponents\Model\PostFactory $postFactory,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
parent::__construct($context);
$this->postFactory = $postFactory;
$this->resultPageFactory = $resultPageFactory;
}
public function execute()
{
$id = $this->getRequest()->getParam('post_id');
if ($id) {
$post = $this->postFactory->create();
$post->load($id);
$post->delete();
$this->messageManager->addSuccess(__('The Post has been deleted.'));
}
$this->_redirect('*/*/index');
}
}
批量删除
ThankIT/UIcomponents/view/adminhtml/ui_component/thankit_uicomponents_post_listing.xml 中定义批量删除对应的 url 为 thankit_uicomponent/post/massDelete
<massaction name="listing_massaction">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="selectProvider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns.ids</item>
<item name="indexField" xsi:type="string">post_id</item>
</item>
</argument>
<action name="delete">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">delete</item>
<item name="label" xsi:type="string" translate="true">Delete</item>
<item name="url" xsi:type="url" path="thankit_uicomponent/post/massDelete"/>
<item name="confirm" xsi:type="array">
<item name="title" xsi:type="string" translate="true">Delete Posts</item>
<item name="message" xsi:type="string" translate="true">Are you sure you wan't to delete selected Posts?</item>
</item>
</item>
</argument>
</action>
...
</massaction>
ThankIT\UIcomponents\Controller\Adminhtml\Post\MassDelete.php
<?php
namespace ThankIT\UIcomponents\Controller\Adminhtml\Post;
class MassDelete extends \Magento\Backend\App\Action
{
const ADMIN_RESOURCE = 'ThankIT_UIcomponents::post';
protected $postFactory;
public function __construct(
\Magento\Backend\App\Action\Context $context,
\ThankIT\UIcomponents\Model\PostFactory $postFactory,
\Magento\Ui\Component\MassAction\Filter $filter,
\ThankIT\UIcomponents\Model\ResourceModel\Post\CollectionFactory $collectionFactory,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
parent::__construct($context);
$this->postFactory = $postFactory;
$this->resultPageFactory = $resultPageFactory;
$this->collectionFactory = $collectionFactory;
$this->filter = $filter;
}
public function execute()
{
// $data = $this->getRequest()->getParam('selected');
// var_dump($data);
$collection = $this->filter->getCollection($this->collectionFactory->create());
$collectionSize = $collection->getSize();
foreach ($collection as $page) {
$page->delete();
}
$this->messageManager->addSuccess(__('A total of %1 record(s) have been deleted.', $collectionSize));
$this->_redirect('*/*/index');
}
}
批量修改
ThankIT/UIcomponents/view/adminhtml/ui_component/thankit_uicomponents_post_listing.xml
...
<container name="listing_top">
...
<massaction name="listing_massaction">
...
<action name="edit">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">edit</item>
<item name="label" xsi:type="string" translate="true">Edit</item>
<item name="callback" xsi:type="array">
<item name="provider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns_editor</item>
<item name="target" xsi:type="string">editSelected</item>
</item>
</item>
</argument>
</action>
</massaction>
</container>
<columns name="thankit_uicomponents_post_columns">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="editorConfig" xsi:type="array">
<item name="selectProvider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns.ids</item>
<item name="enabled" xsi:type="boolean">true</item>
<item name="indexField" xsi:type="string">post_id</item>
<item name="clientConfig" xsi:type="array">
<item name="saveUrl" xsi:type="url" path="thankit_uicomponent/post/inlineEdit"/>
<item name="validateBeforeSave" xsi:type="boolean">false</item>
</item>
</item>
<!-- column clickable -->
<item name="childDefaults" xsi:type="array">
<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">thankit_uicomponents_post_listing.thankit_uicomponents_post_listing.thankit_uicomponents_post_columns_editor</item>
<item name="target" xsi:type="string">startEdit</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">${ $.$data.rowIndex }</item>
<item name="1" xsi:type="boolean">true</item>
</item>
</item>
</item>
</item>
</argument>
....
<column name="name">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="editor" xsi:type="array">
<item name="editorType" xsi:type="string">text</item>
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">true</item>
</item>
</item>
<item name="label" xsi:type="string" translate="true">Name</item>
</item>
</argument>
</column>
....
</columns>
...
批量修改的 url 是 thankit_uicomponent/post/inlineEdit
这是一个 ajax 请求。
ThankIT\UIcomponents\Controller\Adminhtml\Post\InlineEdit.php
<?php
namespace ThankIT\UIcomponents\Controller\Adminhtml\Post;
class InlineEdit extends \Magento\Backend\App\Action
{
const ADMIN_RESOURCE = 'ThankIT_UIcomponents::post';
protected $postFactory;
public function __construct(
\Magento\Backend\App\Action\Context $context,
\ThankIT\UIcomponents\Model\PostFactory $postFactory,
\Magento\Framework\Controller\Result\JsonFactory $jsonFactory
) {
parent::__construct($context);
$this->postFactory = $postFactory;
$this->jsonFactory = $jsonFactory;
}
public function execute()
{
// 请求类似 http://192.168.0.251/M220/admin/thankit_uicomponent/post/inlineEdit/key/5fae8c36c82d28ec5e605d8787782dce781512bb4f1f7f14520f00c5cfb5ab53/?isAjax=true
$resultJson = $this->jsonFactory->create();
$error = false;
$messages = [];
$postItems = $this->getRequest()->getParam('items', []);
if (!($this->getRequest()->getParam('isAjax') && count($postItems))) {
return $resultJson->setData([
'messages' => [__('Please correct the data sent.')],
'error' => true,
]);
}
foreach (array_keys($postItems) as $postId) {
$post = $this->postFactory->create()->load($postId);
try {
$postData = $postItems[$postId]; //todo: handle dates
$post->addData($postData);
$post->save();
} catch (\Magento\Framework\Exception\LocalizedException $e) {
$messages[] = $this->getErrorWithPostId($post, $e->getMessage());
$error = true;
} catch (\RuntimeException $e) {
$messages[] = $this->getErrorWithPostId($post, $e->getMessage());
$error = true;
} catch (\Exception $e) {
$messages[] = $this->getErrorWithPostId(
$post,
__('Something went wrong while saving the Post.')
);
$error = true;
}
}
return $resultJson->setData([
'messages' => $messages,
'error' => $error,
]);
}
protected function getErrorWithPostId(\ThankIT\UIcomponents\Model\Post $post, $errorText)
{
return '[Post ID: ' . $post->getId() . '] ' . $errorText;
}
}
转发:https://www.hellomagento2.com/ui-components-%E5%AE%8C%E6%95%B4%E6%A1%88%E4%BE%8B/