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(): получается компактный, читабельный и часто более производительный массовый апдейт.