У товара было видео для карточки. Его загружали в админке, учётная система о нём ничего не знает и не присылает. Однажды поле в админке оказалось пустым, хотя на сайте видео ещё играло. После следующего обмена оно пропало и с сайта. Файл в медиатеке был цел. Его стирал сам импорт, хотя этого поля он не трогал.

Это пятая часть цикла «Массовый импорт в WordPress».

Как импорт обновлял группу полей

Поля товара лежат в одной группе ACF. Учётная система присылает только часть полей: цену, наличие, описание. Чтобы не потерять остальные, обмен сливал старые значения с новыми:

$old = get_field('katalog', $id);                        // форматированные значения!
update_field('katalog', array_merge($old ?? [], $new), $id);

Выглядит безопасно, но get_field и update_field говорят на разных языках. get_field отдаёт значения в формате вывода (return_format поля), а update_field ждёт то, что хранится в базе.

Деградация за два обмена

Поле «файл» с форматом вывода «URL» хранит в базе ID вложения, а get_field отдаёт строку с адресом:

ШагВ базеАдминкаСайт
исходноID вложенияфайл выбранработает
после первого обменастрока с адресомполе пустое — адрес не IDработает: адрес и выводится
после второго обмена0пустовидео пропало

Страдают только поля, у которых формат вывода не совпадает с хранением:

  • файл и картинка с выводом «URL» — ID теряется безвозвратно;
  • галерея — если обмен не присылает картинки;
  • связь с записью без вывода «ID».

Тексты, числа, флажки и связи, отдающие ID, проходят цикл без потерь — потому баг и жил незамеченным. Поля с выводом «массив» тоже выживают: ACF достаёт ID из массива обратно. На сайте, где все файловые поля были объявлены с выводом «массив», испортить данные не удалось даже нарочно: клон товара пережил два обмена с галереей из 35 картинок.

Лечение и две ловушки

Сливать надо с сырыми значениями: get_field($name, $id, false). Но сырая группа приходит с ключами полей (field_664b…), а новые значения — с именами. Если слить как есть, в массиве окажется и ключ, и имя одного поля, и ACF выберет значение по ключу — то есть старое. Первая наивная версия патча так и сделала: на тестовом клоне товара цена из обмена не обновилась. Выпусти такой патч — цены перестали бы обновляться на всём каталоге, молча.

$oldRaw = [];
foreach ((array) get_field('katalog', $id, false) as $key => $value) {
    $field = function_exists('acf_get_field') ? acf_get_field($key) : null;
    $oldRaw[$field ? $field['name'] : $key] = $value;     // ключ поля → имя
}
update_field('katalog', array_merge($oldRaw, $new), $id);

Вторая ловушка: форматированное $old удалять нельзя. Выше по коду по нему читаются ID картинок для очистки старых вложений — там нужен именно формат вывода.

Поле, объявленное кодом, по ключу не находится. На третьем сайте одно подполе добавлялось фильтром acf/load_fields. acf_get_field() его ключ не узнавал, ключ оставался в массиве, и свежий файл из учётной системы проигрывал старому значению. Помогло объявить поле через acf_add_local_field() без родителя. С родителем ACF берёт у группы только локальные поля и теряет подполя из базы.

То же самое руками: цвет массивом и фатальная ошибка

Тот же механизм ловит и разовые скрипты. Нужно было поправить одну строку в повторителе на главной:

$g = get_field('home', 8);
$g['banki']['list'][2]['note'] = 'новый текст';
update_field('home', $g, 8);

Поле «цвет» в той же группе get_field отдал массивом (red, green, blue, alpha), и в базу он лёг массивом. Следующий get_field упал с фатальной ошибкой trim(): Argument #1 must be of type string, array given, и блок на главной стал отдавать 500. Картинки и связи ACF при записи сводит к ID сам, а ссылку, цвет и дату — нет.

Точечная правка — точечными функциями:

update_sub_field(['home', 'banki', 'list', 3, 'note'], 'новый текст', 8);   // строки с единицы
// или сырая мета, строки с нуля:
update_post_meta(8, 'home_banki_list_2_note', 'новый текст');

Как проверить свой сайт

  1. найти в группе, которую обновляет импорт, файлы, картинки, галереи и связи с выводом не «ID» и не «массив»: SELECT post_excerpt, post_content FROM wp_posts WHERE post_type = 'acf-field' AND post_parent = <ID группы>;
  2. есть хоть одно — баг боевой. Только «массив» — патч всё равно стоит поставить: следующее новое поле может оказаться с «URL»;
  3. испорченные значения восстанавливать после патча, иначе ближайший обмен затрёт их снова. Файлы в медиатеке целы — их достаточно выбрать заново.

Поля товаров «сами» пустеют после обмена?

Пришлите сайт — проверю форматы полей и сделаю слияние, которое не теряет данные.

· полезно?
АЧ
Алексей Чикун веб-разработчик, команда TRIOHEAD

Делаю сайты и сервисы целиком: от вёрстки до сервера. Пишу о том, что проверил на своих проектах.

Комментарии

// загружаю комментарии…

Комментарии публикуются после проверки. Ссылки и HTML не принимаются.