Проблема большинства проектов заключается в отсутствии ясности. Я хочу показать как потратив немного времени можно внести эту ясность. Давайте разберем структуру компонента, которую я предлагаю использовать в проекте BoolRental и способ работы с сущностями.
Стандартный датаменеджер предоставляет исчерпывающий функционал для работы с сущностями, но мне не нравится, что объект сущности не является таковым в классическом смысле.
Я предпочитаю работать с элементами предметной области в явном виде, классы таблиц использовать в качестве репозиториев, а операции вставки, обновления и удаления поручить менеджеру управления сущностями.
Шаг 1 создание сущностей
Как сказано выше, я предпочитаю работать с конкретными объектами, название и свойства которых соответствуют элементам предметной области и не содержат ничего лишнего.
Рассмотрим структуру файлов компонента с которой будем работать:
- local/php_interface/classes/Aclips/BookRental/
-- Entity
---- Element.php
---- ...
- Internal
---- ElementTable.php
---- ...
В директории Entity хранятся классы сущностей;
В Internal — классы, описывающие таблицы сущностей.
Файлам из директории Internal была посвящена предыдущая статья, а вот содержимое Entity я хочу показать на примере сущности «издание»
<?php namespace Aclips\BookRental\Entity; /** * Class Edition * Сущность Издание * @package Aclips\BookRental\Entity */ class Edition { /** * Идентификатор издания * @var int|null */ public ?int $id = null; /** * Название издания * @var string|null */ public ?string $name = null; /** * Год публикации * @var int|null */ public ?integer $publishingYear = null; /** * Идентификатор издательства * @var int|null */ public ?integer $publishingHouseId = null; /** * Идентификатор жанра * @var int|null */ public ?int $genreId = null; /** * Идентификатор файла обложки * @var int|null */ public ?int $bookCoverId = null; /** * Описание издания * @var string|null */ public ?string $description = null; }
Обновлённое описание EditionTable.php
<?php namespace Aclips\BookRental\Internal; use Bitrix\Main\ORM\Data\DataManager; use Bitrix\Main\ORM\Fields\IntegerField; use Bitrix\Main\ORM\Fields\StringField; use Bitrix\Main\ORM\Fields\TextField; class EditionTable extends DataManager { public static function getTableName() { return "a_bookrental_editions"; } public static function getMap() { return [ (new IntegerField('ID')) ->configurePrimary() ->configureAutocomplete(), (new StringField('NAME')) ->configureRequired(), (new IntegerField('PUBLISHING_YEAR')) ->configureRequired(), (new IntegerField('PUBLISHING_HOUSE_ID')) ->configureRequired(), (new IntegerField('GENRE_ID')) ->configureRequired(), (new IntegerField('BOOK_COVER_ID')), (new TextField('DESCRIPTION')), ]; } }
Шаг 2 установка bitrix-entity-manager
В качестве менеджера управления сущностями я использую библиотеку enoffspb/bitrix-entity-manager, разработанную совместно с коллегой по цеху.
На момент написания статьи библиотеку еще не добавили в composer. Установка из github репозитория через composer выглядит следующим образом:
Файл local/php_interface/composer.json
{ "name": "aclips/bookRental", "type": "project", "repositories": [ { "type": "vcs", "url": "https://github.com/enoffspb/bitrix-entity-manager" } ], "require": { "enoffspb/bitrix-entity-manager": "dev-main" }, "require-dev": { "phpunit/phpunit": "^9.5" } }
UPD библиотека добавлена в composer
{ "name": "aclips/bookRental", "type": "project", "require": { "enoffspb/bitrix-entity-manager": "dev-main" }, "require-dev": { "phpunit/phpunit": "^9.5" } }
Устанавливаем пакеты в директории local/php_interface
composer install
или
php ./composer.phar install
в зависимости от того, как был установлен composer.
Установка через composer: пакет enoffspb/bitrix-entity-manager (версия dev-main)
Шаг 3 конфигурирование проекта
Сущности готовы, репозитории созданы, EntityManager установлен. Теперь нужно все это собрать воедино.
Чтобы не собирать конфигурационный массив и не создавать объект EntityManager в коде всякий раз, когда нам потребуется выполнить какую-либо операцию над сущностью я создам функцию, я оберну весь процесс в функцию и буду обращаться к ней.
use enoffspb\BitrixEntityManager\EntityManagerInterface; use enoffspb\BitrixEntityManager\BitrixEntityManager; /** * Получение EntityManager для работы с сущностями BookRental * @return BitrixEntityManager */ function getlEntityManager(): EntityManagerInterface { $entitiesConfig = [ \Aclips\BookRental\Entity\Edition::class => [ 'tableClass' => \Aclips\BookRental\Internal\EditionTable::class, ], ]; $config['entitiesConfig'] = $entitiesConfig; return new BitrixEntityManager($config); }
Начиная с версии 20.5.400 в bitrix24 появился сервис локатор, который идеально подходит для инициализации сервисов, в т.ч. менеджера управления сущностями.
В статье «Как я настраиваю проект после установки» я упоминал про регистрацию служб в сервис локаторе при рассмотрении структуры директории php_interface.
В нашем случае файл kernel.php примет следующий вид:
<?php use Bitrix\Main\DI\ServiceLocator; if (class_exists('\Bitrix\Main\DI\ServiceLocator')) { $serviceLocator = ServiceLocator::getInstance(); $serviceLocator->addInstanceLazy('aclips.bookrental.entityManager', [ 'constructor' => static function () use ($serviceLocator) { $entitiesConfig = [ \Aclips\BookRental\Entity\Edition::class => [ 'tableClass' => \Aclips\BookRental\Internal\EditionTable::class, ], ]; $config['entitiesConfig'] = $entitiesConfig; return new \enoffspb\BitrixEntityManager\BitrixEntityManager($config); } ]); }
Проверим, что все в порядке на примере создания нового элемента
use Bitrix\Main\DI\ServiceLocator; use Aclips\BookRental\Entity\Edition; $serviceLocator = ServiceLocator::getInstance(); // Получение сервиса $entityManager = $serviceLocator->get('aclips.bookrental.entityManager'); // Создание объекта "издание" $edition = new Edition(); // Заполняем свойства объекта $edition->name = 'Котенок Шмяк и большая тыква'; $edition->publishingYear = 2020; $edition->publishingHouseId = 1; $edition->genreId = 3; $edition->description = 'Приключения котёнка Шмяка...'; // Сохранение в базу if($entityManager->save($edition)) { // Получаем объект из репозитория $processRepository = $entityManager->getRepository(Edition::class); $smyakEdition = $processRepository->getById($edition->id); print "Название публикации: ".$smyakEdition->name; // Название публикации: Котенок Шмяк и большая тыква }
Порядок!
При дальнейшей работе над проектом всегда будет ясно о чем идёт речь. Мы избавились в коде от операций над сущностями через DataManager, делегировав их менеджеру управления сущностями, сделали сущности удобными и понятными, благодаря чему сигнатуры будут определены строже.
4 комментария
Привет!
Никита, а почему получается вот такая ситуация:
Если при создании элемента в таблице использовать Сервис, как вы указываете, тогда
У вас для имени:
(new IntegerField(‘NAME’))
->configureRequired(),
Запись имени:
$edition->name = ‘Котенок Шмяк и большая тыква’;
Т.е. в числовое поле пишем строку — и самое интересное — строка и записывается )))
НО!
Если при создании нового элемента использовать фабрику сущностей, т.е. сделать так:
$newBook = new \Aclips\BookRental\Internal\EditionTable::createObject();
$newBook->setTitle(‘Котенок Шмяк и большая тыква’);
….
….
$newBook->save();
В таком случае числовое поле запишется 0 — что верно, так как поле у нас числовое.
Почему так?
Привет.
На самом деле всё куда проще. Это опечатка в коде, конечно же должно быть (new StringField(‘NAME’)), спасибо за бдительность:)
PS случись такая ситуация взаправду, в бд был бы сохранён 0, как и в приведённом вами примере с сохранением сущности.
«случись такая ситуация взаправду, в бд был бы сохранён 0, как и в приведённом вами примере с сохранением сущности.»
К сожалению это не так.
В БД, в случае использования BitrixEntityManager — сохраняется не 0, а строка.
Проверьте сами.
Если мы создаём таблицу в БД на основании модели, в которой указано (new IntegerField(‘NAME’)), то поле NAME будет иметь тип int(11), что не допускает сохранения текстового значения в это поле.
$elementWithIntName = new Edition();
$elementWithIntName->name = 123;
$entityManager->save($elementWithIntName);
$elementWithStringName = new Edition();
$elementWithStringName->name = 'Котёнок Шмяк и большая тыква';
$entityManager->save($elementWithStringName);