Модуль для маркетплейс — от идеи до старта продаж. Часть 5 — держим себя в руках. Файловая структура модуля, инсталлятор

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

Ложка меда и бочка дегтя

Кстати, в маркетплейсе есть модуль, который помогает создавать модули — bitrix.mpbuilder. Разработал его Битрикс, видимо, в помощь партнерам. Но, судя по комментариям, этот модуль уже 2 года не получал обновлений и вообще работает не полностью. Меня это поначалу не смутило, поэтому я все равно поставил этот модуль и попытался сгенерировать из него структуру файлов.

Модуль предлагает разработчику пройти 5 шагов: создать структуру модуля, выделить языковые фразы, редактор языковых фраз (который не работает), создать архив с модулем и собрать новую версию для обновления модуля в маркете. Иду на первый шаг, заполняю там все интересующие меня на данный момент поля:

Вот что получилось из этого:

Он создал директорию install, в которой лежит готовый инсталлятор и файл с описанием версии. Все языковые фразы, которые я вводил (описание модуля, название и т.д.) он выделил в необходимый для этого языковой файл. Также он создал необходимый для работы модуль include.php. Все бы хорошо, но …

Все это не годится и придется переделывать 🙂 Почему? Причины:

  • описание класса инсталлятора ну вообще не годится. Обилие устаревших конструкций, конструктор в формате php4, отсутствие модификаторов доступа к методам класса, табы вместо пробелов, куча лишнего кода …
  • закрывающие теги в php файлах! Мало того, что они есть, так после них есть еще и переносы строк!
    что будет расцениваться как вывод в стандартный поток вывода и в некоторых случаях может привести к очень трудноуловимым ошибкам (на эту тему напишу отдельный пост)
  • часть логики вынесено в include.php, а именно создание пункта меню. Я понимаю, что битрикс рекомендует это делать, нужно это для того, чтобы обеспечить работу бесплатного пробного периода (весь код include.php обфусцируется, и туда добавляется проверка на пробный режим). Но я бы выносил в эту часть что-то более существенное, наверно. Кстати, надо бы подумать, что вынести туда 🙂

В довесок к этому, в модуле не работает третий шаг (редактор языковых файлов). В общем у битрикса как всегда — подзабили…

Придется создавать самому. Кстати, вот неплохая идея для опенсорса — запилить конструктор модулей с помощью symfony/console и composer create-project, думаю будет быстрее, удобнее, и более гибко, а соответственно расширяемо, по сравнению с этим bitrix.mpbuilder. Туда же можно будет прикрутить сборку пакета с модулем, а также сборку файлов обновлений на основании версионного контроля. Найти бы время … Или может быть уже есть что-то подобное?

Для начала нам понадобится раздобыть инсталляцию битрикса. Можно взять официальную демку, и развернуть чистую битру по мануалу. Но я не буду заморачиваться и просто начну работу по модулю в одном из своих существующих проектов, чтобы не тратить время на развертывание.

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

Создадим новый проект в PhpStorm и создадим там структуру файлов и папок по мануалу.

Жутковато, правда? Но ничего, справимся. Тут я создал полную версию всей структуры, за исключением устаревших возможностей и с учетом того, что модуль будет работать на mysql-подобной базе. Я сохраню всю эту структуру на будущее для того, чтобы может быть потом взяться за конструктор модулей, а сейчас пока удалю все лишнее. Исходя из ограничений, принятых на проекте, я буду использовать только d7 для разработки своих классов, а значит директория /classes мне не понадобится. Также мне не понадобится файл /admin/menu.php, т.к. меню у меня будет добавляться с помощью обработчика события. /install/images и /install/js мне тоже не понадобятся, т.к. все механизмы, связанные с интерактивом я буду выносить в компоненты. Может быть потом появится какой-то красивый логотип, тогда добавлю его в пункт меню, но не более. Кастомизировать внешний вид админки мы тоже не будем, поэтому директория /panel тоже не нужна. Поскольку я буду работать с таблицами d7, то и создание таблиц будет происходить с помощью ORM, а значит мне не понадобится (по крайней мере пока), директория /install/db. Еще я хочу удалить директорию /install/admin (битрикс называет скрипты из этой папки «вызывающими»). Вот и у меня эти скрипты вызывают много вопросов и из-за них я впадаю в уныние, поэтому не буду хранить их в проекте а просто буду генерировать на лету в процессе инсталляции. Ну и напоследок, можно удалить файл /prolog.php, который имеет весьма сомнительное предназначение. Если он мне понадобится, то заведу, но сейчас не вижу в нем никакого смысла. Итого получаем следующую структуру:

Уже не так страшно.

В .gitignore добавим директорию /vendor, которая скорее всего у меня тут появится для хранения скриптов, нужных для разработки.

Для того, чтобы иметь отдельный проект, но при этом иметь возможность работать с этим кодом в другом проекте, я просто создал симлинк из одного проекта в другой. В проекте, где используется битрикс я просто создал симлинк /local/modules/maximaster.coupanda, который ссылается на мой проект. Но тут можно придумать 100500 схем работы с модулем внутри другого проекта, дело вкуса.

Код

Наконец-то, добрался я до разработки. Инсталлятор должен находиться в директории /install/index.php. Это должен быть класс, наследник от \CModule с именем maximaster_coupanda. Данный класс должен присвоить значения свойствам:

Часть из них обязательны, часть нет. Все свойства в родителе объявлены через var, поэтому являются публичными. В конструкторе создадим набор методов, который будет инициализировать значения этих свойств:

Тут все довольно тривиально, за исключением версии модуля. Номер версии модуля должен храниться в файле /install/version.php в виде массива. Плюс к этому нужно в классе модуля записывать эти значения. Зачем так сделано? Да хз… Но в связи с этим нужно налепить небольшую городушку, которая будет брать значение из этого файла и записывать его в модуль. Получаем нечто такое для какой-то … версии … :

Тут я в некоторых местах использовал венгерскую нотацию (которую, к слову, не очень то воспринимаю в условиях современного процесса разработки), но это обусловлено необходимостью работать с битриксом.

Инсталляция/деинсталляция

Для процесса инсталлирования в битриксе предусмотрен метод DoInstall(). А для деинсталлирования DoUninstall() соответственно. Кроме этого битриксом заложен заранее предустановленный набор методов, которые должны проинсталлировать файлы модуля, почтовые события и данные для БД — InstallFiles, InstallEvents и InstallDB соответственно, в автоматическом режиме. Для деинсталляции предусмотрены аналогичные методы. Пока что модуль у нас пустой, инсталлировать кроме него самого нечего, поэтому методы инсталляции файлов и почтовых событий оставлю пока пустыми, а в методе инсталляции БД пропишем регистрацию модуля в системе, а сам метод инсталляции БД добавим в DoInstall. Для остальных методов инсталляции добавим заглушки и тоже добавим их в DoInstall. Получается вот такая штука:

Ровно то же самое получается для деинсталляции.

Система прав доступа

В битриксе есть несколько уровней доступа — права на файлы/директории, права модулей (на основе ролей или «просто права»). Права на файлы нас не интересуют, ими управляет модуль управления структурой. По условиям задачи мне потребуется только 2 роли — администратор модуля (который будет иметь права на все) и все остальные (которые не будут иметь доступа).

Я думаю, что для реализации этих возможностей можно не использовать систему ролей, чтобы не усложнять, а обойтись системой обычных прав доступа. Создам 2 права доступа пока — «[D] Доступ запрещен» и «[W] Полный доступ». Буквенные обозначения будут использоваться в коде для проверки прав. Данные буквы выбраны из соображения «привычности» для мира битрикса, но можно использовать любые, главное чтобы вы с оператором сравнения могли их нормально обрабатывать. Тут понятно, что W (Write) больше чем D (Denied), главное не напутать.

Для того, чтобы обозначить список прав доступа, нужно в классе модуля добавить метод GetModuleRightList. Этот модуль должен возвращать массив описания прав доступа, доступных для вашего модуля. В моем случае получился вот такой простой метод:

Если же нужно использовать систему ролей, то необходимо вместо этого метода добавить метод GetModuleTasks, где описать массив чуть более сложной структуры, но суть его проста — добавить строковые имена всем возможным операциям и привязать эти операции к ролям и также дать буквенные обозначения. Благодаря этому администратор главного модуля сможет добавлять собственные роли к вашему модулю в разделе «Настройки > Пользователи > Уровни доступа». Но мне это не нужно, поэтому пока пропустим. Надо бы статейку про права накатать, а то инфы мало по этому поводу в интернете …

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

Первый пуск

Итак, что мы имеем. В /local/modules/ у нас лежит директория maximaster.coupanda, в которой есть минимально необходимая структура для работы модуля, инсталлятор модуля и языковые файлы модуля.

Идем в админку (Marketplace > Установленные решения) и смотрим, что там имеется:

Проверяем, что модуль действительно устанавливается и удаляется:

Можно сделать первый коммит 🙂

В следующей «серии» будем делать функционал по части БД и прикручивать его к инсталлятору.