Обновление системы онлайн-оплаты курсов для альплагеря Туюк-Су

Обновил модуль, который разрабатывал для alplager.kz: по чертежам Кирилла Белоцерковского (инструктор лагеря и администратор сайта) переделал список программ и форму бронирования, добавил подробный просмотр и ещё больше разных улучшений в бэкенд и под капот системы. Теперь программы красиво выводятся плиткой, по ним есть удобная навигация и возможность сразу забронировать место в лагере на смену, которая понравилась. Кирилл подробно написал об этом на сайте.

В этой статье я опишу интересные моменты в разработке плагина под WordPress, который интегрирован с системой оплаты processing.kz. Плагин (далее — модуль) управляет сменами и заявками на бронирование мест в лагере, рассылает письма, архивирует прошедшие смены и старые заявки, и помогает автоматизировать работу инструкторов лагеря с регистрацией участников и онлайн-оплатой.
В статье будут примеры с разбором URL-тегов, шорткодами и тестированием проекта с PHPUnit.

До обновления список программ выглядел так:

С тех пор, как я в 2013-м начал разработку этого модуля, прошло порядочно времени, и благодаря скудному API вордпресса и большим перерывам между обновлениями и введением новых функций, проект к началу нового витка разработки представлял собой окаменелые наслоения кода различной степени качества и технологичности. Каждый раз, как я открывал исходники, чтобы добавить что-то новое, я с тоской смотрел на низкоуровневый код: в вордпрессе нет ORM, сервисов, не было удобных гридов с поддержкой AJAX (сортировка, пагинация), это всё пришлось писать самому.

Рефакторинг архитектуры и файловой системы

Я решил наконец навести порядок, и принялся за обновление с самого конца TODO-листа: «визуально незаметные» изменения я откладывал на потом.

Совсем махнув рукой на стандарты оформления кода WP, я ввёл автозагрузку классов с именованием и файловой структурой, как мне нравится (как в Symfony).
Из одного главного файла с заголовком модуля я выделил стопку контроллеров, каждый для своей задачи: настройки, админка, ajax, список программ, бронирование и так далее. В итоге, остался один «заголовочный» файл, который теперь чистый бутстраппер: подключает необходимые файлы и запускает автозагрузку классов.

Для гридов в админке я когда-то дописал абстрактный класс DataTable, который расширял стандартный WP_List_Table и который наследовали все остальные сущности-условные модели. Из этих моделей я выделил сервисы, и структура модуля стала проясняться.

Все скрипты, стили и картинки и вынес в отдельную директорию с ресурсами, разделил пухлую директорию шаблонов для страниц на поддиректории-пространства имён.

Миграция Bazaar в git

Все остальные мои проекты давно переехали на гит, и каждый коммит альпмодуля выглядел так:

git add . bzr commit

Мигрировать на гит оказалось делом пары команд. Без проблем перенеслись все коммиты, история и тэги.

Переход на SCSS-стили

Взял, и конвертировал весь чистый CSS в SCSS, потому что писать стили так намного удобней. IDE позволяет настроить автотрансляцию в пару кликов (раньше надо было вручную запустить вотчер в терминале, не перепутав параметры), это тоже ускорило и упростило разработку.

Публичные страницы

Кирилл решил, что программа должна иметь свою отдельную страницу, с описанием и выбором, на какие даты забронировать место.
Так как программы — это сущности модуля, то плодить дополнительный тип материала было бы лишним. Вместо этого я добавил в модель программы фото для превью и адрес страницы. Для описания выводится WYSIWYG-редактор.

Добавить визуальный редактор в указанное поле в админке оказалось просто:

Так как поле с описанием программы теперь выводится как содержимое страницы, логично было бы иметь в нём поддержку шорткодов. Это тоже занимает полминуты:

Работа с URL

Каждая программа имеет свой URL-тег, по которому её можно открыть: alplager.kz/programms/beginner.
Я решил, что легче всего программы отдельно выводить через шорткод, который будет выполнять роль фронт-контроллера. Шорткод «слушает» адрес страницы, и если есть заданный URL-тег (слаг), то модуль проверяет, существует ли такая программа, и показывает её.

Реализовать разбор параметров в URL в шорткоде можно так:

У роутинга есть один скрытый нюанс: после добавления или обновления тега в add_rewrite_tag обязательно нужно сбросить кэш постоянных ссылок в админке на странице wp-admin/options-permalink.php (нажать Сохранить изменения), иначе ничего не заработает.

В шорткоде-роутере:

Поменять странице заголовок для соответствующей программы можно с помощью фильтра pre_get_document_title:

Да, адрес проверяется в двух местах, но на сайте настроено кэширование, и пользователю отдаются уже скомпилированные страницы, так что лишний запрос в БД будет происходить редко.

Парсинг Markdown и сборка с Phing

Сделал человеческую документацию в интерфейсе модуля. Чтобы вывести справку по категориям, я разбираю Markdown-файл с доками библиотекой Parsedown + ParsedownExtra, и получаю таким образом HTML-разметку для вывода. При этом в читаемом (ну, почти) виде остаётся и оригинальный текстовый файл, который можно открыть в терминале, если потребуется.

Документация Альпмодуля

Документация в админке

Документация в Markdown

Документация в Markdown

Давно хотел настроить Phing для сборки produce-версии модуля, и когда сделал это, пожалел, что не пользовался раньше.
Теперь с помощью одной команды можно получить produce-версию модуля: с реальными, а не тестовыми адресами веб-сервисов processing.kz; с отключенными отладочными проверками; без директорий VCS, тестов и прочих системных файлов.

Тесты

Модуль стал таким большим и сложным, что разрабатывать проект без тестов было бы безумием.
Структура тестов организована так:

В директории Test обитает целый зоопарк:

  • обычные юнит-тесты в PHPUnit, которые тестируют методы-хэлперы вроде форматтеров строк или денежных сумм;
  • функциональные тесты, о которых я писал в статье про тестирование с Selenium;
  • тесты с использованием API WordPress в WP, про это на блоге тоже есть отдельная статья: тестирование с WP_UnitTestCase.

Скрипт run-tests.sh запускает всё по очереди:

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

Целая стопка тестов проверяет разные части модуля в разных ситуациях: открываются ли страницы админки на списки и редактирование сущностей, доступны ли публичные страницы модуля со сменами и бронированием, выводится ли на них верная информация, работает ли заполнение формы и отправка на processing.kz, отвечают ли AJAX-сервисы.
Чем больше тестов, тем спокойнее — ввёл новую функциональность, запустил тесты, закомиттил код. Больше никакой паранойи: если фронтенд или оплата отвалится, разработчик узнаёт об этом сразу, а не после десятка пропущенных звонков. Кроме того, с помощью тестов удаётся вылавливать баги на ранней стадии, и не пускать их в продакшен. Красиво, технологично.

Отладка модуля с Selenium выглядит так:

Оптимизировать тесты и вычистить возможные баги можно, установив модуль на другой сайт с вордпрессом, и запустив тесты там. Если есть, то сразу вылезают зависимости от темы или сторонних плагинов.
Ещё один бонус: поменяв строчку с URL, который должен открываться тестами селена, можно тестировать и мониторить produce-сайт.

В общем

В статье я разобрал процесс рефакторинга модуля для WordPress и рецепты:

  • Обработку шорткодов для кастомного содержимого
  • Тестирование плагинов
  • Роутинг или маршрутизацию URL
  • Добавление визуального редактора
  • Разбор Markdown-разметки и конвертирование в HTML