PHP - Post-Redirect-Get (PRG)

Hello there, aspiring programmers! Today, we're going to dive into an important web development pattern called Post-Redirect-Get, or PRG for short. Don't worry if you're new to programming; I'll guide you through this concept step by step, just like I've done for countless students in my years of teaching. So, grab a cup of coffee (or your favorite beverage), and let's embark on this exciting journey together!

PHP - Post-Redirect-Get (PRG)

What is Post-Redirect-Get (PRG)?

Imagine you're filling out a form to order a pizza online. You click submit, and then... oops! You accidentally refresh the page. Suddenly, you've ordered two pizzas instead of one! This is the kind of problem that the Post-Redirect-Get pattern helps to solve.

PRG is a web development design pattern that prevents duplicate form submissions when a user refreshes the page after submitting a form. It's like a traffic cop for your web applications, ensuring that data flows smoothly and doesn't get accidentally duplicated.

How PRG Works

  1. The user submits a form using a POST request.
  2. The server processes the form data.
  3. Instead of sending a response directly, the server sends a redirect (usually to a success page).
  4. The user's browser follows the redirect with a GET request.
  5. The server responds to the GET request with the final page.

Now, let's see this in action with some code examples!

Example 1: A Simple PRG Implementation

Let's start with a basic example of how PRG works in PHP. We'll create a simple form that allows users to submit their favorite color.

Step 1: The HTML Form (form.php)

<!DOCTYPE html>
<html>
<head>
    <title>Favorite Color Form</title>
</head>
<body>
    <h1>What's Your Favorite Color?</h1>
    <form action="process.php" method="post">
        <input type="text" name="color" required>
        <input type="submit" value="Submit">
    </form>
</body>
</html>

This form sends a POST request to process.php when submitted.

Step 2: Processing the Form (process.php)

<?php
session_start();

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

    // Save the color to the session
    $_SESSION['favorite_color'] = $color;

    // Redirect to the result page
    header('Location: result.php');
    exit;
} else {
    // If someone tries to access this page directly, redirect them to the form
    header('Location: form.php');
    exit;
}

Let's break this down:

  1. We start a session to store data between pages.
  2. We check if the request method is POST.
  3. If it is, we save the submitted color to the session.
  4. We then redirect the user to result.php.
  5. If someone tries to access this page directly (not via POST), we send them back to the form.

Step 3: Displaying the Result (result.php)

<?php
session_start();

if (isset($_SESSION['favorite_color'])) {
    $color = $_SESSION['favorite_color'];
    // Clear the session variable to prevent displaying old data on refresh
    unset($_SESSION['favorite_color']);
} else {
    $color = 'No color submitted';
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>Your Favorite Color</title>
</head>
<body>
    <h1>Your Favorite Color</h1>
    <p>You said your favorite color is: <?php echo htmlspecialchars($color); ?></p>
    <a href="form.php">Submit another color</a>
</body>
</html>

Here's what's happening:

  1. We start the session again to access the stored data.
  2. We check if a favorite color was stored in the session.
  3. If it was, we display it and then clear the session variable.
  4. If not, we display a default message.

Now, even if the user refreshes the result page, they won't resubmit the form data. Neat, right?

Example 2: PRG with Database Interaction

Let's take our example a step further and imagine we're running a pizza ordering system. We'll use a database to store orders and implement PRG to prevent duplicate orders.

Step 1: The Order Form (order_form.php)

<!DOCTYPE html>
<html>
<head>
    <title>Pizza Order Form</title>
</head>
<body>
    <h1>Order Your Pizza</h1>
    <form action="process_order.php" method="post">
        <label for="pizza_type">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">Size:</label>
        <select name="size" id="size" required>
            <option value="small">Small</option>
            <option value="medium">Medium</option>
            <option value="large">Large</option>
        </select>
        <br><br>
        <input type="submit" value="Place Order">
    </form>
</body>
</html>

Step 2: Processing the Order (process_order.php)

<?php
session_start();

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

    // In a real application, you'd want to sanitize and validate this input

    // Connect to the database (you'd typically put this in a separate file)
    $db = new PDO('mysql:host=localhost;dbname=pizza_shop', 'username', 'password');

    // Insert the order
    $stmt = $db->prepare("INSERT INTO orders (pizza_type, size) VALUES (?, ?)");
    $stmt->execute([$pizza_type, $size]);

    // Get the order ID
    $order_id = $db->lastInsertId();

    // Store the order ID in the session
    $_SESSION['last_order_id'] = $order_id;

    // Redirect to the confirmation page
    header('Location: order_confirmation.php');
    exit;
} else {
    // If someone tries to access this page directly, redirect them to the form
    header('Location: order_form.php');
    exit;
}

This script:

  1. Processes the form data.
  2. Inserts the order into a database.
  3. Stores the order ID in the session.
  4. Redirects to a confirmation page.

Step 3: Order Confirmation (order_confirmation.php)

<?php
session_start();

if (isset($_SESSION['last_order_id'])) {
    $order_id = $_SESSION['last_order_id'];
    // Clear the session variable
    unset($_SESSION['last_order_id']);

    // In a real application, you'd fetch the order details from the database here
    // For this example, we'll just display the order ID
    $message = "Your order (ID: $order_id) has been placed successfully!";
} else {
    $message = "No recent order found. Please place a new order.";
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>Order Confirmation</title>
</head>
<body>
    <h1>Order Confirmation</h1>
    <p><?php echo htmlspecialchars($message); ?></p>
    <a href="order_form.php">Place another order</a>
</body>
</html>

This confirmation page:

  1. Retrieves the order ID from the session.
  2. Displays a confirmation message.
  3. Clears the session variable to prevent showing the same order on refresh.

Why PRG is Important

  1. Prevents Duplicate Submissions: If a user refreshes the confirmation page, they won't accidentally resubmit their order.
  2. Improves User Experience: Users see a clean URL in their address bar, not a long string of form data.
  3. Follows REST Principles: GET requests are used for retrieving data, while POST requests are used for submitting data.

Common PRG Methods

Here's a table of common methods used in the PRG pattern:

Method Description
$_SERVER['REQUEST_METHOD'] Checks the HTTP request method (GET, POST, etc.)
header('Location: ...') Sends a redirect header to the browser
exit Ensures no further code is executed after the redirect
session_start() Starts a new session or resumes an existing one
$_SESSION Stores and retrieves session data
isset() Checks if a variable is set and is not NULL
unset() Destroys a specified variable

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