walk() для ORM-коллекций: практичные массовые операции с инфоблоками
Проблема/контекст
В D7 вы часто получаете из ORM не массив, а типизированную коллекцию объектов через fetchCollection(). Перебирать её через foreach нормально, но в реальном коде обычно хочется:
- компактно “пройтись и изменить” объекты;
- сохранить изменения одной цепочкой вызовов;
- не плодить лишние временные переменные.
Начиная с main 26.0.0 у Bitrix\Main\ORM\Objectify\Collection появился метод walk(): это “аналог foreach, но как метод”, который удобно вставляется в цепочку.
Решение с кодом
walk() вызывает ваш callback для каждого объекта коллекции и возвращает эту же коллекцию (для чейнинга). В callback приходят:
- объект сущности,
- ключ итерации — это ключ внутреннего массива коллекции (обычно сериализованный первичный ключ; для составного — строка).
Пример 1. Массово деактивировать элементы инфоблока и сохранить одной цепочкой
Частый кейс: найти элементы по условию, поставить ACTIVE = 'N' и сохранить максимально “чисто”.
<?php
declare(strict_types=1);
use Bitrix\Iblock\ElementTable;
use Bitrix\Main\Loader;
function deactivateSectionElements(int $iblockId, int $sectionId): void
{
Loader::includeModule('iblock');
$saveResult = ElementTable::query()
->setSelect(['ID', 'ACTIVE', 'IBLOCK_ID', 'IBLOCK_SECTION_ID'])
->where('IBLOCK_ID', $iblockId)
->where('IBLOCK_SECTION_ID', $sectionId)
->where('ACTIVE', 'Y')
->fetchCollection()
->walk(static function($element): void {
$element->setActive('N');
})
->save()
;
if (!$saveResult->isSuccess())
{
throw new \RuntimeException(implode('; ', $saveResult->getErrorMessages()));
}
}
Практические детали:
- При одинаковом изменении для всех объектов
save()умеет выполнять групповой UPDATE (однотипные правки). Это как раз тот случай, когдаwalk()хорошо “стыкуется” с коллекциями. - Если вы меняете разные значения для каждого объекта (например,
SORTпо формуле), ORM может уйти в сериюUPDATEпо одному объекту.
Пример 2. “Пройтись и собрать” данные без лишнего foreach
walk() удобен не только для мутаций, но и для побочных эффектов: собрать ID, составить карту, подсчитать суммы.
<?php
declare(strict_types=1);
use Bitrix\Iblock\ElementTable;
use Bitrix\Main\Loader;
function collectElementIdsAndNames(int $iblockId, int $limit = 50): array
{
Loader::includeModule('iblock');
$ids = [];
$namesById = [];
ElementTable::query()
->setSelect(['ID', 'NAME'])
->where('IBLOCK_ID', $iblockId)
->setLimit($limit)
->fetchCollection()
->walk(static function($element, $key) use (&$ids, &$namesById): void {
$id = (int)$element->getId();
$ids[] = $id;
$namesById[$id] = (string)$element->getName();
})
;
return [
'ids' => $ids,
'namesById' => $namesById,
];
}
Пример 3. Когда walk() лучше, чем filter()
У коллекции есть “функциональный” filter(), но он не сработает, если в коллекции есть несохранённые изменения — тогда выбрасывается CollectionFilterException.
Поэтому практическое правило такое:
- хотите “сначала отобрать, потом поменять” — делайте
filter()первым шагом; - хотите просто “пройтись и поменять” — берите
walk().
<?php
declare(strict_types=1);
use Bitrix\Iblock\ElementTable;
use Bitrix\Main\Loader;
function deactivateActiveElementsInIblock(int $iblockId): void
{
Loader::includeModule('iblock');
$saveResult = ElementTable::query()
->setSelect(['ID', 'ACTIVE'])
->where('IBLOCK_ID', $iblockId)
->fetchCollection()
->filter(static fn($element) => $element->getActive() == 'Y')
->walk(static function($element): void {
$element->setActive('N');
})
->save();
if (!$saveResult->isSuccess())
{
throw new \RuntimeException(implode('; ', $saveResult->getErrorMessages()));
}
}
Ограничения, о которых полезно помнить
walk()не даёт “прервать” обход по условию. Если нужен ранний выход — используйтеforeachилиfind()у коллекции.- В callback вторым аргументом приходит ключ итерации. Он удобен для отладки/логики по PK, но не стоит на него “завязывать” бизнес-логику (особенно при составных ключах).
Итог
walk() — маленький метод, но в D7 он хорошо раскрывается именно в связке fetchCollection() -> walk() -> save(): получается компактный, читабельный и часто более производительный массовый апдейт.