Jak dodać kolumnę z filtrem do siatki zamówień Magento 2?
Opublikowany: 2020-02-03Często administratorzy sklepów Magento 2 wymagają dodatkowych opcji, jeśli chodzi o korzystanie i dostosowywanie siatki zamówień – filtrowanie w oparciu o konkretny, out-of-the-box parametr może stać się prawdziwym wyzwaniem.
Niedawno skontaktował się ze mną programista Magento z pytaniem. Próbował rozszerzyć siatkę zamówień na Magento 2.3.1. Nie pomogły stare posty dostępne w sieci – naturalnie wiele się zmieniło w Magento od dwóch lat.
Na podstawie jego pytania proponuję przyjrzeć się rozwiązaniu konkretnego przypadku:
Do siatki zamówień należy dodać kolumnę ― z kodem regionalnym klienta, który dokonał zakupu ―. Dodatkowo administrator sklepu musi mieć możliwość filtrowania zamówień według tej nowo dodanej kolumny.
Może wydawać się to łatwe, ale jest kilka rzeczy, na które warto zwrócić uwagę. Na przykład kupujący, który dokonał zakupu, może nie mieć adresu dostawy ― w przypadku zamówienia wirtualnego. Albo jak mamy wymienić regiony? Wszystko to należy wziąć pod uwagę przystępując do rozwoju. Na podstawie wspomnianych wcześniej pytań załóżmy:
- produkty wirtualne nie będą miały regionu dostawy („null”). Pomoże Ci to wybrać je na podstawie tego parametru,
- regiony zostaną wymienione i będą miały wygląd kodów bez ich przekształcania w etykiety, tak jak w domyślnym Magento 2.
*Pamiętaj, że na końcu tego artykułu będzie link do modułu z otwartym dostępem na GitHub. Chociaż zamieszczam link w tym akapicie, jeśli nie zdołasz dokończyć tego artykułu: https://github.com/mageworx/articles-extended-orders-grid.
Jednak mimo braku czasu zachęcam do dalszej lektury.?
W związku z tym, aby dodać nową kolumnę do siatki zamówień, musisz:
Spis treści
- 1. Utwórz nowy moduł
- 2. Dodaj kolumnę do siatki
- Wyjaśnienia
- 3. Dodaj dane do kolumny
- Jak dodać dodatkową kolumnę?
- [Aktualizacja] Jak dodać kolumnę z informacjami o pozycjach zamówienia?
1. Utwórz nowy moduł
Najpierw zdecydujmy o nazwie modułu i dostawcy. Cóż, nie muszę wybierać nazwy dostawcy – to MageWorx (jakbym miał wybór, żartuję). Nadal mogę wybrać nazwę modułu. Niech będzie to ExtendedOrdersGrid (przy okazji mamy rozszerzenie o tej samej nazwie dla Magento 2). W rzeczywistości używanie MageWorx jako nazwy dostawcy w przestrzeni nazw nie daje prawa do żądania bezpłatnego wsparcia. W każdym razie, jeśli członkowie naszego zespołu wsparcia mieli dobry weekend, nadal możesz spróbować w poniedziałek.?
Stwórzmy następujący katalog: `app/code/MageWorx/ExtendedOrdersGrid`. Aby zarejestrować moduł, będziemy potrzebować kilku standardowych plików:
> registration.php php <?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'MageWorx_ExtendedOrdersGrid', __DIR__ ); > composer.json json { "name": "mageworx/module-extended-orders-grid", "description": "Extended Orders Grid Extension", "require": { "magento/module-ui" : ">=100.1.0 < 102", "magento/module-sales" : ">=100.0.0 <103" }, "type": "magento2-module", "version": "1.0.0", "license": [ "OSL-3.0", "AFL-3.0" ], "autoload": { "files": [ "registration.php" ], "psr-4": { "MageWorx\\ExtendedOrdersGrid\\": "" } } } > etc/module.xml 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="MageWorx_ExtendedOrdersGrid" setup_version="1.0.0"> <sequence> <module name="Magento_Sales"/> <module name="Magento_Ui"/> </sequence> </module> </config>
Należy zauważyć, że ograniczyłem prawa autorskie, które są automatycznie dodawane do mojego IDE. W ten sposób możesz bez problemu korzystać z tego kodu. ?
Następnie uruchom kilka poleceń:
> sudo -u www-data php bin/magento module:enable MageWorx_ExtendedOrdersGrid > sudo -u www-data php bin/magento setup:upgrade
I voila! Teraz nasz moduł można zobaczyć w Magento 2! Och, jeśli pracujesz na zdalnym serwerze, nie zapomnij przesłać plików przed testowaniem.
2. Dodaj kolumnę do siatki
Następnie za pomocą interfejsu Magento 2 dodajmy nową kolumnę do standardowej siatki. W tym celu utwórz plik:
> view/adminhtml/ui_component/sales_order_grid.xml
o następującej treści (jego znaczenie omówimy dalej):
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="sales_order_columns"> <column name="code"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/column</item> <item name="label" xsi:type="string" translate="true">Region Code</item> <item name="sortOrder" xsi:type="number">60</item> <item name="align" xsi:type="string">left</item> <item name="dataType" xsi:type="string">text</item> <item name="visible" xsi:type="boolean">true</item> <item name="filter" xsi:type="string">text</item> </item> </argument> </column> </columns> </listing>
Tutaj musisz określić, że nazwa nie jest nigdzie zabrana. Dla doświadczonych programistów jest to jasne, ale dla tych, którzy dopiero poznają Magento 2, dodam kilka wyjaśnień na końcu tego akapitu.
Do dodawania kolumn do siatki użyliśmy standardowego węzła `columns`, gdzie należy również dodać nową kolumnę pod nazwą 'code'. Następnie zapiszmy atrybuty naszej kolumny w siatce:
1) Komponent . Jest to klasa JS odpowiedzialna za tworzenie i przetwarzanie tej kolumny. Znajduje się w module Magento_Ui pod adresem:
`dostawca/magento/moduł-ui/widok/baza/web/js/grid/kolumny/kolumna.js`
Właściwie, jeśli jesteś ciekawskim deweloperem, spójrz także na inne realizacje. Jest wiele ekscytujących rzeczy.
2) Etykieta. Jest to wiersz z nazwą kolumny, która zostanie wyświetlona użytkownikowi końcowemu. Nie zapomnij dodać go do pliku lokalizacyjnego i18n, jeśli ustanowiono właściwość `translate`. ?
3) Porządek sortowania. To jest pozycja kolumny w siatce. Jeśli moduł został zainstalowany na Magento 2, który nigdy nie był zarządzany przez administratora sklepu, zadziała. W przeciwnym razie na Magento 2, które jest używane, nasza kolumna zostanie dodana na koniec listy, bez względu na to, co zrobimy.
4) Wyrównaj. Oznacza to wyrównanie zawartości kolumny. Myślę, że to jasne.
5) Typ danych. To jest typ danych, którym będziemy manipulować. W naszym przypadku jest to po prostu wiersz tekstu. Może to być jednak lista lub wartość logiczna, a także liczby, jeśli mówimy na przykład o ilości produktów.
6) Widoczne. To nic innego jak widoczność kolumn, choć wiele zależy od tego, czy siatka była wcześniej używana, czy nie.
Wszystkie dane dotyczące siatki są przechowywane w bazie danych Magento, w tabeli `ui_bookmark`. Tak więc, jeśli nie ma zapisu o naszej kolumnie dla zmodyfikowanej siatki w momencie instalacji naszego modułu, nie oczekuj żadnego cudu.
7) Filtruj . To jest typ filtra. Tutaj określiliśmy, że filtrowanie powinno odbywać się tak, jak w przypadku zwykłego tekstu. Podczas działania kodu zostanie on przekształcony w stan MySQL `LIKE %value%`.
Wyjaśnienia
Krótko mówiąc, nazwa rozszerzonego komponentu interfejsu użytkownika musi odpowiadać nazwie oryginału. W naszym przypadku jest to `vendor/magento/module-sales/view/adminhtml/ui_component/sales_order_grid.xml` i jest obliczany z katalogu głównego modułu `view/adminhtml/ui_component/sales_order_grid.xml`.
Sama siatka interfejsu użytkownika jest dodawana do wymaganego kontrolera za pomocą układu `vendor/magento/module-sales/view/adminhtml/layout/sales_order_index.xml` w następujący sposób:
xml <?xml version="1.0"?> <!-- /** * Copyright Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <update handle="styles"/> <body> <referenceContainer name="content"> <uiComponent name="sales_order_grid"/> </referenceContainer> </body> </page>
Oznacza to *dodaj ten komponent UI pod nazwą `sales_order_grid` do zawartości strony* ― tak jak w przypadku bloków. Jednak zamiast bloków mamy `uiComponent` z jego atrybutami i ograniczeniami.
Teraz wyczyść pamięć podręczną Magento 2 za pomocą następującego polecenia:
> sudo -u www-data php bin/magento cache:clean config
i sprawdź wyniki w siatce zamówień:
3. Dodaj dane do kolumny
Być może zauważyłeś, że kolumna się pojawiła, ale jej zawartość nie wygląda tak, jak oczekiwano… w ogóle nie ma tam wyświetlanych danych. W porządku, nic nie dodaliśmy, więc nie ma tam treści.
Przejdźmy do napisania prostej wtyczki, która pomoże Ci wypełnić naszą kolumnę. W tym celu przechwyćmy zapytanie kolekcji siatki, wykonaj sprzężenie w wymaganej tabeli i siatce. Wtyczka będzie wyglądać tak:
> app/code/MageWorx/ExtendedOrdersGrid/Plugin/AddDataToOrdersGrid.php
php <?php namespace MageWorx\ExtendedOrdersGrid\Plugin; /** * Class AddDataToOrdersGrid */ class AddDataToOrdersGrid { /** * @var \Psr\Log\LoggerInterface */ private $logger; /** * AddDataToOrdersGrid constructor. * * @param \Psr\Log\LoggerInterface $customLogger * @param array $data */ public function __construct( \Psr\Log\LoggerInterface $customLogger, array $data = [] ) { $this->logger = $customLogger; } /** * @param \Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory $subject * @param \Magento\Sales\Model\ResourceModel\Order\Grid\Collection $collection * @param $requestName * @return mixed */ public function afterGetReport($subject, $collection, $requestName) { if ($requestName !== 'sales_order_grid_data_source') { return $collection; } if ($collection->getMainTable() === $collection->getResource()->getTable('sales_order_grid')) { try { $orderAddressTableName = $collection->getResource()->getTable('sales_order_address'); $directoryCountryRegionTableName = $collection->getResource()->getTable('directory_country_region'); $collection->getSelect()->joinLeft( ['soa' => $orderAddressTableName], 'soa.parent_id = main_table.entity_id AND soa.address_type = \'shipping\'', null ); $collection->getSelect()->joinLeft( ['dcrt' => $directoryCountryRegionTableName], 'soa.region_id = dcrt.region_id', ['code'] ); } catch (\Zend_Db_Select_Exception $selectException) { // Do nothing in that case $this->logger->log(100, $selectException); } } return $collection; } }
Będzie odnosić się do następującej klasy:
`Magento\Framework\Widok\Element\UiComponent\DataProvider\Fabryka Kolekcji`
> etc/adminhtml/di.xml xml <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <!-- Plugins --> <!-- Adds additional data to the orders grid collection --> <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory"> <plugin name="mageworx_extended_orders_grid_add_data_to_orders_grid" type="MageWorx\ExtendedOrdersGrid\Plugin\AddDataToOrdersGrid" sortOrder="10" disabled="false"/> </type> </config>
Wtyczka działa w następujący sposób:
Ponieważ przechwyci absolutnie wszystkie kolekcje, dodajmy walidację do wymaganej tabeli `sales_order_grid`. Gdy zostanie znaleziony, a Magento spróbuje uzyskać dane na jego temat, wykonujemy połączenie tabeli z adresem `sales_order_address` według `order_id` (`entity_id` w tabeli `sales_order_grid` i `parent_id` w tabeli `sales_order_address` ( „soa” alias)). Teraz mamy dostęp do danych adresowych dostawy zamówienia klienta, które możemy wykorzystać do określenia `region_id`. Znajduje się on jako liczba w tabeli `sales_order_address`. Ta liczba odpowiada indeksowi w tabeli `directory_country_region` (alias `dcrt`). Ponieważ potrzebujemy tego regionalnego kodu z tabeli, jak wspomniano powyżej, połączmy kolumnę „kod”. Ta dokładna kolumna otrzyma dane wyjściowe (odpowiada wartości `kolumny` w `sales_order_grid.xml`).
Następnie wyczyść pamięć podręczną:
> sudo -u www-data php bin/magento cache:clean config
Zaloguj się w panelu administracyjnym do siatki zamówień. Jeśli wszystko zostało zrobione poprawnie, zobaczymy nasze kody regionalne:
Bezpłatny dostęp do modułu jest dostępny na GitHubie: https://github.com/mageworx/articles-extended-orders-grid.
Ważna uwaga!
Aby dodać jakiekolwiek dane do tej kolumny, należy pobrać kolumnę 'twoja_nazwa_kolumny' i wszystkie potrzebne dane dodane podczas tworzenia kolekcji. Oznacza to, że musisz zrobić coś innego ― zastąpić nazwę tabeli i napisać własne sprzężenie. Nie zapomnij również edytować nazwy kolumny w pliku `sales_order_grid.xml`, jeśli jest to wymagane.
Jak dodać dodatkową kolumnę?
Jeśli zdecydujesz się dodać jeszcze jedną kolumnę, postępuj zgodnie z wytycznymi opisanymi w następującym zatwierdzeniu Git: https://github.com/mageworx/articles-extended-orders-grid/commit/d31c364a25ce493ab64731c5ca0481e146dbbac3
Tam dodaliśmy kolumnę telephone
do siatki z tabeli sales_order_address
dla adresu typu shipping
. Jak widać w kodzie zatwierdzenia, nie były wymagane żadne znaczące modyfikacje kodu.
Dodatkowo kolumny te można z powodzeniem wyeksportować ze standardowego interfejsu „siatki zamówień”.
Oto jak wygląda siatka z kolumną „telefon”:
Na poniższym zrzucie ekranu możesz zobaczyć te same zamówienia wyeksportowane do pliku .csv:
[Aktualizacja] Jak dodać kolumnę z informacjami o pozycjach zamówienia?
Jak wiesz, wszystkie zamówienia mają ilość przedmiotów, zaczynając od jednego do ponad liczbę (tak, marzenie kupców). Ale jak możemy wyświetlić te informacje w siatce zamówień, aby łatwiej rozpocząć wyszukiwanie lub przeprowadzić analizę?
Nie możemy po prostu dodać kolumny, jak to zrobiliśmy wcześniej, ponieważ w takim przypadku otrzymujemy tylko jedną nazwę produktu lub SKU w każdym wierszu. Albo zapisy zamówień zostaną zduplikowane… Nie potrzebujemy też tego bałaganu.
Dlatego postaram się wyjaśnić, jak możemy to zrobić we właściwy sposób (moim zdaniem).
Załóżmy, że potrzebujemy kolumny danych „Nazwa produktu” w siatce zamówień.
Najpierw dodajmy nową kolumnę w definicji sales_order_grid
, tak jak to zrobiliśmy wcześniej:
<columns> .... <column name="name"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/column</item> <item name="filter" xsi:type="string">text</item> <item name="label" xsi:type="string" translate="true">Product Name</item> <item name="visible" xsi:type="boolean">false</item> <item name="sortOrder" xsi:type="number">70</item> </item> </argument> </column> </columns>
Następnie wszystko, czego potrzebujemy, to zbudowanie odpowiedniego zapytania dla naszej nowej kolumny. Musi zawierać nazwę każdego produktu kupionego w tej kolejności, oddzieloną przecinkiem, z możliwością wyszukiwania według wartości tej kolumny itp. Utwórzmy nową metodę we wtyczce mageworx_extended_orders_grid_add_data_to_orders_grid
:
/** * Adds products name column to the orders grid collection * * @param OrderGridCollection $collection * @return OrderGridCollection */ private function addProductsNameColumn(OrderGridCollection $collection): OrderGridCollection { return $collection; }
i nazwij tę metodę w oryginalnej treści metody:
if ($collection->getMainTable() === $collection->getResource()->getTable('sales_order_grid')) { try { $orderAddressTableName = $collection->getResource()->getTable('sales_order_address'); ... // Add product's name column $this->addProductsNameColumn($collection); } catch (\Zend_Db_Select_Exception $selectException) { ...
Aby uzyskać żądane dane w jednej kolumnie, musimy utworzyć podselekcję z dwiema kolumnami: order_id
i name
(nazwa produktu) w indywidualnym wyborze, który można później dołączyć do głównej kolekcji:
// Get original table name $orderItemsTableName = $collection->getResource()->getTable('sales_order_item'); // Create new select instance $itemsTableSelectGrouped = $collection->getConnection()->select(); // Add table with columns which must be selected (skip useless columns) $itemsTableSelectGrouped->from( $orderItemsTableName, [ 'name' => new \Zend_Db_Expr('GROUP_CONCAT(DISTINCT name SEPARATOR \',\')'), 'order_id' => 'order_id' ] ); // Group our select to make only one column for one order $itemsTableSelectGrouped->group('order_id');
Wyjaśnienia:
-
$collection->getConnection()->select()
utworzy nową instancjęMagento\Framework\Db\Select
.
Jest to wymagane, ponieważ nie możemy użyć oryginalnego wyboru z kolekcji, ponieważ zawiera on swoje własne dane, a wszelkie modyfikacje spowodują błędy. - Kolumna
name
musi zawierać wszystkie nazwy produktów dla określonego zamówienia, tj. musi być pogrupowana przy użyciu\Zend_Db_Expr('GROUP_CONCAT(DISTINCT name SEPARATOR \',\')')
. W tym celu dodajemy późniejgroup('order_id')
do wyboru. Bez grupowania nie możemy użyć funkcjiGROUP_CONCAT
.
Teraz możemy dodać naszą podselekcję do głównej kolekcji i będzie to logiczny koniec metody addProductsNameColumn
:
// Add our sub-select to main collection with only one column: name $collection->getSelect() ->joinLeft( ['soi' => $itemsTableSelectGrouped], 'soi.order_id = main_table.entity_id', ['name'] ); return $collection;
Wyjaśnienia:
-
soi
to alias naszej pseudo-tabeli. -
order_id
to klucz, którego używamy do łączenia naszej głównej tabeli (siatki) z danymi pozycji zamówienia. -
['name']
jest jedyną kolumną, która jest dodawana do wyniku, ponieważ nie potrzebujemy innych informacji.
Ostateczny wynik jest dostępny w oficjalnym repozytorium tego przykładu.
Oto link do konkretnego zatwierdzenia: https://github.com/mageworx/articles-extended-orders-grid/commit/0cdffcd4ba66cacb2fd857ba7626fdbcfc0d6fe3
Oto jak ta kolumna wygląda na naszym hoście pomostowym:
A oto wynik eksportu (CSV):
Oto jak wygląda zapytanie podczas wyszukiwania zamówień z produktami „czarnymi” (na naszym hoście deweloperskim):
SELECT main_table
.*, soat
. telephone
, dcrt
. code
, soi
. name
Z sales_order_grid
JAKO main_table
PO LEWEJ DOŁĄCZ sales_order_address
AS soat
ON soat.parent_id = main_table.entity_id AND soat.address_type = 'dostawa'
DOŁĄCZ DO LEWEJ directory_country_region
AS dcrt
ON soat.region_id = dcrt.region_id
LEWY DOŁĄCZ (WYBIERZ GROUP_CONCAT(DISTINCT name SEPARATOR ',') AS name
, sales_order_item
. order_id
FROM sales_order_item
GROUP BY order_id
) AS soi
ON soi.order_id = main_table.entity_id
soi
. name
LIKE '%Czarny%'
Najwyraźniej nie jest to najszybszy sposób na wyprowadzanie danych. Jednak to chyba najłatwiejsze. Najlepszym sposobem jest zgromadzenie danych o nazwach produktów w osobnej kolumnie osobnej tabeli (identyfikator_zamówienia, nazwa_produktu) i dodanie tej tabeli bez dodatkowego grupowania i podselekcji.
W tym celu dodajemy grupę ('order_id') do naszego select (na końcu kodu metody).
Wybierz oznacza element.
Grupa oznacza metodę wyboru.
Myślę, że to wszystko. Jeśli masz jakieś pytania lub prośby, zostaw komentarz w polu komentarza.