В данной статье мы рассмотрим структуру модуля Magento 2. Посмотрим из чего она состоит, разберем какие бывают конфигурационные файлы и для чего они нужны. В будущем оставлю ссылки на статьи, где каждая упомянутая тут тема будет рассмотрена в деталях.

Базовые файлы для запуска модуля

Модуль может находиться в одном из двух мест относительно корня проекта:

  1. в папке app/code/[Vendor Name]/[Module Name]

  2. если установлен через composer, то в vendor/[vendor-name]/[module-name]

В процессе разработки модуля его части кладут именно в первую папку.

Также, чтобы модуль работал, в нем должно быть, как минимум, два обязательных файла, и еще один, чтобы модуль можно было ставить через упомянутый выше composer. Лежат они в корне модуля [Vendor]/[Module Name]:

  • registration.php

  • etc/module.xml

  • composer.json

registration.php

Этот файл нужен для того, чтобы зарегистрировать модуль в системе.

Выглядит он следующим образом:

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
  \Magento\Framework\Component\ComponentRegistrar::MODULE,
  'VendorName_ModuleName',
  DIR
);

В файле вызывается статический метод register, класса Magento\Framework\Component\ComponentRegistrar, в который передается тип компонента (в нашем случае module), имя вендора и имя модуля через нижнее подчеркивание VendorName_ModuleName, а так же магическая константа __DIR__, которая указывает на директорию, где лежит файл. А так выглядит изнутри сам класс ComponentRegistrar:

<?php
namespace Magento\Framework\Component;

/**
 * Provides ability to statically register components.
 */
class ComponentRegistrar implements ComponentRegistrarInterface
{
    /*
     * Different types of components
     */
    const MODULE = 'module';
    const LIBRARY = 'library';
    const THEME = 'theme';
    const LANGUAGE = 'language';
    const SETUP = 'setup';

    private static $paths = [
        self::MODULE => [],
        self::LIBRARY => [],
        self::LANGUAGE => [],
        self::THEME => [],
        self::SETUP => []
    ];

    /**
     * Sets the location of a component.
     */
    public static function register($type, $componentName, $path)
    {
        self::validateType($type);
        if (isset(self::$paths[$type][$componentName])) {
            throw new \LogicException(
                ucfirst($type) . ' \'' . $componentName . '\' from \'' . $path . '\' '
                . 'has been already defined in \'' . self::$paths[$type][$componentName] . '\'.'
            );
        }
        self::$paths[$type][$componentName] = str_replace('\\', '/', $path);
    }

    public function getPaths($type)
    {
        self::validateType($type);
        return self::$paths[$type];
    }

    public function getPath($type, $componentName)
    {
        self::validateType($type);
        return self::$paths[$type][$componentName] ?? null;
    }

    /**
     * Checks if type of component is valid
     */
    private static function validateType($type)
    {
        if (!isset(self::$paths[$type])) {
            throw new \LogicException('\'' . $type . '\' is not a valid component type');
        }
    }
}

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

После этого M2 ищет упомянутый выше файл etc/module.xml

etc/module.xml

Выглядит файл следующим образом:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="VendorName_ModuleName" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Customer"/>
            <module name="Magento_Checkout"/>
            <module name="Magento_Quote"/>
        </sequence>
    </module>
</config>

В шапке указывается XSD-схема файла. Если интересно покопаться, то лежит она тут: vendor/magento/framework/Module/etc/module.xsd.

В узле <module> атрибута name отмечается разработчик модуля VendorName и имя модуля ModuleName через нижнее подчеркивание.

В setup_version указывается актуальная версия модуля. Версию важно менять при добавлении нового функционала и при добавлении Install/Upgrade скриптов, которые вносят в базу данных изменения. Если ее не менять, то изменения не будут применены.

Также есть необязательная вложенность с узлом <sequence>, где указывается зависимость вашего модуля от других модулей и то, в какой последовательности они будут загружаться, а также, в какой последовательности будут использоваться pluginsevent observers и preferences (об это всем подробнее в будущих статьях). Данный параметр крайне важно указывать, если ваш модуль предполагает кастомизацию функционала других модулей. Т.е. если вы изменяете какой-либо метод класса ProductRepository, то вам в обязательном порядке надо указать зависимость от модуля Magento_Catalog, поскольку этот класс лежит именно там, в противном случае ваши изменения могут быть проигнорированы.

composer.json

Данный файл не является обязательным для того, чтобы модуль стал работать, однако, если вы хотите, чтобы его можно было установить через composer, то файл нужен. Пример:

{  
  "name": "vendor/module-test",
  "description": "Magento 2 extension",
  "require": {
    "php": ">=7.4",
    "magento/framework": "*"
  },
  "type": "magento2-module",
  "version": "1.0.0",
  "license": [
    "GPL-3.0"
  ],
  "autoload": {
    "files": [
      "registration.php"
    ],
    "psr-4": {
      "VendorName\\ModuleName\\": ""
    }
  }
}

Это вполне себе обычный composer.json файл, который используется повсеместно, а не только в M2. Про его структуру почитать можно здесь.

Вообще, M2 использует стандарт PSR-4, однако для подгрузки модулей из папки app/code также применяется стандарт PSR-0. Это можно заметить в composer.json в корне M2:

Также в секции files можно заметить запись, где упоминается app/etc/NonComposerRegistration.php

Название файла говорит само за себя. Вот как NonComposerRegistration.php выглядит внутри:

<?php

declare(strict_types=1);

//Register components (via a list of glob patterns)
namespace Magento\NonComposerComponentRegistration;

use RuntimeException;

/**
 * Include files from a list of glob patterns
 */
(static function (): void {
    $globPatterns = require __DIR__ . '/registration_globlist.php';
    $baseDir = \dirname(__DIR__, 2) . '/';
foreach ($globPatterns as $globPattern) {
        // Sorting is disabled intentionally for performance improvement
        $files = \glob($baseDir . $globPattern, GLOB_NOSORT);
        if ($files === false) {
            throw new RuntimeException("glob(): error with '$baseDir$globPattern'");
        }
\array_map(
            static function (string $file): void {
                require_once $file;
            },
            $files
        );
    }})();

Модули можно включать и выключать через консольные команды. Если модуль выключен, то это не значит, что созданные ранее им таблицы в базе данных будут удалены.

Включаем:

php bin/magento module:enable VendorName_ModuleName

Выключаем:

php bin/magento module:disable VendorName_ModuleName

Чтобы данные удалились, нужно использовать другую команду. Однако важно, чтобы в модуле присутствовал класс Setup/Uninstall.php:

php bin/magento module:uninstall VendorName_ModuleName

Структура папок модуля Magento 2

Пройдемся по структуре папок модуля в M2. Папки будут указаны относительно корня модуля.

/Api и /Api/Data

Тут хранятся, так называемые, service contracts, т.е. интейфесы моделей и интерфейсы моделей данных. M2 настоятельно рекомендует их к использованию.

/Block

Папка для блоков и view models. Вью модель — это прослойка между данными и представлением в архитектурном паттерне MVVM. Она нужна для того, чтобы разделить само представление (HTML) и логику, необходимую для подготовки этих данных к дальнейшему их выводу. Подробнее про MVVM можно почитать тут. Как создавать вью модели будет рассмотрено в следующих статьях.

/Console

Содержит в себе код, который вы будете использовать для запуска каких-либо задач через консоль:

php bin/magento [команда]

Для создания своей команды нужно добавить класс, который будет наследовать Symfony\Component\Console\Command\Command

Как видим, без Symfony не обошлось и если вы раньше работали с пакетом symfony/consoleто способ создания консольной команды будет вам уже знаком.

В дальнейшем мы подробнее рассмотрим какие cli-скрипты есть у M2 “из коробки” и как создавать свои собственные.

/Controller

Папка для контроллеров, которые отвечают за прием web запросов. По-умолчанию URL запроса состоит из трех сегментов: frontName (берется из файла routes.xml в модуле), название папки, где лежит файл контроллера и название класса контроллера. Как создать свой контроллер и вывести ‘Hello World’ также рассмотрим в дальнейшем.

/Controller/Adminhtml

То же самое, только для админ панели.

/Cron

Все крон-скрипты лежат здесь. Можно также создавать и настраивать свои.

/etc

Папка для конфигурационных файлов в формате xml. Например, module.xml, config.xml и т.д. Также в данной папке могут содержаться поддиректории в зависимости от того, для какой области (далее area)нужно их использовать, например /etc/frontend, /etc/adminhtml, /etc/webapi_soap и т.д. Если подпапка не указана, то конфигурация глобальная, т.е. работает для любой области и наоборот — некоторые файлы должны находиться только в определенной области.

/Helper

Папка для “хелперов”. Поскольку само это понятие считается анти-паттерном, крайне рекомендуется создавать только такие классы “хелперов”, которые можно было бы сделать статическими, или вообще отказаться от их использования.

/i18n

Папка для файлов перевода в формате .CSV, который должен содержать две колонки: оригинальная фраза и перевод. Имя файла указывается в виде локали, на какой язык будем переводить. Например: en_US.csv

i18n, если кто не знал, расшифровывается как internationalization

/Model

Здесь хранятся модели, а именно, классы для работы с данными и бизнес-логика.

/Model/ResourceModel

Классы взаимодействия с БД. Прямые запросы к ней хранятся именно здесь.

/Observer

Папка для event listeners. Класс event listenerа должен наследовать интерфейс \Magento\Framework\Event\ObserverInterface и реализовавать метод execute(). Вся логика должна быть вынесена в отдельный класс, а сам класс должен подключаться в listener через инъекцию зависимостей. При таком подходе бизнес логику можно легко тестировать, а код будет оставаться чистым.

/Plugin

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

/Setup

Здесь находятся файлы установки и обновления БД.

  • InstallSchema.php (для создания схемы БД)

  • UpgradeSchema.php (для обновления схемы БД)

  • InstallData.php (для добавления данных в БД)

  • UpgradeData.php (для обновления данных в БД)

  • Recurring.php (вызывается после каждого обновления схемы БД)

  • RecurringData.php (вызывается после каждого обновления данных БД)

  • Uninstall.php (используется для удаления данных из БД при удалении модуля)

Кстати, с версии 2.3 появилась возможность делать изменения в БД при помощи XML файлов. Вот статья в документации.

/Test

Папка для разных видов тестов: юнит-тестов, интеграционных и т.д

/Ui

Папка для классов, которые наполняют Ui-компоненты данными (data providers).

Ui-компоненты — это стандартизированные элементы пользовательского интерфейса, которые нужны для повторного применения в целях отрисовки форм, таблиц, окон и т.д. Для вывода используется JavaScript и HTML, конфигурация происходит с помощью xml, а данные готовятся при помощи Data Providers и приходят с сервера.

Довольно сложная и обширная тема, которая будет рассмотрена в следующих статьях.

/view/[area]/layout

В данной папке хранятся файлы в формате .xml, которые отвечают за связь шаблонов и блоков, т.е. представления и вью модели.

/view/[area]/templates

HTML шаблоны в формате .phtml, а именно, представление. Отвечает за вывод данных на frontend.

/view/adminhtml/ui_component

Файлы конфигурации Ui-компонентов в формате xml, которые используются для вывода Ui-элементов в админке. Например: формы, сетки (grid).

Пример сетки продуктов
Пример сетки продуктов

/view/[area]/web

Папка для JavaScript, CSS, Less, Sass файлов.

/view/[area]/web/template

Папка шаблонов в формате html. используется для JavaScript и Knockout JS. Knockout — это JS-библиотека на подобие ReactJs или Vue, только постарше и похуже. Подронее про него тут

Конфигурационные файлы модуля Magento 2

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

/etc/adminhtml/system.xml

Определяет tabs, sections, groups, fields в админке в разделе Store>Configuration. Иными словами, с помощью него создаются варианты настроек в админ-панели.

/etc/acl.xml

В данном файле описываются правила для ограничения прав доступа пользователей, которые будет использовать админку или API.

/etc/config.xml

Настройки админки Store->Configuration по-умолчанию. Используются до того момента, пока пользователь не внес свои изменения. Сохраненные настройки хранятся в БД в таблице core_config_data.

/etc/crontab.xml

Здесь определяются задачи планировщика (cron jobs). При настройке M2 не нужно выносить в crontab каждую задачу на уровне системы, а лишь одну — родительскую. Запускаться она должна каждую минуту.

* * * * * /usr/bin/php /var/www/html/magento2/bin/magento cron:run

Все остальные задачи настраиваются, планируются и выполняются самим приложением.

/etc/[area]/di.xml

Пожалуй, наиболее часто используемый файл при работе с модулями — настройка инъекции зависимостей. Здесь переопределяются классы (preference), определяются service contracts, плагины (plugins), виртуальные типы (virtual types), передаются аргументы в конструкторы объектов.

/etc/email_templates.xml

Тут определяются шаблоны писем для email-рассылок.

/etc/[area]/events.xml

Файл для определения слушателей событий (event listener). В то время как плагинам разрешено менять результат выполнения какого-либо метода, у событий и их слушателей другие цели, а именно, выполнять какое-то действие до или после отработки какого-либо функционала. Например, отправить пользователю письмо после размещения заказа. Подробнее в следующих статьях.

/etc/indexer.xml

Определяет индексеры. Вся информация о продукте или категории содержится в разных таблицах в БД и, чтобы собрать всю эту информацию, mysql-запрос может долго выполняться и\или требовать много ресурсов. Для того, чтобы информация доставалась быстрее, существуют индексеры. Они собирают все необходимые данные и записывают ее в специальные таблицы в БД, откуда они и берутся при запросе от клиента. Можно создавать и свои собственные индексеры, а также посмотреть их статус в админке System>Index Management

Админ-панель. Статус индексеров в Magento 2
Админ-панель. Статус индексеров в Magento 2

/etc/adminhtml/menu.xml

Содержит настройки меню админки: табы, пункты меню и прочее.

Меню Magento 2
Меню Magento 2

/etc/mview.xml

Вызывает тип event, когда были изменены данные в определенной колонки в БД. Часто используется для индексации.

/etc/[area]/routes.xml

Когда происходит запрос к серверу, определяет первый элемент пути к layout файлу. Содержит routeId и frontName — используется в качестве первого элемента пути в URL.

Например: http://localhost/sales/order/history, где sales — frontName, order — папка контроллера, history — имя контроллера.

Пример использования routes.xml
Пример использования routes.xml

/etc/view.xml

То же самое, что и config.xml, только для тем, т.е. настройки темы по-умолчанию.

/etc/webapi.xml

Определяет доступные методы, классы API для доступа к M2 из вне.

/etc/widget.xml

Файл для определения виджетов — маленьких статических или динамических блоков с данными, которые можно встраивать на страницы веб-сайта.

/etc/extension_attributes.xml

Конфигурационный файл для добавления дополнительных атрибутов к определенной сущности (напр. к сущности продукта, заказа, квоте).


Это был не весь список, но базовые файлы я перечислил. С полным списком файлов можно ознакомиться здесь. Также каждый файл из перечисленных выше является частью отдельной темы. Каждая из этих тем будет рассмотрена в дальнейших статьях.

Комментарии (0)