PHP - Post-Redirect-Get (PRG)

Здравствуйте, ambitные программисты! Сегодня мы погрузимся в важный паттерн веб-разработки под названием Post-Redirect-Get, или PRG в кратком виде. Не волнуйтесь, если вы новички в программировании; я проведу вас через это понятие шаг за шагом, как я уже делал это для countless студентов на протяжение лет моего преподавания. Так что возьмите чашечку кофе (или ваш любимый напиток) и отправляйтесь в это захватывающее путешествие вместе со мной!

PHP - Post-Redirect-Get (PRG)

Что такое Post-Redirect-Get (PRG)?

Представьте, что вы заполняете форму для заказа пиццы в интернете. Вы жмете на кнопку "отправить", и... ой! Вы случайно обновили страницу. И suddenly, вы заказали две пиццы вместо одной! Это precisely та проблема, которую помогает решить паттерн Post-Redirect-Get.

PRG - это паттерн веб-разработки, который предотвращает дублирование отправки форм при обновлении страницы пользователем после отправки формы. Это как регулировщик движения для ваших веб-приложений, который обеспечивает плавный поток данных и prevents их случайное дублирование.

Как работает PRG

  1. Пользователь отправляет форму с использованием POST-запроса.
  2. Сервер обрабатывает данные формы.
  3. Вместо отправки прямого ответа сервер отправляет редирект (обычно на страницу успешного завершения).
  4. Браузер пользователя следует редиректу с GET-запросом.
  5. Сервер отвечает на GET-запрос с конечной страницей.

Теперь давайте посмотрим на это в действии с примерами кода!

Пример 1: Простая реализация PRG в PHP

Давайте начнем с базового примера работы PRG в PHP. Мы создадим простую форму, позволяющую пользователям отправлять их любимый цвет.

Шаг 1: HTML-форма (form.php)

<!DOCTYPE html>
<html>
<head>
<title>Форма любимого цвета</title>
</head>
<body>
<h1>Какой ваш любимый цвет?</h1>
<form action="process.php" method="post">
<input type="text" name="color" required>
<input type="submit" value="Отправить">
</form>
</body>
</html>

Эта форма отправляет POST-запрос на process.php при отправке.

Шаг 2: Обработка формы (process.php)

<?php
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$color = $_POST['color'];

// Сохраняем цвет в сессию
$_SESSION['favorite_color'] = $color;

// Перенаправляем на страницу с результатом
header('Location: result.php');
exit;
} else {
// Если кто-то пытается получить доступ к этой странице напрямую, перенаправляем их на форму
header('Location: form.php');
exit;
}

Давайте разберем это:

  1. Мы начинаем сессию для хранения данных между страницами.
  2. Мы проверяем метод запроса.
  3. Если это POST, мы сохраняем отправленный цвет в сессию.
  4. Затем мы перенаправляем пользователя на result.php.
  5. Если кто-то пытается получить доступ к этой странице напрямую (не через POST), мы перенаправляем их на форму.

Шаг 3: Отображение результата (result.php)

<?php
session_start();

if (isset($_SESSION['favorite_color'])) {
$color = $_SESSION['favorite_color'];
// Очищаем переменную сессии, чтобы предотвратить отображение старых данных при обновлении
unset($_SESSION['favorite_color']);
} else {
$color = 'Не был отправлен ни один цвет';
}
?>

<!DOCTYPE html>
<html>
<head>
<title>Ваш любимый цвет</title>
</head>
<body>
<h1>Ваш любимый цвет</h1>
<p>Вы сказали, что ваш любимый цвет это: <?php echo htmlspecialchars($color); ?></p>
<a href="form.php">Отправить другой цвет</a>
</body>
</html>

Вот что происходит:

  1. Мы начинаем сессию снова, чтобы получить сохраненные данные.
  2. Мы проверяем, был ли сохранен любимый цвет в сессии.
  3. Если да, мы отображаем его и затем очищаем переменную сессии.
  4. Если нет, мы отображаем сообщение по умолчанию.

Теперь, даже если пользователь обновит страницу с результатом, он не отправит форму заново. Круто, правда?

Пример 2: PRG с взаимодействием с базой данных

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

Шаг 1: Форма заказа (order_form.php)

<!DOCTYPE html>
<html>
<head>
<title>Форма заказа пиццы</title>
</head>
<body>
<h1>Закажите свою пиццу</h1>
<form action="process_order.php" method="post">
<label for="pizza_type">Тип пиццы:</label>
<select name="pizza_type" id="pizza_type" required>
<option value="margherita">Margherita</option>
<option value="pepperoni">Pepperoni</option>
<option value="veggie">Veggie</option>
</select>
<br><br>
<label for="size">Размер:</label>
<select name="size" id="size" required>
<option value="small">Маленькая</option>
<option value="medium">Средняя</option>
<option value="large">Большая</option>
</select>
<br><br>
<input type="submit" value="Заказать">
</form>
</body>
</html>

Шаг 2: Обработка заказа (process_order.php)

<?php
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pizza_type = $_POST['pizza_type'];
$size = $_POST['size'];

// В реальном приложении вы бы хотели sanitизировать и проверять это входное данные

// Подключаемся к базе данных (обычно вы бы поместили это в отдельный файл)
$db = new PDO('mysql:host=localhost;dbname=pizza_shop', 'username', 'password');

// Вставляем заказ
$stmt = $db->prepare("INSERT INTO orders (pizza_type, size) VALUES (?, ?)");
$stmt->execute([$pizza_type, $size]);

// Получаем ID заказа
$order_id = $db->lastInsertId();

// Сохраняем ID заказа в сессию
$_SESSION['last_order_id'] = $order_id;

// Перенаправляем на страницу подтверждения заказа
header('Location: order_confirmation.php');
exit;
} else {
// Если кто-то пытается получить доступ к этой странице напрямую, перенаправляем их на форму
header('Location: order_form.php');
exit;
}

Этот скрипт:

  1. Обрабатывает данные формы.
  2. Вставляет заказ в базу данных.
  3. Сохраняет ID заказа в сессию.
  4. Перенаправляет на страницу подтверждения заказа.

Шаг 3: Подтвердждение заказа (order_confirmation.php)

<?php
session_start();

if (isset($_SESSION['last_order_id'])) {
$order_id = $_SESSION['last_order_id'];
// Очищаем переменную сессии
unset($_SESSION['last_order_id']);

// В реальном приложении вы бы получили детали заказа из базы данных здесь
// Для этого примера мы просто отображаем ID заказа
$message = "Ваш заказ (ID: $order_id) успешно оформлен!";
} else {
$message = "Не найден ни один последний заказ. Пожалуйста, оформите новый заказ.";
}
?>

<!DOCTYPE html>
<html>
<head>
<title>Подтвердждение заказа</title>
</head>
<body>
<h1>Подтвердждение заказа</h1>
<p><?php echo htmlspecialchars($message); ?></p>
<a href="order_form.php">Оформить другой заказ</a>
</body>
</html>

Эта страница подтверждения:

  1. Получает ID заказа из сессии.
  2. Отображает confirmационное сообщение.
  3. Очищает переменную сессии, чтобы предотвратить отображение того же заказа при обновлении.

Why PRG is Important

  1. Предотвращает дублирование отправки: Если пользователь обновит confirmационную страницу, они случайно не отправят заказ заново.
  2. Улучшает пользовательский опыт: Пользователи видят чистый URL в строке адреса, а не длинную строку данных формы.
  3. Следует принципам REST: GET-запросы используются для получения данных, а POST-запросы для их отправки.

Common PRG Methods

Вот таблица_common методов, используемых в паттерне PRG:

Method Description
$_SERVER['REQUEST_METHOD'] Проверяет HTTP-метод запроса (GET, POST и т.д.)
header('Location: ...') Отправляет заголовок редиректа браузеру
exit Обеспечивает, чтобы после редиректа не выполнялся further код
session_start() Начинает новую сессию или возобновляет existующую
$_SESSION Хранит и retrieve данные сессии
isset() Проверяет, установлена ли переменная и не NULL
unset() Удаляет указанную переменную

Remember, young padawans, the PRG pattern is like using the Force in web development. It brings balance to your applications, preventing the dark side of duplicate form submissions. May the code be with you!

In conclusion, the Post-Redirect-Get pattern is a powerful tool in your web development toolkit. It helps create more robust and user-friendly applications by preventing accidental duplicate submissions. As you continue your journey in PHP and web development, you'll find many more situations where PRG can save the day. Keep practicing, stay curious, and happy coding!

Credits: Image by storyset