ORM в D7 1c-bitrix. Первый подход к снаряду

d7

Реализация ORM в ядре D7 — очередная интересная, перспективная, но как обычно плохо документированная разработка от 1с-Битрикс :) Призвана она абстрагировать разработчика от механики работы с таблицами на уровне запросов к БД, введя понятие сущности и поля сущности. На зимней партнерской конференции Алексей Кирсанов провел мастер-класс по созданию модуля с применением ORM для выборки данных из своей таблицы. После чего я решил провести небольшое исследование на предмет возможности построения более сложных, чем в примере, запросов.

Интересовало меня как общее устройство системы и принцип постороения кода, так и частные случаи запросов. Например, можно ли с помощью ORM использовать HAVING, DISTINCT и разные типы JOIN’ов. Забегая вперед, получилось далеко не все, но в общем конструктор эффективен и относительно прост.

Для начала изучаем понятия сущности, поля сущности и датаменеджера в документации. По-простому, сущность в терминах битрикса — это таблица, поля сущности — столбцы или «ссылки» на другие сущности, а датаменеджер — система управления данными. Для каждой сущности нужно создать описание, например

class ElementTable extends Entity\DataManager
{
    public static function getFilePath()
    {
        return __FILE__;
    }

    public static function getTableName()
    {
        return 'b_iblock_element';
    }

    public static function getMap()
    {
        return array(
            'ID' => array(
                'data_type' => 'integer',
                'primary' => true,
                'autocomplete' => true,
                'title' => Loc::getMessage('IBLOCK_ELEMENT_ENTITY_ID_FIELD'),
            ),
            'IBLOCK_ID' => array(
                'data_type' => 'integer',
            ),
            'IBLOCK' => array(
                'data_type' => 'Iblock',
                'reference' => array('=this.IBLOCK_ID' => 'ref.ID'),
            ),
            ...

В getMap перечислены все поля таблицы, включая описание связей с другими сущностями. В примере таким образом указано отношение столбца IBLOCK_ID текущей таблицы и столбца ID сущности Iblock. В дальнейшем по reference-полям возможно выбирать поля связанных сущностей и использовать их в фильтрах.

Лайвхак: автоматически сгенерировать класс с описанием любой таблицы можно на странице Производительность-Таблицы, добавив параметр &orm=y в адрес.

Тренироваться будем на таблицах типов инфоблока, самих инфоблоков и элементов. Для них описаны сущности TypeTable, IblockTable и ElementTable, их можно посмотреть в исходниках модуля iblock. Операции добавления, удаления и выборки данных по первичному ключу (getById) нас не интересуют, будем строить обычные getList-запросы произвольного вида через цепочку вызовов методов класса Query.

Пример #1

Выберем 10 первых элементов из инфоблока ID=1.

\Bitrix\Main\Loader::IncludeModule("iblock");
// создаем объект Query. В качестве параметра он принимает объект сущности, относительно которой мы строим запрос
$query = new \Bitrix\Main\Entity\Query(Bitrix\Iblock\ElementTable::getEntity());
// можно еще так: :)
// $query = new \Bitrix\Main\Entity\Query(Bitrix\Main\Entity\Base::getInstance("Bitrix\Iblock\ElementTable"));
$query
    ->setSelect(array("ID", "NAME"))
    ->setFilter(array("IBLOCK_ID" => 1))
    ->setOrder(array("ID" => "ASC"))
    ->setLimit(10);
$query->exec();
$query->dump();

Пример #2

Выберем элементы инфоблока с группировкой по названию. Стоит отметить, что битрикс автоматически добавляет в число полей для группировки все, что указано в setSelect.

$query
    ->setSelect(array("NAME"))
    ->setFilter(array("IBLOCK_ID" => 1))
    ->setGroup(array("NAME"))

Пример #3

В запросах можно использовать агрегатные функции mysql. Для это служит метод registerRuntimeField, регистрирующий новое поле на время выполнения запроса.

Подсчитаем количество элеметов с группировкой по имени.

$query
    // cnt - название поля
    ->registerRuntimeField("cnt", array(
        // тип вычисляемого поля
        "data_type" => "integer",
        // агрегатная функция (count, max, sum, avg...) и поле для подстановки
        "expression" => array("count(%s)", "NAME")
        )
    )
    ->setSelect(array("NAME", "cnt"))
    ->setFilter(array("IBLOCK_ID" => 1))
    ->setGroup(array("NAME"));

Пример #4

Допустим, нам нужно в предыдущем примере ограничить выборку только теми названиями, которые встречаются более 5 раз. Написав фильтр по агрегатному runtime-полю, битрикс автоматически переносит его в условие having! Причем в параметрах группировки не обязательно указывать поле, к которому применяется агрегатная функция.

$query
    ->registerRuntimeField("cnt", array(
        "data_type" => "integer",
        "expression" => array("count(%s)", "NAME")
        )
    )
    ->setSelect(array("NAME", "cnt"))
    ->setFilter(array("IBLOCK_ID" => 1, ">cnt" => 5));

Будет сгенерирован запрос вида:

select NAME, count(NAME) as cnt from table where IBLOCK_ID=1 group by NAME having cnt>5

Пример #5

Через сущность «элемент» можно выбирать или ставить условия на поля связанной сущности «инфоблок». Связанная таблица по умолчанию присоединяется с помощью left join. Вспомним reference-поле IBLOCK в описании ElementTable и выберем название инфоблока с ID=1 через таблицу с элементами:

$query
    ->setSelect(array("IBLOCK.NAME"))
    ->setFilter(array("IBLOCK.ID" => 1))
    // или
    //->setFilter(array("IBLOCK_ID" => 1))
    ->setLimit(1);

Запрос:

SELECT  `iblock_element_iblock`.`NAME` AS `IBLOCK_ELEMENT_IBLOCK_NAME`
FROM `b_iblock_element` `iblock_element` LEFT JOIN `b_iblock` `iblock_element_iblock` ON `iblock_element`.`IBLOCK_ID` = `iblock_element_iblock`.`ID`
WHERE `iblock_element_iblock`.`ID` = 1
LIMIT 0, 1

Пример #6

Runtime- может быть не только вычисляемое поле, но и ссылка на другую сущность. Т.е. в функции getMap мы можем не описывать связь, а сформировать ее прямо в запросе. Например, создадим объект Query для сущности IblockTable, свяжем ее с ElementTable и выберем элемент с ID=1:

// Query(IblockTable)
$query = new \Bitrix\Main\Entity\Query(Bitrix\Iblock\IblockTable::getEntity());
$query
    // поле element как ссылка на таблицу b_iblock_element
    ->registerRuntimeField("element", array(
        // тип - сущность ElementTable
        "data_type" => "Bitrix\Iblock\ElementTable",
        // обратите внимание, что this.ID относится к таблице, относительно которой строится запрос
        // т.е. b_iblock.ID = b_iblock_element.IBLOCK_ID
        'reference' => array('=this.ID' => 'ref.IBLOCK_ID'),
        )
    )
    // все поля элемента и название инфоблока
    ->setSelect(array("element", "NAME"))
    ->setFilter(array("element.ID" => 1));

Небольшая особенность — в setSelect нужно обязательно указывать runtime-поле, что добавляет в select все поля связанной таблицы.

Пример #7

В определении runtime-reference-поля можно указывать тип join’a, а в фильтре использовать сложную логику как в CIblockElement::GetList():

$query = new \Bitrix\Main\Entity\Query(Bitrix\Iblock\IblockTable::getEntity());
$query
    ->registerRuntimeField("element", array(
        "data_type" => "Bitrix\Iblock\ElementTable",
        'reference' => array('=this.ID' => 'ref.IBLOCK_ID'),
        'join_type' => "LEFT"
        )
    )
    ->registerRuntimeField("type", array(
        "data_type" => "Bitrix\Iblock\TypeTable",
        'reference' => array('=this.IBLOCK_TYPE_ID' => 'ref.ID'),
        'join_type' => "RIGHT"
        )
    )
    ->setSelect(array("element", "type"))
    ->setFilter(array(
            "LOGIC" => "OR",
            array("element.ID" => 1),
            array("ID" => 3)
        )
    )
    ->setLimit(2);

Пример #8

Если несколько таблиц в описании сущностей (не через runtime-поля!) связаны по цепочке, то их можно использовать в запросе через символ «.». Например, выберем ID типа инфоблока для элемента с ID=1:

$query = new \Bitrix\Main\Entity\Query(Bitrix\Iblock\ElementTable::getEntity());
$query
    // b_iblock_element.IBLOCK_ID = b_iblock.TYPE_ID = b_iblock_type.ID
    ->setSelect(array("IBLOCK.TYPE.ID"))
    ->setFilter(array("ID" => 1));

К сожалению, не получилось создать запрос, на который повлиял бы метод enableDataDoubling и выяснить, что можно передавать в метод setOptions. У кого есть мысли на этот счет, прошу в комментарии.

Напоследок привет от битрикса :)

    
    /*
    /bitrix/modules/main/lib/entity/query.php
    * The most magic method. Do not edit without strong need, and for sure run tests after.
    */
    protected function collectExprChains(QueryChain $chain, $storages = array('hidden'))
Если вам понравилась статья, подписывайтесь на обновления блога по rss или присоединяйтесь в twitter

Поделиться ссылкой с друзьями:

Метки: ,

Категории: Bitrix

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

  1. Максим:

    Отличная статья, было бы не плохо добавить пример с фильтрацией по свойствам элементам, я считаю это актуально пример

  2. Алексей Валеев:

    Максим, свойства элементов инфоблока на момент написания статьи еще были не реализованы в классах сущностей, поэтому соответствующих примеров добавить не получилось.

  3. А че им DQL не нравится? Как раз для любителей сферических абстрактных коней в вакууме с напухшими вишнями, чтобы детей по ночам пугать :)
    Это уже не ПэХаПэ — это уже перловые лиспы и не иначе :D

    Кроме шуток: что в реальности это дает на практике программисту кроме бумажки в рамочке и умного вида?

  4. Дмитрий, ORM в общем — это круто. ORM в Битриксе — это говно. Я не понимаю, про какие бумажки ты сейчас говоришь, но ООП в умелых руках помогает писать гибкие и расширяемые приложения, внося минимальные изменения в функционал. Это возможно благодаря наследованию. Это не ваши ссаные повсюду объявленые функции и скопированный во все места одинаковый код.

  5. Оооххх … я боюсь даже подумать о таких людях, которые сами пишут ОРМ c мутабельным ООП под свои разработки. Чаще все на самописах пытаются и там все … Ну мягко говоря не до таких расширений сознания, которые может потенциально принести лавандосом потребитель, не говоря про нагрузку.

    Жизненно, но непотребно из-за 15 возможностей наворачивать обертки классов и говорить по полиморфизм.

    А вообще да. Но это снова выход на урвень туповатого чмощника который сидит в визивиге и не понимает зачем ему те или иные инстансы :D

    Имхо.

  6. Andrey:

    как можно добавить DISTINCT в select ?

  7. Алексей, подскажите, пожалуйста, а с админкой для сrm-сущностей вы уже делали? интересует спискок записей и страница детальной записи… с возможностью из редактирования и удаления

  8. Иван:

    Уберите из описания DISTINCT чтобы людей не обманывать)
    DISTINCT запросы делать нельзя.

  9. Алексей:

    DISTINCT можно делать через Bitrix\Main\Entity\ExpressionField.

  10. Столкнулся с тем что было необходимо через ORM написать запрос MATCH AGAINST. Вот пример реализации:
    $myIterator = MyTable::getList(array(
    ‘runtime’ => array(
    new Entity\ExpressionField(’1′, ’1′),
    ),
    ‘filter’ => array(
    ‘=1′ => new DB\SqlExpression(’1 AND MATCH(a,b) AGAINST (?s)’, $query)
    )
    ));
    где $query — искомая строка, a,b — fulltext индекс таблицы.

  11. Иван:

    Забавно, ТП сказала что анреал сделать)
    http://i.imgur.com/GGWKBCU.png

    Можно небольшой пример?)

  12. Andreyprg:

    удалите,пожалуйста!

  13. Jannaacd:

    удалите,пожалуйста!

  14. Sergyur:

    Novosti

  15. Svetlanatpk:

    urenrjrjkvnm

  16. Ivanybh:

    news

  17. Svetluwl:

    Novyny

  18. Igorqst:

    Ukraine

  19. Davidrby:

    coin

  20. Lindawon:

    Есть кто дома? :)

    XEvil может решить любую капчу…

    XEvil.Net

    Купоны, дающие скидку 45% на XEvil до 31-го января:

    J2021-45-210131-fuxc8kua0cko19fg
    J2021-45-210131-fuxc8kuabg5iitgq
    J2021-45-210131-fuxc8kuamkola5gi
    J2021-45-210131-fuxc8kuauwkex93h
    J2021-45-210131-fuxc8kub60z9pqxf
    J2021-45-210131-fuxc8kubecpbhout
    J2021-45-210131-fuxc8kubmoy7mnpn
    J2021-45-210131-fuxc8kubxs4gil6t
    J2021-45-210131-fuxc8kuc64bo3m4y
    J2021-45-210131-fuxc8kuch8k4hj7b

  21. Jannaikn:

    coin

  22. Veronajfj:

    urenrjrjkvnm

  23. Serzhhz:

    coin

  24. Ilushikgvr:

    urenrjrjkvnm

  25. Ivantqu:

    news

  26. Viktorigmr:

    Cinema

  27. Svetlanabau:

    urenrjrjkvnm

  28. Leonsxb:

    urenrjrjkvnm

  29. Davidbxc:

    coin

  30. Svetlanalof:

    urenrjrjkvnm

  31. Ivanwtm:

    news

  32. Svetlanared:

    urenrjrjkvnm

  33. Alexgxl:

    coin

  34. Sergnvm:

    Novosti

  35. Svetlanaxzv:

    urenrjrjkvnm

  36. Ivanmcp:

    news

  37. Alexwck:

    coin

  38. Sergpuj:

    Novosti

  39. Viktoricja:

    Cinema

  40. Veronalav:

    Life

  41. Ilushikies:

    urenrjrjkvnm

  42. Veronaihh:

    urenrjrjkvnm

  43. Davidxjx:

    coin

  44. Leonxsq:

    urenrjrjkvnm

  45. Svetlcqk:

    Novyny

  46. Ilushikawx:

    urenrjrjkvnm

  47. Daviddzx:

    coin

  48. Viktorizdy:

    Cinema

  49. Veronamos:

    Life

  50. Leongpd:

    urenrjrjkvnm

  51. Veronadtl:

    urenrjrjkvnm

  52. Igortid:

    Ukraine

  53. Daviddpc:

    coin

  54. Veronakjr:

    Life

  55. Svetlpmf:

    Novyny

  56. Veronalzl:

    urenrjrjkvnm

  57. Veronavpp:

    urenrjrjkvnm

  58. Svetlwyw:

    Novyny

  59. Andreitqh:

    Установка скважинного адаптера в Минске и области Специалисты компании «БурАвтоГрупп» — одни из наиболее опытных и профессиональных установщиков адаптера в скважине в Минске, который не только оптимизирует подачу воды в любых климатических условиях, но и позволит вам сэкономить деньги на приобретении более дорогостоящих альтернативных устройств.Скважинный адаптер. Что это такое?Скважинный адаптер — это современное универсальное изделие из латуни, которое предназначается для прокладывания труб водопровода от скважин к домам. Адаптер является единственным альтернативным заменителем дорогостоящих и громоздких кессонов. Значительная разница в стоимости заметна даже при монтаже. Сам адаптер служит для врезки в обсадную трубу. Его использование позволит круглый год пользоваться скважиной, не волнуясь о промерзании грунта. Состоит он из двух половин, одна из них статическая (крепящаяся к обсадной трубе), а вторая половина оборудована муфтой и предназначена для крепежа насосной трубы. Абсолютно все соединения герметичны и препятствуют просачиванию воды.Монтаж скважинного адаптера — порядок работ и стоиомсть
    Для монтирования адаптера размером в 1 дюйм вначале производят сверление обсадной трубы. Для скважинного адаптера предусматривается коронка, а так же уплотнение из резины для фиксации. Специальным ключом адаптер помещается внутрь трубы. Далее, на глубине промерзания, работа остается исключительно за монтажником. Он уплотняет резьбу сальником, кольцом и гайкой. После этого монтажные работы по установке скважинного адаптера закончены.Использование адаптера помогает в разы облегчить работы и миновать проблемы использования кессонов, которые имеют «привычку» давать пробоины в местах сварочных швов. Помимо этого, адаптер предоставит свободный доступ к насосу в случаях поломки или его замены.
    Стоит отметить, что в работе со скваженными адаптерами не может идти речь о самостоятельном монтаже. Зачастую после подобных попыток они приходят в негодность. К отсутствию необходимого давления присоединяется и нарушенная герметичность, что просто повлечет за собой лишние расходы.Стоимость монтажа скважинного адаптера в Минске и Минской области По сравнению с установкой кессона, цены на монтаж скважинных адаптеров смотрятся очень привлекательно. Пусть это вас не смущает, ведь технология адаптера и кессона отличается, что объясняет невысокую стоимость услуг по установке и обслуживанию. Кроме этого, если вы сравните ценовую политику аналогичных нам компаний, то сразу убедитесь в демократичности и лояльности цен «БурАвтоГрупп».Заказывайте установку скважинного адаптера в Минске в «БурАвтоГрупп» — наше качество работ, оперативность и приемлемые цены не оставят вас равнодушными! Также обращаем Ваше внимание, что мы оказываем услуги бурения скважин Под Ключ.

  60. Donaldggt:

    Привет дамы и господа
    Обустройство скважины
    Обустройство скважины – это комплекс земельных и монтажных работ, необходимых для качественного и комфортного пользования чистой водой из только что пробуренной скважины. В результате него долгожданная вода из скважины, наконец, поступает в дом. Процесс требует точных расчетов и тщательной подготовки, которую выполняют специалисты высокой квалификации, дополненной многолетним опытом в этой сфере. При обустройстве скважины компания ” БурТехСервис” ориентируется прежде всего на нужды своего заказчика, помогая выбрать оптимальный вариант, подходящий и по цели, и по цене.
    Основные этапы обустройства скважины
    Подготовка.
    На этом этапе мы с вами уже определившись со способом обустройства скважины, а именно – подобрали соответствующее оборудование (кессон, адаптер либо оголовок). А также приготовили необходимые материалы и инструменты. При выборе способа обустройства скважины специалисты руководствуются следующими данными: глубиной и диаметром скважины, учли расход воды в разных точках участка, протяженность водопроводных труб (длиной трубопровода). Несомненно, правильная подготовка помогает предупредить возникновение возможных проблем в будущем, а также избежать дорогостоящего ремонта.
    Прибытие на место монтажа. Оборудование, а также материалы и необходимые инструменты доставляются на Ваш участок.Начинаем с земляных работ. Нами подготавливается котлован при обустройстве кессоном, либо копается приямок для обустройства с адаптером. Траншея выкапывается по необходимости. Глубина при копке должна быть ниже уровня промерзания (1,5-2 метра).Далее обрезаем обсадную трубу на нужной высоте при монтаже кессона. При монтаже адаптера труба не обрезается.После этого устанавливаем кессон, либо делаем врезку в трубу скважинного адаптера.Устанавливаем также в скважину погружной насос.
    Специалисты «БурТехСервис» руководствуясь данными из паспорта скважины, подбирают насос. Необходимо учесть следующие данные:
    Глубину скважиныУровни воды в статике и динамикеДебит (производительность скважины)Горизонтальный перепад (уклон) высот скважиныРасстояние от самой высокой точки забора воды до уровня земли.Продолжаем монтаж. В кессон опускается и устанавливается оголовок, гидроаккумулятор, система автоматики с реле давления. На участке устанавливают летний поливочный кран.Для прокладки трубопровода к дому ранее уже выкопана траншея. В дом подводят две магистрали: одна водоподающая, вторая – для подводки электрического кабеля от насоса. При необходимости устанавливаем греющий кабель.После всех работ выполняем подключение оборудования к электричеству. Обязательно проводится проверка работы систем автоматики.Обустройство скважины завершено!

  61. Julisrz:

    Novosti

  62. Vikixqy:

    urenrjrjkvnm

  63. Stasenhwe:

    urenrjrjkvnm

  64. Juliidh:

    Novosti

  65. Vikiywb:

    urenrjrjkvnm

  66. Stasenvtq:

    urenrjrjkvnm

  67. Julifzu:

    Novosti

  68. Julixzd:

    Novosti

  69. Vikiiim:

    urenrjrjkvnm

  70. Vikiylm:

    urenrjrjkvnm

  71. Tolikrpa:

    Novosti

  72. Tolikcey:

    Novosti

  73. Tolikylm:

    Novosti

  74. Tolikpnu:

    Novosti

Оставить комментарий