Просмотров: 270842
Дата последнего изменения: 08.08.2023
Татьяна Старкова
Сложность урока:
4 уровень - сложно, требуется сосредоточиться, внимание деталям и точному следованию инструкции.
1
2
3
4
5
Недоступно в лицензиях:
Ограничений нет
Чтобы новое API выглядело для разработчика менее пугающим и более знакомым, сохранено имя самого популярного метода: getList. Но если раньше каждый getList имел свой набор параметров и зашитое непрозрачное поведение, то теперь этот метод един для всех сущностей и подчиняется одним законам. Даже при желании у разработчика сущности сделать "костыль" в getList ничего не выйдет.
Сущность BookTable, взятая в качестве примера, не исключение. Какие параметры принимает метод BookTable::getList?
BookTable::getList(array(
'select' => ... // имена полей, которые необходимо получить в результате
'filter' => ... // описание фильтра для WHERE и HAVING
'group' => ... // явное указание полей, по которым нужно группировать результат
'order' => ... // параметры сортировки
'limit' => ... // количество записей
'offset' => ... // смещение для limit
'runtime' => ... // динамически определенные поля
));
getList всегда возвращает объект DB\Result, из которого можно получить данные с помощью метода fetch():
При этом будут выбраны только поля ScalarField, а поля-выражения ExpressionField и отношения с другими сущностями затронуты не будут - их всегда нужно указывать явно.
Вычисляемое поле в select минуя runtime
В примере ниже показана упомянутая выше автоматическая группировка — система сама распознала, что необходимо группировать по полю PUBLISH_DATE. Подробнее такое поведение описывается в блоге разработчиков.
Если вычисляемое поле необходимо вам только в секции `select`, как это чаще всего бывает, то секцию `runtime` использовать необязательно: можно сэкономить время, поместив выражение сразу в select.
Система позволяет использовать вложенные выражения, которые будут последовательно развернуты в финальном SQL коде:
BookTable::getList(array(
'select' => array(
'runtime' => array(
new ORM\Fields\ExpressionField('MAX_AGE', 'MAX(%s)', array('AGE_DAYS'))
)
));
// SELECT MAX(DATEDIFF(NOW(), PUBLISH_DATE)) AS MAX_AGE FROM my_book
Обратите внимание, что внутри нового Expression поля MAX_AGE было использовано уже существующее другое Expression поле AGE_DAYS. Таким образом, система позволяет использовать вложенные выражения, которые будут последовательно развернуты в финальном SQL коде.
Внутри нового Expression поля MAX_AGE было использовано уже существующее другое Expression поле AGE_DAYS.
В секции runtime можно регистрировать не только Expression поля, но и поля любых других типов. Механизм runtime работает таким образом, что к сущности добавляется новое поле, будто оно было описано в ней изначально в методе getMap. Но такое поле находится в зоне видимости только в рамках одного запроса - в следующем вызове getList такое поле уже будет недоступно, потребуется заново его зарегистрировать.
// WHERE ID = 1
BookTable::getList(array(
'filter' => array('=ID' => 1)
));
// WHERE TITLE LIKE 'Patterns%'
BookTable::getList(array(
'filter' => array('%=TITLE' => 'Patterns%')
));
Фильтр может быть многоуровневым массивом со склейкой выражений AND/OR:
// WHERE ID = 1 AND ISBN = '9780321127426'
BookTable::getList(array(
'filter' => array(
'=ID' => 1,
'=ISBN' => '9780321127426'
)
));
// WHERE (ID=1 AND ISBN='9780321127426') OR (ID=2 AND ISBN='9781449314286')
BookTable::getList(array(
'filter' => array(
'LOGIC' => 'OR',
array(
// 'LOGIC' => 'AND', // по умолчанию элементы склеиваются через AND
'=ID' => 1,
'=ISBN' => '9780321127426'
),
array(
'=ID' => 2,
'=ISBN' => '9781449314286'
)
)
));
Полный список операторов сравнения, которые можно использовать в фильтре:
= равно (работает и с массивами)
% подстрока
> больше
< меньше
@ IN (EXPR), в качестве значения передается массив или объект DB\SqlExpression
!@ NOT IN (EXPR), в качестве значения передается массив или объект DB\SqlExpression
!= не равно
!% не подстрока
>< между, в качестве значения передается массив array(MIN, MAX)
>= больше или равно
<= меньше или равно
=% LIKE
%= LIKE
Пояснение по префиксам
Префиксы %= и =% эквивалентны, все примеры для одного подходят для второго:
'%=NAME' => 'тест' - отбор строки по LIKE (НЕ ПОДСТРОКИ)
'%=NAME' => 'тест%' - отбор записей, содержащих "тест" в начале поля NAME
'%=NAME' => '%тест' - отбор записей, содержащих "тест" в конце поля NAME
'%=NAME' => '%тест%' - отбор записей, содержащих подстроку "тест" в поле NAME
Последний вариант отличается от %NAME => тест итоговым sql-запросом.
== булевое выражение для ExpressionField (например, для EXISTS() или NOT EXISTS())
!>< не между, в качестве значения передается массив array(MIN, MAX)
!=% NOT LIKE
!%= NOT LIKE
'==ID' => null - условие, что поле ID равно NULL (в sql-запросе будет преобразовано в ID IS NULL)
'!==NAME' => null - условие, что поле NAME не равно NULL (в sql-запросе будет преобразовано в NAME IS NOT NULL)
Внимание! Если не указывать явно оператор сравнения =, то по умолчанию будет выполнен LIKE. В данном случае использован код построения фильтров из модуля Инфоблоков, который по историческим причинам подразумевает такое поведение.
Для полей типа int ставится:
-
до выхода объектного ORM
С версии 18.0.3 модуля main.
: = (сравнение по равенству, массив разворачивается в набор условий OR =)
- после выхода: IN().
group
В параметре `group` перечисляются поля для группировки:
В подавляющем большинстве случаев явно указывать группировку не требуется - система автоматически сделает это. Подробнее смотрите ниже в секции про динамически определенные поля.
order
Параметр `order` позволяет указать порядок сортировки:
Параметры `offset` и `limit` помогут ограничить количество выбираемых записей или реализовать постраничную выборку:
// 10 последних записей
BookTable::getList(array(
'order' => array('ID' => 'DESC')
'limit' => 10
));
// 5-я страница с записями, по 20 на страницу
BookTable::getList(array(
'order' => array('ID')
'limit' => 20,
'offset' => 80
));
runtime
Упоминаемые в 1-й части вычисляемые поля (ExpressionField) часто нужны не столько в описании сущности, сколько при выборке для различных вычислений с группировкой.
Самый простой пример, подсчет количества записей в сущности, можно выполнить следующим образом:
BookTable::getList(array(
'select' => array('CNT'),
'runtime' => array(
new ORM\Fields\ExpressionField('CNT', 'COUNT(*)')
)
));
// SELECT COUNT(*) AS CNT FROM my_book
В данном примере вычисляемое поле не просто преобразовывает значение какого-то поля, а реализует произвольное SQL выражение с функцией COUNT.
После регистрации поля в секции `runtime`, на него можно ссылаться не только в секции `select`, но и в других секциях:
BookTable::getList(array(
'select' => array('PUBLISH_DATE'),
'filter' => array('>CNT' => 5),
'runtime' => array(
new ORM\Fields\ExpressionField('CNT', 'COUNT(*)')
)
));
// выбрать дни, в которые выпущено более 5 книг
// SELECT PUBLISH_DATE, COUNT(*) AS CNT FROM my_book GROUP BY PUBLISH_DATE HAVING COUNT(*) > 5
Примечание. В данном примере показана упомянутая выше автоматическая группировка — система сама распознала, что необходимо группировать по полю PUBLISH_DATE. Подробнее такое поведение описывается здесь.
Если вычисляемое поле необходимо только в секции `select` (как это чаще всего бывает), то секцию `runtime` использовать необязательно: можно сэкономить время, поместив выражение сразу в `select`.
BookTable::getList(array(
'select' => array(
new ORM\Fields\ExpressionField('MAX_AGE', 'MAX(%s)', array('AGE_DAYS'))
)
));
// SELECT MAX(DATEDIFF(NOW(), PUBLISH_DATE)) AS MAX_AGE FROM my_book
Обратите внимание, что внутри нового Expression поля MAX_AGE было использовано уже существующее другое Expression поле AGE_DAYS. Таким образом, система позволяет использовать вложенные выражения, которые будут последовательно развернуты в финальном SQL коде.
В секции `runtime` можно регистрировать не только Expression поля, но и поля любых других типов. Механизм `runtime` работает таким образом, что к сущности добавляется новое поле, будто оно было описано в ней изначально в методе `getMap`. Но такое поле находится в зоне видимости только в рамках одного запроса - в следующем вызове getList такое поле уже будет недоступно, потребуется заново его зарегистрировать.
Кеширование выборки
Доступно кеширование конкретной выборки с версии 16.5.9 . В самой сущности ничего не надо описывать. По умолчанию не кешируется.
Столкнулся с тем что было необходимо используя ORM написать запрос MATCH AGAINST. К сожалению, в моей версии ядра ничего подходящего не нашел. Вот пример реализации:
Цитата
$myIterator = MyTable::getList(array( 'runtime' => array( new Entity\ExpressionField('1', '1'), ), 'filter' => array( '=1' => new DB\SqlEx * pression('1 AND MATCH(a,b) AGAINST (?s)', $query) ) ));
где $query — искомая строка, a,b — fulltext индекс таблицы.