Создание модуля бронирования в PHP для магазина

Раздел: -> Электронная коммерция

Введение в системы бронирования на PHP

Система бронирования (booking) - один из ключевых компонентов электронной коммерции, позволяющий клиентам заказывать услуги, товары или ресурсы на определённые даты и временные слоты. В этой статье разбираются практические варианты реализации на PHP - от простого файлового хранения до решения с базой данных и AJAX. Каждый подход сопровождается кодом, пояснениями и типичными проблемами.

Основное решение: бронирование с использованием MySQL и PDO

Наиболее эффективный способ - хранение данных в реляционной базе (MySQL) и работа через PDO. Это обеспечивает целостность, защиту от SQL-инъекций и поддержку параллельных запросов.

1. Создание таблиц

Пример структуры для бронирования номеров отеля:


CREATE TABLE rooms (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    capacity INT NOT NULL,
    price DECIMAL(10,2) NOT NULL
);

CREATE TABLE bookings (
    id INT AUTO_INCREMENT PRIMARY KEY,
    room_id INT NOT NULL,
    guest_name VARCHAR(100) NOT NULL,
    guest_email VARCHAR(100) NOT NULL,
    check_in DATE NOT NULL,
    check_out DATE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE
);
  

Booking php (система бронирования на php)

2. Проверка доступности и бронирование

Скрипт book.php обрабатывает форму, проверяет пересечение дат с уже существующими бронями и вставляет запись. Для защиты от race condition используется транзакция с блокировкой SELECT ... FOR UPDATE.


<?php
$pdo = new PDO('mysql:host=localhost;dbname=booking', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$roomId = $_POST['room_id'];
$checkIn = $_POST['check_in'];
$checkOut = $_POST['check_out'];
$guestName = $_POST['guest_name'];
$guestEmail = $_POST['guest_email'];

try {
    $pdo->beginTransaction();

    // Блокировка строк с комнатой для исключения одновременных броней
    $stmt = $pdo->prepare("SELECT id FROM rooms WHERE id = ? FOR UPDATE");
    $stmt->execute([$roomId]);

    // Проверка пересечения дат
    $stmt = $pdo->prepare("
        SELECT COUNT(*) FROM bookings
        WHERE room_id = ?
        AND check_in < ?
        AND check_out > ?
    ");
    $stmt->execute([$roomId, $checkOut, $checkIn]);
    $overlap = $stmt->fetchColumn();

    if ($overlap > 0) {
        throw new Exception('Выбранные даты недоступны.');
    }

    // Вставка брони
    $stmt = $pdo->prepare("
        INSERT INTO bookings (room_id, guest_name, guest_email, check_in, check_out)
        VALUES (?, ?, ?, ?, ?)
    ");
    $stmt->execute([$roomId, $guestName, $guestEmail, $checkIn, $checkOut]);

    $pdo->commit();
    echo 'Бронирование успешно оформлено.';
} catch (Exception $e) {
    $pdo->rollBack();
    echo 'Ошибка: ' . $e->getMessage();
}
  

Orders php orders action (заказы в php)

3. Вывод свободных номеров

Пример выборки комнат, свободных в заданном диапазоне дат:


$checkIn = '2025-07-01';
$checkOut = '2025-07-05';

$stmt = $pdo->prepare("
    SELECT * FROM rooms
    WHERE id NOT IN (
        SELECT room_id FROM bookings
        WHERE check_in < ?
        AND check_out > ?
    )
");
$stmt->execute([$checkOut, $checkIn]);
$rooms = $stmt->fetchAll();
  

Product view php (просмотр товара php)

Возможные проблемы и их решение

  • Race condition - если не использовать транзакции и блокировку, два пользователя могут одновременно получить информацию о свободном номере и забронировать его. Решение: BEGIN TRANSACTION + FOR UPDATE.
  • SQL-инъекции - решаются через PDO с подготовленными выражениями.
  • Некорректные даты - необходима валидация на стороне PHP (например, check_in < check_out, дата не в прошлом).

Цели и случаи использования

Подходит для средних и крупных проектов, где важна производительность, безопасность и поддержка множества одновременных бронирований. Примеры: бронирование гостиниц, конференц-залов, автомобилей.

Варианты реализации

Как создать систему бронирования без базы данных?

Использование JSON-файла для хранения броней. Подходит для прототипов, небольших проектов или временных решений.


$file = 'bookings.json';
$bookings = file_exists($file) ? json_decode(file_get_contents($file), true) : [];

// Проверка доступности
function isAvailable($newStart, $newEnd, $bookings, $roomId) {
    foreach ($bookings as $b) {
        if ($b['room_id'] == $roomId) {
            if ($b['check_in'] < $newEnd && $b['check_out'] > $newStart) {
                return false;
            }
        }
    }
    return true;
}

// Добавление брони
if (isAvailable($checkIn, $checkOut, $bookings, $roomId)) {
    $bookings[] = [
        'room_id' => $roomId,
        'guest_name' => $guestName,
        'check_in' => $checkIn,
        'check_out' => $checkOut
    ];
    file_put_contents($file, json_encode($bookings, JSON_PRETTY_PRINT));
}
  

Catalogue php page (каталог на php)

Проблемы файлового хранилища

  • Одновременный доступ - возможна потеря данных (race condition). Решение: блокировка файла flock() или использование БД.
  • Низкая скорость при большом количестве записей.
  • Сложность поиска и фильтрации.

Цели:

Быстрое создание MVP, обучение, проекты с минимальными требованиями к надёжности.

Как быстро внедрить готовый календарь бронирования?

Использование PHP-библиотеки PHP Booking Calendar. Позволяет получить готовый веб-интерфейс и управление бронями.


// Установка через Composer
// composer require ivandos/booking-calendar

require_once 'vendor/autoload.php';

use BookingCalendar\BookingCalendar;

$calendar = new BookingCalendar([
    'db' => [
        'host' => 'localhost',
        'user' => 'root',
        'password' => '',
        'dbname' => 'booking_test'
    ],
    'table' => 'bookings',
    'dateStart' => '2025-07-01',
    'dateEnd' => '2025-07-31'
]);
$calendar->render();
  

Типичные сложности

  • Настройка внешнего вида - требуется знание CSS/JS библиотеки.
  • Ограниченная гибкость под нестандартные бизнес-процессы.
  • Зависимость от стороннего кода (обновления, совместимость).

Цели:

Быстрая интеграция в уже существующий проект, когда нет времени на разработку с нуля.

Как реализовать бронирование в реальном времени без перезагрузки страницы?

Использование AJAX-запросов к PHP-скрипту для проверки доступности и оформления брони. Фронтенд может быть реализован на простом JavaScript или Vue.js.


// JavaScript (fetch)
document.getElementById('bookForm').addEventListener('submit', function(e) {
    e.preventDefault();
    const formData = new FormData(this);

    fetch('book_ajax.php', {
        method: 'POST',
        body: formData
    })
    .then(response => response.json())
    .then(data => {
        if (data.success) {
            alert('Бронь подтверждена');
        } else {
            alert('Ошибка: ' + data.error);
        }
    })
    .catch(error => console.error(error));
});
  

// PHP (book_ajax.php)
header('Content-Type: application/json');
$pdo = new PDO(...);
// ... логика бронирования как в основном решении
echo json_encode(['success' => true]);
  

Возможные ошибки

  • CORS-политика - если скрипт и HTML лежат на разных доменах.
  • Отсутствие CSRF-защиты - необходимо добавлять токен.
  • Необходимость обработки ошибок на стороне клиента (сетевые сбои).

Цели:

Современные интерактивные интерфейсы, повышение удобства пользователя, снижение нагрузки на сервер за счёт частичной валидации на клиенте.

Расширенные примеры работы с бронированием

1. Отмена бронирования с возвратом статуса

Пример скрипта, который отменяет бронь по ID и возвращает сообщение об успехе.

Пример

<?php
$bookingId = $_POST['booking_id'];
$pdo = new PDO('mysql:host=localhost;dbname=booking', 'user', 'pass');
$stmt = $pdo->prepare("DELETE FROM bookings WHERE id = ?");
$stmt->execute([$bookingId]);

if ($stmt->rowCount() > 0) {
    echo 'Бронь #' . $bookingId . ' отменена.';
} else {
    echo 'Бронь не найдена.';
}
?>
Бронь #15 отменена.

2. Управление временными слотами (например, почасовое бронирование)

Изменение структуры: вместо check_in / check_out использовать start_time / end_time типа DATETIME. Пример проверки пересечения для временных слотов.

Пример

$stmt = $pdo->prepare("
    SELECT COUNT(*) FROM bookings
    WHERE room_id = ?
    AND start_time < ?
    AND end_time > ?
");
$stmt->execute([$roomId, $endTime, $startTime]);

3. Административная панель: просмотр и фильтрация броней

Вывод всех броней за текущий месяц с возможностью поиска по имени гостя.

Пример

$stmt = $pdo->prepare("
    SELECT b.id, r.name, b.guest_name, b.check_in, b.check_out
    FROM bookings b
    JOIN rooms r ON b.room_id = r.id
    WHERE b.check_in BETWEEN ? AND ?
    ORDER BY b.check_in
");
$stmt->execute([$firstDayOfMonth, $lastDayOfMonth]);
$bookings = $stmt->fetchAll();
+----+-----------+-------------+------------+------------+
| id | name      | guest_name  | check_in   | check_out  |
+----+-----------+-------------+------------+------------+
|  1 | Люкс      | Иванов Иван | 2025-07-03 | 2025-07-05 |
|  2 | Стандарт  | Петров Петр | 2025-07-04 | 2025-07-06 |
+----+-----------+-------------+------------+------------+

4. Генерация отчёта по загрузке номеров

Подсчёт количества забронированных дней для каждой комнаты за период.

Пример

$stmt = $pdo->query("
    SELECT r.name,
           SUM(DATEDIFF(b.check_out, b.check_in)) as booked_days
    FROM bookings b
    JOIN rooms r ON b.room_id = r.id
    WHERE b.check_in >= '2025-07-01' AND b.check_out <= '2025-07-31'
    GROUP BY r.id
");
while ($row = $stmt->fetch()) {
    echo $row['name'] . ': ' . $row['booked_days'] . ' дней
'; }
Люкс: 10 дней
Стандарт: 7 дней

5. Работа с часовыми поясами

Сохранение времени в UTC и отображение в локальном времени пользователя. Пример установки временной зоны в PHP.

Пример

date_default_timezone_set('UTC');
// сохраняем время в UTC
$stmt = $pdo->prepare("INSERT INTO bookings (start_time) VALUES (?)");
$stmt->execute([(new DateTime('now', new DateTimeZone('UTC')))->format('Y-m-d H:i:s')]);

// при выводе преобразуем в локальное
$localTz = new DateTimeZone('Europe/Moscow');
$dt = new DateTime($row['start_time'], new DateTimeZone('UTC'));
$dt->setTimezone($localTz);
echo $dt->format('Y-m-d H:i:s');

6. Использование готового REST API для бронирования

Создание простого RESTful эндпоинта на PHP, принимающего JSON и возвращающего результат.

Пример

// api.php
header('Content-Type: application/json');
$method = $_SERVER['REQUEST_METHOD'];

if ($method === 'POST') {
    $data = json_decode(file_get_contents('php://input'), true);
    // валидация и бронирование...
    echo json_encode(['status' => 'ok', 'booking_id' => 42]);
} elseif ($method === 'GET' && isset($_GET['room_id'])) {
    // возврат занятых дат
    echo json_encode(['dates' => ['2025-07-05', '2025-07-06']]);
}
// Ответ на POST запрос
{"status":"ok","booking_id":42}

Все приведённые примеры могут быть адаптированы под конкретный бизнес-процесс: бронирование столиков, рабочего пространства, оборудования или туров. Рекомендуется всегда использовать подготовленные запросы (PDO) и тщательно проверять входящие данные.

Система бронирования на PHP - comments

En
Booking php (php)