172  /  382
Справочник

getList

Просмотров: 270685
Дата последнего изменения: 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():

$rows = array();
$result = BookTable::getList(array(
	...
));
while ($row = $result->fetch())
{
	$rows[] = $row;
}

Для получения сразу всех записей можно воспользоваться методом fetchAll():

$result = BookTable::getList($parameters);
$rows = $result->fetchAll();

// или совсем лаконично:
$rows = BookTable::getList($parameters)->fetchAll();

Теперь рассмотрим подробнее все параметры.

select

Параметр `select` определяется в виде массива с именами полей сущности:

BookTable::getList(array(
	'select' => array('ISBN', 'TITLE', 'PUBLISH_DATE')
));

// SELECT ISBN, TITLE, PUBLISH_DATE FROM my_book

Если по каким-то причинам не устраивают оригинальные названия полей в результате, можно использовать алиасы:

BookTable::getList(array(
	'select' => array('ISBN', 'TITLE', 'PUBLICATION' => 'PUBLISH_DATE')
));

// SELECT ISBN, TITLE, PUBLISH_DATE AS PUBLICATION FROM my_book

В данном примере название поля `PUBLISH_DATE` заменяется на `PUBLICATION`, именно такое название будет фигурировать в результирующем массиве.

Если необходимо выбрать все поля, то можно воспользоваться символом '*':

BookTable::getList(array(
	'select' => array('*')
));

При этом будут выбраны только поля 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 такое поле уже будет недоступно, потребуется заново его зарегистрировать.

filter

Параметр `filter` унаследовал формат фильтра инфоблоков:

// 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
  • Пояснение по префиксам
  • == булевое выражение для 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` перечисляются поля для группировки:

BookTable::getList(array(
	'group' => array('PUBLISH_DATE')
));

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

order

Параметр `order` позволяет указать порядок сортировки:

BookTable::getList(array(
	'order' => array('PUBLISH_DATE' => 'DESC', 'TITLE' => 'ASC')
));

BookTable::getList(array(
	'order' => array('ID') // направление по умолчанию - ASC
));

offset/limit

Параметры `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 . В самой сущности ничего не надо описывать. По умолчанию не кешируется.

В getList, в параметры добавился ключ cache:

$res = \Bitrix\Main\GroupTable::getList(array("filter"=>array("=ID"=>1), "cache"=>array("ttl"=>3600)));

То же самое реализуется и с помощью Query:

$query = \Bitrix\Main\GroupTable::query();
$query->setSelect(array('*'));
$query->setFilter(array("=ID"=>1));
$query->setCacheTtl(150);
$res = $query->exec();

Возможно, что в результате кешированной выборки придет объект ArrayResult.

По умолчанию выборки с JOIN не кешируются. Но, если вы уверены в том, что делаете, можно явно закешировать:

"cache"=>array("ttl"=>3600, "cache_joins"=>true);
//or
$query->cacheJoins(true);

Сброс кеша происходит в любом методе add/update/delete. Принудительный сброс кеша для таблицы:

/* Пример для таблицы пользователей */
\Bitrix\Main\UserTable::getEntity()->cleanCache();

Администратору проекта доступен запрет кеширования или изменение TTL.


Примечание: Если необходимо произвести форматирование данных при выборке, используйте fetch_data_modification.

Получение всех элементов

Для получения списка всех элементов без постраничного вывода используйте параметр count_total со значением true.

$res = MyTable::getList(...'count_total' => true, );

Это позволяет получить весь список в один запрос.

$res->getCount(); // все элементы без пагинации

59
Курсы разработаны в компании «1С-Битрикс»

Если вы нашли неточность в тексте, непонятное объяснение, пожалуйста, сообщите нам об этом в комментариях.
Развернуть комментарии
Алексей Омелич
Столкнулся с тем что было необходимо используя 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 индекс таблицы.
Алексей Попович
пример фильтрации данных по дате.
Отбираем записи, созданные менее часа назад:
Цитата
$ourAgo = new \Bitrix\Main\Type\DateTime();
$ourAgo->add("-1 hours");
$checkTimestamp = $ourAgo->getTimestamp();
$rows = EntityTable::getList([
     'filter' => [
        "<CREATED" => $ourAgo
     ]
  ])->fetchAll();

foreach ($rows as $arRow)
{
...
}
Роман Нестеренко
Выборка пользователей D7 с пагинацией , тут подробно расписано про пагинацию