За что я люблю Magento 2

от автора

Знаете, сейчас, в эпоху хороших фреймворков принято презирать всякие {название CMS, которую считаете ужасной} и прочие битриксы. И не мудрено, ведь эти вещи изначально создавались как будто не для программистов а для кодеров. Это можно оправдать наличием большого количества легаси, т.к. они писались давным-давно в далёкой-далёкой галактике. Они решают множество нужных и полезных задач, имеют огромные коммьюнити и тысячи плагинов, но когда ты смотришь под капот — медленно седеющие волосы на голове начинают шевелиться в такт «архитектуре».


Дисклеймер
  1. Все нижеуказанное не несет никакой ценности для опытных Magento разработчиков, т.к. не содержит ничего для них нового. Эта статья написана скорее с целью популяризации Magento 2, и немного — хвастовства в виде «смотри как оно умеет». Также, возможно, статья подойдет новичкам в качестве туториала.

  2. Я не спорю, что помимо плюсов есть еще и минусы, и не говорю что Magento идеальна во всех местах.

Я один раз заглянул в проект на ModX (и пусть меня закидают ссаными тапками камнями), и мой мир никогда не будет прежним. Возможно, поэтому, когда в разговоре меня спрашивают, с чем я работаю, а в ответ слышат «magento», сразу кривятся носы, а мнение, скорее всего, падает куда-то очень низко.

Мне обидно от этого, ведь Magento уже давно не (да и никогда до конца не была) топ-примером того, как никогда делать не стоит. Напротив, начиная со второй версии, разработчики двигаются по правильному пути. Да, есть еще много легаси, а некоторые решения заставляют округлить зенки и накрыть лицо пятерней, но в целом, на данный момент, на magento 2 можно и легко (и во многом система сама диктует) построить грамотную архитектуру проекта/модуля.

Итак, топ вещей, которые реализованы не (намного) хуже, а где-то и лучше, чем в популярных фреймворках:

Мощная система DI

Мы не задумываемся над созданием объектов, система делает все за нас. Мы просто указываем список нужных параметров в конструкторе с типами, и используем их

<?php  namespace Vendor\Module\Model;  use Vendor\Module\Api\FooInterface; use Vendor\Module\Model\Bar;  class SomeModel {     private $foo;     private $bar;     private $param      public function __construct(FooInterface $foo, Bar $bar, string $param)     {         $this->foo = $foo;         $this->bar = $bar;         $this->param = $param;     } }

Хочу отметить, что тонкие настройки аргументов, указание классов для интерфейсов и многое другое — описывается в xml файле di.xml:

<?xml version="1.0" encoding="UTF-8"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">     <!-- body --> </config> 

1. Подмена классов и указание реализации для интерфейсов

указываем preference для интерфейса, и потом везде в коде используем только интерфейс

<preference for="Vendor\Module\Api\FooInterface"              type="Vendor\Module\Model\Foo" />

Также можно сделать наследника какого-то класса, и сказать, что теперь везде должен использоваться наследник. Соответственно во всех конструкторах и использованиях ObjectManager объект родительского класса подменится на объект дочернего.

<preference for="Vendor\Module\Model\Bar"              type="Vendor\Module\Model\BarChild" />

2. Указание аргументов

Мы можем указать для конкретного класса список его аргументов, или подменить какой-либо параметр

<type name="Vendor\Module\Model\SomeModel">     <arguments>         <argument name="bar" xsi:type="object">Vendor\Module\Model\BarChild</argument>         <argument name="param" xsi:type="string">some string</argument>     </arguments> </type>

3. Виртуальные типы

Виртуальный тип — это обычный класс с набором своих личных параметров. Такие виртуальные типы физически не существуют, и могут быть использованы только как аргументы в других классах

<virtualType name="Vendor\Module\Model\SomeModelForSomethingElse"               type="Vendor\Module\Model\SomeModel">     <arguments>         <argument name="param" xsi:type="string">another string</argument>     </arguments> </virtualType>  <type name="Vendor\Module\Model\SomeService">     <arguments>         <argument name="someModel" xsi:type="object">Vendor\Module\Model\SomeModelForSomethingElse</argument>     </arguments> </type>

4. Плагины

Практически на любой существующий публичный метод класса можно повесить плагины. Можно выбрать, когда будет выполняться плагин — до (префикс before), во время (префикс around) или после (префикс after) выполнения метода. Давайте рассмотрим пример:

<?php  namespace Vendor\Module\Model;  class SomeService {     public function doSomething(Foo $foo, Bar $bar, string $param, int $number)     {         // something     } }

Задаём плагин:

<type name="Vendor\Module\Model\SomeService">     <plugin name="Vendor_Module::MyCustomPlugin"              type="Vendor\Module\Plugin\SomeServicePlugin" /> </type>

Код плагина (методы плагина должны называться: префикс + название основного метода с большой буквы):

<?php  namespace Vendor\Module\Plugin;  use Vendor\Module\Model\SomeService;  class SomeServicePlugin {     // вызывается до вызова основного метода. Мы можем изменить входные параметры.     public function beforeDoSomething(SomeService $subject, Foo $foo, Bar $bar, string $param, int $number)     {         $param = 'change param';          // если изменять параметры не нужно - то можно ничего не возвращать         return [$foo, $bar, $param, $number];     }      // метод вызывается "во время" вызова основного метода. В параметрах - Closure - это собственно основной метод     public function aroundDoSomething(SomeService $subject, \Closure $proceed, Foo $foo, Bar $bar, string $param, int $number)     {         // do something         $result = $proceed($foo, $bar, $param, $number); // вызываем основной метод         // do something else          return $result;     }      // метод вызывается после вызова основного метода. Мы можем менять результат     public function afterDoSomething(SomeService $subject, $result, Foo $foo, Bar $bar, string $param, int $number)     {         $result += 1; // меняем результат          return $result;     } }

Также в каждый метод плагина передаётся сам покрываемый плагином объект. Это удобно, если нам нужно вызвать какой-то другой его метод в процессе выполнения плагина.

5. ObjectManager

Если нам нужно создавать объекты на лету в процессе выполнения алгоритма — для этого есть ObjectManager. Он имеет два метода — get и create. get — берет существующий экземпляр класса (или создает его, если еще не создавал ранее), а create — создаёт.

Взгляните на это объявление пула процессоров:

<?php  namespace Vendor\Module\Model;  use Magento\Framework\ObjectManagerInterface;  class ProcessorPool {     private $objectManager;     private $processors;      public function __construct(ObjectManagerInterface $objectManager, array $processors)     {         $this->objectManager = $objectManager;         $this->processors = $processors;     }      public function getProcessor(string $code)     {         return $this->objectManager->get($this->processors[$code]);     } }

И xml:

<type name="Vendor\Module\Model\ProcessorPool">     <arguments>         <argument name="processors" xsi:type="array">             <item name="foo" xsi:type="string">Vendor\Module\Model\Processor\FooProcessor</item>             <item name="bar" xsi:type="string">Vendor\Module\Model\Processor\BarProcessor</item>         </argument>     </arguments> </type>

С учетом того, что все di.xml всех модулей мержатся в одну большую инструкцию для ObjectManager — мы можем добиться довольно высокой гибкости кода. Где-то в другом модуле мы можем описать еще один процессор, и заменить один из существующих:

<type name="Vendor\Module\Model\ServicePool">     <arguments>         <argument name="processors" xsi:type="array">             <item name="foo" xsi:type="string">Vendor\NewModule\Model\Processor\NewFooProcessor</item>             <item name="something" xsi:type="string">Vendor\NewModule\Model\Processor\SomethingProcessor</item>         </argument>     </arguments> </type>

6. Автогенерация фабрик и прокси

В некоторых случаях нам нужно всегда создавать новый объект, например — entity-модель. Для этого лучше всего подходят фабрики. Но нам не нужно писать код фабрики руками, для нас это сделает встроенный автогенератор. Достаточно в конструкторе просто дописать слово Factory к требуемому классу, и мы получим его фабрику. Это работает также и с интерфейсами:

<?php  namespace Vendor\Module\Model;  use Vendor\Module\Api\Data\SomeModelInterfaceFactory;  class SomeModelRepository {     private $someModelFactory;      // класс \Vendor\Module\Api\Data\SomeModelInterfaceFactory будет сгенерирован автоматически     public function __construct(SomeModelInterfaceFactory $someModelFactory)     {         $this->someModelFactory = $someModelFactory;     }      public function doSomething()     {         // создаст объект класса, имплементирующего \Vendor\Module\Api\Data\SomeModelInterface со всеми его зависимостями         $someModel = $this->someModelFactory->create();     } }

Если у нас есть какой-то сервис, который при инициализации тратит много ресурсов, и мы не уверены, будем ли мы точно его использовать в другом классе — мы можем обернуть его в Proxy. Мы можем дописать «\Proxy» точно также, как и «Factory» к типу параметра в конструкторе, но это нарушит расширяемость кода, поэтому лучше задавать Proxy через di.xml

<?php  namespace Vendor\Module\Model;  class SomeModelClass {     private $someBigService;      public function __construct(SomeBigService $someBigService)     {         $this->someBigService = $someBigService;     }      public function doSomething(bool $condition)     {         // мы не уверены, что сервис нам понадобится, и т.к. его инициализация занимает много времени         // лучше создавать его только тогда, когда он точно нужен         if ($condition) {             $this->someBigService->execute();         }     } }

di.xml

<type name="Vendor\Module\Model\SomeModelClass">     <arguments>         <argument name="someBigService"                    xsi:type="object">Vendor\Module\Model\SomeBigService\Proxy</argument>     </arguments> </type>

И, вуаля, объект SomeBigService создастся только в момент вызова execute().

Layout

Layout, на мой взгляд, одно из самых гибких реализаций для сборки страницы. Реализация xml layouts в Magento позволяет расширить или изменить страницу из множества мест, будь то модуль, тема, или другой участок кода. Layout файл в Magento обычно называется примерно так:

catalog_product_view.xml

Название файла — это handle, где catalog — перекликается с названием модуля (это название роута в конфигурации модуля), product — название папки, в которой лежит контроллер, а view — название контроллера. Это типовое наименование позволяет не указывать в контроллере, какой именно Layout выбирать, и подставляется автоматически. Однако ничто не мешает назвать layout по своему, и указать его название в контроллере. Т.к. это xml — мы можем подключить сколько угодно layout в контроллере, и они все будут смержены в один.

Для добавления на страницу продукта какого-то расширяемого блока, нам достаточно написать:

<?xml version="1.0"?> <!-- Vendor/Module/view/frontend/layout/catalog_product_view.xml --> <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">             <!-- Мы имеем аттрибуты before и after, чтобы указать место, в котором нужно будет вывести блок -->             <container name="vendor.module.some.container"                        htmlTag="div"                        htmlClass="my_custom_container"                        before="-">                 <block class="Magento\Framework\View\Element\Template"                        name="vendor.module.custom_product_block"                        template="Vendor_Module::product/view/custom_block.phtml" />             </container>         </referenceContainer>     </body> </page>

Я намеренно обернул блок в контейнер. Теперь в другом модуле мы можем без проблем добавить в то же место еще один блок, перед или после существующего:

<?xml version="1.0"?> <!-- Vendor/AnotherModule/view/frontend/layout/catalog_product_view.xml --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">     <body>         <referenceContainer name="vendor.module.some.container">             <block class="Magento\Framework\View\Element\Template"                    name="vendor.another_module.custom_product_block"                    template="Vendor_AnotherModule::another_custom_block.phtml"                     after="vendor.module.custom_product_block"/>         </referenceContainer>     </body> </page>

Layouts также являются «наследуемыми»:

<?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>         <!-- Подключаем какой-то другой layout -->         <update handle="my_custom_layout_handle" />         <!-- можно удалять ненужное -->         <referenceContainer name="container1" remove="true"/>         <referenceBlock name="child_block1" remove="true"/>          <!-- Или переместить: например container3 внутрь container2 -->         <move element="container3" destination="container2"/>     </body> </page>

Т.к. практически все отображение в Magento использует layouts, то можно смело утверждать (с некоторыми оговорками), что это даёт возможность кастомизировать что угодно и где угодно (ну почти).

ViewModel

Когда разрабатываешь систему, которая требует отображения множества различных данных на фронте, в том числе — повторяющиеся на некоторых страницах данные, приходится постоянно пробрасывать эти самые данные через Controller во View. Это может быть утомительно, и приходится придумывать различного рода уловки, расширения, чтобы получать эти данные на уровне View, не повторяясь в контроллерах.

Долгое время в Magento использовались классы блоков (например Magento\Catalog\Block\Product\View), которые предоставляли доступ к данным из template — вида

<?php  // Vendor/Module/view/frontend/templates/some_template.phtml // внутри template доступна переменная $block, являющаяся объектом указанного в layoutкласса ?>  <?= $block->getSomeData() ?>

Но с годами блоки разрастались, обрастали наследниками (т.к. содержали в себе много нужного в других местах кода), и превращались в полотна из несвязанных между собой методов. В Magento 2.2 были представлены ViewModel, как возможность использовать сразу несколько моделей внутри template, и построить свой код архитектурно грамотнее:

<?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>         <block class="Magento\Framework\View\Element\Template"                name="some_name"                template="Vendor_Module::some_template.phtml">             <arguments>                 <argument name="some_view_model" xsi:type="object">Vendor\Module\ViewModel\SomeViewModel</argument>                 <argument name="another_view_model" xsi:type="object">Vendor\Module\ViewModel\AnotherViewModel</argument>             </arguments>         </block>     </body> </page>

и в template:

<?php /** @var Vendor\Module\ViewModel\SomeViewModel $someViewModel */ $someViewModel = $block->getSomeViewModel(); /** @var Vendor\Module\ViewModel\AnotherViewModel $anotherViewModel */ $anotherViewModel = $block->getAnotherViewModel(); ?>

Соответственно, одну ViewModel можно использовать на многих страницах, не завися и не задумываясь о контроллере.

Declarative Schema

Самый популярный способ накатывать обновления на БД — это миграции. И я видел множество проектов, где папки с миграциями, да и сами миграции выглядели довольно внушительно. И вдобавок (если уж говорить о продуктовой разработке, когда пользователи получают новые версии продукта для обновления, как раз Magento-случай), если разворачивать все с нуля — все миграции должны быть выполнены.

Declarative Schema — это, по сути, текущее состояние таблицы, описанное в xml файле. Нам не нужно писать и поддерживать миграции, мы просто меняем xml файлик. Magento сама считывает его, сравнивает с текущим состоянием таблицы в БД, и меняет таблицу, если это необходимо.

Конечно, далеко не все миграции — это изменение структуры БД, поэтому наряду с Declarative Schema существует еще и механизм патчей (php-классов, выполняющих какую-то логику).

UI компоненты

Т.к. xml — довольно гибкий формат, он позволяет нам достаточно многое. Помните layoutы? Xml Layouts + DI позволяют нам строить дерево JS компонентов для рендеринга страницы также гибко, как и дерево PHP блоков. Взгляните на layout для чекаута (осторожно, очень много текста)

checkout_index_index.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" layout="checkout" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">     <body>         <referenceContainer name="content">             <block class="Magento\Checkout\Block\Onepage" name="checkout.root" template="Magento_Checkout::onepage.phtml" cacheable="false">                 <arguments>                     <argument name="jsLayout" xsi:type="array">                         <item name="types" xsi:type="array">                             <item name="form.input" xsi:type="array">                                 <item name="component" xsi:type="string">Magento_Ui/js/form/element/abstract</item>                                 <item name="config" xsi:type="array">                                     <item name="provider" xsi:type="string">checkoutProvider</item>                                     <item name="deps" xsi:type="array">                                         <item name="0" xsi:type="string">checkoutProvider</item>                                     </item>                                     <item name="template" xsi:type="string">ui/form/field</item>                                     <item name="elementTmpl" xsi:type="string">ui/form/element/input</item>                                 </item>                             </item>                         </item>                         <item name="components" xsi:type="array">                             <item name="checkout" xsi:type="array">                                 <item name="component" xsi:type="string">uiComponent</item>                                 <item name="config" xsi:type="array">                                     <item name="template" xsi:type="string">Magento_Checkout/onepage</item>                                 </item>                                 <item name="children" xsi:type="array">                                     <item name="errors" xsi:type="array">                                         <item name="sortOrder" xsi:type="string">0</item>                                         <item name="component" xsi:type="string">Magento_Ui/js/view/messages</item>                                         <item name="displayArea" xsi:type="string">messages</item>                                     </item>                                     <item name="authentication" xsi:type="array">                                         <item name="sortOrder" xsi:type="string">1</item>                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/authentication</item>                                         <item name="displayArea" xsi:type="string">authentication</item>                                         <item name="children" xsi:type="array">                                             <!--Additional authentication fields-->                                             <item name="errors" xsi:type="array">                                                 <item name="sortOrder" xsi:type="string">0</item>                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/authentication-messages</item>                                                 <item name="displayArea" xsi:type="string">messages</item>                                             </item>                                         </item>                                     </item>                                     <item name="progressBar" xsi:type="array">                                         <item name="sortOrder" xsi:type="string">0</item>                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/progress-bar</item>                                         <item name="displayArea" xsi:type="string">progressBar</item>                                         <item name="config" xsi:type="array">                                             <item name="deps" xsi:type="array">                                                 <item name="0" xsi:type="string">checkout.steps.shipping-step.shippingAddress</item>                                                 <item name="1" xsi:type="string">checkout.steps.billing-step.payment</item>                                             </item>                                         </item>                                     </item>                                     <item name="estimation" xsi:type="array">                                         <item name="sortOrder" xsi:type="string">10</item>                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/estimation</item>                                         <item name="displayArea" xsi:type="string">estimation</item>                                         <item name="config" xsi:type="array">                                             <item name="template" xsi:type="string">Magento_Checkout/estimation</item>                                             <item name="deps" xsi:type="array">                                                 <item name="0" xsi:type="string">checkout.sidebar</item>                                             </item>                                         </item>                                     </item>                                     <item name="steps" xsi:type="array">                                         <item name="component" xsi:type="string">uiComponent</item>                                         <item name="displayArea" xsi:type="string">steps</item>                                         <item name="children" xsi:type="array">                                             <item name="shipping-step" xsi:type="array">                                                 <item name="component" xsi:type="string">uiComponent</item>                                                 <item name="sortOrder" xsi:type="string">1</item>                                                 <item name="children" xsi:type="array">                                                     <item name="step-config" xsi:type="array">                                                         <item name="component" xsi:type="string">uiComponent</item>                                                         <item name="children" xsi:type="array">                                                             <item name="shipping-rates-validation" xsi:type="array">                                                                 <item name="children" xsi:type="array">                                                                     <!--Step configuration components-->                                                                 </item>                                                             </item>                                                         </item>                                                     </item>                                                     <item name="shippingAddress" xsi:type="array">                                                         <item name="config" xsi:type="array">                                                             <item name="deps" xsi:type="array">                                                                 <item name="0" xsi:type="string">checkout.steps.shipping-step.step-config</item>                                                                 <item name="1" xsi:type="string">checkoutProvider</item>                                                             </item>                                                             <item name="popUpForm" xsi:type="array">                                                                 <item name="element" xsi:type="string">#opc-new-shipping-address</item>                                                                 <item name="options" xsi:type="array">                                                                     <item name="type" xsi:type="string">popup</item>                                                                     <item name="responsive" xsi:type="boolean">true</item>                                                                     <item name="innerScroll" xsi:type="boolean">true</item>                                                                     <item name="title" xsi:type="string" translate="true">Shipping Address</item>                                                                     <item name="trigger" xsi:type="string">opc-new-shipping-address</item>                                                                     <item name="buttons" xsi:type="array">                                                                         <item name="save" xsi:type="array">                                                                             <item name="text" xsi:type="string" translate="true">Ship Here</item>                                                                             <item name="class" xsi:type="string">action primary action-save-address</item>                                                                         </item>                                                                         <item name="cancel" xsi:type="array">                                                                             <item name="text" xsi:type="string" translate="true">Cancel</item>                                                                             <item name="class" xsi:type="string">action secondary action-hide-popup</item>                                                                         </item>                                                                     </item>                                                                 </item>                                                             </item>                                                         </item>                                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/shipping</item>                                                         <item name="provider" xsi:type="string">checkoutProvider</item>                                                         <item name="sortOrder" xsi:type="string">10</item>                                                         <item name="children" xsi:type="array">                                                             <item name="customer-email" xsi:type="array">                                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/form/element/email</item>                                                                 <item name="displayArea" xsi:type="string">customer-email</item>                                                                 <item name="tooltip" xsi:type="array">                                                                     <item name="description" xsi:type="string" translate="true">We'll send your order confirmation here.</item>                                                                 </item>                                                                 <item name="children" xsi:type="array">                                                                     <item name="before-login-form" xsi:type="array">                                                                         <item name="component" xsi:type="string">uiComponent</item>                                                                         <item name="displayArea" xsi:type="string">before-login-form</item>                                                                         <item name="children" xsi:type="array">                                                                             <!-- before login form fields -->                                                                         </item>                                                                     </item>                                                                     <item name="additional-login-form-fields" xsi:type="array">                                                                         <item name="component" xsi:type="string">uiComponent</item>                                                                         <item name="displayArea" xsi:type="string">additional-login-form-fields</item>                                                                         <item name="children" xsi:type="array">                                                                             <!-- additional login form fields -->                                                                         </item>                                                                     </item>                                                                 </item>                                                             </item>                                                             <item name="before-form" xsi:type="array">                                                                 <item name="component" xsi:type="string">uiComponent</item>                                                                 <item name="displayArea" xsi:type="string">before-form</item>                                                                 <item name="children" xsi:type="array">                                                                     <!-- before form fields -->                                                                 </item>                                                             </item>                                                             <item name="before-fields" xsi:type="array">                                                                 <item name="component" xsi:type="string">uiComponent</item>                                                                 <item name="displayArea" xsi:type="string">before-fields</item>                                                                 <item name="children" xsi:type="array">                                                                     <!-- before fields -->                                                                 </item>                                                             </item>                                                             <item name="address-list" xsi:type="array">                                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/shipping-address/list</item>                                                                 <item name="displayArea" xsi:type="string">address-list</item>                                                             </item>                                                             <item name="address-list-additional-addresses" xsi:type="array">                                                                 <item name="component" xsi:type="string">uiComponent</item>                                                                 <item name="displayArea" xsi:type="string">address-list-additional-addresses</item>                                                                 <item name="children" xsi:type="array">                                                                     <!-- address-list-additional-addresses -->                                                                 </item>                                                             </item>                                                             <item name="before-shipping-method-form" xsi:type="array">                                                                 <item name="component" xsi:type="string">uiComponent</item>                                                                 <item name="displayArea" xsi:type="string">before-shipping-method-form</item>                                                                 <item name="children" xsi:type="array">                                                                     <!-- address-list-additional-addresses -->                                                                 </item>                                                             </item>                                                             <item name="shipping-address-fieldset" xsi:type="array">                                                                 <item name="component" xsi:type="string">uiComponent</item>                                                                 <item name="config" xsi:type="array">                                                                     <item name="deps" xsi:type="array">                                                                         <item name="0" xsi:type="string">checkoutProvider</item>                                                                     </item>                                                                 </item>                                                                 <item name="displayArea" xsi:type="string">additional-fieldsets</item>                                                                 <item name="children" xsi:type="array">                                                                     <!-- The following items override configuration of corresponding address attributes -->                                                                     <item name="region" xsi:type="array">                                                                         <!-- Make region attribute invisible on frontend. Corresponding input element is created by region_id field -->                                                                         <item name="visible" xsi:type="boolean">false</item>                                                                     </item>                                                                     <item name="region_id" xsi:type="array">                                                                         <item name="component" xsi:type="string">Magento_Ui/js/form/element/region</item>                                                                         <item name="config" xsi:type="array">                                                                             <item name="template" xsi:type="string">ui/form/field</item>                                                                             <item name="elementTmpl" xsi:type="string">ui/form/element/select</item>                                                                             <item name="customEntry" xsi:type="string">shippingAddress.region</item>                                                                         </item>                                                                         <item name="validation" xsi:type="array">                                                                             <item name="required-entry" xsi:type="boolean">true</item>                                                                         </item>                                                                         <!-- Value of region_id field is filtered by the value of county_id attribute -->                                                                         <item name="filterBy" xsi:type="array">                                                                             <item name="target" xsi:type="string"><![CDATA[${ $.provider }:${ $.parentScope }.country_id]]></item>                                                                             <item name="field" xsi:type="string">country_id</item>                                                                         </item>                                                                     </item>                                                                     <item name="postcode" xsi:type="array">                                                                         <!-- post-code field has custom UI component -->                                                                         <item name="component" xsi:type="string">Magento_Ui/js/form/element/post-code</item>                                                                         <item name="validation" xsi:type="array">                                                                             <item name="required-entry" xsi:type="boolean">true</item>                                                                         </item>                                                                     </item>                                                                     <item name="company" xsi:type="array">                                                                         <item name="validation" xsi:type="array">                                                                             <item name="min_text_length" xsi:type="number">0</item>                                                                         </item>                                                                     </item>                                                                     <item name="fax" xsi:type="array">                                                                         <item name="validation" xsi:type="array">                                                                             <item name="min_text_length" xsi:type="number">0</item>                                                                         </item>                                                                     </item>                                                                     <item name="telephone" xsi:type="array">                                                                         <item name="config" xsi:type="array">                                                                             <item name="tooltip" xsi:type="array">                                                                                 <item name="description" xsi:type="string" translate="true">For delivery questions.</item>                                                                             </item>                                                                         </item>                                                                     </item>                                                                 </item>                                                             </item>                                                         </item>                                                     </item>                                                 </item>                                             </item>                                             <item name="billing-step" xsi:type="array">                                                 <item name="component" xsi:type="string">uiComponent</item>                                                 <item name="sortOrder" xsi:type="string">2</item>                                                 <item name="children" xsi:type="array">                                                     <item name="payment" xsi:type="array">                                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/payment</item>                                                         <item name="config" xsi:type="array">                                                             <item name="title" xsi:type="string" translate="true">Payment</item>                                                             <item name="sortOrder" xsi:type="string">20</item>                                                         </item>                                                         <item name="children" xsi:type="array">                                                             <item name="renders" xsi:type="array">                                                                 <item name="component" xsi:type="string">uiComponent</item>                                                                 <item name="children" xsi:type="array">                                                                     <!-- merge payment method renders here -->                                                                 </item>                                                             </item>                                                             <item name="additional-payment-validators" xsi:type="array">                                                                 <item name="component" xsi:type="string">uiComponent</item>                                                                 <item name="children" xsi:type="array">                                                                     <!-- merge payment validators here -->                                                                     <item name="email-validator" xsi:type="array">                                                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/payment/email-validator</item>                                                                     </item>                                                                 </item>                                                             </item>                                                             <item name="customer-email" xsi:type="array">                                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/form/element/email</item>                                                                 <item name="displayArea" xsi:type="string">customer-email</item>                                                                 <item name="tooltip" xsi:type="array">                                                                     <item name="description" xsi:type="string" translate="true">We'll send your order confirmation here.</item>                                                                 </item>                                                                 <item name="children" xsi:type="array">                                                                     <item name="before-login-form" xsi:type="array">                                                                         <item name="component" xsi:type="string">uiComponent</item>                                                                         <item name="displayArea" xsi:type="string">before-login-form</item>                                                                         <item name="children" xsi:type="array">                                                                             <!-- before login form fields -->                                                                         </item>                                                                     </item>                                                                     <item name="additional-login-form-fields" xsi:type="array">                                                                         <item name="component" xsi:type="string">uiComponent</item>                                                                         <item name="displayArea" xsi:type="string">additional-login-form-fields</item>                                                                         <item name="children" xsi:type="array">                                                                             <!-- additional login form fields -->                                                                         </item>                                                                     </item>                                                                 </item>                                                             </item>                                                             <item name="place-order-captcha" xsi:type="array">                                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/checkout/placeOrderCaptcha</item>                                                                 <item name="displayArea" xsi:type="string">place-order-captcha</item>                                                                 <item name="formId" xsi:type="string">payment_processing_request</item>                                                                 <item name="configSource" xsi:type="string">checkoutConfig</item>                                                             </item>                                                             <item name="beforeMethods" xsi:type="array">                                                                 <item name="component" xsi:type="string">uiComponent</item>                                                                 <item name="displayArea" xsi:type="string">beforeMethods</item>                                                                 <item name="children" xsi:type="array">                                                                     <!-- merge additional data before payment methods here -->                                                                 </item>                                                                 <item name="validation" xsi:type="array">                                                                     <item name="validate-select" xsi:type="string">true</item>                                                                 </item>                                                                 <!-- Value of region_id field is filtered by the value of county_id attribute -->                                                                 <item name="filterBy" xsi:type="array">                                                                     <item name="target" xsi:type="string">${ $.provider }:${ $.parentScope }.country_id</item>                                                                     <item name="field" xsi:type="string">country_id</item>                                                                 </item>                                                             </item>                                                             <item name="payments-list" xsi:type="array">                                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/payment/list</item>                                                                 <item name="displayArea" xsi:type="string">payment-methods-list</item>                                                                 <item name="config" xsi:type="array">                                                                     <item name="deps" xsi:type="array">                                                                         <item name="0" xsi:type="string">checkout.steps.billing-step.payment.renders</item>                                                                         <item name="1" xsi:type="string">checkout.steps.billing-step.payment.additional-payment-validators</item>                                                                     </item>                                                                 </item>                                                                 <item name="children" xsi:type="array">                                                                     <item name="before-place-order" xsi:type="array">                                                                         <item name="component" xsi:type="string">uiComponent</item>                                                                         <item name="displayArea" xsi:type="string">before-place-order</item>                                                                         <item name="dataScope" xsi:type="string">before-place-order</item>                                                                         <item name="provider" xsi:type="string">checkoutProvider</item>                                                                         <item name="config" xsi:type="array">                                                                             <item name="template" xsi:type="string">Magento_Checkout/payment/before-place-order</item>                                                                         </item>                                                                     </item>                                                                 </item>                                                             </item>                                                             <!-- merge your payment methods here -->                                                             <item name="afterMethods" xsi:type="array">                                                                 <item name="component" xsi:type="string">uiComponent</item>                                                                 <item name="displayArea" xsi:type="string">afterMethods</item>                                                                 <item name="children" xsi:type="array">                                                                     <!-- merge additional data after payment methods here -->                                                                 </item>                                                             </item>                                                         </item>                                                     </item>                                                 </item>                                             </item>                                         </item>                                     </item>                                     <item name="sidebar" xsi:type="array">                                         <item name="sortOrder" xsi:type="string">50</item>                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/sidebar</item>                                         <item name="displayArea" xsi:type="string">sidebar</item>                                         <item name="config" xsi:type="array">                                             <item name="template" xsi:type="string">Magento_Checkout/sidebar</item>                                             <item name="deps" xsi:type="array">                                                 <item name="0" xsi:type="string">checkout.steps</item>                                             </item>                                         </item>                                         <item name="children" xsi:type="array">                                             <item name="summary" xsi:type="array">                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/summary</item>                                                 <item name="displayArea" xsi:type="string">summary</item>                                                 <item name="config" xsi:type="array">                                                     <item name="template" xsi:type="string">Magento_Checkout/summary</item>                                                 </item>                                                 <item name="children" xsi:type="array">                                                     <item name="totals" xsi:type="array">                                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/summary/totals</item>                                                         <item name="displayArea" xsi:type="string">totals</item>                                                         <item name="config" xsi:type="array">                                                             <item name="template" xsi:type="string">Magento_Checkout/summary/totals</item>                                                         </item>                                                         <item name="children" xsi:type="array">                                                             <!-- sort order for this totals is configured on admin panel-->                                                             <!-- Stores->Configuration->SALES->Sales->General->Checkout Totals Sort Order -->                                                             <item name="subtotal" xsi:type="array">                                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/summary/subtotal</item>                                                                 <item name="config" xsi:type="array">                                                                     <item name="title" xsi:type="string" translate="true">Cart Subtotal</item>                                                                 </item>                                                             </item>                                                             <item name="shipping" xsi:type="array">                                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/summary/shipping</item>                                                                 <item name="config" xsi:type="array">                                                                     <item name="title" xsi:type="string" translate="true">Shipping</item>                                                                     <item name="notCalculatedMessage" xsi:type="string" translate="true">Not yet calculated</item>                                                                 </item>                                                             </item>                                                             <item name="grand-total" xsi:type="array">                                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/summary/grand-total</item>                                                                 <item name="config" xsi:type="array">                                                                     <item name="title" xsi:type="string" translate="true">Order Total</item>                                                                 </item>                                                             </item>                                                         </item>                                                     </item>                                                     <item name="itemsBefore" xsi:type="array">                                                         <item name="component" xsi:type="string">uiComponent</item>                                                         <item name="children" xsi:type="array">                                                             <!-- merge your components here -->                                                         </item>                                                     </item>                                                     <item name="cart_items" xsi:type="array">                                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/summary/cart-items</item>                                                         <item name="children" xsi:type="array">                                                             <item name="details" xsi:type="array">                                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/summary/item/details</item>                                                                 <item name="children" xsi:type="array">                                                                     <item name="thumbnail" xsi:type="array">                                                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/summary/item/details/thumbnail</item>                                                                         <item name="displayArea" xsi:type="string">before_details</item>                                                                     </item>                                                                     <item name="subtotal" xsi:type="array">                                                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/summary/item/details/subtotal</item>                                                                         <item name="displayArea" xsi:type="string">after_details</item>                                                                     </item>                                                                     <item name="message" xsi:type="array">                                                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/summary/item/details/message</item>                                                                         <item name="displayArea" xsi:type="string">item_message</item>                                                                     </item>                                                                 </item>                                                             </item>                                                         </item>                                                     </item>                                                     <item name="itemsAfter" xsi:type="array">                                                         <item name="component" xsi:type="string">uiComponent</item>                                                         <item name="children" xsi:type="array">                                                             <!-- merge your components here -->                                                         </item>                                                     </item>                                                 </item>                                             </item>                                             <item name="shipping-information" xsi:type="array">                                                 <item name="component" xsi:type="string">Magento_Checkout/js/view/shipping-information</item>                                                 <item name="config" xsi:type="array">                                                     <item name="deps" xsi:type="string">checkout.steps.shipping-step.shippingAddress</item>                                                 </item>                                                 <item name="displayArea" xsi:type="string">shipping-information</item>                                                 <item name="children" xsi:type="array">                                                     <item name="ship-to" xsi:type="array">                                                         <item name="component" xsi:type="string">Magento_Checkout/js/view/shipping-information/list</item>                                                         <item name="displayArea" xsi:type="string">ship-to</item>                                                     </item>                                                 </item>                                             </item>                                         </item>                                     </item>                                 </item>                             </item>                             <item name="checkoutProvider" xsi:type="array">                                 <item name="component" xsi:type="string">uiComponent</item>                             </item>                         </item>                     </argument>                 </arguments>             </block>         </referenceContainer>         <referenceContainer name="page.messages" remove="true"/>     </body> </page> 

Мы задаем инструкции для рендеринга страницы так, как будто мы делаем это в PHP Array, но с более наглядным отображением, и возможностью расширения. Благодаря xml и Magento 2 мы можем подменить любой кусочек или добавить что-то свое.

В админке таким же образом можно расширить любую UI Component форму, грид или что-то еще (да, еще остались формы и гриды, написанные «по-старому» в PHP классах), например вот таким образом можно добавить колонку на UI Grid:

<?xml version="1.0" encoding="UTF-8"?> <!-- /**  * Copyright © Magento, Inc. All rights reserved.  * See COPYING.txt for license details.  */ --> <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="refunded_to_store_credit" class="Magento\Sales\Ui\Component\Listing\Column\Price">             <settings>                 <label translate="true">Refunded to Store Credit</label>                 <visible>false</visible>             </settings>         </column>     </columns> </listing>

Напоследок хочу отметить то, что коммьюнити не стоит на месте в плане новинок, например последние на данный момент версии Magento поддерживают только php 7.3 и 7.4, elasticsearch 7.4/7.6

P.S. Я хочу еще раз подчеркнуть, что в статье приводятся достоинства, которые, в свою очередь, не лишены недостатков :). Я не преследовал цель заставить всех поверить, что Magento — квинтэссенция всего, и нужно срочно бросать свое {cms/frameworkName} и переходить на Magento. Я лишь хочу показать, что Magento 2 не для «кодинга», а очень даже для «программирования».

ссылка на оригинал статьи https://habr.com/ru/post/532232/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *