W3docs

PHP AJAX Опрос

Создайте AJAX-опрос на PHP: запись голосов и вывод результатов без перезагрузки страницы с fetch, JSON и MySQL.

Что такое AJAX-опрос?

AJAX-опрос — небольшой виджет голосования, позволяющий пользователю выбрать вариант ответа, отправить голос на сервер в фоновом режиме и увидеть обновлённые результаты — всё это без перезагрузки страницы. AJAX (Asynchronous JavaScript and XML) — техника обмена данными с сервером через JavaScript уже после загрузки страницы.

В этой главе мы создадим полноценный опрос: таблицу MySQL для хранения вариантов ответов и счётчиков голосов, PHP-скрипт, который записывает голос и возвращает текущий подсчёт в формате JSON, а также небольшой JavaScript-код, отправляющий голос и отображающий результаты.

Если вам нужно непрерывно обновлять данные по таймеру (живое табло, панель статусов), применяются те же принципы — такой подход называется поллингом, и мы рассмотрим его в конце главы.

Зачем использовать AJAX для опроса?

  • Без перезагрузки страницы. Пользователь остаётся на странице; обновляется только область опроса. Это происходит мгновенно и сохраняет состояние остальной части страницы (позицию прокрутки, видео, введённые данные формы).
  • Меньше передаваемых данных. Вместо повторной отправки всего HTML-документа сервер возвращает только счётчики голосов в виде небольшого JSON-пакета.
  • Результаты в реальном времени. Поскольку ответ содержит данные, а не разметку, можно сразу перерисовать полосу результатов и даже обновлять её по таймеру, отражая голоса других пользователей.

Шаг 1 — Создание таблицы базы данных

Каждый вариант ответа — отдельная строка. Мы храним текст варианта и текущий счётчик голосов.

CREATE TABLE poll_options (
    id INT AUTO_INCREMENT PRIMARY KEY,
    option_text VARCHAR(100) NOT NULL,
    votes INT NOT NULL DEFAULT 0
);

INSERT INTO poll_options (option_text) VALUES
    ('PHP'),
    ('JavaScript'),
    ('Python');

Если создание таблиц для вас ново, смотрите Создание таблицы MySQL и Вставка данных.

Шаг 2 — Создание HTML-формы

Форма отображает варианты ответов в виде переключателей и резервирует контейнер для результатов.

<form id="pollForm">
  <p>What is your favorite language?</p>
  <label><input type="radio" name="option" value="1"> PHP</label>
  <label><input type="radio" name="option" value="2"> JavaScript</label>
  <label><input type="radio" name="option" value="3"> Python</label>
  <button type="submit">Vote</button>
</form>

<div id="pollResults"></div>

Значение value каждого переключателя — это id варианта из базы данных: именно это нам нужно отправить на сервер.

Шаг 3 — Написание PHP-обработчика

PHP-скрипт выполняет две задачи: записывает голос (если он отправлен) и возвращает текущие результаты в формате JSON. Всегда используйте подготовленные выражения, чтобы значение, пришедшее из браузера, никогда не могло быть интерпретировано как SQL.

<?php
header('Content-Type: application/json');

$dsn = "mysql:host=localhost;dbname=your_database;charset=utf8mb4";
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];

try {
    $pdo = new PDO($dsn, 'username', 'password', $options);

    // If a vote was submitted, increment that option's counter.
    if (isset($_POST['option'])) {
        $optionId = (int) $_POST['option'];
        $update = $pdo->prepare(
            "UPDATE poll_options SET votes = votes + 1 WHERE id = :id"
        );
        $update->execute([':id' => $optionId]);
    }

    // Always return the current tally.
    $rows = $pdo->query(
        "SELECT id, option_text, votes FROM poll_options ORDER BY id"
    )->fetchAll();

    $total = array_sum(array_column($rows, 'votes'));

    echo json_encode([
        'status'  => 'success',
        'total'   => $total,
        'options' => $rows,
    ]);
} catch (PDOException $e) {
    http_response_code(500);
    echo json_encode(['status' => 'error', 'message' => 'Database error']);
}

Приведение входящего id с помощью (int) и его привязка через :id гарантируют, что значение обрабатывается строго как число, а не как часть запроса. Подробнее читайте в PHP Подготовленные выражения и json_encode().

Шаг 4 — Отправка голоса через JavaScript

Мы перехватываем событие submit формы, отправляем выбранный вариант через fetch(), затем отображаем полосу результатов на основе JSON-ответа.

const form = document.getElementById('pollForm');
const resultsBox = document.getElementById('pollResults');

form.addEventListener('submit', function (event) {
  event.preventDefault(); // Stop the normal page reload.

  const selected = form.querySelector('input[name="option"]:checked');
  if (!selected) return; // Nothing chosen yet.

  const body = new URLSearchParams({ option: selected.value });

  fetch('poll.php', { method: 'POST', body })
    .then(response => response.json())
    .then(showResults)
    .catch(error => console.error('Vote failed:', error));
});

function showResults(data) {
  if (data.status !== 'success') {
    resultsBox.textContent = 'Could not load results.';
    return;
  }

  resultsBox.innerHTML = data.options.map(opt => {
    const percent = data.total
      ? Math.round((opt.votes / data.total) * 100)
      : 0;
    return `<div>${opt.option_text}: ${opt.votes} votes (${percent}%)</div>`;
  }).join('');
}

event.preventDefault() останавливает полную отправку формы браузером — именно в этом и смысл AJAX. Процентное соотношение вычисляется на клиенте из счётчиков, возвращённых сервером, поэтому полоса всегда соответствует актуальным данным.

Шаг 5 — Поддержание актуальности результатов с помощью поллинга

До сих пор результаты обновлялись только когда голосовал текущий пользователь. Чтобы отражать голоса других пользователей, опрашивайте сервер по таймеру — запрашивайте последний подсчёт каждые несколько секунд и перерисовывайте:

function refreshResults() {
  fetch('poll.php') // GET, no vote — just read the tally.
    .then(response => response.json())
    .then(showResults)
    .catch(error => console.error('Refresh failed:', error));
}

setInterval(refreshResults, 5000); // Every 5 seconds.

Поскольку PHP-обработчик возвращает подсчёт при каждом запросе (голос или нет), для получения свежих данных достаточно обычного GET-запроса. Выбирайте интервал аккуратно: короткий интервал создаёт ощущение большей живости, но увеличивает количество запросов. Для данных, которые должны быть по-настоящему в реальном времени, WebSockets или Server-Sent Events подходят лучше, чем поллинг с фиксированным интервалом.

Типичные ошибки

  • Забыть event.preventDefault(). Без него форма перезагружает страницу, и AJAX-запрос отменяется на полпути.
  • Доверять id варианта. Всегда приводите/валидируйте его и используйте подготовленное выражение; никогда не конкатенируйте $_POST в SQL.
  • Двойной подсчёт. Этот базовый пример позволяет одному браузеру голосовать множество раз. В продакшене ограничьте голосование по сессии, cookie или IP.
  • Слишком агрессивный поллинг. Интервал в одну секунду на загруженной странице может перегрузить сервер; обычно достаточно 3–10 секунд.

Итоги

AJAX-опрос сочетает в себе таблицу MySQL, PHP-эндпоинт, который записывает голос и возвращает подсчёт в формате JSON, и JavaScript, отправляющий данные через fetch() и отображающий результаты — без перезагрузки страницы. Добавьте поллинг через setInterval, чтобы цифры оставались актуальными для всех.

graph TD;
    A(User Selects Option and Clicks Vote) --> B(JavaScript Sends POST with fetch);
    B --> C(PHP Increments Vote with Prepared Statement);
    C --> D(PHP Returns Tally as JSON);
    D --> E(JavaScript Renders Results Bar);
    F(Timer Every 5s) --> G(GET poll.php for Latest Tally);
    G --> D;

Практика

Практика
Каковы шаги создания AJAX-опроса на PHP?
Каковы шаги создания AJAX-опроса на PHP?
Was this page helpful?