Войти

Что вас интересует?

Настройки и использование ORM в 1С-Битрикс

О чем и для кого эта статья

Статья будет вам интересна, если Вы уже видели и слышали что-то про ORM в 1С-Битрикс: Управление сайтом, и даже пробовали разрабатывать сайты на 1С-Битрикс с собственными ORM-классами. Статья состоит из трех частей:

  • Краткое напоминание о базовых возможностях ORM в БУС;
  • Генератор ORM-классов;
  • Подробно про работу ORM c БД. Реальный случай из практики: использование ORM для подключения из linux’а к MSSQL БД и выбора данных из хранимых процедур вместо таблиц.

Базовые возможности ORM в 1С-Битрикс: Управление сайтом

Согласно SRP, каждый класс в программе должен иметь только одну обязанность. Например: работа с форматом валют, работа с языковым пакетом, работа с настройками приложения.

В такой серьезной системе, как 1С-Битрикс: Управление сайтом классов и обязанностей должно быть немало. Моя IDE подсказывает, что в Интернет-магазине версии 16.0.1 объявлено 2759 классов. Я точно уверен, что самая многочисленная “группа” — классы для работы с таблицами в БД. CIBlockElement для работы с таблицей b_iblock_element (элементы инфоблоков), CSaleOrder для таблицы b_sale_order (заказы), CUser для работы с b_user (пользователи) и т.д. В БД сейчас 413 таблиц. Выходит, должно быть 413 классов для работы с каждой из них.

До недавнего времени каждый такой класс обладал уникальностью снежинки. Да, во многих выборка из таблицы выполнялась в методе с названием “GetList”, но это не более чем соглашение. Это нигде не было закреплено и никем не проверялось.

Более того, несмотря на общее название, эти методы вполне могли работать по-разному. вспомните 3 метода, имеющих одинаковое название:
Пожалуй, только опытный программист сможет сходу назвать особенности их применения без подглядывания в справку. А ведь есть еще такие острые вопросы, как добавление в БД, обработка ошибок и т.п. И, зачастую, каждый класс решает “сам за себя”.

Но сейчас ситуация меняется. Появилось новое ядро D7, с новыми идеями, механизмами и концепциями. Поговорим об одной из них, краеугольной - ORM (Object Relation Model). Об ORM подробно рассказывают в блоге разработчиков и есть отличное руководство в документации. Постараюсь не повторяться и буду считать, что вы уже написали пару ORM-классов, ориентируясь на эти статьи.

Я выделю только основное. Если класс отвечает за доступ к таблице БД, он должен быть наследником класса Bitrix\Main\Entity\DataManager и должен переопределять только два метода:
  1. getTableName для получения имени таблицы;
  2. getMap для получения массива колонок таблицы — объектов Bitrix\Main\Entity\Field

Метод getFilePath, который упоминается в блоге, больше не является обязательным!

92e10115a83cef8db77fb237733dd045.pngХочется похвалить разработчиков за ORM. Это не просто слова и планы на будущее. На момент написания этого текста (декабрь 2015) переведено на “новые рельсы” 215 классов. То есть, больше половины всех классов для работы с БД уже имеют D7-аналоги и используются.

Выводы по базовым возможностям ORM в 1С-Битрикс: УС. У нас появился очень мощный API для работы с БД с правильной современной архитектурой. Стоит незамедлительно переводить все свои старые классы на “ORM-рельсы”. Руководство по разработке этих классов достаточно подробное и отвечает на главные вопросы.

Автоматическая генерация ORM-классов

Первое, о чем хочется рассказать в этой статье об ORM — генератор ORM классов. По неизвестной мне причине он тщательно скрыт в недрах панели управления сайтом. Когда пользуешься им, возникает ощущение, что прикасаешься к чему-то запретному :) Хотя в среде разработчиков о нем часто говорят: и на сайте идей, и в блогах.

Чтобы его использовать, нужно открыть страницу Настройки > Настройка продуктов > Настройка модулей > Монитор производительности
и там отметить галочку на вкладке Генератор таблетов > Разрешить генерацию таблетов для ORM.

Снимок.PNG

После заходим Настройки > Производительность > Таблицы и добавить GET-параметр orm=y. Адрес будет выглядеть так: /bitrix/admin/perfmon_tables.php?lang=ru&orm=y

После этого для любой таблицы в БД сайта можно автоматически создавать ORM-класс. Для примера выберем штатную таблицу шаблонов сайта b_site_template.

b0ee9f60f71505df44c9fe8b1b518de6.png

После перезагрузки страницы имеем следующий код: код в Gist. Результат после небольших преобразований может быть сведен к настоящему классу для работы с шаблонами, расположенному по пути /bitrix/modules/main/lib/sitetemplate.php.

Важная особенность генератора: поля описываются не объектами-потомками Bitrix\Main\Entity\Field, а ассоциативными массивами. Этот формат считается устаревшим, хотя поддерживается и используется во многих системных классах.

Отличное подспорье для создателей модулей: генератор позволит поставить производство классов на поток.

Работа с удаленными БД

Помимо очевидного, есть под капотом ORM и куда более интересные возможности. Например, работа с разными БД (вертикальный шардинг)! Теперь, заканчивая работу по созданию класса никто не мешает сказать “ах да, эта таблица лежит в другой БД с другим логином и паролем”.

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

Чтобы указать, в какой БД требуется искать таблицу, в ORM-классе требуется переопределить метод getConnectionName. Здесь указывается псевдоним подключения (по умолчанию “default” — главное подключение, та же БД, в которую установлены штатные таблицы).

55556e76bedaf374bd16a1c5309a689b.png

Само подключение должно быть вручную прописано в настройках ядра D7 (файл /bitrix/.settings.php), узел connections > value > имя подключения.

67171749488e8cef98b3a4c4660885f1.png

Если с host, database, login, password вопросов, в принципе, нет, то первый параметр className заслуживает отдельного внимания. Для этого потребуется сделать небольшое отступление и рассказать об организации работы с БД в новом ядре.

Нюансы работы с разными типами БД

В новом ядре изменилась так же работа с БД. “Главными” по этому вопросу стали классы в пространстве имен Bitrix\Main\DB. Конкретно за подключение к БД и выполнение всех запросов отвечают классы семейства Bitrix\Main\DB\Connection, а именно:

Наверное, для 95% сайтов хватает этого набора (а для 90% только Bitrix\Main\DB\MysqliConnection). А что же делать, если подключение к БД очень экзотическое?

Например, у заказчика чрезвычайно устаревшая (или наоборот, слишком свежая) версия СУБД и невозможно использовать встроенный драйвер для PHP. В таких случаях на помощь спешат, конечно же, программисты.

Чтобы добавить в 1С-Битрикс: Управление сайтом поддержку нового типа БД, необходимо следовать простой инструкции:

  1. Создать класс подключения (наследник Bitrix\Main\DB\Connection). В нем определить все “базовые” операции с БД: подключение, отключение, выполнение произвольного запроса, работу с транзакциями;
  2. Создать класс SQL-хелпер (наследник Bitrix\Main\DB\SqlHelper) и возвращать его экземпляр в методе createSqlHelper. Класс предназначен для самой низкоуровневой работы с БД - он добавляет экранирование, работает с датами, предоставляет доступ к базовым SQL-функциям и т.п.;
  3. Создать класс для результата выборки (наследник Bitrix\Main\DB\Result). В нем требуется определить методы-обертки над традиционными функциями работы с результатом выборки.
420e0574c65ab6850d3aeaa4bc70bb75.png

Теперь вы знаете, какие className можно указывать в /bitrix/.settings.php и как создавать собственные подключения к БД.

Реальный пример: использование ORM для подключения из PHP к MSSQL

В данном примере я получаю элементы инфоблока из таблицы b_iblock_element (таблица элементов инфоблока) и связанных таблиц b_iblock_property (таблица наименование  свойств) и b_iblock_element_property ( таблица значении свойств)

<?
$_SERVER["DOCUMENT_ROOT"] = "/var/www/ecobalt.ru"; // или можно еще так: realpath(dirname(__FILE__)."/../..");
$DOCUMENT_ROOT = $_SERVER["DOCUMENT_ROOT"];

define("NO_KEEP_STATISTIC", true);
define("NOT_CHECK_PERMISSIONS", true);

require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");
set_time_limit(0);
Bitrix\Main\Loader::includeModule('iblock');
use \Bitrix\Main\Entity\Query;
global $DB;
$connection = \Bitrix\Main\Application::getConnection();

//Таблица элементов инфоблоков
$resultSqlElements = $connection->query('SELECT b_iblock_element.ID FROM b_iblock_element WHERE IBLOCK_ID = 106 LIMIT 3000');
while($resultElements=$resultSqlElements->fetch())
{
	$elementsID['LOGIC'] = 'OR';
	$elementsID[]['element.ID']= $resultElements['ID'];
}

// создаем объект Query, в качестве параметра передаем объект сущности (инфоблок)
$query = new Bitrix\Main\Entity\Query(
	Bitrix\Iblock\ElementPropertyTable::getEntity()
);
// поле element как ссылка на таблицу b_iblock_element
$query->registerRuntimeField(
	'element',
	array(
		// тип — сущность ElementTable
		'data_type' => 'Bitrix\Iblock\ElementTable',
		// this.ID относится к таблице, относительно которой строится
		// запрос, т.е. b_iblockElementPropertyTable.ID = b_iblock_element.IBLOCK_ID
		'reference' => array('=this.IBLOCK_ELEMENT_ID' => 'ref.ID'),
		// тип соединения INNER JOIN
		'join_type' => 'INNER'
	)
);

// поле type как ссылка на таблицу b_iblock_type
$query->registerRuntimeField(
	'type',
	array(
		'data_type' => 'Bitrix\Iblock\PropertyTable',
		'reference' => array('=this.IBLOCK_PROPERTY_ID' => 'ref.ID'),
		'join_type' => 'INNER'
	)
);
// выбираем название инфоблока, символьный код инфоблока, название элемента,
// символьный код элемента и идентификатор типа инфоблока
$query->setSelect(array('IBLOCK_PROPERTY_ID', 'IBLOCK_ELEMENT_ID','VALUE','element.ID', 'element.NAME', 'element.CODE', 'type.ID', 'type.CODE', 'type.NAME'));
// выбираем элементы с идентификаторами 348 или 349
$query->setFilter(
	$elementsID
);

// посмотрим, какой запрос был сформирован

// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
	$elements[]=$row;
}

$ids = [];
foreach ($elements as $key => $value) {
	$ids[$value['IBLOCK_ELEMENT_ID']][$value['IBLOCK_ELEMENT_PROPERTY_type_CODE']] = $value;
	// Вместо $value['id'] можете поставить $key если вам нужны ключи массива.
	// Либо поставить $value['prof'], если вам нужен список профессий
}

header("Content-type: application/json; charset=utf-8");
//printr($ids);
echo json_encode($ids);


?>
<? require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/epilog_after.php"); ?>

Теги:
Комментрии
Комементариев нет, будьте первыми....
Оставить комментарий
Пожалуйста, введите ваше Имя.
Пожалуйста, введите ваш Email.
Пожалуйста, напишите комментарий.