Перейти к содержимому

Свой модуль или код в проекте - когда пора выносить в модуль

Решаем, где жить прикладному коду: в файлах проекта рядом с шаблонами или в своём модуле с установщиком, настройками и версией.

Что нужно знать заранее

Код в каталоге проекта стартует мгновенно и не требует установки вообще. Это верный выбор для правок одного сайта, пока их немного и они не образуют самостоятельную подсистему.

Модуль - это упаковка решения: установщик, версия, настройки, права и события. Он окупается там, где решение ставится на несколько сайтов или его надо включать и выключать целиком.

Файл инициализации перестаёт справляться заметно раньше, чем кажется автору. Полтысячи строк обработчиков в одном файле читаются плохо, а искать в них причину поведения приходится каждому новому разработчику.

Шаги

  1. Ответить честно, поедет ли этот код на другой проект в обозримом будущем.
  2. Оценить объём кода: несколько обработчиков или самостоятельная подсистема со своими данными.
  3. Проверить, нужны ли свои настройки, права доступа и страницы в административной части.
  4. Выбрать вариант и оформить границу: обращения к решению идут через его классы.
  5. Заложить время на упаковку, если решение будет продаваться или ставиться нескольким клиентам.

Решение

Держим код проекта разложенным по смыслу:

/local/php_interface/init.php - только подключение файлов, без логики
/local/php_interface/events.php - регистрация обработчиков событий проекта
/local/lib/Vendor/Catalog/ - классы прикладной логики проекта
/local/components/vendor/ - свои компоненты витрины
# файл инициализации остаётся коротким и читаемым даже через два года

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

Регистрируем автозагрузку своих классов:

/local/php_interface/init.php
\Bitrix\Main\Loader::registerNamespace('Vendor\\Catalog', $_SERVER['DOCUMENT_ROOT'] . '/local/lib/Vendor/Catalog');
require_once __DIR__ . '/events.php'; // обработчики отдельным файлом

Оформляем решение модулем, когда пора:

class vendor_catalog extends CModule
{
public $MODULE_ID = 'vendor.catalog';
public $MODULE_VERSION = '1.0.0';
public function DoInstall(): void
{
\Bitrix\Main\ModuleManager::registerModule($this->MODULE_ID);
$this->installEvents(); // обработчики регистрируются при установке
$this->installDB(); // таблицы создаются здесь же
}
}

Установщик - главное отличие модуля от кода проекта. Он умеет создавать таблицы, регистрировать обработчики и убирать всё это обратно, а код в каталоге проекта такого не умеет вовсе.

Регистрируем обработчики модуля постоянно:

$em = \Bitrix\Main\EventManager::getInstance();
$em->registerEventHandler('sale', 'OnSaleOrderSaved', $this->MODULE_ID,
'\Vendor\Catalog\OrderHandler', 'onSaved');
// постоянная регистрация живёт в базе и переживает перезагрузку сайта

Храним настройки решения по имени модуля:

\Bitrix\Main\Config\Option::set('vendor.catalog', 'sync_enabled', 'Y');
printf("обмен включён: %s\n", \Bitrix\Main\Config\Option::get('vendor.catalog', 'sync_enabled'));
// у модуля появляется своя страница настроек в административном разделе

Настройки с именем модуля - ещё одна причина упаковки. Они видны администратору в привычном месте, переживают обновления и не теряются среди чужих значений в файле настроек проекта.

Типичные проблемы

Файл инициализации разросся до тысячи строк.

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

Решение не ставится на второй сайт.

Код разбросан по каталогу проекта и не собран в переносимую единицу поставки. Для переноса между проектами решение упаковывают в отдельный модуль.

После удаления модуля остались таблицы и обработчики.

В установщике не написан обратный код удаления данных и всех регистраций. Удаление пишут сразу вместе с установкой, а не когда-нибудь потом.

Обработчики перестали работать после переноса.

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

Настройки решения теряются при обновлении.

Значения лежат в файле настроек проекта вместе с чужими секциями настроек. Настройки решения хранят по имени модуля штатными средствами платформы.

Частые вопросы

Когда точно хватает кода в проекте?

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

Обязателен ли модуль для продажи решения?

Да, торговая площадка принимает именно модули с установщиком и версией. Код проекта продать нельзя технически.

Можно ли переупаковать код проекта в модуль позже?

Можно, но это отдельная работа: переезд классов, установщик и регистрация событий. Дешевле решить на старте, если перспектива понятна.

Где хранить таблицы решения?

При модуле - создавать их в установщике и удалять при деинсталляции. В коде проекта таблицы приходится заводить миграциями отдельно.

Что делать с общими библиотеками?

Сторонние библиотеки ставят менеджером зависимостей и подключают один раз. Модуль при этом объявляет свои зависимости в документации, а не тащит копии.

Смежное

Первоисточник