Logistan — новостной портал о логистике (logistan.info)
Отраслевой новостной портал (логистика, Центральная Азия) · Логистика / отраслевые медиа (новостной интернет-портал)
Читатели портала о логистике нажимали «Загрузить ещё» и видели те же новости, что уже прочитали. Нашёл причину и починил, не тронув работавшие разделы.
- PHP
- JavaScript
- jQuery
- WordPress
- Gutenberg/FSE
- LazyBlocks
- GhostKit
- CSS/SCSS
- MySQL
- AJAX (admin-ajax)
- WP Rocket
- Prepros
В цифрах
- 0 дублей
- после фикса на /tag/uzbekistan, /tag/kyrgyzstan, /tag/tadzhikistan — приёмочная проверка 12/12 постов по тегу, 0 пересечений с первым экраном
- 3 → 12 из 12
- релевантность догрузки на широком теге: до фикса лишь 3 из 12 догруженных постов реально по тегу, после — все 12
- 2 файла
- правки только в functions.php и assets/js/main.js; БД не изменялась
- 1167 / 836 / 221
- объём публикаций по проблемным тегам (Узбекистан/Кыргызстан/Таджикистан), сверен со счётчиками админки
Ключевое
- Проверил три версии причины по очереди — база, код, кэш — вместо того чтобы чинить вслепую на живом сайте.
- Оказалось, страница помнила рубрику, но забывала страну и дату: сервер при догрузке отдавал свежие новости всего сайта, они и пересекались с показанными.
- Подборка по стране догружает 12 новостей из 12 по этой стране — раньше по теме было только 3 из 12.
- Выкатил с резервной копией файлов и базы, проверкой синтаксиса и готовой инструкцией отката; правки затронули два файла, база не менялась.
- Разобрал устройство фронтенда портала и передал вёрстку коллеге документом: где что лежит, чего не хватает, где заложен скрытый дефект.
Задача
Новостной портал о логистике Центральной Азии собирает материалы в подборки — по рубрикам и по странам. Читатель открывал подборку по стране, нажимал «Загрузить ещё» и получал те же новости, которые уже прочитал выше. На рубриках при этом всё работало правильно.
Причина была неочевидна: повреждённая база, ошибка в коде темы или закэшированный ответ — каждая версия объясняла симптом одинаково хорошо. Чинить наугад было нельзя: портал живой, а неверная правка сломала бы рубрики, которые работали.
Что сделано
- Проверил три версии по очереди, вместо того чтобы гадать. Запросом к базе убедился, что связи «новость — тема» целы. Затем повторил ровно тот запрос, который отправляет браузер при нажатии кнопки, — напрямую, мимо кэша и плагинов. И только после этого пошёл в код.
- Нашёл корневую причину: страница запоминала, к какой рубрике относится, но не запоминала страну, автора публикации и дату. При догрузке сервер этого контекста не получал и отдавал просто свежие новости всего сайта — они и пересекались с уже показанными.
- Починил обе стороны сразу: сервер теперь принимает и проверяет эти признаки, а браузер их передаёт. Приходящие значения очищаются штатными средствами движка — адрес страницы правит кто угодно, и доверять ему нельзя.
- Выкатил с резервной копией файлов и базы, проверкой синтаксиса, сбросом кэша и заранее написанной инструкцией отката.
- Проверил не только починенное, но и то, что работало раньше: рубрики, главная и отдельные публикации остались прежними.
Итог
Подборка по стране догружает 12 новостей из 12 по этой стране — до правки по теме было только 3 из 12, остальные приходили случайные. Повторов с уже показанными новостями не осталось ни одного. Рубрики, главная и отдельные публикации работают как прежде: правки затронули два файла темы, база данных не менялась.
Технические детали
Решение и подход
Диагностика велась в три шага, чтобы не «чинить вслепую»:
- Проверка БД — SQL-запросом по
wp_term_relationships/wp_term_taxonomyискал дублирующие связи «публикация ↔ тег» (результат: 0 строк, база чистая). - Воспроизведение на живых данных — выполнил точно тот же запрос, что шлёт браузер при клике «Загрузить ещё» (
admin-ajax.php,query_render_more_pagination), напрямую через curl, в обход кэша и плагинов; сверил первый экран с догрузкой. - Разбор кода — JS, формирующий запрос, и PHP-обработчик ответа.
Корневая причина: функция query_replace_vars() упаковывала в data-attrs блока core/query только categoryName (плюс принудительно inherit=false), а тег/автора/дату не сохраняла; AJAX-фильтр query_loop_block_query_vars фильтровал тоже только по рубрике. На страницах тегов контекст терялся, и сервер отдавал «свежие записи всего сайта» — пересечение с первым экраном и давало дубли. На рубриках всё работало именно потому, что для них поле было предусмотрено.
Фикс: расширил «паспорт» блока и серверный фильтр полями tag/tagId/authorName/year/monthnum/day с безопасной очисткой (sanitize_title, absint, wp_unslash); зеркально дополнил main.js, чтобы запрос передавал эти поля. Деплой выполнен с бэкапами (файлы + дамп БД ~140 МБ), проверкой синтаксиса php -l, сбросом кэша WP Rocket и приёмочными тестами.
Стек и обоснование
WordPress, FSE / block theme (дочерняя к twentytwentyfour). Контент-разметка — на блоках core/query + core/post-template; кастомные блоки через LazyBlocks (15+ серверных lazyblock-ов, в каталоге 16 папок lazyblock-*), плюс GhostKit (подключён в workspace, утилит-классы ghostkit-*). Свой blocks/loader.php автоматически подхватывает block.css/block.js каждой папки с cache-busting по filemtime(). Стили — SASS (партиалы), компиляция Prepros → assets/css/main.css. Догрузка — jQuery AJAX через admin-ajax.php. Кэш — WP Rocket (в коде темы — универсальный сброс кэша для WP Rocket/W3TC/LiteSpeed/WP Engine/SiteGround; этот участок пред-существовал, не авторская правка). Безопасность входных параметров — штатные функции WordPress (sanitize_title/absint/wp_unslash), как наименее инвазивный и стандартный путь.
Инженерные вызовы
- FSE-специфика: разметка header/footer/sidebar и вставки блоков хранятся в БД, а не в файлах темы; пришлось рассуждать о сериализации атрибутов
core/queryвdata-attrs, а не искать привычные шаблоны. - Разграничение причин: методичное исключение версий (повреждение БД vs баг кода vs кэш) с воспроизводимой проверкой, а не «посмотрел глазами».
- Безопасная правка: починить контекст тега/автора/даты, не сломав работавшую ветку рубрик; добавить валидацию входных параметров.
- Работа с незнакомой кодовой базой: во фронтенд-разведке выявил отсутствие CSS для зарегистрированного стиля
.is-style-grid-weekи латентный баг — разныеaspect-ratioу<figure>для текущей и fallback-недели вlazyblock-glavnoe-za-nedelyu/block.php.
Услуги в проекте
- доработка существующего
- аудит
- багфикс
- фронтенд-разведка/передача
- деплой