Создание модуля бронирования в 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) и тщательно проверять входящие данные.