Magento 2:管理者グリッドへの列の追加

猫の下には、Magento 2管理パネルのメイングリッドテーブルに関連付けられたテーブルのデータを含む列を追加する例と、追加列で機能するフィルターのダーティハックがあります。 これはまったく「マゼント2の方法」ではありませんが、どういうわけか機能するため、存在する権利があります。







データ構造



クライアントの紹介ツリーを作成する問題を解決し(クライアントの親が子の子孫を引き付ける)、 customer_entity



関連付けられた追加のテーブルを作成しました。 つまり、追加テーブルには、親子関係とツリーに関する情報(クライアントの「深さ」とツリー内のクライアントへのパス)が含まれています。







テーブル構造
 CREATE TABLE prxgt_dwnl_customer ( customer_id int(10) UNSIGNED NOT NULL COMMENT 'Reference to the customer.', parent_id int(10) UNSIGNED NOT NULL COMMENT 'Reference to the customer''s parent.', depth int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Depth of the node in the tree.', path varchar(255) NOT NULL COMMENT 'Path to the node - /1/2/3/.../' PRIMARY KEY (customer_id), CONSTRAINT FK_CUSTOMER FOREIGN KEY (customer_id) REFERENCES customer_entity (entity_id) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT FK_PARENT FOREIGN KEY (parent_id) REFERENCES prxgt_dwnl_customer (customer_id) ON DELETE RESTRICT ON UPDATE RESTRICT )
      
      





UIコンポーネント



私の目標は、現在のクライアントの親とツリー内のクライアントの深さに関する情報を含むクライアントグリッドに2列を追加することでした。 顧客グリッドは、XMLファイルvendor/magento/module-customer/view/adminhtml/ui_component/customer_listing.xml



記述されています。 dataSource



ノード、特にデータソースの名前( customer_listing_data_source



)に興味があります。







 <dataSource name="customer_listing_data_source"> <argument name="dataProvider" xsi:type="configurableObject"> <argument name="name" xsi:type="string">customer_listing_data_source</argument> ... </argument> </dataSource>
      
      





(これはデータソースの名前です- 名前nameを持つname属性または引数ノードです。Magentoは最初のバージョン以来、開発者を良好な状態に保つために異なるタイプの要素に同じ名前を使用するのが良い伝統があるため、言うのは難しいです)







データプロバイダー



グリッドのデータソースは、どんなに些細な音でも、コレクションです。 次に、 vendor/magento/module-customer/etc/di.xml



という名前のデータソースの説明を示しvendor/magento/module-customer/etc/di.xml









 <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory"> <arguments> <argument name="collections" xsi:type="array"> <item name="customer_listing_data_source" xsi:type="string">Magento\Customer\Model\ResourceModel\Grid\Collection</item> ... </argument> </arguments> </type>
      
      





つまり、顧客グリッドにデータを提供するクラスは、 \Magento\Customer\Model\ResourceModel\Grid\Collection



です。







コレクションの変更



デバッガでコレクションに入ると、データを取得するためのSQLクエリが次のようになっていることがわかります。







 SELECT `main_table`.* FROM `customer_grid_flat` AS `main_table`
      
      





これは、Magentoのもう1つの優れた伝統です。これらの「インデックステーブル」を使用して、柔軟性の向上に関連するアプリケーションの低迷を克服します。 クライアントの場合、フラットテーブルがあり、それに統合できる可能性は十分にありますが、より普遍的な方法を探していました。 JOINが必要でした。







JOINの機能は、 \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection::_beforeLoad



のみ見つかりました:







 protected function _beforeLoad() { ... $this->_eventManager->dispatch('core_collection_abstract_load_before', ['collection' => $this]); ... }
      
      





モジュールでcore_collection_abstract_load_before



イベント(ファイルetc/events.xml



)にサブスクライブしました。







 <event name="core_collection_abstract_load_before"> <!-- Add additional attributes to the Customer Grid in adminhtml. --> <observer name="praxigento_donwlilne_on_core_collection_abstract_load_before" instance="Praxigento\Downline\Observer\CoreCollectionAbstractLoadBefore"/> </event>
      
      





そして、彼はこのイベントに応答するクラスを作成し、そこで最初のリクエストを修正しました。







 class CoreCollectionAbstractLoadBefore implements ObserverInterface { const AS_FLD_CUSTOMER_DEPTH = 'prxgtDwnlCustomerDepth'; const AS_FLD_PARENT_ID = 'prxgtDwnlParentId'; const AS_TBL_CUST = 'prxgtDwnlCust'; public function execute(\Magento\Framework\Event\Observer $observer) { $collection = $observer->getData('collection'); if ($collection instanceof \Magento\Customer\Model\ResourceModel\Grid\Collection) { $query = $collection->getSelect(); $conn = $query->getConnection(); /* LEFT JOIN `prxgt_dwnl_customer` AS `prxgtDwnlCust` */ $tbl = [self::AS_TBL_CUST => $conn->getTableName('prxgt_dwnl_customer')]; $on = self::AS_TBL_CUST . 'customer_id.=main_table.entity_id'; $cols = [ self::AS_FLD_CUSTOMER_DEPTH => 'depth', self::AS_FLD_PARENT_ID => 'parent_id' ]; $query->joinLeft($tbl, $on, $cols); $sql = (string)$query; /* dirty hack for filters goes here ... */ } return; } }
      
      





その結果、変更後、SQLクエリは次のようになり始めました。







 SELECT `main_table`.*, `prxgtDwnlCust`.`depth` AS `prxgtDwnlCustomerDepth` `prxgtDwnlCust`.`parent_id` AS `prxgtDwnlParentId` FROM `customer_grid_flat` AS `main_table` LEFT JOIN `prxgt_dwnl_customer` AS `prxgtDwnlCust` ON prxgtDwnlCust.customer_id = main_table.entity_id
      
      





なぜなら 独自のテーブル(prxgtDwnlCustomerDepthおよびprxgtDwnlParentId)からのデータにエイリアスを使用している場合、このアプローチを使用する他の開発者が追加フィールドの名前で私と一致することを心配することはできません(ほとんどの場合、 prxgtからのデータ)が、これはグリッドからのフィルタリングが機能しなくなったという事実にもつながりました。







列を追加



グリッド内の列を再定義するには、元のUIコンポーネント( view/adminhtml/ui_component/customer_listing.xml



)を記述するものと同じ名前のモジュールにXMLファイルを作成し、データフィールドを名前として使用して追加の列を作成する必要がありますエイリアス:







 <?xml version="1.0" encoding="UTF-8"?> <listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> <columns name="customer_columns"> <column name="prxgtDwnlParentId"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">textRange</item> <item name="label" xsi:type="string" translate="true">Parent ID</item> </item> </argument> </column> <column name="prxgtDwnlCustomerDepth"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">textRange</item> <item name="label" xsi:type="string" translate="true">Depth</item> </item> </argument> </column> </columns> </listing>
      
      





結果





(スピーカーを手で動かし、余分なものを隠した-新しいMagentoの素晴らしい機能)







フィルターの「ダーティハック」



編集 :より直接的な解決策は、 $collection->addFilterToMap(...)



メソッドを使用するプラグインを使用することです。 この場合、コレクションは使用の直前ではなく、作成直後に変更されます。







新しい列のフィルターを機能させるために、最初のクエリ( CoreCollectionAbstractLoadBefore



)にJOINを追加して、同じクラスですべての逆変換 "alias" => "table.field"を実行する方法よりも良いものは思いつきませんでした。







 public function execute(\Magento\Framework\Event\Observer $observer) { ... /* the dirty hack */ $where = $query->getPart('where'); $replaced = $this->_replaceAllAliasesInWhere($where); $query->setPart('where', $replaced); ... } protected function _replaceAllAliasesInWhere($where) { $result = []; foreach ($where as $item) { $item = $this->_replaceAliaseInWhere($item, self::AS_FLD_CUSTOMER_DEPTH, self::AS_TBL_CUST, 'depth'); $item = $this->_replaceAliaseInWhere($item, self::AS_FLD_PARENT_ID, self::AS_TBL_CUST, 'parent_id'); $result[] = $item; } return $result; } protected function _replaceAliaseInWhere($where, $fieldAlias, $tableAlias, $fieldName) { $search = "`$fieldAlias`"; $replace = "`$tableAlias`.`$fieldName`"; $result = str_replace($search, $replace, $where); return $result; }
      
      






All Articles