Создание своей сущности. Упаковываем модуль, создаем таблицы и 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
Назначение:
Создает таблицу новостей в БД, если она не существует.
Логика работы:
- Получает подключение к БД через
Application::getConnection(). - Проверяет существование таблицы с помощью
$connection->isTableExists(), используя имя таблицы изNewsTable::getTableName(). - Если таблицы нет:
- Создает ее через ORM-механизм
Base::getInstance(NewsTable::class)->createDbTable().
- В случае ошибки пробрасывает исключение.
Пример вызова:
Выполняется при установке модуля или применении миграций.
down(): void
Назначение:
Удаляет таблицу новостей из БД.
Логика работы:
- Получает подключение к БД.
- Выполняет SQL-запрос на удаление таблицы:
DROP TABLE IF EXISTS <имя_таблицы>. - В случае ошибки пробрасывает исключение.
Пример вызова:
Выполняется при удалении модуля или откате миграций.
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
- Получает соединение с БД
- Проверяет отсутствие таблицы (
isTableExists()) - Создает таблицу с указанной структурой
Структура таблицы:
- NEWS_ID: INT, NOT NULL (внешний ключ к таблице новостей)
- TAG_ID: INT, NOT NULL (внешний ключ к таблице тегов)
- Составной первичный ключ: (NEWS_ID, TAG_ID)
- Внешние ключи с каскадным удалением:
NEWS_ID→b_chipmunk_news(ID)TAG_ID→b_chipmunk_tags(ID)
down(): void
- Получает соединение с БД
- Удаляет таблицу (с проверкой существования)
- Обрабатывает возможные ошибки
Скачать код из статьи
Похожие статьи