ORM OM NOM NOM или тайны ORM в 1С-Битрикс

cookie monster.jpg

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

Эта статья будет интересной, если Вы уже видели и слышали что-то про ORM в 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 метода, имеющих одинаковое название:

  1. CUser::GetList
  2. CIBlockElement::GetList
  3. CSaleOrder::GetList

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

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

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

  1. getTableName для получения имени таблицы
  2. getMap для получения массива колонок таблицы — объектов Bitrix\Main\Entity\Field

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

class2.png

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

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

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

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

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

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

4.png

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

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

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

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

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

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

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

2.png

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

1.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 ). В нем требуется определить методы-обертки над традиционными функциями работы с результатом выборки.

class.png

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

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

Как уже было отмечено выше, класс Bitrix\Main\DB\MssqlConnection основан на расширении sqlsrv, которое доступно только на windows-сервере. В одном из наших проектов возникла необходимость подключиться к MSSQL с linux-сервера, то есть решение от 1C-Битрикс нам не подходило (а компиляция драйвера в linux ничем хорошим не закончилась). Помогла природная смекалка и знание ООП.

На сервер было установлено расширение mssql ( http://php.net/manual/ru/book.mssql.php ) и была разработана следующая архитектура:

class3.png

Был разработан собственный набор MSSQL-классов, многие методы были унаследованы от стандартных Bitrix\Main\DB\Mssql* . Пришлось буквально в паре десятков мест произвести замены вроде sqlsrv_query => mssql_query. К этому пакету (и способу его получения) вернемся в завершении статьи.

Выбор данных из хранимых процедур вместо таблиц

ORM подходит даже для таких экзотических запросов, как выборка данных не из таблицы, а из хранимых процедур. Такие процедуры могут быть созданы в MSSQL-базе данных. Что ж, попробуем “обмануть” ORM и подсунуть ей процедуру вместо имени таблицы.

Укажем название функции в методе getTableName .

5.png

Однако, сразу такой код работать не будет. Дело в том, что при использовании подключения Bitrix\Main\DB\MssqlConnection все вхождения имен таблиц проходят через экранирование. Попытка сразу выполнить такой запрос приведет к выбрасыванию исключения:

MS Sql query error: Invalid object name 'foo_table_procedure()'. (400)

SELECT

[base].[bar] AS [BAR],

[base].[baz] AS [BAZ],

FROM [foo_table_procedure()] [base]

Увы, не получилось. На самом деле, мы в одном шаге от успеха, помешали только знаки “[“ и “]”, которыми MssqlSqlHelper защитил имя используемой “таблицы”. Проблема решается “в лоб” созданием собственного подключения ( Connection ) и SqlHelper ’а.

На сервер было установлено расширение mssql ( http://php.net/manual/ru/book.mssql.php ) и была разработана следующая архитектура:

6.png

Где self::isKnownFunctionCall — метод проверки, который возвращает true, если в $identifier находится “foo_table_procedure()”.

Выводы

Новое ядро D7 уже здесь. Каждый месяц появляются все новые и новые классы, они постепенно заменяют старые. Если в Ваших проектах или модулях есть классы, чья ответственность — предоставление доступа к 1 таблице в БД (локальной или сторонней) — требуется его переписать, поставить “на рельсы" одной из ключевых фич в новом 1С-Битрикс: Управлении сайтом.

Что касается пакета классов для подключения с linux-сервера к MSSQL БД. Если Вы хотите получить этот пакет (3 класса: MssqlConnection , MssqlSqlHelper , MsssqlResult ), оставьте комментарий к статье и мы Вам его вышлем.

Комментарии (4)

...
  • Владислав
  • 24.01.2016 01:17:34
Что касается пакета классов для подключения с linux-сервера к MSSQL БД. Если Вы хотите получить этот пакет (3 класса: MssqlConnection , MssqlSqlHelper , MsssqlResult ), оставьте комментарий к статье и мы Вам его вышлем"

Спасибо за стать. Беду признателен за код
...
  • Алексей Попович
  • 24.01.2016 11:30:02
Премного благодарен за пример с работой со сторонними базами:)
Последнее время очень много приходится делать переносы сайтов на битрикс и момент переноса контента - один из основных в данном процессе и я постоянно пользуюсь PDO для подключения, а ORM-сущности для промежуточного хранения данных. Выходит, что создаю 2 класса: один для подключения к сторонней базе, а второй - для работы с сущностями ORM.
На следующем проекте обязательно попробую сделать подключение без PDO
...
  • Сергей Питкевич
  • 09.06.2016 22:51:10
Спасибо за статью. Используем подключение к Mssql, но без классов Битрикс. Просьба прислать классы MssqlConnection , MssqlSqlHelper , MsssqlResult.
...
  • Шаврин Александр
  • 14.09.2016 13:49:51
Очень позновательная статья, если не затруднит, пришлите пакет классов.