Magento 2: Hinzufügen einer Spalte zum Admin-Grid

  • Tutorial

Unter der Katze finden Sie ein Beispiel für das Hinzufügen einer zusätzlichen Spalte mit Daten aus der Tabelle, die der Hauptrastertabelle im Magento 2-Administrationsfenster zugeordnet ist, und einen fehlerhaften Hack für den Filter, der auf die zusätzliche Spalte angewendet werden soll. Ich gebe zu, dass dies kein "Magento 2-Weg" ist, aber es funktioniert irgendwie und hat daher ein Existenzrecht.


Datenstruktur


Ich habe das Problem des Erstellens eines Verweisbaums von Clients (der übergeordnete Client zieht den untergeordneten Client an) gelöst und eine zusätzliche Tabelle erstellt, an die gebunden ist customer_entity. Kurz gesagt, die zusätzliche Tabelle enthält die Eltern-Kind-Beziehung und Informationen zum Baum (die "Tiefe" des Clients und den Pfad zum Client im Baum).


Tabellenstruktur
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-Komponente


Mein Ziel war es, dem Client-Raster zwei zusätzliche Spalten hinzuzufügen, die Informationen über das übergeordnete Element des aktuellen Clients und die Tiefe des Clients in der Baumstruktur enthalten. Das Client-Grid wird in einer XML-Datei beschrieben vendor/magento/module-customer/view/adminhtml/ui_component/customer_listing.xml. Wir interessieren uns für den Knoten dataSourceund insbesondere für den Namen der Datenquelle ( customer_listing_data_source):


customer_listing_data_source
        ...
    

(Dass dies der Name der Datenquelle - der Attribut Name oder Argument-Name Knotenname , schwer zu sagen, in Magento seit der ersten Version hat eine gute Tradition die gleichen Namen für verschiedene Arten von Gegenständen zu verwenden Entwickler in Ton zu halten)


Datenanbieter


Die Datenquelle für das Raster ist die Sammlung, egal wie banal sie klingt. Hier ist eine Beschreibung der Datenquelle mit dem Namen customer_listing_data_sourcein der Datei vendor/magento/module-customer/etc/di.xml:


Magento\Customer\Model\ResourceModel\Grid\Collection
            ...
        

Dies ist die Klasse, die Daten für das Client-Grid bereitstellt \Magento\Customer\Model\ResourceModel\Grid\Collection.


Sammlungsänderung


Wenn Sie mit einem Debugger in die Auflistung gelangen, sehen Sie, dass die SQL-Abfrage zum Abrufen von Daten ungefähr so ​​aussieht:


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

Dies ist eine weitere gute Tradition in Magento - um die erhöhte Trägheit der Anwendung zu überwinden, die mit einer erhöhten Flexibilität verbunden ist, indem diese "Indextabellen" verwendet werden. Bei Kunden gibt es einen flachen Tisch, in den man sich möglicherweise integrieren könnte, aber ich habe nach einem universelleren Weg gesucht. Ich brauchte einen JOIN.


Ich habe JOINs Fähigkeit nur in der Methode gefunden \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection::_beforeLoad:


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

Ich habe die Veranstaltung core_collection_abstract_load_before(Datei etc/events.xml) in meinen Modulen abonniert :



Und er hat eine Klasse erstellt, die auf dieses Ereignis reagiert, in der er die ursprüngliche Anforderung geändert hat:


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;
    }
}

Infolgedessen sah die SQL-Abfrage nach der Änderung ungefähr so ​​aus:


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

Weil Ich verwende Aliase für Daten aus meiner eigenen Tabelle (prxgtDwnlCustomerDepth und prxgtDwnlParentId), dann kann ich nicht sehr befürchten, dass ein anderer Entwickler, der diesen Ansatz verwendet, mit dem Namen zusätzlicher Felder mit mir übereinstimmt (kaum jemand wird beginnen, sie aufzurufen) Daten prxgt ), aber auch auf die Tatsache, daß das Filtergitter Arbeits gestoppt hat.


Spalte hinzufügen


Um die Spalten im Raster neu zu definieren, müssen Sie in Ihrem Modul eine XML-Datei mit demselben Namen wie diejenige view/adminhtml/ui_component/customer_listing.xmlerstellen , die die ursprüngliche UI-Komponente beschreibt ( ), und zusätzliche Spalten erstellen, indem Sie Aliase als Datenfeldnamen verwenden:


textRangeParent IDtextRangeDepth

Ergebnis



(Ich habe meine Lautsprecher mit den Händen bewegt und das Überflüssige versteckt - ein großartiges Feature im neuen Magento)


"Dirty Hack" für den Filter


EDITED : Eine direktere Lösung ist durch Plugins mit der Methode $collection->addFilterToMap(...). In diesem Fall ändert sich die Sammlung unmittelbar nach ihrer Erstellung und nicht unmittelbar vor ihrer Verwendung.


Damit Filter für neue Spalten funktionieren, habe ich mir nichts Besseres ausgedacht, als die inverse Transformation "alias" => "table.field" in derselben Klasse durchzuführen, indem ich der ursprünglichen Abfrage JOIN ( CoreCollectionAbstractLoadBefore) hinzufügte :


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;
}

Jetzt auch beliebt: