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

?v=1

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

Дисклеймер

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

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

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

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

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

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

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

foo = $foo;
        $this->bar = $bar;
        $this->param = $param;
    }
}

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



    

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

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

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

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

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


    
        Vendor\Module\Model\BarChild
        some string
    

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

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


    
        another string
    



    
        Vendor\Module\Model\SomeModelForSomethingElse
    

4. Плагины

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

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


    

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

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

5. ObjectManager

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

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

objectManager = $objectManager;
        $this->processors = $processors;
    }

    public function getProcessor(string $code)
    {
        return $this->objectManager->get($this->processors[$code]);
    }
}

И xml:


    
        
            Vendor\Module\Model\Processor\FooProcessor
            Vendor\Module\Model\Processor\BarProcessor
        
    

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


    
        
            Vendor\NewModule\Model\Processor\NewFooProcessor
            Vendor\NewModule\Model\Processor\SomethingProcessor
        
    

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

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

someModelFactory = $someModelFactory;
    }

    public function doSomething()
    {
        // создаст объект класса, имплементирующего \Vendor\Module\Api\Data\SomeModelInterface со всеми его зависимостями
        $someModel = $this->someModelFactory->create();
    }
}

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

someBigService = $someBigService;
    }

    public function doSomething(bool $condition)
    {
        // мы не уверены, что сервис нам понадобится, и т.к. его инициализация занимает много времени
        // лучше создавать его только тогда, когда он точно нужен
        if ($condition) {
            $this->someBigService->execute();
        }
    }
}

di.xml


    
        Vendor\Module\Model\SomeBigService\Proxy
    

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

Layout

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

catalog_product_view.xml

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

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




    
        
            
            
                
            
        
    











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




    
        
            
        
    

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



    
        
        
        
        
        

        
        
    

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

ViewModel

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

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



getSomeData() ?>

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



    
        
            
                Vendor\Module\ViewModel\SomeViewModel
                Vendor\Module\ViewModel\AnotherViewModel
            
        
    

и в template:

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




    
        
            
                
                    
                        
                            
                                Magento_Ui/js/form/element/abstract
                                
                                    checkoutProvider
                                    
                                        checkoutProvider
                                    
                                    ui/form/field
                                    ui/form/element/input
                                
                            
                        
                        
                            
                                uiComponent
                                
                                    Magento_Checkout/onepage
                                
                                
                                    
                                        0
                                        Magento_Ui/js/view/messages
                                        messages
                                    
                                    
                                        1
                                        Magento_Checkout/js/view/authentication
                                        authentication
                                        
                                            
                                            
                                                0
                                                Magento_Checkout/js/view/authentication-messages
                                                messages
                                            
                                        
                                    
                                    
                                        0
                                        Magento_Checkout/js/view/progress-bar
                                        progressBar
                                        
                                            
                                                checkout.steps.shipping-step.shippingAddress
                                                checkout.steps.billing-step.payment
                                            
                                        
                                    
                                    
                                        10
                                        Magento_Checkout/js/view/estimation
                                        estimation
                                        
                                            Magento_Checkout/estimation
                                            
                                                checkout.sidebar
                                            
                                        
                                    
                                    
                                        uiComponent
                                        steps
                                        
                                            
                                                uiComponent
                                                1
                                                
                                                    
                                                        uiComponent
                                                        
                                                            
                                                                
                                                                    
                                                                
                                                            
                                                        
                                                    
                                                    
                                                        
                                                            
                                                                checkout.steps.shipping-step.step-config
                                                                checkoutProvider
                                                            
                                                            
                                                                #opc-new-shipping-address
                                                                
                                                                    popup
                                                                    true
                                                                    true
                                                                    Shipping Address
                                                                    opc-new-shipping-address
                                                                    
                                                                        
                                                                            Ship Here
                                                                            action primary action-save-address
                                                                        
                                                                        
                                                                            Cancel
                                                                            action secondary action-hide-popup
                                                                        
                                                                    
                                                                
                                                            
                                                        
                                                        Magento_Checkout/js/view/shipping
                                                        checkoutProvider
                                                        10
                                                        
                                                            
                                                                Magento_Checkout/js/view/form/element/email
                                                                customer-email
                                                                
                                                                    We'll send your order confirmation here.
                                                                
                                                                
                                                                    
                                                                        uiComponent
                                                                        before-login-form
                                                                        
                                                                            
                                                                        
                                                                    
                                                                    
                                                                        uiComponent
                                                                        additional-login-form-fields
                                                                        
                                                                            
                                                                        
                                                                    
                                                                
                                                            
                                                            
                                                                uiComponent
                                                                before-form
                                                                
                                                                    
                                                                
                                                            
                                                            
                                                                uiComponent
                                                                before-fields
                                                                
                                                                    
                                                                
                                                            
                                                            
                                                                Magento_Checkout/js/view/shipping-address/list
                                                                address-list
                                                            
                                                            
                                                                uiComponent
                                                                address-list-additional-addresses
                                                                
                                                                    
                                                                
                                                            
                                                            
                                                                uiComponent
                                                                before-shipping-method-form
                                                                
                                                                    
                                                                
                                                            
                                                            
                                                                uiComponent
                                                                
                                                                    
                                                                        checkoutProvider
                                                                    
                                                                
                                                                additional-fieldsets
                                                                
                                                                    
                                                                    
                                                                        
                                                                        false
                                                                    
                                                                    
                                                                        Magento_Ui/js/form/element/region
                                                                        
                                                                            ui/form/field
                                                                            ui/form/element/select
                                                                            shippingAddress.region
                                                                        
                                                                        
                                                                            true
                                                                        
                                                                        
                                                                        
                                                                            
                                                                            country_id
                                                                        
                                                                    
                                                                    
                                                                        
                                                                        Magento_Ui/js/form/element/post-code
                                                                        
                                                                            true
                                                                        
                                                                    
                                                                    
                                                                        
                                                                            0
                                                                        
                                                                    
                                                                    
                                                                        
                                                                            0
                                                                        
                                                                    
                                                                    
                                                                        
                                                                            
                                                                                For delivery questions.
                                                                            
                                                                        
                                                                    
                                                                
                                                            
                                                        
                                                    
                                                
                                            
                                            
                                                uiComponent
                                                2
                                                
                                                    
                                                        Magento_Checkout/js/view/payment
                                                        
                                                            Payment
                                                            20
                                                        
                                                        
                                                            
                                                                uiComponent
                                                                
                                                                    
                                                                
                                                            
                                                            
                                                                uiComponent
                                                                
                                                                    
                                                                    
                                                                        Magento_Checkout/js/view/payment/email-validator
                                                                    
                                                                
                                                            
                                                            
                                                                Magento_Checkout/js/view/form/element/email
                                                                customer-email
                                                                
                                                                    We'll send your order confirmation here.
                                                                
                                                                
                                                                    
                                                                        uiComponent
                                                                        before-login-form
                                                                        
                                                                            
                                                                        
                                                                    
                                                                    
                                                                        uiComponent
                                                                        additional-login-form-fields
                                                                        
                                                                            
                                                                        
                                                                    
                                                                
                                                            
                                                            
                                                                Magento_Checkout/js/view/checkout/placeOrderCaptcha
                                                                place-order-captcha
                                                                payment_processing_request
                                                                checkoutConfig
                                                            
                                                            
                                                                uiComponent
                                                                beforeMethods
                                                                
                                                                    
                                                                
                                                                
                                                                    true
                                                                
                                                                
                                                                
                                                                    ${ $.provider }:${ $.parentScope }.country_id
                                                                    country_id
                                                                
                                                            
                                                            
                                                                Magento_Checkout/js/view/payment/list
                                                                payment-methods-list
                                                                
                                                                    
                                                                        checkout.steps.billing-step.payment.renders
                                                                        checkout.steps.billing-step.payment.additional-payment-validators
                                                                    
                                                                
                                                                
                                                                    
                                                                        uiComponent
                                                                        before-place-order
                                                                        before-place-order
                                                                        checkoutProvider
                                                                        
                                                                            Magento_Checkout/payment/before-place-order
                                                                        
                                                                    
                                                                
                                                            
                                                            
                                                            
                                                                uiComponent
                                                                afterMethods
                                                                
                                                                    
                                                                
                                                            
                                                        
                                                    
                                                
                                            
                                        
                                    
                                    
                                        50
                                        Magento_Checkout/js/view/sidebar
                                        sidebar
                                        
                                            Magento_Checkout/sidebar
                                            
                                                checkout.steps
                                            
                                        
                                        
                                            
                                                Magento_Checkout/js/view/summary
                                                summary
                                                
                                                    Magento_Checkout/summary
                                                
                                                
                                                    
                                                        Magento_Checkout/js/view/summary/totals
                                                        totals
                                                        
                                                            Magento_Checkout/summary/totals
                                                        
                                                        
                                                            
                                                            
                                                            
                                                                Magento_Checkout/js/view/summary/subtotal
                                                                
                                                                    Cart Subtotal
                                                                
                                                            
                                                            
                                                                Magento_Checkout/js/view/summary/shipping
                                                                
                                                                    Shipping
                                                                    Not yet calculated
                                                                
                                                            
                                                            
                                                                Magento_Checkout/js/view/summary/grand-total
                                                                
                                                                    Order Total
                                                                
                                                            
                                                        
                                                    © Habrahabr.ru