У товара было видео для карточки. Его загружали в админке, учётная система о нём ничего не знает и не присылает. Однажды поле в админке оказалось пустым, хотя на сайте видео ещё играло. После следующего обмена оно пропало и с сайта. Файл в медиатеке был цел. Его стирал сам импорт, хотя этого поля он не трогал.
Это пятая часть цикла «Массовый импорт в 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', 'новый текст');
Как проверить свой сайт
- найти в группе, которую обновляет импорт, файлы, картинки, галереи и связи с выводом не «ID» и не «массив»:
SELECT post_excerpt, post_content FROM wp_posts WHERE post_type = 'acf-field' AND post_parent = <ID группы>; - есть хоть одно — баг боевой. Только «массив» — патч всё равно стоит поставить: следующее новое поле может оказаться с «URL»;
- испорченные значения восстанавливать после патча, иначе ближайший обмен затрёт их снова. Файлы в медиатеке целы — их достаточно выбрать заново.
Поля товаров «сами» пустеют после обмена?
Пришлите сайт — проверю форматы полей и сделаю слияние, которое не теряет данные.
// загружаю комментарии…