Создание своей сущности. Упаковываем модуль, создаем таблицы и ORM-сущности. Часть 1.

Авг 31, 2025 • 6 минута
Создание своей сущности. Упаковываем модуль, создаем таблицы и ORM-сущности. Часть 1.

Начнем с создания модуля. Структура модуля планируется следующая:

├── .settings.php
├── install
│   ├── files
│   ├── index.php
│   └── version.php
├── lang
│   └── ru
└── lib
├── Access
├── Controller
├── Dto
├── Entities
├── Handler
├── Migrations
├── Service
├── TagSelector
└── Validator

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

  • Файл .settings.php —  файл настроек, описывающий настройки модуля.
  • Папка install содержит файлы, необходимые для установки модуля.
    • install.php — файл с описанием модуля. Также содержит необходимые инструкции для установки.
    • папка files содержит файлы компонентов, js расширений, а также файлы для публичной части. Эти файлы будут скопированы в bitrix/components, bitrix/js и в публичную часть портала соответственно.
    • version.php — файл с версией и датой выхода модуля.
  • Папка lang содержит языковые файлы.
  • Папка lib содержит классы модуля. Например, в Controller будут расположены контроллеры модуля, в Handler — обработчики событий, в Entities — ORM-модели для таблиц модуля, Migrations — классы для создания таблиц и демо-данных.

Прочитать настройки из .settings.php можно следующим образом:


\Bitrix\Main\Config\Configuration::getInstance($module)

Файл install.php будет состоять из нижеперечисленных методов:

<?php  
  
use Bitrix\Main\Loader;  
use Bitrix\Main\Localization\Loc;  
use Bitrix\Main\ModuleManager;  
use Chipmunk\News\Migrations\AddTestNews;  
use Chipmunk\News\Migrations\CreateNewsTable;  
use Chipmunk\News\Migrations\CreateNewsTagsTable;  
use Chipmunk\News\Migrations\CreateTagsTable;  
  
defined('B_PROLOG_INCLUDED') || die;  
  
final class chipmunk_news extends CModule  
{  
    public function __construct()  
    {  
        $this->MODULE_ID = 'chipmunk.news';  
        $this->MODULE_NAME = Loc::getMessage('CHIPMUNK.NEWS_NAME');  
        $this->MODULE_DESCRIPTION = Loc::getMessage('CHIPMUNK.NEWS_DESCR');  
        $this->PARTNER_NAME = Loc::getMessage('CHIPMUNK.NEWS_PARTNER_NAME');  
        $this->PARTNER_URI = Loc::getMessage('CHIPMUNK.NEWS_PARTNER_URL');  
  
        $version = include __DIR__ . '/version.php';  
  
        $this->MODULE_VERSION = $version['MODULE_VERSION'];  
        $this->MODULE_VERSION_DATE = $version['MODULE_VERSION_DATE'];  
    }  
  
    public function DoInstall(): bool  
    {  
        global $APPLICATION;  
  
        $application = \Bitrix\Main\Application::getInstance();  
        $connection = $application->getConnection();  
        try {  
            $connection->startTransaction();  
            ModuleManager::registerModule($this->MODULE_ID);  
            Loader::requireModule($this->MODULE_ID);  
            $this->installMigrations();  
            $connection->commitTransaction();  
        } catch (\Exception $e) {  
            $connection->rollbackTransaction();  
            $this->unInstallMigrations();  
            $APPLICATION->ThrowException(Loc::getMessage('CHIPMUNK.NEWS_INSTALL_ERROR') . $e->getMessage());  
            ModuleManager::unRegisterModule($this->MODULE_ID);  
            return false;  
        }  
        return true;  
    }  
  
    public function installMigrations(): void  
    {  
        foreach ($this->getMigrations() as $migration) {  
            $migration->up();  
        }  
    }  
  
    public function DoUninstall(): bool  
    {  
        global $APPLICATION;  
        Loader::requireModule($this->MODULE_ID);  
  
        $application = \Bitrix\Main\Application::getInstance();  
        $connection = $application->getConnection();  
        try {  
            $connection->startTransaction();  
            $this->unInstallMigrations();  
            $connection->commitTransaction();  
        } catch (\Exception $e) {  
            $connection->rollbackTransaction();  
            $APPLICATION->ThrowException("Ошибка удаления модуля: " . $e->getMessage());  
            return false;  
        } finally {  
            ModuleManager::unRegisterModule($this->MODULE_ID);  
            Loader::clearModuleCache($this->MODULE_ID);  
        }  
        return true;  
    }  
  
    public function unInstallMigrations(): void  
    {  
        $migrations = array_reverse($this->getMigrations());  
        foreach ($migrations as $migration) {  
            $migration->down();  
        }  
    }  
  
    public function UnInstallDB(): void  
    {  
        Loader::requireModule($this->MODULE_ID);  
  
        $this->unInstallMigrations();  
  
        ModuleManager::unRegisterModule($this->MODULE_ID);  
  
    }  
    public function getMigrations(): array  
    {  
        return [  
            new CreateNewsTable(),  
            new CreateTagsTable(),  
            new CreateNewsTagsTable(),  
            new AddTestNews()  
        ];  
    }  
}

Пошаговое описание каждого метода класса chipmunk_news:

  • __construct() — конструктор класса, инициализирует основные свойства модуля
    • MODULE_ID – идентификатор модуля (chipmunk.news)
    • MODULE_NAME – название модуля (из языковых файлов)
    • MODULE_DESCRIPTION – описание модуля (из языковых файлов)
    • PARTNER_NAME – название партнёра (из языковых файлов)
    • PARTNER_URI – URL партнёра ( из языковых файлов)
    • MODULE_VERSION и MODULE_VERSION_DATE – версия и дата версии модуля (из файла version.php)
  • DoInstall(): bool — основной метод установки модуля
    • Начинает транзакцию БД
    • Регистрирует модуль в системе через ModuleManager::registerModule()
    • Подключает модуль через Loader::requireModule()
    • Вызывает installMigrations() для применения миграций
    • Фиксирует транзакцию (commitTransaction())
    • В случае ошибки:
      • Откатывает транзакцию (rollbackTransaction())
      • Отменяет миграции (unInstallMigrations())
      • Выводит сообщение об ошибке
      • — Удаляет модуль (ModuleManager::unRegisterModule())
  • installMigrations(): void — накатывает миграции
    • Получает список миграций через getMigrations()
    • Для каждой миграции вызывает метод up() (создание таблиц/заполнение данными)
  • DoUninstall(): bool — основной метод удаления модуля
    • Подключает модуль через Loader::requireModule()
    • Начинает транзакцию БД
    • Вызывает unInstallMigrations() для отката миграций
    • Фиксирует транзакцию (commitTransaction())
    • В случае ошибки:
      • Откатывает транзакцию (rollbackTransaction())
      • Выводит сообщение об ошибке
      • Возвращает false
    • В любом случае:
      • Удаляет модуль (ModuleManager::unRegisterModule())
  • unInstallMigrations(): void — откатывает все миграции
    • Получает список миграций через getMigrations() (в обратном порядке)
    • Для каждой миграции вызывает метод down() (удаление таблиц/данных)
  • getMigrations(): array — возвращает массив объектов миграций в порядке их выполнения
    • CreateNewsTable – создание таблицы новостей
    • CreateTagsTable – создание таблицы тегов
    • CreateNewsTagsTable – создание связующей таблицы новости-теги
    • AddTestNews – добавление тестовых данных

Для начала работы над модулем нам понадобятся 3 таблицы и ORM модели к ним:

1. Таблица «Новости»

Класс-миграция для создания таблицы:

<?php  
  
namespace Chipmunk\News\Migrations;  
  
use Bitrix\Main\Application;  
use Bitrix\Main\Entity\Base;  
use Chipmunk\News\Entities\NewsTable;  
  
class CreateNewsTable  
{  
    public static function up(): void  
    {  
        $connection = Application::getConnection();  
  
        if (!$connection->isTableExists(NewsTable::getTableName())) {  
            try {  
                Base::getInstance(NewsTable::class)->createDbTable();  
            } catch (\Exception $e) {  
                throw new \Exception($e);  
            }  
        }  
    }  
  
    public static function down(): void  
    {  
        $connection = Application::getConnection();  
        try {  
            $connection->queryExecute('DROP TABLE IF EXISTS ' . NewsTable::getTableName());  
        }  
        catch (\Exception $e) {  
            throw new \Exception($e);  
        }  
    }  
}

Описание класса CreateNewsTable и его методов:

up(): void

Назначение:
Создает таблицу новостей в БД, если она не существует.

Логика работы:

  1. Получает подключение к БД через Application::getConnection().
  2. Проверяет существование таблицы с помощью $connection->isTableExists(), используя имя таблицы из NewsTable::getTableName().
  3. Если таблицы нет:
    • Создает ее через ORM-механизм
    • Base::getInstance(NewsTable::class)->createDbTable().
  4. В случае ошибки пробрасывает исключение.

Пример вызова:
Выполняется при установке модуля или применении миграций.

down(): void

Назначение:
Удаляет таблицу новостей из БД.

Логика работы:

  1. Получает подключение к БД.
  2. Выполняет SQL-запрос на удаление таблицы:
    DROP TABLE IF EXISTS <имя_таблицы>.
  3. В случае ошибки пробрасывает исключение.

Пример вызова:
Выполняется при удалении модуля или откате миграций.

ORM класс таблицы «Новости»

<?  
  
namespace Chipmunk\News\Entities;  
  
use Bitrix\Main\Entity;  
use Bitrix\Main\Type;  
use Bitrix\Main\ORM\Fields\Relations\ManyToMany;  
use Bitrix\Main\UserTable;  
  
class NewsTable extends Entity\DataManager  
{  
    public static function getTableName()  
    {  
        return 'b_chipmunk_news';  
    }  
  
    public static function getMap()  
    {  
        return [  
            (new Entity\IntegerField('ID'))  
                ->configureAutocomplete()  
                ->configurePrimary(),  
  
            (new Entity\StringField('TITLE', [  
                'validation' => function () {  
                    return [  
                        new Entity\Validator\Length(null, 255)  
                    ];  
                }  
            ]))->configureRequired(),  
  
            (new Entity\TextField('TEXT'))->configureRequired(),  
  
            (new Entity\IntegerField('IMAGE_ID'))->configureRequired(),  
  
            (new Entity\IntegerField('AUTHOR_ID'))->configureRequired(),  
  
            new Entity\ReferenceField(  
                'AUTHOR',  
                UserTable::class,  
                ['=this.AUTHOR_ID' => 'ref.ID'],  
                ['join_type' => 'LEFT']  
            ),  
  
            (new Entity\DatetimeField('DATE_CREATE'))  
                ->configureDefaultValue(new Type\DateTime),  
  
            (new Entity\DatetimeField('DATE_UPDATE')),  
            (new ManyToMany('TAGS', TagTable::class))  
                ->configureTableName('b_chipmunk_news_tags')  
                ->configureLocalPrimary('ID', 'NEWS_ID')  
                ->configureLocalReference('NEWS')  
                ->configureRemotePrimary('ID', 'TAG_ID')  
                ->configureRemoteReference('TAG'),  
  
        ];  
    }  
}

Ниже детальное описание класса NewsTable и его методов:

getTableName(): string

Назначение:
Возвращает имя таблицы в базе данных.

Возвращаемое значение:
'b_chipmunk_news' — таблица будет создана с этим именем в БД.

getMap(): array

Назначение:
Определяет структуру таблицы и связи между сущностями.

Возвращаемое значение:
Массив с описанием полей таблицы и связей.

Описание полей:

ID:

  • Тип: IntegerField
  • Настройки:
    • configureAutocomplete() — автоинкремент
    • configurePrimary() — первичный ключ

TITLE:

  • Тип: StringField
  • Настройки:
    • Валидация длины до 255 символов
    • configureRequired() — обязательное поле

TEXT:

  • Тип: TextField
  • Настройки: configureRequired()

IMAGE_ID:

  • Тип: IntegerField
  • Настройки: configureRequired()

AUTHOR_ID:

  • Тип: IntegerField
  • Настройки: configureRequired()

Связь AUTHOR:

  • Тип: ReferenceField
  • Связывает с таблицей пользователей (UserTable)
  • Условие связи: AUTHOR_ID = ID из UserTable
  • Тип соединения: LEFT JOIN

DATE_CREATE:

  • Тип: DatetimeField
  • Настройки:
    • Значение по умолчанию: текущая дата/время

DATE_UPDATE:

  • Тип: DatetimeField
  • Без значения по умолчанию

Связь TAGS (ManyToMany):

  • Связь многие-ко-многим с таблицей тегов (TagTable)
  • Использует промежуточную таблицу b_chipmunk_news_tags
  • Локальный ключ: ID (из NewsTable) → NEWS_ID (в промежуточной таблице)
  • Внешний ключ: ID (из TagTable) → TAG_ID (в промежуточной таблице)

2. Таблица «Теги»

Класс-миграция для создания таблицы

<?php  
  
namespace Chipmunk\News\Migrations;  
  
use Bitrix\Main\Application;  
use Bitrix\Main\Entity\Base;  
use Chipmunk\News\Entities\TagTable;  
  
class CreateTagsTable  
{  
    public static function up(): void  
    {  
        $conn = Application::getConnection();  
  
        if (!$conn->isTableExists(TagTable::getTableName())) {  
            try {  
                Base::getInstance(TagTable::class)->createDbTable();  
            } catch (\Exception $e) {  
                throw new \Exception($e->getMessage());  
            }  
        }  
    }  
  
    public static function down(): void  
    {  
        $conn = Application::getConnection();  
        try {  
            $conn->queryExecute('DROP TABLE IF EXISTS ' . TagTable::getTableName());  
        } catch (\Exception $e) {  
            throw new \Exception($e->getMessage());  
        }  
    }  
}

ORM класс таблицы

<?  
  
namespace Chipmunk\News\Entities;  
  
use Bitrix\Main\Entity;  
use Bitrix\Main\ORM\Fields\Relations\ManyToMany;  
  
class TagTable extends Entity\DataManager  
{  
    public static function getTableName()  
    {  
        return 'b_chipmunk_tags';  
    }  
  
    public static function getMap()  
    {  
        return [  
            (new Entity\IntegerField('ID'))  
                ->configurePrimary()  
                ->configureAutocomplete(),  
  
            (new Entity\StringField('NAME', [  
                 'validation' => function() {  
                    return [  
                        new Entity\Validator\Length(null, 255)  
                    ];  
                }  
            ]))->configureRequired(),  
  
            (new ManyToMany('NEWS', NewsTable::class))  
                ->configureTableName('b_chipmunk_news_tags')  
                ->configureLocalPrimary('ID', 'TAG_ID')  
                ->configureLocalReference('TAG')  
                ->configureRemotePrimary('ID', 'NEWS_ID')  
                ->configureRemoteReference('NEWS')  
  
        ];  
    }  
}

3. Таблица «Новости и теги»

Класс-миграция для создания таблицы

<?php  
  
namespace Chipmunk\News\Migrations;  
  
use Bitrix\Main\Application;  
  
class CreateNewsTagsTable  
{  
    protected const TABLE_NAME = 'b_chipmunk_news_tags';  
  
    public static function up(): void  
    {  
        $conn = Application::getConnection();  
  
        if (!$conn->isTableExists(self::TABLE_NAME)) {  
            try {  
                $conn->queryExecute("  
                    CREATE TABLE " . self::TABLE_NAME . " (  
                        NEWS_ID INT NOT NULL,                        TAG_ID INT NOT NULL,                        PRIMARY KEY (NEWS_ID, TAG_ID),  
                        FOREIGN KEY (NEWS_ID) REFERENCES b_chipmunk_news(ID) ON DELETE CASCADE,  
                        FOREIGN KEY (TAG_ID) REFERENCES b_chipmunk_tags(ID) ON DELETE CASCADE  
                    )                ");  
            } catch (\Exception $e) {  
                throw new \Exception($e);  
            }  
        }  
    }  
  
    public static function down(): void  
    {  
        $conn = Application::getConnection();  
        try {  
            $conn->queryExecute('DROP TABLE IF EXISTS ' . self::TABLE_NAME);  
        } catch (\Exception $e) {  
            throw new \Exception($e);  
        }  
    }  
}

up(): void

  1. Получает соединение с БД
  2. Проверяет отсутствие таблицы (isTableExists())
  3. Создает таблицу с указанной структурой

Структура таблицы:

  • NEWS_ID: INT, NOT NULL (внешний ключ к таблице новостей)
  • TAG_ID: INT, NOT NULL (внешний ключ к таблице тегов)
  • Составной первичный ключ: (NEWS_ID, TAG_ID)
  • Внешние ключи с каскадным удалением:
    • NEWS_IDb_chipmunk_news(ID)
    • TAG_IDb_chipmunk_tags(ID)

down(): void

  1. Получает соединение с БД
  2. Удаляет таблицу (с проверкой существования)
  3. Обрабатывает возможные ошибки

Скачать код из статьи

Похожие статьи

Создание своей сущности в Битрикс24
Cоздадим свою сущность в Битрикс24 с гридом, CRUD, своим провайдером для tagSelector, массовым редактирование записей и не только
Настройка прав доступа к редактированию структуры компании в Битрикс 24
Подробная инструкция по настройке прав доступа к редактированию организационной структуры в Битрикс24. Узнайте, как назначать роли пользователям, управлять отделами и сотрудниками, настраивать уровни доступа для администраторов, HR и руководителей