Простая система реакций для постов как у меня в блоге. PHP8+MYSQL+JS
Недавно я писал как сделать систему лайков для сайта и я решил пойти дальше написав код для создания системы реакций, как у меня на сайте. Данная система будет реализована на PHP не ниже 7.4, у меня она работает на PHP 8 с подключением к базе данных MYSQL и небольшой скрипт JS.
Содержание статьи
Требования к реакциям
- Реакцию можно поставить только 1 раз. Определяем пользователя по IP.
- Все реакции пользователей должны храниться в базе данных.
- Для каждой реакции выводим счетчик их количества из базы данных.
HTML код блока реакций
<div class="reaction-container" data-post-id="{{id}}" id="reactionContainer">
<div class="reaction" data-emoji="👍" data-id="like"> <img src="/assets/svg/thumbs.svg" alt="" style="width: 24px;height: 24px;"> <span>0</span> </div>
<div class="reaction" data-emoji="❤️" data-id="heart"> <img src="/assets/svg/heart.svg" alt="" style="width: 24px;height: 24px;"> <span>0</span> </div>
<div class="reaction" data-emoji="🔥" data-id="fire"> <img src="/assets/svg/fire.svg" alt="" style="width: 24px;height: 24px;"> <span>0</span> </div>
<div class="reaction" data-emoji="😂" data-id="laugh"> <img src="/assets/svg/tears.svg" alt="" style="width: 24px;height: 24px;"> <span>0</span> </div>
<div class="reaction" data-emoji="😮" data-id="surprise"> <img src="/assets/svg/astonished.svg" alt="" style="width: 24px;height: 24px;"> <span>0</span> </div>
</div>
Разъяснения по коду выше. Вообще-то это не совсем чистый HTML. У меня CMS Publii, который генерирует статический HTML код через файлы hbs.
В атрибуте data-post-id="{{id}}"
я получаю ID поста. Я не знаю какая у вас CMS или фреймворк, поэтому вы должны посмотреть в документации как получить id конкретного поста и сделать это.
В атрибуте data-emoji=""
я вывожу emoji реакции. Вы можете определить свой набор emoji, например вы можете взять их из этой таблицы.
PHP обработка добавления реакции в базу данных
Для начала нужно создать таблицу user_reactions в базе данных Mysql, которая будет содержать уникальный id, id поста, тип реакции, ip адрес пользователя, дата и время реакции.
CREATE TABLE `user_reactions` (
`id` int(11) NOT NULL,
`post_id` int(11) NOT NULL,
`reaction_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`ip_address` varchar(50) DEFAULT NULL,
`created_at` datetime DEFAULT current_timestamp()
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
Добавим обязательные индексы:
ALTER TABLE `user_reactions`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `unique_reaction` (`post_id`,`ip_address`);
Добавим AUTO_INCREMENT для колонки id, чтобы автоматически добавлялся уникальная цифра, при каждом добавлении реакции:
ALTER TABLE `user_reactions`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
Должна получиться такая структура:
Создаем файл reactions_handler.php, который будет обрабатывать добавление реакции
<?php
// Запрет прямого доступа
if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] !== 'XMLHttpRequest') {
http_response_code(403);
exit('Access denied');
}
header('Content-Type: application/json; charset=utf-8');
$host = 'localhost';
$dbname = 'bd';
$username = 'bd_user';
$password = 'password';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
} catch (PDOException $e) {
echo json_encode(['status' => 'error', 'message' => 'Database connection failed']);
exit();
}
$data = json_decode(file_get_contents('php://input'), true);
if (!isset($data['postId']) || !isset($data['emoji'])) {
echo json_encode(['status' => 'error', 'message' => 'Invalid data']);
exit();
}
$postId = intval($data['postId']);
$emoji = $data['emoji'];
$ipAddress = $_SERVER['REMOTE_ADDR'];
// Список допустимых реакций
$validReactions = ['❤️', '👍', '😂', '🔥', '😮'];
if (!in_array($emoji, $validReactions)) {
echo json_encode(['status' => 'error', 'message' => 'Invalid reaction']);
exit();
}
// Проверяем существующую реакцию
$stmt = $pdo->prepare("
SELECT id FROM user_reactions
WHERE post_id = :post_id AND ip_address = :ip_address
");
$stmt->execute(['post_id' => $postId, 'ip_address' => $ipAddress]);
$userReactionExists = $stmt->fetchColumn();
if ($userReactionExists) {
echo json_encode(['status' => 'error', 'message' => 'Вы уже поставили реакцию этому посту']);
exit();
}
// Добавляем реакцию
$stmt = $pdo->prepare("
INSERT INTO user_reactions (post_id, reaction_type, ip_address, created_at)
VALUES (:post_id, :reaction_type, :ip_address, NOW())
");
$stmt->execute(['post_id' => $postId, 'reaction_type' => $emoji, 'ip_address' => $ipAddress]);
echo json_encode(['status' => 'success']);
Важные моменты по коду:
В этой части нужно прописать данные подключения к базе данных, которые выдает хостинг-провайдер.
$host = 'localhost';
$dbname = 'bd'; // Имя базы данных
$username = 'bd_user'; // Имя пользователя
$password = 'password'; // Пароль базы данных
В этой переменной указываем все emoji реакции, которые вы изначально добавили в HTML.
// Список допустимых реакций
$validReactions = ['❤️', '👍', '😂', '🔥', '😮'];
PHP обработка получения реакции из базы данных
Теперь нужно данные, которые были записаны в БД подтянуть для вывода на сайте, а это счетчик каждой реакции.
Создаем файл get_reactions.php.
<?php
// Запрет прямого доступа
if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] !== 'XMLHttpRequest') {
http_response_code(403);
exit('Access denied');
}
header('Content-Type: application/json; charset=utf-8');
$host = 'localhost';
$dbname = 'bd';
$username = 'bd_user';
$password = 'password';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
} catch (PDOException $e) {
echo json_encode(['status' => 'error', 'message' => 'Database connection failed']);
exit();
}
if (!isset($_GET['post_id'])) {
echo json_encode(['status' => 'error', 'message' => 'Post ID is missing']);
exit();
}
$postId = intval($_GET['post_id']);
$ipAddress = $_SERVER['REMOTE_ADDR'];
// Получаем реакции для поста
$stmt = $pdo->prepare("
SELECT reaction_type, COUNT(*) as count
FROM user_reactions
WHERE post_id = :post_id
GROUP BY reaction_type
");
$stmt->execute(['post_id' => $postId]);
$reactions = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Проверяем реакцию текущего пользователя
$stmt = $pdo->prepare("
SELECT reaction_type
FROM user_reactions
WHERE post_id = :post_id AND ip_address = :ip_address
");
$stmt->execute(['post_id' => $postId, 'ip_address' => $ipAddress]);
$userReaction = $stmt->fetchColumn();
$response = [
'status' => 'success',
'reactions' => array_map(function ($reaction) {
return [
'emoji' => $reaction['reaction_type'],
'count' => intval($reaction['count']),
];
}, $reactions),
'userReaction' => $userReaction,
];
echo json_encode($response);
И опять в этом файле указываем подключение к базе данных. Да, можно было создать отдельный файл config.php и подключать через него не указывая 2 раза данные для подключения к БД, но в моем случае в этом нет необходимости, а вы можете это сделать, если знаете как.
Скрипт JS для обработки реакций
Теперь нужно подключить файле reactions.js, чтобы система реакций корректно заработала.
document.addEventListener('DOMContentLoaded', () => {
const reactionContainers = document.querySelectorAll('.reaction-container');
// Обработка каждого контейнера реакций
reactionContainers.forEach(container => {
const postId = container.getAttribute('data-post-id');
// Загрузка реакций для каждого поста
fetch(`/get_reactions.php?post_id=${postId}`, {
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
updateReactionsUI(container, data.reactions, data.userReaction);
}
})
.catch(err => console.error('Error loading reactions:', err));
// Обработка кликов на реакции
container.addEventListener('click', (event) => {
const reaction = event.target.closest('.reaction');
if (!reaction) return;
const emoji = reaction.getAttribute('data-emoji');
console.log(`Clicked on post ID: ${postId}, reaction: ${emoji}`);
// Отправка реакции на сервер
fetch('/reactions_handler.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({ postId, emoji })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
const countSpan = reaction.querySelector('span');
countSpan.textContent = parseInt(countSpan.textContent, 10) + 1;
} else {
alert(data.message);
}
})
.catch(err => console.error('Error sending reaction:', err));
});
});
function updateReactionsUI(container, reactions, userReaction) {
reactions.forEach(reaction => {
const reactionElement = container.querySelector(`[data-emoji="${reaction.emoji}"]`);
if (reactionElement) {
const countSpan = reactionElement.querySelector('span');
countSpan.textContent = reaction.count;
if (reaction.emoji === userReaction) {
reactionElement.classList.add('active');
} else {
reactionElement.classList.remove('active');
}
}
});
}
});
Данный скрипт:
- Находит блок с классом reaction-container, который мы создали раннее.
- В переменную postId получает id поста из атрибута data-post-id.
- Отправляет POST запрос на сервер в php обработчик reactions_handler.php.
- Возвращает для каждого поста данные из базы данных при помощи php обработчика get_reactions.php.
- При помощи функции updateReactionsUI обновляет счетчик реакции.
Для пользователей, которые возможно будут внедрять данную систему реакций я подготовил архив, куда поместил все необходимые файлы. Вот и всё! Удачный проектов!