#はじめに
この記事は Magento Advent Calendar 2018 の13日目です。
今回は、Magentoモジュールにおける管理画面のグリッドを作成する方法の一つ、UIComponentの書き方について、備忘録も兼ねて簡単に解説していこうと思います。
また私が初めてグリッドを作成した際、こちらの質問が大いに参考になりました。なので今回の記事もこれに準じていますが、コードは私が実際に書いたものを基にしているので幾分か差があります。
#グリッドについて
まず始めにグリッドがどのような部品でできているのか画像で見てみましょう。
これから書くコードをコピペすれば大体こんな感じのグリッドになります。(ちなみに上部のGrid
というタイトル表示はControllerで変更できます。)
これらの要素をUIComponentで記述していくことになります。
#定義
今回のグリッドで扱うモジュール名やデータベース名は以下のものとします。
適宜自分のものと読み替えてください。
- ベンダー名
- Company
- モジュール名
- Module
- テーブル名
- table
- カラム1
- table_id
- →主キー、int型、 auto_increment
- カラム2
- table_name
- →varchar(255)
#書き方
layoutファイルでUIComponentファイルを定義します。
このファイルはapp/code/Company/Module/adminhtml/view/layout/
に配置します。
<?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="module_table_list"/>
</referenceContainer>
</body>
</page>
この場合、uiComponentノードで"module_table_list"
と指定されているので、UIComponentファイルの名前はmodule_table_list.xml
となります。
UIComponentファイルはapp/code/Company/Module/adminhtml/view/ui_component/
に配置します。
UIComponentファイルは一見複雑そうに見えますが、実際には下記のシンプルな構造になっています。
- <listing>
- <argument>
- <dataSource>
- <container>
- <columns>
作りたいグリッドによって、これらのノードが増えたり減ったりします。特に<container>の子ノードは不要なものを省くことができます。実際にコードを書くときは量が多いのでコピペしてリプレースする方が楽でしょう。コード内で出てくるmodule
やtable
といった部分は適宜自分のモジュール名やテーブル名に置き換えてください。ではノードごとにコードを分けて解説していきます。
###listingノード
<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Ui/etc/ui_configuration.xsd">
<!--この中に以下のノードが入ります。-->
</listing>
最上位のノードです。
###argumentノード
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">module_table_list.module_table_listing_data_source</item>
<item name="deps" xsi:type="string">module_table_list.module_table_listing_data_source</item>
</item>
<item name="spinner" xsi:type="string">module_table_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 Data</item>
<item name="class" xsi:type="string">primary</item>
<item name="url" xsi:type="string">*/*/new</item>
</item>
</item>
</argument>
- <argument>にはモジュールに関する基本的なデータを記述します。
- js_config/providerとdepsノードは
"{ファイル名}.{データソース名}"
とします。{データソース名}
はわかりやすいように"{モジュール名}_{テーブル名}_listing_data_source"
としています。(実際に{データソース名}
を定義する箇所は次のノードです。) - spinnerノードはわかりやすいように
"{モジュール名}_{テーブル名}_columns"
としています。(後から出てきます。) - これ以降のコードで
module_table_list
等が出てきた場合、それは"{ファイル名}"
などと同じです。 - グリッド上部のボタンを設定する<button>には、レコードを追加するボタンを設定しています。buttons/add/urlノードでボタンを押した後のURLを変更できるので、Controllerでデータを追加する処理を記述しています。また
label
はボタン上に書かれる文字なので、役割に合わせて変更しましょう。
###dataSourceノード
<dataSource name="module_table_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">module_table_listing_data_source</argument>
<argument name="primaryFieldName" xsi:type="string">table_id</argument>
<argument name="requestFieldName" xsi:type="string">table_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 name="storageConfig" xsi:type="array">
<item name="indexField" xsi:type="string">table_id</item>
</item>
</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>
- <dataSource>内の
name
には{データソース名}
を入れましょう。これ以降のコードでも頻繁に出てきます。 - 一つ目の大きな<argument>はグリッドに表示されるデータを準備する役割を持つクラスを定義します。
-
class
はdi.xml
で定義される名前と同様にしなければなりません。 -
primaryFieldName
はデータベースの主キーと、requestFieldName
HTTPリクエスト変数と関連していて、両者は同じでよい場合がほとんどです。
-
- 二つ目の大きな<argument>はグリッドのAjaxを担うJavascriptファイルを参照します。デフォルトでは
Magento/Ui/view/base/web/js/grid/provider.js
となっています。
###containerノード
<container name="listing_top">
<!--この中に以下のノードが入ります。-->
</container>
最上位のノードです。name
のlisting_top
はこれ以降のコードでたびたび出てきます。機能によって以下の子ノードに分かれます。
- <argument>
- <bookmark>
- <component>
- <filtersearch>
- <filters>
- <massaction>
- <paging>
一気にすべて書くと長くなるのでノードごとにコードを区切って解説します。
####argumentノード
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="template" xsi:type="string">ui/grid/toolbar</item>
</item>
</argument>
レイアウトとすべてのアクションを処理するテンプレートファイルを定義するノードです。デフォルトではMagento/Ui/view/base/web/templates/grid/toolbar.html
となっています。
####bookmarkノード
<bookmark name="bookmarks"/>
ブックマーク機能を追加するノードです。name
のbookmarks
はこれ以降のコードで少しだけ出てきます。
####componentノード
<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">module_table_list.module_table_list.module_table_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>
-
component
はグリッドを表示するJavascriptファイルを参照していて、デフォルトではMagento/Ui/view/base/web/templates/grid/controls/columns.html
というテンプレートを使っているMagento/Ui/view/base/web/js/grid/controls/columns.js
を指定しています。 -
displayArea
はcontainer/argument/config/templateノードで定義されたテンプレートファイル内のgetRegion('dataGridActions')
を参照しています。
####filterSearchノード
<filterSearch name="fulltext">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="provider" xsi:type="string">module_table_list.module_table_listing_data_source</item>
<item name="chipsProvider" xsi:type="string">module_table_list.module_table_list.listing_top.listing_filters_chips</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">module_table_list.module_table_list.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.search</item>
</item>
</item>
</argument>
</filterSearch>
このノードはデータのテキスト検索機能をページに追加します。わかりやすく言うと"Search by keyword"のやつです。この検索機能はInstallSchema.phpでインデックスを作成しないと機能しないようです。
####filtersノード
<filters name="listing_filters">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsProvider" xsi:type="string">module_table_list.module_table_list.module_table_columns</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">module_table_list.module_table_list.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">module_table_list.module_table_list.listing_top.listing_filters</item>
<item name="imports" xsi:type="array">
<item name="visible" xsi:type="string">module_table_list.module_table_list.module_table_columns.${ $.index }:visible</item>
</item>
</item>
</item>
<item name="observers" xsi:type="array">
<item name="column" xsi:type="string">column</item>
</item>
</argument>
</filters>
- このノードはフィルターボタンを定義しています。
-
templates
では特定のフィルタオプションをレンダリングするために使用されるファイルを定義することができます。この場合select
のみが定義されMagento/Ui/view/base/web/js/form/element/ui-select.js
とMagento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html
が指定されています。 - 今回は
select
のみがデフォルトをオーバーライドするために定義されていますが、<filter>ノードや他のノードのデフォルト値はMagento/Ui/view/base/ui_component/etc/definition.xml
で定義されています。
####massactionノード
<massaction name="listing_massaction">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="selectProvider" xsi:type="string">module_table_list.module_table_list.module_table_columns.ids</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/tree-massactions</item>
<item name="indexField" xsi:type="string">table_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="*/*/massDelete"/>
<item name="confirm" xsi:type="array">
<item name="title" xsi:type="string" translate="true">Delete</item>
<item name="message" xsi:type="string" translate="true">Do you want to delete selected row record?</item>
</item>
</item>
</argument>
</action>
</massaction>
- 選択したデータに対して一括で削除などを行う機能を追加するノードです。
name
はユニークでなければなりません。 - <argument>は基本的な情報を記述します。
- <action>は実際に行う処理を指定しますが、今回は削除機能のみ搭載させています。
- 他の機能は
vender/magento/module-ui/view/base/ui_component/etc/definition.xml
に書いてあるので適宜参照してください。
####pagingノード
<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">module_table_list.module_table_list.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.paging</item>
</item>
<item name="selectProvider" xsi:type="string">module_table_list.module_table_list.module_table_columns.ids</item>
</item>
</argument>
</paging>
一ページ当たりの列の表示量選択などの機能を追加するノードです。
以上で長かった<container>ノードは終了です。
###columnsノード
<columns name="module_table_columns">
<selectionsColumn name="ids">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="indexField" xsi:type="string">table_id</item>
<item name="sortOrder" xsi:type="number">0</item>
</item>
</argument>
</selectionsColumn>
<column name="table_id">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Table ID</item>
<item name="sortOrder" xsi:type="number">10</item>
</item>
</argument>
</column>
<column name="table_name">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Table Name</item>
<item name="sortOrder" xsi:type="number">20</item>
</item>
</argument>
</column>
<actionsColumn name="actions" class="Company\Module\Ui\Component\Listing\Table\Column\Action">
<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">100</item>
<item name="indexField" xsi:type="string">table_id</item>
<item name="sortOrder" xsi:type="number">30</item>
</item>
</argument>
</actionsColumn>
</columns>
- <selectionsColumn>は<massaction>のためにデータを選択するチェックボックスのカラムです。
- <column>は実際のデータを表示するカラムです。
- <name>には表示させたいテーブルのカラム名を入れましょう。(テーブルの指定自体はResourceModelの定義の段階で済んでいるはずです)
-
filter
は<filter>で追加したフィルター機能の中で、カラムごとにどのようにフィルタリングさせるか指定できます。text
の場合はテキスト形式のフィルタリングができるようになります。 -
label
はグリッドに表示するカラムの名前です。必ずしも実際のテーブルのカラム名と同様にする必要はありません。
- <actionsColumn>は一つ一つのデータに対して削除や編集機能の処理をするリンクを表示するカラムを追加します。実際にどのような処理を追加するかはUIComponentではなくControllerで記述するためここでは省略します。
#さいごに
このファイルの書き方についてより詳しく解説する開発者ガイドのようなものがなかなか見つけられないので書きました。私にもわかっていない部分が多いし間違いもあるかもしれませんが、これを読んだ方にとって少しでも参考になれば幸いです。