Перейти к содержимому
Эльдар Шахвалиев
Обсудить проект
← Все проекты
202604.2026 — 04.2026В продеполный цикл

Logistan — новостной портал о логистике (logistan.info)

Отраслевой новостной портал (логистика, Центральная Азия) · Логистика / отраслевые медиа (новостной интернет-портал)

Backend/fullstack WordPress-инженер: диагностика и багфикс FSE-темы, фронтенд-разведка и передача задач

Читатели портала о логистике нажимали «Загрузить ещё» и видели те же новости, что уже прочитали. Нашёл причину и починил, не тронув работавшие разделы.

  • 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, остальные приходили случайные. Повторов с уже показанными новостями не осталось ни одного. Рубрики, главная и отдельные публикации работают как прежде: правки затронули два файла темы, база данных не менялась.

Технические детали

Решение и подход

Диагностика велась в три шага, чтобы не «чинить вслепую»:

  1. Проверка БД — SQL-запросом по wp_term_relationships/wp_term_taxonomy искал дублирующие связи «публикация ↔ тег» (результат: 0 строк, база чистая).
  2. Воспроизведение на живых данных — выполнил точно тот же запрос, что шлёт браузер при клике «Загрузить ещё» (admin-ajax.php, query_render_more_pagination), напрямую через curl, в обход кэша и плагинов; сверил первый экран с догрузкой.
  3. Разбор кода — 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 (партиалы), компиляция Preprosassets/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.
  • доработка существующего
  • аудит
  • багфикс
  • фронтенд-разведка/передача
  • деплой