Полный цикл в digital

Структура модуля

Готовый код можно скачать в моем репозитории на GitFlic.

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

Что умеет сборка

При установке:

  1. Установка модуля в один и несколько этапов
  2. Создание таблиц, описанных с помощью ORM-сущностей
  3. Установка агента
  4. Установка событий
  5. Копирование файлов компонента и для административной части
  6. Наполнение таблиц тестовыми данными
  7. Описание прав доступа к модулю

При удалении:

  1. Удаление модуля в один и несколько этапов с предложением сохранить существующие данные
  2. Удаление таблиц, описанных с помощью ORM-сущностей
  3. Удаление агентов
  4. Удаление событий
  5. Удаление файлов компонента и административной части

Что реализовано интересного:

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

Виды модулей

Системные модули распологаются в папке /bitrix/modules/ и мы их не трогаем.

Собственные модули распологаются в папке /local/modules/. В качестве названия корневой папки модуля, может выступать любое не занятое название, но при этом есть свои особенности.

Расположение модуля в админке

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

Системные модули

Страница установки системных модулей находится в Настройки -> Настройки продукта -> Модули:

В этом случае название корневой папки модуля пишется одним словом, например:

hmarketing

В этом случае название класса в файле /local/modules/hmarketing/install/index.php будет class hmarketing extends CModule.

Партнерские модули

Если название вашего модуля написан через точку, модуль считается партнёрским и его местоположение будет другое Marketplace -> Установленные решения:

Такой модуль требует специфического названия состаящего из:

  1. Кода партнера
  2. Названия модуля
код_партнёра.название_модуля

В этом случае название корневой папки модуля пишется двумя словами, например hmarketing.d7, название класса в файле /local/modules/hmarketing.d7/install/index.php будет class hmarketing_d7 extends CModule.

Структура

  • Папка hmarketing.d7
  • Папка admin
    • Файл menu.php расположение модуля в меню админки Битрикс
    • Файл hmarketing.php админка для пользователей Битрикс без прав администратора, нужно самим создавать ORM для записи в БД
  • Папка install
    • Папка admin
      • Файл hmarketing.php файл который будет скопирован в дирикторию bitrix и будет инклудить админку для пользователей Битрикс без прав администратора
    • Папка components содержит свой компонент для установки в Битрих путем копирования
    • Папка files
      • Файл hmarketing.php файл который будет скопирован в дирикторию / для проверки обработчика событий
    • Файл index.php основной файл установки
    • Файл instalInfo-step1.php установка модуля в несколько шагов (1-й шаг)
    • Файл instalInfo-step2.php установка модуля в несколько шагов (2-й шаг)
    • Файл instalInfo.php установка модуля в один шаг
    • Файл deInstalInfo-step1.php удаление модуля в несколько шагов (1-й шаг)
    • Файл deInstalInfo-step2.php удаление модуля в несколько шагов (2-й шаг)
    • Файл deInstalInfo.php удаление модуля в один шаг
    • Файл version.php файл с версией и датой выхода модуля
  • Папка lang
    • Папка ru
      • Папка lib
        • Файл data.php языковой файл базы данных
      • Папка install
        • Файл index.php языковой файл установки
        • Файл instalInfo-step1.php языковой файл вывода сообщения в админке сайта при установке модуля в несколько шагов (1-й шаг)
        • Файл instalInfo-step2.php языковой файл вывода сообщения в админке сайта при установке модуля в несколько шагов (2-й шаг)
        • Файл instalInfo.php языковой файл вывода сообщения в админке сайта при установке модуля
        • Файл deInstalInfo-step1.php языковой файл вывода сообщения в админке сайта при удаление модуля в несколько шагов (1-й шаг)
        • Файл deInstalInfo-step2.php языковой файл вывода сообщения в админке сайта при удаление модуля в несколько шагов (2-й шаг)
        • Файл deInstalInfo.php языковой файл вывода сообщения в админке сайта при удалении модуля
  • Папка lib
    • Файл Agent.php класс агента
    • Файл Author.php вспомогательная таблица модуля написанная через ORM
    • Файл Data.php основная таблица модуля написанная через ORM
    • Файл Events.php класс события
    • Файл Main.php основной класс модуля
  • Файл include.php используется для подключения файлов модуля
  • Файл options.php настройки модуля в админке с правами администратора, Битрикс сам создает записи в БД

Файл /admin/menu.php

Файл вызывается ядром Битрикс автоматически при установке модуля.

Данный файл создаёт пункты меню в административном интерфейсе Битрикс, дефолтные настройки:

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

local/modules/hmarketing.d7/admin/menu.php<?
defined('B_PROLOG_INCLUDED') and (B_PROLOG_INCLUDED === true) or die();
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
// сформируем верхний пункт меню
$aMenu = array(
    // пункт меню в разделе Контент
    'parent_menu' => 'global_menu_services',
    // сортировка
    'sort' => 1,
    // название пункта меню
    'text' => "Модули Эйч Маркетинг",
    // идентификатор ветви
    "items_id" => "menu_webforms",
    // иконка
    "icon" => "form_menu_icon",
);
// дочерния ветка меню
$aMenu["items"][] =  array(
    // название подпункта меню
    'text' => 'Страница модуля',
    // ссылка для перехода
    'url' => 'hmarketing.php?lang=' . LANGUAGE_ID
);
// дочерния ветка меню
$aMenu["items"][] =  array(
    // название подпункта меню
    'text' => 'Админка модуля',
    // ссылка для перехода
    'url' => 'settings.php?lang=ru&mid=hmarketing.d7'
);
// возвращаем основной массив $aMenu
return $aMenu;

Файл /admin/hmarketing.php

Файл добаввляется в дирикторию bitrix и вызывается при переходе в админки.

Данный файл создаёт пункты меню в административном интерфейсе Битрикс, дефолтные настройки:

local/modules/hmarketing.d7/admin/hmarketing.php<?
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_admin_before.php';
// пространство имен для автозагрузки модулей
use \Bitrix\Main\Loader;
// получим права доступа текущего пользователя на модуль
$POST_RIGHT = $APPLICATION->GetGroupRight("hmarketing.d7");
// если нет прав - отправим к форме авторизации с сообщением об ошибке
if ($POST_RIGHT == "D") {
    $APPLICATION->AuthForm(GetMessage("ACCESS_DENIED"));
}
// вывод заголовка
$APPLICATION->SetTitle("Настройка Pop Up");
// подключаем языковые файлы
IncludeModuleLangFile(__FILE__);
$aTabs = array(
    array(
        // название вкладки в табах 
        "TAB" => "Параметры",
        // заголовок и всплывающее сообщение вкладки
        "TITLE" => "Параметры вывода pop-up"
    )
);
// отрисовываем форму, для этого создаем новый экземпляр класса CAdminTabControl, куда и передаём массив с настройками
$tabControl = new CAdminTabControl(
    "tabControl",
    $aTabs
);
// подключаем модуль для того что бы был видем класс ORM
Loader::includeModule("hmarketing.d7");
// Необходимость сохранения изменений мы определим по следующим параметрам: 1)Страница вызвана методом POST; 2)Среди входных данных есть идентификаторы кнопок "Сохранить" и "Применить". Если эти условия сооблюдены и пройдены проверки безопасности, можно сохранять переданные скрипту данные:
if (
    // проверка метода вызова страницы
    $REQUEST_METHOD == "POST"
    &&
    // проверка нажатия кнопок Сохранить
    $save != ""
    &&
    // проверка наличия прав на запись для модуля
    $POST_RIGHT == "W"
    &&
    // проверка идентификатора сессии
    check_bitrix_sessid()
) {
    // класс таблицы в базе данных
    $bookTable = new \Hmarketing\d7\DataTable;
    // обработка данных формы
    $arFields = array(
        "ACTIVE" => ($ACTIVE == '') ? 'N' : 'Y',
        "SITE" => json_encode($SITE),
        "LINK" => htmlspecialchars($LINK),
        "LINK_PICTURE" => htmlspecialchars($LINK_PICTURE),
        "ALT_PICTURE" => htmlspecialchars($ALT_PICTURE),
        "EXCEPTIONS" => $EXCEPTIONS == "" ? "" : trim(htmlspecialchars($EXCEPTIONS)),
        "DATE" => new \Bitrix\Main\Type\DateTime(date("d.m.Y H:i:s")),
        "TARGET" => htmlspecialchars($TARGET),
    );
    // обновляем запись
    $res = $bookTable->Update(1, $arFields);
    // если обновление прошло успешно
    if ($res->isSuccess()) {
        // перенаправим на новую страницу, в целях защиты от повторной отправки формы нажатием кнопки Обновить в браузере
        if ($save != "") {
            // если была нажата кнопка Сохранить, отправляем обратно на форму
            LocalRedirect("/bitrix/admin/hmarketing.php?mess=ok&lang=" . LANG);
        }
    }
    // если обновление прошло не успешно 
    if (!$res->isSuccess()) {
        // если в процессе сохранения возникли ошибки - получаем текст ошибки
        if ($e = $APPLICATION->GetException())
            $message = new CAdminMessage("Ошибка сохранения: ", $e);
        else {
            $mess = print_r($res->getErrorMessages(), true);
            $message = new CAdminMessage("Ошибка сохранения: " . $mess);
        }
    }
}
// подготовка данных для формы, полученные из БД данные будем сохранять в переменные с префиксом str_
$result = \Hmarketing\d7\DataTable::GetByID(1);
if ($result->getSelectedRowsCount()) {
    $bookTable = $result->fetch();
    $str_ACTIVE = $bookTable["ACTIVE"];
    $str_SITE = json_decode($bookTable["SITE"]);
    $str_LINK = $bookTable["LINK"];
    $str_LINK_PICTURE = $bookTable["LINK_PICTURE"];
    $str_ALT_PICTURE = $bookTable["ALT_PICTURE"];
    $str_EXCEPTIONS = $bookTable["EXCEPTIONS"];
    $str_TARGET = $bookTable["TARGET"];
}
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_admin_after.php';
// eсли есть сообщения об успешном сохранении, выведем их
if ($_REQUEST["mess"] == "ok") {
    CAdminMessage::ShowMessage(array("MESSAGE" => "Сохранено успешно", "TYPE" => "OK"));
}
// eсли есть сообщения об не успешном сохранении, выведем их
if ($message) {
    echo $message->Show();
}
// eсли есть сообщения об не успешном сохранении от ORM, выведем их
if ($bookTable->LAST_ERROR != "") {
    CAdminMessage::ShowMessage($bookTable->LAST_ERROR);
}
?>
<form method="POST" action="<?= $APPLICATION->GetCurPage() ?>" ENCTYPE="multipart/form-data" name="post_form">
<?
// проверка идентификатора сессии
echo bitrix_sessid_post();
// отобразим заголовки закладок
$tabControl->Begin();
$tabControl->BeginNextTab();
?>
<tr>
    <td width="40%"><?= "Активность" ?></td>
    <td width="60%"><input type="checkbox" name="ACTIVE" value="Y" <? if ($str_ACTIVE == "Y") echo " checked" ?>></td>
</tr>
<tr>
    <td>
        <label for="SITE"><?= "Сайты" ?></label>
    </td>
    <td>
        <select name="SITE[]" multiple>
            <option value="s1" <?= in_array('s1', $str_SITE) ? 'selected' : '' ?>>Для России</option>
            <option value="kz" <?= in_array('kz', $str_SITE) ? 'selected' : '' ?>>Для Казахстана</option>
        </select>
    </td>
</tr>
<tr>
    <td width="40%"><?= "Ссылка для перехода" ?></td>
    <td width="60%"><input type="text" name="LINK" value="<?= $str_LINK ?>" /></td>
</tr>
<tr>
    <td width="40%"><?= "Ссылка на картинку" ?></td>
    <td width="60%"><input type="text" name="LINK_PICTURE" value="<?= $str_LINK_PICTURE ?>" /></td>
</tr>
<tr>
    <td width="40%"><?= "Alt картинки" ?></td>
    <td width="60%"><input type="text" name="ALT_PICTURE" value="<?= $str_ALT_PICTURE ?>" /></td>
</tr>
<tr>
    <td width="40%"><?= "Исключения" ?></td>
    <td width="60%"><textarea cols="50" rows="15" name="EXCEPTIONS"><?= $str_EXCEPTIONS ?></textarea></td>
</tr>
<tr>
    <td width="40%"><?= "Значение TARGET (self/blank)" ?></td>
    <td width="60%"><input type="text" name="TARGET" value="<?= $str_TARGET ?>" /></td>
</tr>
<?
// выводит стандартные кнопки отправки формы
$tabControl->Buttons();
?>
<input class="adm-btn-save" type="submit" name="save" value="Сохранить настройки" />
<?
// завершаем интерфейс закладки
$tabControl->End();
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_admin.php';
?>

Файл /install/admin/hmarketing.php

Файл будет скопирован в дирикторию bitrix и будет инклудить админку для пользователей Битрикс.

local/modules/hmarketing.d7/install/admin/hmarketing.php<?
// определяем в какой папке находится модуль, если в bitrix, инклудим файл с меню из папки bitrix
if (is_dir($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/hmarketing.d7/")) {
    // присоединяем и копируем файл
    require_once($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/hmarketing.d7/admin/hmarketing.php");
}
// определяем в какой папке находится модуль, если в local, инклудим файл с меню из папки local
if (is_dir($_SERVER["DOCUMENT_ROOT"] . "/local/modules/hmarketing.d7/")) {
    // присоединяем и копируем файл
    require_once($_SERVER["DOCUMENT_ROOT"] . "/local/modules/hmarketing.d7/admin/hmarketing.php");
}

Файл /install/files/hmarketing.php

Файл будет скопирован в дирикторию / для проверки обработчика событий.

local/modules/hmarketing.d7/install/files/hmarketing.php<?
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");
// собираем зарегистрированные через RegisterModuleDependences и AddEventHandler обработчики события OnSomeEvent
$rsHandlers = GetModuleEvents("hmarketing.d7", "OnSomeEvent");
// перебираем зарегистрированные в системы события
while ($arHandler = $rsHandlers->Fetch()) {
    // выполняем каждое зарегистрированное событие по одному
    ExecuteModuleEventEx($arHandler, array(/* параметры которые нужно передать в модуль */));
}

Файл /install/index.php

Файл вызывается ядром Битрикс автоматически при установке модуля.

Инсталлятор должен находиться в директории /install/index.php. Имя класса должно соответствовать названию папке модуля и являться наследником от CModule. Только вместо точки, если мы делаем партнерский модуль стоит прописать нижние подчеркивание, это нужно для определения модуля в системе. Файл выполняется при установке или удалении модуля через административный интерфейс.

В конструкторе класса указывается справочная информация о модуле, часть информации обязательна, часть нет. Данный класс должен присвоить значения свойствам:

public $MODULE_NAME;
public $MODULE_DESCRIPTION;
public $MODULE_VERSION;
public $MODULE_VERSION_DATE;
public $MODULE_ID;
public $MODULE_SORT;
public $SHOW_SUPER_ADMIN_GROUP_RIGHTS;
public $MODULE_GROUP_RIGHTS;
public $PARTNER_NAME;
public $PARTNER_URI;

Тут все довольно тривиально, за исключением версии модуля. Номер версии модуля должен храниться в файле /install/version.php в виде массива. Плюс к этому нужно в классе модуля записывать эти значения.

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

  • DoInstall отрабатывает при установке модуля, внутри должна вызываться функция RegisterModule для регистрации модуля в базе. Так же доступны дополнительные методы для: создания/удаления таблицы в базе, установки/удаления событий и дополнительных файлов модуля. Для реализации нашего модуля не нужны события и дополнительные файлы, методы оставим пустыми для будущего расширения возможностей модуля
  • DoUninstall отрабатывает при удалении модуля, внутри должна вызываться функция UnRegisterModule для удаления регистрации модуля в базе. Так же доступны дополнительные методы для: создания/удаления таблицы в базе, установки/удаления событий и дополнительных файлов модуля. Для реализации нашего модуля не нужны события и дополнительные файлы, методы оставим пустыми для будущего расширения возможностей модуля

Кроме этого, заложены заранее предустановленный наборы методов, которые должны проинсталлировать файлы модуля, события и данные для БД — InstallFiles, InstallEvents, InstallDB в автоматическом режиме.

local/modules/hmarketing.d7/install/index.php<?
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// пространство имен для управления (регистрации/удалении) модуля в системе/базе
use Bitrix\Main\ModuleManager;
// пространство имен для работы с параметрами модулей хранимых в базе данных
use Bitrix\Main\Config\Option;
// пространство имен с абстрактным классом для любых приложений, любой конкретный класс приложения является наследником этого абстрактного класса
use Bitrix\Main\Application;
// пространство имен для работы c ORM
use \Bitrix\Main\Entity\Base;
// пространство имен для автозагрузки модулей
use \Bitrix\Main\Loader;
// пространство имен для событий
use \Bitrix\Main\EventManager;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
class Hmarketing_D7 extends CModule
{
    // переменные модуля
    public  $MODULE_ID;
    public  $MODULE_VERSION;
    public  $MODULE_VERSION_DATE;
    public  $MODULE_NAME;
    public  $MODULE_DESCRIPTION;
    public  $PARTNER_NAME;
    public  $PARTNER_URI;
    public  $SHOW_SUPER_ADMIN_GROUP_RIGHTS;
    public  $MODULE_GROUP_RIGHTS;
    public  $errors;
    // конструктор класса, вызывается автоматически при обращение к классу
    function __construct()
    {
        // создаем пустой массив для файла version.php
        $arModuleVersion = array();
        // подключаем файл version.php
        include_once(__DIR__ . '/version.php');
        // версия модуля
        $this->MODULE_VERSION = $arModuleVersion['VERSION'];
        // дата релиза версии модуля
        $this->MODULE_VERSION_DATE = $arModuleVersion['VERSION_DATE'];
        // id модуля
        $this->MODULE_ID = "hmarketing.d7";
        // название модуля
        $this->MODULE_NAME = "Пример модуля D7";
        // описание модуля
        $this->MODULE_DESCRIPTION = "Тестовый модуль для разработчиков, можно использовать как основу для разработки новых модулей для Битрикс";
        // имя партнера выпустившего модуль
        $this->PARTNER_NAME = "Эйч Маркетинг";
        // ссылка на рисурс партнера выпустившего модуль
        $this->PARTNER_URI = "https://hmarketing.ru";
        // если указано, то на странице прав доступа будут показаны администраторы и группы
        $this->SHOW_SUPER_ADMIN_GROUP_RIGHTS = 'Y';
        // если указано, то на странице редактирования групп будет отображаться этот модуль
        $this->MODULE_GROUP_RIGHTS = 'Y';
    }
    // метод отрабатывает при установке модуля
    function DoInstall()
    {
        //*************************************//
        // Пример с установкой в один шаг      //
        //*************************************//
        // // глобальная переменная с обстрактным классом
        // global $APPLICATION;
        // // регистрируем модуль в системе
        // ModuleManager::RegisterModule("hmarketing.d7");
        // // создаем таблицы баз данных, необходимые для работы модуля
        // $this->InstallDB();
        // // создаем первую и единственную запись в БД
        // $this->addData();
        // // регистрируем обработчики событий
        // $this->InstallEvents();
        // // копируем файлы, необходимые для работы модуля
        // $this->InstallFiles();
        // // устанавливаем агента
        // $this->installAgents();
        // // подключаем скрипт с административным прологом и эпилогом
        // $APPLICATION->includeAdminFile(
        //     Loc::getMessage('INSTALL_TITLE'),
        //     __DIR__ . '/instalInfo.php'
        // );
        //*************************************//
        // Пример с установкой в два шага      //
        //*************************************//
        // получаем контекст и из него запросы
        $context = Application::getInstance()->getContext();
        $request = $context->getRequest();
        // глобальная переменная с обстрактным классом
        global $APPLICATION;
        // проверяем какой сейчас шаг, если он не существует или меньше 2, то выводим первый шаг установки
        if ($request["step"] < 2) {
            // подключаем скрипт с административным прологом и эпилогом
            $APPLICATION->IncludeAdminFile(
                Loc::getMessage('INSTALL_TITLE_STEP_1'),
                __DIR__ . '/instalInfo-step1.php'
            );
        }
        // проверяем какой сейчас шаг, усли 2, производим установку
        if ($request["step"] == 2) {
            // регистрируем модуль в системе
            ModuleManager::RegisterModule("hmarketing.d7");
            // создаем таблицы баз данных, необходимые для работы модуля
            $this->InstallDB();
            // регистрируем обработчики событий
            $this->InstallEvents();
            // копируем файлы, необходимые для работы модуля
            $this->InstallFiles();
            // устанавливаем агента
            $this->installAgents();
            // проверяим ответ формы введеный пользователем на первом шаге
            if ($request["add_data"] == "Y") {
                // создаем первую и единственную запись в БД
                $this->addData();
            }
            // подключаем скрипт с административным прологом и эпилогом
            $APPLICATION->IncludeAdminFile(
                Loc::getMessage('INSTALL_TITLE_STEP_2'),
                __DIR__ . '/instalInfo-step2.php'
            );
        }
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    // метод отрабатывает при удалении модуля
    function DoUninstall()
    {
        //*************************************//
        // Пример с удалением в один шаг       //
        //*************************************//
        // // глобальная переменная с обстрактным классом
        // global $APPLICATION;
        // // удаляем таблицы баз данных, необходимые для работы модуля
        // $this->UnInstallDB();
        // // удаляем обработчики событий
        // $this->UnInstallEvents();
        // // удаляем файлы, необходимые для работы модуля
        // $this->UnInstallFiles();
        // // удаляем агента
        // $this->unInstallAgents();
        // // удаляем регистрацию модуля в системе
        // ModuleManager::UnRegisterModule("hmarketing.d7");
        // // подключаем скрипт с административным прологом и эпилогом
        // $APPLICATION->includeAdminFile(
        //     Loc::getMessage('DEINSTALL_TITLE'),
        //     __DIR__ . '/deInstalInfo.php'
        // );
        //*************************************//
        // Пример с удалением в два шага       //
        //*************************************//
        // получаем контекст и из него запросы
        $context = Application::getInstance()->getContext();
        $request = $context->getRequest();
        // глобальная переменная с обстрактным классом
        global $APPLICATION;
        // проверяем какой сейчас шаг, если он не существует или меньше 2, то выводим первый шаг удаления
        if ($request["step"] < 2) {
            // подключаем скрипт с административным прологом и эпилогом
            $APPLICATION->IncludeAdminFile(
                Loc::getMessage('DEINSTALL_TITLE_1'),
                __DIR__ . '/deInstalInfo-step1.php'
            );
        }
        // проверяем какой сейчас шаг, усли 2, производим удаление
        if ($request["step"] == 2) {
            // удаляем таблицы баз данных, необходимые для работы модуля
            //$this->UnInstallDB();
            // проверяим ответ формы введеный пользователем на первом шаге
            if ($request["save_data"] == "Y") {
                // удаляем таблицы баз данных, необходимые для работы модуля
                $this->UnInstallDB();
            }
            // удаляем обработчики событий
            $this->UnInstallEvents();
            // удаляем файлы, необходимые для работы модуля
            $this->UnInstallFiles();
            // удаляем агента
            $this->unInstallAgents();
            // удаляем регистрацию модуля в системе
            ModuleManager::UnRegisterModule("hmarketing.d7");
            // подключаем скрипт с административным прологом и эпилогом
            $APPLICATION->IncludeAdminFile(
                Loc::getMessage('DEINSTALL_TITLE_2'),
                __DIR__ . '/deInstalInfo-step2.php'
            );
        }
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    // метод для создания таблицы баз данных
    function InstallDB()
    {
        // подключаем модуль для того что бы был видем класс ORM
        Loader::includeModule($this->MODULE_ID);
        // через класс Application получаем соединение по переданному параметру, параметр берем из ORM-сущности (он указывается, если необходим другой тип подключения, отличный от default), если тип подключения по умолчанию, то параметр можно не передавать. Далее по подключению вызываем метод isTableExists, в который передаем название таблицы полученное с помощью метода getDBTableName() класса Base
        if (!Application::getConnection(\Hmarketing\d7\DataTable::getConnectionName())->isTableExists(Base::getInstance("\Hmarketing\d7\DataTable")->getDBTableName())) {
            // eсли таблицы не существует, то создаем её по ORM сущности
            Base::getInstance("\Hmarketing\d7\DataTable")->createDbTable();
        }
        if (!Application::getConnection(\Hmarketing\d7\DataTable::getConnectionName())->isTableExists(Base::getInstance("\Hmarketing\d7\AuthorTable")->getDBTableName())) {
            // eсли таблицы не существует, то создаем её по ORM сущности
            Base::getInstance("\Hmarketing\d7\AuthorTable")->createDbTable();
        }
    }
    // метод для удаления таблицы баз данных
    function UnInstallDB()
    {
        // подключаем модуль для того что бы был видем класс ORM
        Loader::includeModule($this->MODULE_ID);
        // делаем запрос к бд на удаление таблицы, если она существует, по подключению к бд класса Application с параметром подключения ORM сущности
        Application::getConnection(\Hmarketing\d7\DataTable::getConnectionName())->queryExecute('DROP TABLE IF EXISTS ' . Base::getInstance("\Hmarketing\d7\DataTable")->getDBTableName());
        Application::getConnection(\Hmarketing\d7\DataTable::getConnectionName())->queryExecute('DROP TABLE IF EXISTS ' . Base::getInstance("\Hmarketing\d7\AuthorTable")->getDBTableName());
        // удаляем параметры модуля из базы данных битрикс
        Option::delete($this->MODULE_ID);
    }
    // метод для создания обработчика событий
    function InstallEvents()
    {
        // для произвольной работы
        EventManager::getInstance()->registerEventHandler(
            // идентификатор модуля-источника события
            $this->MODULE_ID,
            // событие на которое мы подписываемся, OnSomeEvent для произвольной работы
            "OnSomeEvent",
            // идентификатор модуля, который подписывается
            $this->MODULE_ID,
            // класс выполняющий обработку (для callback-обработчика, если файловый - пустая строка)
            "\Hmarketing\d7\Main",
            // метод класса выполняющий обработку (для callback-обработчика, если файловый - пустая строка)
            'get'
        );
        // для работы с ORM, есть три типа событий: onBefore<Action> - перед вызовом запроса (можно изменить входные параметры), после следуют валидаторы. on<Action> - уже нельзя изменить входные параметры, после выполняется SQL-запрос. onAfter<Action> - после выполнения операции, операция уже совершена
        // три события <Action> итого 9 событий: Add, Update, Delete
        EventManager::getInstance()->registerEventHandler(
            // идентификатор модуля, для которого регистрируется событие
            $this->MODULE_ID,
            // тип события, класс называется DataTable, но должно передаваться по имени файла, то есть просто Data
            "\Hmarketing\d7\Data::OnBeforeUpdate",
            // идентификатор модуля к которому относится регистрируемый обработчик, из какого модуля берется класс, нужно если необходимо связать 2 модуля, если используем один, то дублируем поле с первым
            $this->MODULE_ID,
            // класс обработчика
            "\Hmarketing\d7\Events",
            // метод обработчика
            'eventHandler'
        );
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    // метод для удаления обработчика событий
    function UnInstallEvents()
    {
        // удаление событий, аналогично установке
        EventManager::getInstance()->unRegisterEventHandler(
            $this->MODULE_ID,
            "OnSomeEvent",
            $this->MODULE_ID,
            "\Hmarketing\d7\Main",
            'get'
        );
        // удаление событий, аналогично установке
        EventManager::getInstance()->unRegisterEventHandler(
            $this->MODULE_ID,
            "\Hmarketing\d7\Data::OnBeforeUpdate",
            $this->MODULE_ID,
            "\Hmarketing\d7\Events",
            'eventHandler'
        );
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    // метод для копирования файлов модуля при установке
    function InstallFiles()
    {
        // скопируем файлы на страницы админки из папки в битрикс, копирует одноименные файлы из одной директории в другую директорию
        CopyDirFiles(
            __DIR__ . "/admin",
            $_SERVER["DOCUMENT_ROOT"] . "/bitrix/admin",
            true, // перезаписывает файлы
            true  // копирует рекурсивно
        );
        // скопируем компоненты из папки в битрикс, копирует одноименные файлы из одной директории в другую директорию
        CopyDirFiles(
            __DIR__ . "/components",
            $_SERVER["DOCUMENT_ROOT"] . "/bitrix/components",
            true, // перезаписывает файлы
            true  // копирует рекурсивно
        );
        // копируем файлы страниц, копирует одноименные файлы из одной директории в другую директорию
        CopyDirFiles(
            __DIR__ . '/files',
            $_SERVER["DOCUMENT_ROOT"] . '/',
            true, // перезаписывает файлы
            true  // копирует рекурсивно
        );
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    // метод для удаления файлов модуля при удалении
    function UnInstallFiles()
    {
        // удалим файлы из папки в битрикс на страницы админки, удаляет одноименные файлы из одной директории, которые были найдены в другой директории, функция не работает рекурсивно
        DeleteDirFiles(
            __DIR__ . "/admin",
            $_SERVER["DOCUMENT_ROOT"] . "/bitrix/admin"
        );
        // удалим компонент из папки в битрикс 
        if (is_dir($_SERVER["DOCUMENT_ROOT"] . "/bitrix/components/" . $this->MODULE_ID)) {
            // удаляет папка из указанной директории, функция работает рекурсивно
            DeleteDirFilesEx(
                "/bitrix/components/" . $this->MODULE_ID
            );
        }
        // удалим файлы страниц, удаляет одноименные файлы из одной директории, которые были найдены в другой директории, функция не работает рекурсивно
        DeleteDirFiles(
            __DIR__ . "/files",
            $_SERVER["DOCUMENT_ROOT"] . "/"
        );
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    // заполнение таблиц тестовыми данными
    function addData()
    {
        // подключаем модуль для видимости ORM класса
        Loader::includeModule($this->MODULE_ID);
        // добавляем запись в таблицу БД
        \Hmarketing\d7\DataTable::add(
            array(
                "ACTIVE" => "N",
                "SITE" => '["s1"]',
                "LINK" => " ",
                "LINK_PICTURE" => "/bitrix/components/hmarketing.d7/popup.baner/templates/.default/img/banner.jpg",
                "ALT_PICTURE" => " ",
                "EXCEPTIONS" => " ",
                "DATE" => new \Bitrix\Main\Type\DateTime(date("d.m.Y H:i:s")),
                "TARGET" =>  "self",
                "AUTHOR_ID" =>  "1",
            )
        );
        // добавляем запись в таблицу БД
        \Hmarketing\d7\AuthorTable::add(
            array(
                "NAME" => "Иван",
                "LAST_NAME" => "Иванов",
            )
        );
        // для успешного завершения, метод должен вернуть true
        return true;
    }
    // установка агентов
    function installAgents()
    {
        \CAgent::AddAgent(
            // строка PHP для запуска агента-функции
            "\Hmarketing\d7\Agent::superAgent();",
            // идентификатор модуля, необходим для подключения файлов модуля (необязательный) 
            $this->MODULE_ID,
            // период, нужен для агентов, которые должны выполняться точно в срок. Если агент пропустил запуск, то он сделает его столько раз, сколько он пропустил. Если значение N, то агент после первого запуска будет запускаться с заданным интервалам (необязательный, по умолчанию N)                   
            "N",
            // интервал в секундах (необязательный, по умолчанию 86400 (сутки))                                
            120,
            // дата первой проверки (необязательный, по умолчанию текущее время)
            "",
            // активность агента (необязательный, по умолчанию Y) 
            "Y",
            // дата первого запуска (необязательный, по умолчанию текущее время)
            "",
            // сортировка (влияет на порядок выполнения агентов (очередность), для тех, которые запускаются в одно время) (необязательный, по умолчанию 100)  
            100
        );
    }
    // удаление агентов
    function unInstallAgents()
    {
        \CAgent::RemoveModuleAgents($this->MODULE_ID);
    }
}

Файл /install/instalInfo-step1.php

Файл вызывается при установки модуля из /install/index.php

Установка модуля в несколько шагов (1-й шаг).

local/modules/hmarketing.d7/install/instalInfo-step1.php<?
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
// проверка идентификатора сессии
if (!check_bitrix_sessid()) {
    return;
}
?>
<!-- выводим кнопку для перехода на страницу модулей, мы и так находимся на этой странице но с выведенным файлом, значит просто получаем текущую директорию для перенаправления -->
<form action="<?= $APPLICATION->GetCurPage() ?>">
    <!-- обязательное получение сессии -->
    <?= bitrix_sessid_post() ?>
    <!-- в форме обязательно должно быть поле lang, с айди языка, чтобы язык не сбросился -->
    <input type="hidden" name="lang" value="<?= LANGUAGE_ID ?>">
    <!-- айди модуля для установки -->
    <input type="hidden" name="id" value="hmarketing.d7">
    <!-- обязательно указывать поле install со значением Y, иначе просто перейдем на страницу модулей -->
    <input type="hidden" name="install" value="Y">
    <!-- определение шага установки модуля -->
    <input type="hidden" name="step" value="2">
    <!-- чекбокс для определния параметра -->
    <p><?= Loc::getMessage("MOD_ADD_DATA") ?></p>
    <p><input type="checkbox" name="add_data" id="add_data" value="Y" checked><label for="add_data"><?= Loc::getMessage("MOD_ADD_DATA_BUTTON") ?></label></p>
    <!-- MOD_INSTALL - системная языковая переменная для кнопки установки -->
    <input type="submit" name="" value="<?= Loc::getMessage("MOD_INSTALL") ?>">
</form>

Файл /install/instalInfo-step2.php

Файл вызывается при установки модуля из /install/index.php

Установка модуля в несколько шагов (2-й шаг).

local/modules/hmarketing.d7/install/instalInfo-step2.php<?
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
// проверка идентификатора сессии
if (!check_bitrix_sessid()) {
    return;
}
// проверяем была ли выброшена ошибка при установке, если да, то записываем её в переменную $errorException
if ($errorException = $APPLICATION->getException()) {
    // вывод сообщения об ошибке при установке модуля
    CAdminMessage::showMessage(
        Loc::getMessage('INSTALL_FAILED') . ': ' . $errorException->GetString()
    );
} else {
    // вывод уведомления при успешной установке модуля
    CAdminMessage::showNote(
        Loc::getMessage('INSTALL_SUCCESS')
    );
}
?>
<!-- выводим кнопку для перехода на страницу модулей, мы и так находимся на этой странице но с выведенным файлом, значит просто получаем текущую директорию для перенаправления -->
<form action="<?= $APPLICATION->GetCurPage() ?>">
    <!-- в форме обязательно должно быть поле lang, с айди языка, чтобы язык не сбросился -->
    <input type="hidden" name="lang" value="<?= LANGUAGE_ID ?>">
    <!-- MOD_BACK - системная языковая переменная для возврата -->
    <input type="submit" name="" value="<?= Loc::getMessage("MOD_BACK") ?>">
</form>

Файл /install/instalInfo.php

Файл вызывается при установки модуля из /install/index.php

Установка модуля в один шаг.

local/modules/hmarketing.d7/install/instalInfo.php<?
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
// проверка идентификатора сессии
if (!check_bitrix_sessid()) {
    return;
}
// проверяем была ли выброшена ошибка при установке, если да, то записываем её в переменную $errorException
if ($errorException = $APPLICATION->getException()) {
    // вывод сообщения об ошибке при установке модуля
    CAdminMessage::showMessage(
        Loc::getMessage('INSTALL_FAILED') . ': ' . $errorException->GetString()
    );
} else {
    // вывод уведомления при успешной установке модуля
    CAdminMessage::showNote(
        Loc::getMessage('INSTALL_SUCCESS')
    );
}
?>
<!-- выводим кнопку для перехода на страницу модулей, мы и так находимся на этой странице но с выведенным файлом, значит просто получаем текущую директорию для перенаправления -->
<form action="<?= $APPLICATION->GetCurPage() ?>">
    <!-- в форме обязательно должно быть поле lang, с айди языка, чтобы язык не сбросился -->
    <input type="hidden" name="lang" value="<?= LANGUAGE_ID ?>">
    <!-- MOD_BACK - системная языковая переменная для возврата -->
    <input type="submit" name="" value="<?= Loc::getMessage("MOD_BACK") ?>">
</form>

Файл /install/deInstalInfo-step1.php

Файл вызывается при установки модуля из /install/index.php

Удаление модуля в несколько шагов (1-й шаг).

local/modules/hmarketing.d7/install/deInstalInfo-step1.php<?
// пространство имен для подключений ланговых файлов
use \Bitrix\Main\Localization\Loc;
// проверка идентификатора сессии
if (!check_bitrix_sessid()) {
    return;
}
?>
<form action="<?= $APPLICATION->GetCurPage() ?>">
    <!-- обязательное получение сессии -->
    <?= bitrix_sessid_post() ?>
    <!-- в форме обязательно должно быть поле lang, с айди языка, чтобы язык не сбросился -->
    <input type="hidden" name="lang" value="<?= LANGUAGE_ID ?>">
    <!-- айди модуля для удаления -->
    <input type="hidden" name="id" value="hmarketing.d7">
    <!-- обязательно указывать поле uninstall со значением Y, иначе просто перейдем на страницу модулей -->
    <input type="hidden" name="uninstall" value="Y">
    <!-- определение шага удаления модуля -->
    <input type="hidden" name="step" value="2">
    <!-- предупреждение об удалении модуля, MOD_UNINST_WARN - системная языковая переменная -->
    <?= CAdminMessage::ShowMessage(Loc::getMessage("MOD_UNINST_WARN")) ?>
    <!-- чекбокс для определния параметра удаления, MOD_UNINST_SAVE - системная языковая переменная -->
    <p><?= Loc::getMessage("MOD_UNINST_SAVE") ?></p>
    <!-- MOD_UNINST_DATA - системная языковая переменная -->
    <p><input type="checkbox" name="save_data" id="save_data" value="Y" checked><label for="save_data"><?= Loc::getMessage("MOD_UNINST_DATA") ?></label></p>
    <!-- MOD_UNINST_DATA_BUTTON - системная языковая переменная -->
    <input type="submit" name="" value="<?= Loc::getMessage("MOD_UNINST_DATA_BUTTON") ?>">
</form>

Файл /install/deInstalInfo-step2.php

Файл вызывается при установки модуля из /install/index.php

Удаление модуля в несколько шагов (2-й шаг).

local/modules/hmarketing.d7/install/deInstalInfo-step2.php<?
// пространство имен для подключений ланговых файлов
use \Bitrix\Main\Localization\Loc;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
// проверка идентификатора сессии
if (!check_bitrix_sessid()) {
    return;
}
// метод возвращает объект класса CApplicationException, содержащий последнее исключение
if ($errorException = $APPLICATION->getException()) {
    // вывод сообщения об ошибке при удалении модуля
    CAdminMessage::showMessage(
        Loc::getMessage('DEINSTALL_FAILED') . ': ' . $errorException->GetString()
    );
} else {
    // вывод уведомления при успешном удалении модуля
    CAdminMessage::showNote(
        Loc::getMessage('DEINSTALL_SUCCESS')
    );
}
?>
<!-- выводим кнопку для перехода на страницу модулей, мы и так находимся на этой странице но с выведенным файлом, значит просто получаем текущую директорию для перенаправления -->
<form action="<?= $APPLICATION->GetCurPage() ?>">
    <!-- в форме обязательно должно быть поле lang, с айди языка, чтобы язык не сбросился -->
    <input type="hidden" name="lang" value="<?= LANGUAGE_ID ?>">
    <!-- MOD_BACK - системная языковая переменная для возврата -->
    <input type="submit" name="" value="<?= Loc::getMessage("MOD_BACK") ?>">
</form>

Файл /install/deInstalInfo.php

Файл вызывается при установки модуля из /install/index.php

Удаление модуля в один шаг.

local/modules/hmarketing.d7/install/deInstalInfo.php<?
// пространство имен для подключений ланговых файлов
use \Bitrix\Main\Localization\Loc;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
// проверка идентификатора сессии
if (!check_bitrix_sessid()) {
    return;
}
// метод возвращает объект класса CApplicationException, содержащий последнее исключение
if ($errorException = $APPLICATION->getException()) {
    // вывод сообщения об ошибке при удалении модуля
    CAdminMessage::showMessage(
        Loc::getMessage('DEINSTALL_FAILED') . ': ' . $errorException->GetString()
    );
} else {
    // вывод уведомления при успешном удалении модуля
    CAdminMessage::showNote(
        Loc::getMessage('DEINSTALL_SUCCESS')
    );
}
?>
<!-- выводим кнопку для перехода на страницу модулей, мы и так находимся на этой странице но с выведенным файлом, значит просто получаем текущую директорию для перенаправления -->
<form action="<?= $APPLICATION->GetCurPage() ?>">
    <!-- в форме обязательно должно быть поле lang, с айди языка, чтобы язык не сбросился -->
    <input type="hidden" name="lang" value="<?= LANGUAGE_ID ?>">
    <!-- MOD_BACK - системная языковая переменная для возврата -->
    <input type="submit" name="" value="<?= Loc::getMessage("MOD_BACK") ?>">
</form>

Файл /install/version.php

Файл вызывается при установки модуля из /install/index.php

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

local/modules/hmarketing.d7/install/version.php<?
$arModuleVersion = array(
   'VERSION'      => '1.0.0',
   'VERSION_DATE' => '2023-01-01 10:00:00'
);

Файлы в дириктории /lang/ru/

Файл вызывается ядром Битрикс автоматически при установке модуля.

Файлы служат для реализации переводов, в них находится простой массив $MESS в котором хранятся пары ключ=>значение перевода, часть ru в пути к файлу является указанимем на язык перевода:

/lang/ru/install/deInstalInfo-step1.php<?
$MESS["MOD_UNINST_DATA"] = "Удалить данные таблицы?";
$MESS["MOD_UNINST_DATA_BUTTON"] = "Выполнить";
/lang/ru/install/deInstalInfo-step2.php<?
$MESS['DEINSTALL_FAILED'] = 'Ошибка при удалении модуля';
$MESS['DEINSTALL_SUCCESS'] = 'Модуль успешно удален';
/lang/ru/install/deInstalInfo.php<?
$MESS['DEINSTALL_FAILED'] = 'Ошибка при удалении модуля';
$MESS['DEINSTALL_SUCCESS'] = 'Модуль успешно удален';
/lang/ru/install/index.php<?
$MESS['INSTALL_TITLE'] = 'Установка модуля';
$MESS['INSTALL_TITLE_STEP_1'] = 'Установка модуля шаг 1';
$MESS['INSTALL_TITLE_STEP_2'] = 'Установка модуля шаг 2';
$MESS['DEINSTALL_TITLE'] = 'Удаление модуля';
$MESS['DEINSTALL_TITLE_STEP_1'] = 'Удаление модуля шаг 1';
$MESS['DEINSTALL_TITLE_STEP_2'] = 'Удаление модуля шаг 2';
/lang/ru/install/instalInfo-step1.php<?
$MESS["MOD_ADD_DATA"] = "Записать тестовые данные?";
$MESS["MOD_ADD_DATA_BUTTON"] = "Выполнить";
/lang/ru/install/instalInfo-step2.php<?
$MESS['INSTALL_SUCCESS'] = 'Модуль успешно установлен';
$MESS['INSTALL_FAILED'] = 'Ошибка при установке модуля';
/lang/ru/install/instalInfo.php<?
$MESS['INSTALL_SUCCESS'] = 'Модуль успешно установлен';
$MESS['INSTALL_FAILED'] = 'Ошибка при установке модуля';
$MESS['RETURN_MODULES'] = 'Вернуться в список модулей';
/lang/ru/lib/data.php<?
$MESS["DATA_ENTITY_ID_FIELD"] = "ID";
$MESS["DATA_ENTITY_TITLE_FIELD"] = "Заголовок";
$MESS["DATA_ENTITY_SORT_FIELD"] = "Сортировка";
$MESS["DATA_ENTITY_CREATED_FIELD"] = "Дата создания";

Файл /lib/Main.php

Файл вызывается из файла /include.php, или за счет правильного расположения с автоподключением через простраство имен.

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

local/modules/hmarketing.d7/lib/Main.php<?
// пространство имен модуля
namespace Hmarketing\d7;
// пространство имен для подключения класса с ORM
use \Hmarketing\d7\DataTable;
// пространство имен для получения данных сущности таблицы по событиям
use \Bitrix\Main\Entity\Event;
// основной класс модуля
class Main
{
    // метод для получения строки из таблицы базы данных
    public static function get()
    {
        // запрос к базе
        $result = DataTable::getList(
            array(
                'select' => array('*')
            )
        );
        // преобразование запроса от базы
        $row = $result->fetch();
        // распечатываем массив с ответом на экран
        print "<pre>";
        print_r($row);
        print "</pre>";
        // возвращаем ответ от баззы
        return $row;
    }
}

Файл /lib/Agent.php

Файл вызывается из файла /include.php, или за счет правильного расположения с автоподключением через простраство имен.

Класс с методом для работы агента.

local/modules/hmarketing.d7/lib/Agent.php<?
// пространство имен модуля
namespace Hmarketing\d7;
// класс агента
class Agent
{
    // для примера функция пишет в папку модуля время
    static public function superAgent()
    {
        if (is_dir($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/hmarketing.d7/"))
            file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/hmarketing.d7/superAgentLog.txt", date("Y-m-d H:i:s"), FILE_APPEND);
        elseif (is_dir($_SERVER["DOCUMENT_ROOT"] . "/local/modules/hmarketing.d7/"))
            file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/local/modules/hmarketing.d7/superAgentLog.txt", date("Y-m-d H:i:s"), FILE_APPEND);
        // функция обязательно должна возвращать имя по которому вызывается, иначе битрикс её удаляет
        return "\Hmarketing\d7\Agent::superAgent();";
    }
}

Файл /lib/Author.php

Файл вызывается из файла /include.php, или за счет правильного расположения с автоподключением через простраство имен.

Вспомогательная таблица модуля написанная через ORM. В файле создаётся класс DataTable, который наследуется от класса Entity\DataManager, который в свою очередь реализует парадигму ORM. Для того что бы подключить наш класс к ORM, мы должны указать имя таблицы и задать структуру, для этого есть 2 метода getTableName и getMap соответственно. У Битрикс есть автогенерация класса для реализации ORM.

local/modules/hmarketing.d7/lib/Author.php<?
// пространство имен модуля
namespace Hmarketing\d7;
// пространство имен для ORM
use \Bitrix\Main\Entity;
// вторая таблица, с которой связана первая, хранит информацию об авторе
class AuthorTable extends Entity\DataManager
{
    public static function getTableName()
    {
        return "pop_up_authors";
    }
    public static function getMap()
    {
        return array(
            new Entity\IntegerField(
                "ID",
                array(
                    "primary" => true,
                    "autocomplete" => true,
                )
            ),
            new Entity\StringField(
                "NAME",
                array(
                    "required" => true,
                )
            ),
            new Entity\StringField("LAST_NAME")
        );
    }
}

Файл /lib/Data.php

Файл вызывается из файла /include.php, или за счет правильного расположения с автоподключением через простраство имен.

Основная таблица модуля написанная через ORM. В файле создаётся класс DataTable, который наследуется от класса Entity\DataManager, который в свою очередь реализует парадигму ORM. Для того что бы подключить наш класс к ORM, мы должны указать имя таблицы и задать структуру, для этого есть 2 метода getTableName и getMap соответственно. У Битрикс есть автогенерация класса для реализации ORM.

local/modules/hmarketing.d7/lib/Data.php<?
// пространство имен модуля
namespace Hmarketing\d7;
// пространство имен для ORM
use \Bitrix\Main\Entity;
// пространство имен для кеша
use \Bitrix\Main\Application;
// сущность ORM унаследованная от DataManager
class DataTable extends Entity\DataManager
{
	// название таблицы в базе данных, если не указывать данную функцию, то таблица в бд сформируется автоматически из неймспейса
	public static function getTableName()
	{
		return "pop_up_table";
	}
	// подключение к БД, если не указывать, то будет использовано значение по умолчанию подключения из файла .settings.php. Если указать, то можно выбрать подключение, которое может быть описано в .setting.php
	public static function getConnectionName()
	{
		return "default";
	}
	// метод возвращающий структуру ORM-сущности
	public static function getMap()
	{
		/*
         * Типы полей: 
         * DatetimeField - дата и время
         * DateField - дата
         * BooleanField - логическое поле да/нет
         * IntegerField - числовой формат
         * FloatField - числовой дробный формат
         * EnumField - список, можно передавать только заданные значения
         * TextField - text
         * StringField - varchar
         */
		return array(
			// ID
			new Entity\IntegerField(
				// имя сущности
				"ID",
				array(
					// первичный ключ
					"primary" => true,
					// AUTO INCREMENT
					"autocomplete" => true,
				)
			),
			// активность
			new Entity\BooleanField(
				'ACTIVE',
				array(
					"values" => array('N', 'Y')
				)
			),
			// cайты
			new Entity\StringField(
				// имя сущности
				"SITE",
				array(
					// обязательное поле
					"required" => true,
				)
			),
			// ссылка перехода
			new Entity\StringField(
				// имя сущности
				"LINK",
				array(
					// обязательное поле
					"required" => true,
				)
			),
			// ссылка на картинку
			new Entity\StringField(
				// имя сущности
				"LINK_PICTURE",
				array(
					// имя колонки в таблице
					"column_name" => "LINK_PICTURE_CODE",
					// если необходима валидация поля, то используем массив валидации, можем передать сколько угодно валидаторов, использовать как штатные, так и самописные
					"validation" => function () {
						return array(
							// первым укажем штаный валидатор проверки на уникальность поля
							new Entity\Validator\Unique,
							// вторым напишем свою функцию, которая проверит на длину строку. Аргументы функции: value - значение поля, primary - массив с первичным ключом, row - весь массив данных, переданный в ::add или ::update, field - объект валидируемого поля - Entity\StringField('LINK_PICTURE', ...)
							function ($value, $primary, $row, $field) {
								if (strlen($value) <= 100)
									return true;
								else
									return "Код LINK_PICTURE должен содержать не более 100 символов";
							}
						);
					}
				)
			),
			// описание картинки
			new Entity\StringField(
				// имя сущности
				"ALT_PICTURE",
				array(
					// обязательное поле
					"required" => true,
				)
			),
			// исключения
			new Entity\TextField(
				// имя сущности
				"EXCEPTIONS"
			),
			// дата и время заполнения
			new Entity\DatetimeField(
				// имя сущности
				"DATE",
				array(
					'required' => true,
				)
			),
			// затемнение баннера
			new Entity\EnumField(
				// имя сущности
				"TARGET",
				array(
					// значения доступные для записи
					"values" => array('self', 'blank'),
					// обязательное поле
					"required" => true,
				)
			),
			// поле для хранения айди автора, информация о которых будет храниться в другой таблице, свяжем данную таблицу с другой
			new Entity\IntegerField(
				// имя сущности
				"AUTHOR_ID"
			),
			// для связи двух таблиц, нужно будет создать поле зависимости, фактически такого поля нет в базе, оно является виртуальным
			new Entity\ReferenceField(
				// имя сущности
				"AUTHOR",
				// связываемая сущность другой таблицы
				'\Hmarketing\d7\AuthorTable',
				// this - текущая сущность, ref - связываемая    
				array("=this.AUTHOR_ID" => "ref.ID")
			),
		);
	}
	// // события можно задавать прямо в ORM-сущности, для примера запретим изменять поле LINK_PICTURE
	// public static function onBeforeUpdate(Entity\Event $event)
	// {
	// 	$result = new Entity\EventResult;
	// 	$data = $event->getParameter("fields");
	// 	if (isset($data["LINK_PICTURE"])) {
	// 		$result->addError(
	// 			new Entity\FieldError(
	// 				$event->getEntity()->getField("LINK_PICTURE"),
	// 				"Запрещено менять LINK_PICTURE код у баннера"
	// 			)
	// 		);
	// 	}
	// 	return $result;
	// }
	// очистка тегированного кеша при добавлении
	public static function onAfterAdd(Entity\Event $event)
	{
		DataTable::clearCache();
	}
	// очистка тегированного кеша при изменении
	public static function onAfterUpdate(Entity\Event $event)
	{
		DataTable::clearCache();
	}
	// очистка тегированного кеша при удалении
	public static function onAfterDelete(Entity\Event $event)
	{
		DataTable::clearCache();
	}
	// основной метод очистки кеша по тегу
	public static function clearCache()
	{
		// служба пометки кеша тегами
		$taggedCache = Application::getInstance()->getTaggedCache();
		$taggedCache->clearByTag('popup');
	}
}

Файл /lib/Events.php

Файл вызывается из файла /include.php, или за счет правильного расположения с автоподключением через простраство имен.

Файл в котором описан класс события.

local/modules/hmarketing.d7/lib/Events.php<?
// пространство имен модуля
namespace Hmarketing\d7;
// пространство имен для получения данных сущности таблицы по событиям
use \Bitrix\Main\Entity\Event;
// класс события
class Events
{
    // для примера выводит поля при каком-либо действии (в регистраторе задано перед добавлением)
    static public function eventHandler(Event $event)
    {
        $fields = $event->getParameter("fields");
        echo "<pre>";
        echo "Обработчик события";
        var_dump($fields);
        echo "</pre>";
    }
}

файл /include.php

Файл вызывается ядром Битрикс автоматически.

Файл необходим для подключения классов или дополнительных файлов модуля. Если соблюдать соответствие namespace и имени модуля, то поддерживается автозагрузка класса. Автозагрузка работает медленнее, чем поключение файла в include.php.

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

local/modules/hmarketing.d7/include.php<?
Bitrix\Main\Loader::registerAutoloadClasses(
	// имя модуля
	"hmarketing.d7",
	array(
		// ключ - имя класса с простанством имен, значение - путь относительно корня сайта к файлу
		"hmarketing\\d7\\Main" => "lib/Main.php",
		// файл инклудится за счет правильных имен, иначе будет ошибка при установке и удаленни модуля
		//"Hmarketing\\d7\\DataTable" => "lib/data.php",
	)
);

Пример автозагрузки класса

Рассмотрим пример автозагрузки классов, сам класс лежит в файле asd.metrika/lib/counters.php:

asd.metrika/lib/counters.phpnamespace Asd\Metrika;
class CountersTable extends Entity\DataManager
{
public static function update()
{
}
}

Данный класс в /lib/ принадлежит модулю asd.metrika, к нему после подключения указанного модуля можно обращаться так:

\Asd\Metrika\CountersTable::update();

файл /options.php

Файл вызывается ядром Битрикс автоматически при установке модуля.

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

В панели Битрикс сама страница с настройками находится по адресу Настройки -> Настройки модулей -> Имя_модуля:

На этой странице можно настроить дуступы для разных групп пользователей.

local/modules/hmarketing.d7/options.php<?
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// пространство имен для получения ID модуля
use Bitrix\Main\HttpApplication;
// пространство имен для загрузки необходимых файлов, классов, модулей
use Bitrix\Main\Loader;
// пространство имен для работы с параметрами модулей хранимых в базе данных
use Bitrix\Main\Config\Option;
// подключение ланговых файлов
Loc::loadMessages(__FILE__);
// получение запроса из контекста для обработки данных
$request = HttpApplication::getInstance()->getContext()->getRequest();
// получаем id модуля
$module_id = htmlspecialcharsbx($request["mid"] != "" ? $request["mid"] : $request["id"]);
// получим права доступа текущего пользователя на модуль
$POST_RIGHT = $APPLICATION->GetGroupRight($module_id);
// если нет прав - отправим к форме авторизации с сообщением об ошибке
if ($POST_RIGHT < "S") {
    $APPLICATION->AuthForm(Loc::getMessage("ACCESS_DENIED"));
}
// подключение модуля
Loader::includeModule($module_id);
// настройки модуля для админки в том числе значения по умолчанию
$aTabs = array(
    array(
        // значение будет вставленно во все элементы вкладки для идентификации (используется для javascript)
        "DIV" => "edit1",
        // название вкладки в табах 
        "TAB" => "Название вкладки в табах",
        // заголовок и всплывающее сообщение вкладки
        "TITLE" => "Главное название в админке",
        // массив с опциями секции
        "OPTIONS" => array(
            "Название секции checkbox",
            array(
                // имя элемента формы, для хранения в бд
                "hmarketing_checkbox",
                // поясняющий текст
                "Поясняющий текс элемента checkbox",
                // значение по умолчани, значение checkbox по умолчанию "Да"
                "Y",
                // тип элемента формы "checkbox"
                array("checkbox"),
            ),
            "Название секции text",
            array(
                // имя элемента формы, для хранения в бд
                "hmarketing_text",
                // поясняющий текст
                "Поясняющий текс элемента text",
                // значение по умолчани, значение text по умолчанию "50"
                "Жми!",
                // тип элемента формы "text", ширина, высота
                array(
                    "text",
                    10,
                    50
                )
            ),
            "Название секции selectbox",
            array(
                // имя элемента формы, для хранения в бд
                "hmarketing_selectbox",
                // поясняющий текст
                "Поясняющий текс элемента selectbox",
                // значение по умолчани, значение selectbox по умолчанию "left"
                "460",
                // тип элемента формы "select"
                array("selectbox", array(
                    // доступные значения
                    "460" => "460Х306",
                    "360" => "360Х242",
                ))
            ),
            "Название секции multiselectbox",
            array(
                // имя элемента формы, для хранения в бд
                "hmarketing_multiselectbox",
                // поясняющий текст
                "Поясняющий текс элемента multiselectbox",
                // значение по умолчани, значение selectbox по умолчанию "left"
                "left, bottom",
                // тип элемента формы "multi select"
                array("multiselectbox", array(
                    // доступные значения
                    "left" => "Лево",
                    "right" => "Право",
                    "top" => "Верх",
                    "bottom" => "Низ",
                ))
            )
        )
    ),
    array(
        // значение будет вставленно во все элементы вкладки для идентификации (используется для javascript)
        "DIV"   => "edit2",
        // название вкладки в табах из основного языкового файла битрикс
        "TAB" => Loc::getMessage("MAIN_TAB_RIGHTS"),
        // заголовок и всплывающее сообщение вкладки из основного языкового файла битрикс
        "TITLE" => Loc::getMessage("MAIN_TAB_TITLE_RIGHTS")
    )
);
// проверяем текущий POST запрос и сохраняем выбранные пользователем настройки
if ($request->isPost() && check_bitrix_sessid()) {
    // цикл по вкладкам
    foreach ($aTabs as $aTab) {
        // цикл по заполненым пользователем данным
        foreach ($aTab["OPTIONS"] as $arOption) {
            // если это название секции, переходим к следующий итерации цикла
            if (!is_array($arOption)) {
                continue;
            }
            // проверяем POST запрос, если инициатором выступила кнопка с name="Update" сохраняем введенные настройки в базу данных
            if ($request["Update"]) {
                // получаем в переменную $optionValue введенные пользователем данные
                $optionValue = $request->getPost($arOption[0]);
                // метод getPost() не работает с input типа checkbox, для работы сделал этот костыль
                if ($arOption[0] == "hmarketing_checkbox") {
                    if ($optionValue == "") {
                        $optionValue = "N";
                    }
                }
                // устанавливаем выбранные значения параметров и сохраняем в базу данных, хранить можем только текст, значит если приходит массив, то разбиваем его через запятую, если не массив сохраняем как есть
                Option::set($module_id, $arOption[0], is_array($optionValue) ? implode(",", $optionValue) : $optionValue);
            }
            // проверяем POST запрос, если инициатором выступила кнопка с name="default" сохраняем дефолтные настройки в базу данных 
            if ($request["default"]) {
                // устанавливаем дефолтные значения параметров и сохраняем в базу данных
                Option::set($module_id, $arOption[0], $arOption[2]);
            }
        }
    }
}
// отрисовываем форму, для этого создаем новый экземпляр класса CAdminTabControl, куда и передаём массив с настройками
$tabControl = new CAdminTabControl(
    "tabControl",
    $aTabs
);
// отображаем заголовки закладок
$tabControl->Begin();
?>
<form action="<? echo ($APPLICATION->GetCurPage()); ?>?mid=<? echo ($module_id); ?>&lang=<? echo (LANG); ?>" method="post">
    <? foreach ($aTabs as $aTab) {
        if ($aTab["OPTIONS"]) {
            // завершает предыдущую закладку, если она есть, начинает следующую
            $tabControl->BeginNextTab();
            // отрисовываем форму из массива
            __AdmSettingsDrawList($module_id, $aTab["OPTIONS"]);
        }
    }
    // завершает предыдущую закладку, если она есть, начинает следующую
    $tabControl->BeginNextTab();
    // выводим форму управления правами в настройках текущего модуля
    require_once $_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/admin/group_rights.php";
    // подключаем кнопки отправки формы
    $tabControl->Buttons();
    // выводим скрытый input с идентификатором сессии
    echo (bitrix_sessid_post());
    // выводим стандартные кнопки отправки формы
    ?>
    <input class="adm-btn-save" type="submit" name="Update" value="Применить" />
    <input type="submit" name="default" value="По умолчанию" />
</form>
<?
// обозначаем конец отрисовки формы
$tabControl->End();
// // пример получения значения из настроек модуля конкретного поля
// $op = \Bitrix\Main\Config\Option::get(
//     // ID модуля, обязательный параметр
//     "hmarketing.d7",
//     // имя параметра, обязательный параметр
//     "hmarketing_multiselectbox",
//     // возвращается значение по умолчанию, если значение не задано
//     "",
//     // ID сайта, если значение параметра различно для разных сайтов
//     false
// );
// // пример получения значения из настроек модуля всех полей
// $op = \Bitrix\Main\Config\Option::getForModule("hmarketing.d7");
// остальные команды https://dev.1c-bitrix.ru/api_d7/bitrix/main/config/option/index.php
Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!
Эйч Маркетинг