WordPress: ACF поле не сохраняется — чиним запись meta
ACF поле отображается, но после сохранения пусто. Пошаговое решение: проверка autosave/revision, перенос логики на acf/save_post, код и диагностика через WP-CLI и debug.log.
Требования
- WordPress 6.x
- ACF (Free/Pro) установлен и активен
- Доступ к WP-CLI и логам на prod
WordPress: ACF поле не сохраняется — чиним запись meta
Запрос acf поле не сохраняется wordpress обычно прилетает так: ACF-поля в админке есть, вводишь значение, жмёшь «Обновить», а потом пусто — и get_field() возвращает null или пустую строку. В конце этой инструкции: meta реально пишется в wp_postmeta, get_field() стабильно отдаёт значение, а конфликты с хуками перестают устраивать тихий саботаж.
Сниппеты по статье: ACF Save Debug (MU-plugin) · ACF save_post с проверкой autosave/revision · WP-CLI: проверка post meta для ACF · ACF update_value: нормализация значения
В чём проблема
Симптомы:
- ACF-поля отображаются в админке.
- После сохранения значение пропадает.
- В базе в
wp_postmetaключа либо нет, либо он не меняется. get_field('my_field', $post_id)возвращает пусто.
Самая частая причина на проде: конфликт хуков сохранения.
- ACF сохраняет значения при
acf/save_post. - Ваши (или чужие) функции на
save_post/wp_insert_post/pre_post_updateмогут:- срабатывать на autosave или revision и перетирать meta;
- делать
wp_update_post()внутриsave_postи запускать каскад «сохранился → пересохранился → перезатёрлось»; - вызывать
update_field()/update_post_meta()в неправильный момент (до/вместо ACF-сейва); - чистить
$_POST(да, такое тоже бывает — “безопасность” уровня «удалить данные»).
Почему autosave/revision важны: WordPress создаёт автосохранения и ревизии, и если ваш хук не фильтрует их, он сработает «не на тот пост» и/или перезапишет meta “пустым”. Для проверки есть wp_is_post_autosave() и wp_is_post_revision().
Типичный сценарий на проде: в логе нет ошибок PHP, в админке всё сохраняется “без жалоб”, но на фронте или в REST API поле пустое. Часто виноват не сам ACF, а код темы или плагина на save_post с приоритетом 10: он срабатывает в своём порядке и перезаписывает meta до или после ACF. Ещё вариант — объектный кеш (Redis/Memcached): старые значения meta кешируются, и даже при корректной записи в БД ты видишь пустое значение, пока кеш не сбросится.
Рабочее решение
Ниже — рабочий, «боевой» путь: сначала диагностируем, потом переносим вашу логику на правильный хук ACF и выключаем перезапись на autosave/revision.
1) Включаем логирование на prod без плясок
Файл: wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
Дальше хватаем лог:
tail -f wp-content/debug.log
Если у тебя Nginx + PHP-FPM — параллельно полезно смотреть error.log PHP-FPM / nginx, но debug.log обычно хватает. После правок не забудь перезагрузить PHP (или сервис PHP-FPM), иначе старый описанный конфиг не подхватится.
2) Быстрая проверка: ACF вообще отправляет данные?
Сделаем mu-plugin (так надёжнее: не отключится вместе с темой). Готовый вариант с пояснениями: сниппет «ACF Save Debug (MU-plugin)».
Файл: wp-content/mu-plugins/acf-save-debug.php
(если папки нет — создай)
<?php
/**
* Plugin Name: ACF Save Debug (MU)
*/
add_action('acf/save_post', function ($post_id) {
// Логируем только админку и только реальные посты
if (!is_admin()) return;
// ACF сохраняет значения из $_POST['acf'] (с версии 5+).
$keys = isset($_POST['acf']) && is_array($_POST['acf']) ? array_keys($_POST['acf']) : [];
error_log('[ACF] acf/save_post post_id=' . $post_id . ' keys=' . implode(',', $keys));
}, 1);
Что это даёт:
- Если в логе keys пустой — ACF не получает значения (не тот экран, не тот
post_id, поля disabled/readonly, конфликт JS/редактора, кастомная форма безacf_form_head()и т.д.). - Если keys есть, но meta не меняется — конфликт записи/перезапись ниже по цепочке.
Сохрани пост с заполненным ACF-полем и сразу открой debug.log: должна появиться строка с нужным post_id и списком ключей из $_POST['acf']. Если ключей нет — проблема на этапе отправки формы (фронт или другой плагин режет данные).
3) Проверяем, пишется ли meta в базу (WP-CLI)
Команды и порядок проверки: сниппет «WP-CLI: проверка post meta для ACF». Сначала узнаём post_id и имя поля (name). Дальше:
wp post meta list 123 --format=table
wp post meta get 123 my_field
WP-CLI команды по meta — официальные, стабильные.
Если поле ACF не в my_field, а хранится иначе (например, field_XXXXXXXX как reference) — ты это тоже увидишь в списке. Так можно быстро убедиться, что запись в БД вообще происходит: если после сохранения в админке в wp post meta list появляется нужный ключ с актуальным значением, значит ACF и база в порядке, а проблема может быть в кеше или в месте, откуда ты читаешь поле.
4) Убираем конфликт: переносим вашу логику с save_post на acf/save_post
Если у тебя (или у темы/плагина) есть код вроде этого:
add_action('save_post', function ($post_id) {
update_post_meta($post_id, 'my_field', $_POST['something'] ?? '');
});
…то он легко убьёт ACF, потому что:
- сработает на autosave/revision;
- может записать пустоту раньше/позже ACF;
- может триггерить пересохранение.
Правильная точка интеграции для ACF-полей — acf/save_post. Готовый MU-plugin с проверкой autosave/revision и примером update_field: сниппет «ACF save_post с проверкой autosave/revision».
Файл: wp-content/mu-plugins/acf-save-fix.php
<?php
/**
* Plugin Name: ACF Save Fix (MU)
*/
// 20 = "после того, как ACF обработал свои данные" (часто оптимально на практике)
add_action('acf/save_post', function ($post_id) {
// 1) Не трогаем автосохранения и ревизии.
if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
return;
}
// 2) На всякий — работаем только в админке
if (!is_admin()) {
return;
}
// 3) Пример: если нужно вычислить и сохранить зависимое значение в ACF поле
// (update_field — официальный путь для записи ACF значения).
$value = get_field('source_field', $post_id);
if ($value === null || $value === '') {
return;
}
$computed = mb_strtoupper((string)$value);
update_field('my_field', $computed, $post_id);
}, 20);
Ключевая идея: не мешай ACF сохраняться, а дополняй его после сейва, на acf/save_post. Приоритет 20 выбран не случайно: ACF сам вешает обработчик на acf/save_post, и к моменту срабатывания твоего кода с приоритетом 20 значения полей уже записаны в wp_postmeta. Если нужна логика “сначала ACF, потом я” — используй приоритет 20 или выше; если “сначала я, потом ACF” — приоритет меньше 10 (осторожно: не перезаписывай meta пустым).
5) Если у тебя фильтр acf/update_value — не делай там записи в БД
acf/update_value — фильтр для модификации значения перед сохранением, а не для побочных эффектов. Пример нормализации (trim, регистр) без записи в другие поля: сниппет «ACF update_value: нормализация значения».
То есть норм:
add_filter('acf/update_value/name=my_field', function ($value, $post_id, $field) {
// Нормализуем то, что сохраняем
return is_string($value) ? trim($value) : $value;
}, 10, 3);
А вот делать update_field() внутри acf/update_value — частый путь к рекурсии/гонкам/«то пусто, то густо». Для побочных сохранений используй acf/save_post, как выше.
6) Приоритет хука и порядок выполнения
Иногда поле сохраняется “через раз” или только при втором нажатии «Обновить». Это часто связано с тем, что несколько обработчиков висят на acf/save_post или save_post с разными приоритетами и перезаписывают друг друга. Проверь все места, где вызываются update_post_meta() или update_field() для этого поста: убедись, что они либо на acf/save_post с приоритетом не меньше 15, либо явно пропускают autosave/revision. Один “грязный” хук на приоритете 5 может обнулить meta до того, как ACF успеет записать данные.
Проверка результата
1) Проверка в админке
- Открой пост/страницу с ACF.
- Введи тестовое значение.
- Сохрани.
- Перезагрузи страницу редактирования — значение должно остаться.
Если значение пропало после перезагрузки, повтори цепочку из раздела «Рабочее решение»: проверь лог на наличие ключей в acf/save_post, затем через WP-CLI убедись, что meta есть в базе. Если в БД значение есть, а в админке пусто — возможен кеш страницы или кеш объекта (Redis/Memcached).
2) Проверка через WP-CLI
wp post meta get 123 my_field
Ожидаешь увидеть сохранённое значение.
3) Проверка чтения через PHP (на шаблоне/в плагине)
$value = get_field('my_field', 123);
var_dump($value);
get_field() официально возвращает значение поля из нужной “локации” (post/user/term/options) — важно, чтобы post_id был правильный. На проде после успешной записи имеет смысл один раз сбросить объектный кеш (если используется): wp cache flush через WP-CLI или сброс в панели хостинга, чтобы не ловить старые значения из кеша.
Типичные ошибки
❌ 1) Хук save_post перезаписывает meta пустым
Причина: хук срабатывает на autosave/revision или порядок хуков перетирает ACF.
Исправление: перенеси логику на acf/save_post, добавь проверки wp_is_post_autosave()/wp_is_post_revision().
❌ 2) Неверный post_id для get_field()
Причина: поле не у поста, а у options/user/term, а ты читаешь как пост.
Исправление: передай правильный post_id (например, option, user_{$id}, term_{$id} — по модели ACF) и проверь по документации get_field().
❌ 3) “Я модифицирую значение в acf/update_value, но оно не сохраняется”
Причина: ты возвращаешь null/пустоту, либо делаешь побочные записи в БД прямо в фильтре.
Исправление: в acf/update_value только возвращай нормализованное $value. Любые доп. сохранения — в acf/save_post.
❌ 4) Поле “видно”, но $_POST['acf'] пустой
Причина: кастомная форма/метабокс, конфликт JS, поле disabled/readonly, или это вообще не ACF экран.
Исправление: смотри лог mu-plugin, проверь что ACF реально сабмитит $_POST['acf'] (в ACF 5+ это именно acf).
❌ 5) В БД meta есть, а на фронте или в API пусто
Причина: объектный кеш (Redis, Memcached, кеш плагина) отдаёт старую версию meta; или читаешь поле в неправильном контексте (например, в цикле без передачи post_id).
Исправление: выполни wp cache flush или сброс кеша в админке; при вызове get_field() всегда передавай второй аргумент — ID поста, термина или 'option'.
❌ 6) Поле внутри группы (repeater/flexible) не сохраняется
Причина: обращаешься к полю по имени без учёта группы или подполя; при ручном update_post_meta перезаписываешь сериализованные данные группы.
Исправление: используй get_field('group_name', $post_id) и вложенные ключи для подполей; для записи — только update_field() с путём к полю (например, my_repeater_0_my_subfield или через массив по документации ACF).
Если не работает: чеклист диагностики за 10 минут
-
Убедись, что
acf/save_postвообще срабатывает Смотриwp-content/debug.logи строку вида[ACF] acf/save_post post_id=... keys=.... -
Посмотри, кто трогает meta кроме ACF Поиск по проекту:
grep -R "save_post" -n wp-content/themes wp-content/plugins wp-content/mu-plugins
grep -R "update_post_meta" -n wp-content/themes wp-content/plugins wp-content/mu-plugins
-
Временно отключи подозреваемый плагин/кусок темы На проде аккуратно: сначала на staging. Но если выбора нет — выключай точечно, не “всё подряд”.
-
Проверь meta напрямую в БД (если есть доступ)
SELECT meta_key, meta_value
FROM wp_postmeta
WHERE post_id = 123
ORDER BY meta_id DESC;
-
Проверь, что читаешь то же поле, что сохраняешь В ACF у поля есть
nameиkey. Дляupdate_field()можно передавать и то, и другое — документация это допускает и это часто спасает при переименованиях. -
Сбрось кеш Если на сервере включён объектный кеш или кеш страниц, после исправления кода выполни сброс:
wp cache flush, очистка кеша в панели хостинга или перезапуск Redis/Memcached. Иначе можно долго отлаживать “правильный” код, глядя на закешированный результат.
Где применять
- prod — да, именно здесь чаще всего и всплывает (конфликт плагинов/хуков, кеши, кастомные сохранения). Рецепт тот же: логи, WP-CLI, перенос логики на
acf/save_postи проверка приоритетов. - docker/staging — идеально, чтобы отловить “кто перетирает meta” до выката. Можно временно включить debug.log и поставить mu-plugin для логирования без риска для продакшена.
- CI/CD — можно добавить быстрый smoke-тест через WP-CLI: записать meta и прочитать обратно; при падении теста сразу видно, что сохранение или чтение полей сломалось.
Сниппеты по статье: ACF Save Debug (MU-plugin) · ACF save_post с autosave/revision · WP-CLI post meta для ACF · ACF update_value: нормализация
Пара внутренних ссылок, чтобы не искать по сайту:



Комментарии