Photo Likes
Wordpress plugin for likes in photo gallery
by Astrodj · github.com/imicra/astrodj-photo-likes · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/imicra/astrodj-photo-likes/archive/refs/heads/main.zipAJAX-плагин лайков для фотографий WordPress.
Идея
Плагин предназначен для фотогалереи, где фотографиями являются записи CPT (portfolio, stock).
Основные требования:
- максимально быстрый вывод большого количества фотографий;
- отсутствие повторных SQL-запросов при выводе каждой карточки;
- один лайк от одного посетителя;
- отсутствие авторизации пользователя;
- корректная работа с кешированием страниц (WP Super Cache и аналогичные);
- возможность масштабирования без изменения публичного API.
Архитектура
Frontend
Button
│
▼
Repository
│
▼
Database
│
▼
MySQL
AJAX
photo-likes.js
│
▼
admin-ajax.php
│
▼
Ajax
│
▼
Repository
│
▼
Database
Admin
LikesPage
│
▼
LikesTable
│
▼
Repository
│
▼
Database
Главная идея — любой код вне слоя Database никогда не должен выполнять SQL самостоятельно.
Весь доступ к данным проходит через Repository.
Слой Database
Database отвечает исключительно за SQL.
Он ничего не знает о:
- WordPress Loop;
- текущем пользователе;
- кеше;
- AJAX;
- интерфейсе.
Его задача — выполнить запрос и вернуть результат.
Например:
Database::getLikes()
получает количество лайков.
Database::insert()
создает новый лайк.
Repository
Repository является публичным API всего плагина.
Frontend, AJAX и админка работают только через него.
Repository отвечает за:
- кэширование данных;
- объединение нескольких запросов;
- получение информации о текущем посетителе;
- подготовку данных для интерфейса.
Если потребуется изменить структуру базы данных, изменения будут только внутри Repository и Database.
Почему есть boot()
При выводе архива фотографий может отображаться несколько десятков изображений.
Если Button::render() будет выполнять SQL при каждом вызове, получится N+1 запросов.
Вместо этого Repository работает так:
Button::render()
↓
Repository::likes()
↓
boot()
↓
один SQL для всех фотографий
↓
внутренний кэш
После первого обращения Repository загружает информацию сразу обо всех фотографиях текущего запроса WordPress.
Дальнейшие вызовы работают только с массивами PHP.
Таким образом:
50 фотографий
=
2 SQL-запроса
вместо
50 фотографий
=
100 SQL-запросов
Кэш Repository
Repository хранит два массива.
self::$likes
Количество лайков.
self::$liked
Какие фотографии уже лайкнул текущий посетитель.
После загрузки эти массивы больше не обращаются к базе данных.
Почему используется Visitor
Плагин не требует регистрации пользователя.
Вместо этого используется Visitor.
Он формирует уникальный hash посетителя.
В расчет входят:
- IP;
- User Agent.
В базе хранится только hash.
Это позволяет:
- не хранить персональные данные;
- быстро проверять повторные лайки;
- иметь уникальный индекс.
Один лайк
Повторный лайк проверяется не JavaScript.
Проверка всегда выполняется на сервере.
Алгоритм:
AJAX
↓
Repository
↓
Database::exists()
↓
если найден
↓
ошибка already
иначе
↓
insert()
Даже если пользователь отключил JavaScript или отправил запрос вручную, повторный лайк создать нельзя.
AJAX
Frontend никогда не изменяет счетчик самостоятельно.
После клика выполняется AJAX.
Ответ сервера содержит актуальное количество лайков.
Это исключает рассинхронизацию.
Почему используется делегирование событий
Фотографии могут догружаться через AJAX (Load More).
Поэтому обработчик кликов нельзя навешивать на существующие кнопки.
Используется делегирование:
document
↓
click
↓
.closest('.photo-like')
Поэтому вновь загруженные карточки начинают работать автоматически.
Работа с кешем страниц
Страницы могут быть полностью закешированы.
Из-за этого HTML может содержать старое количество лайков.
После загрузки страницы JavaScript запрашивает состояние фотографий.
Ответ включает:
- количество лайков;
- поставил ли текущий посетитель лайк.
После ответа DOM обновляется.
Таким образом:
HTML может быть полностью закеширован,
но пользователь всегда видит актуальное состояние.
Почему нет Cookie
Cookie не используются.
Вся идентификация выполняется на сервере.
Это уменьшает объем клиентского кода и исключает возможность удаления cookie как способа повторного голосования.
Кнопка Button
Button ничего не знает о SQL.
Он только выводит HTML.
Все данные получает через Repository.
Это позволяет легко заменить источник данных.
Административная часть
Используется стандартный интерфейс WordPress.
Основа:
WP_List_Table
Это обеспечивает:
- сортировку;
- пагинацию;
- внешний вид как у списка записей WordPress.
Страница статистики
Показывает:
- фотографии, имеющие лайки;
- количество лайков;
- последний лайк;
- миниатюру;
- тип записи.
Дополнительно выводится сводка:
- всего фотографий с лайками;
- всего лайков;
- среднее количество лайков;
- ТОП-5 фотографий.
Уведомление в меню
Используется стандартный WordPress bubble.
Количество отображает не число новых лайков, а количество фотографий, получивших новые лайки после последнего просмотра страницы статистики.
Время последнего просмотра хранится в:
user_meta
Это позволяет каждому администратору иметь собственный счетчик.
Почему используется user_meta
Если использовать option, открытие страницы одним администратором сбросит счетчик для всех.
user_meta хранится отдельно для каждого пользователя.
Время
Время лайков хранится в UTC.
При выводе используется часовой пояс WordPress.
Это позволяет без ошибок переносить сайт между серверами.
Поддерживаемые типы записей
Типы записей задаются только в Config.
Config::POST_TYPES
Сейчас:
portfolio
stock
Добавление нового типа записи не требует изменения остального кода.
Принципы проекта
- Один класс — одна зона ответственности.
- SQL только в Database.
- Repository является единой точкой доступа к данным.
- Button отвечает только за HTML.
- Ajax отвечает только за HTTP.
- JavaScript не принимает решений о возможности лайка.
- Все проверки выполняются сервером.
- Минимум SQL-запросов.
- Поддержка кеширования страниц.
- Максимальное использование стандартных API WordPress.
- Совместимость с PHP 7.4.
Возможные направления развития
- REST API вместо admin-ajax.
- Транзиент-кеширование статистики.
- Экспорт статистики в CSV.
- График лайков по дням.
- Виджет популярных фотографий.
- WP-CLI команды.
- Настройки плагина.
- Интеграция с Object Cache.
- Защита от массовых накруток.
- Поддержка multisite.