Я строю систему бронирования в стиле отеля, и я заблудился. Я застрял именно в момент бронирования, когда добавляю бронирование, и меня беспокоят возникающие ситуации.
Мой процесс очень прост, он позволяет нескольким пользователям проходить процесс бронирования для одной и той же комнаты, пока один из них не нажмет кнопку бронирования раньше других. Информация о резервировании хранится в сеансе и никогда не сохраняется в БД до тех пор, пока кто-нибудь не достигнет последнего шага и не нажмет «Забронировать». На каждом шаге система проверяет, доступны ли комнаты / комнаты, а если нет, выдает страницу с ошибкой.
Теперь проблема состоит в том, чтобы предотвратить состояние гонки в сочетании с последней обязательной проверкой, если еще осталось достаточно мест:
Когда пользователь находится на последнем шаге, он / она выбирает способ оплаты и нажимает кнопку «Забронировать». После проверки правильности выбранного варианта оплаты (данные формы не были изменены) он перенаправляется в контроллер бронирования, где:
контроллер бронирования
<?php
ReservationController extends JControllerLegacy{
public function placeReservation(){
//Do some checks to prevent direct access, booking existence and user completed all steps
$reservatioModel = $this ->getModel('Reservation')
if(!$reservationModel->placeReservation()){
//set errors and redirect to error page
return false;
}
//booking had been placed, passes the data to payment plugin
}
?>
а также
Модель бронирования:
<?php
ReservationModel extends JModelLegacy{
public function placeReservation(){
//Get all variables and objects
//Lock the tables to prevent any insert while the last check
$db ->lockTable(#__bookings);
$db ->lockTable(#__booking_room);
//Perform the last mandatory check with tables locked to prevent read the table right one moment before another transaction insert the booking and allowing overbooking. THIS CHECK MUST BE 100% SURE NO ONE ELSE IS MANIPULATING/READING THE TABLE
if(!$this ->checkRoomsAvailability($data))
return false;
try{
$db ->transactionStart();
//Add the reservation in the booking table
if(!$this ->_saveBooking()){
$db ->rollBack();
return false;
}
//Add the room/s to the middle table #__booking_room
if(!$this -> _saveRooms())
$db ->rollBack();
return false;
}
$db ->transactionCommit();
}catch(Exception $e){
$db ->rollBack();
$this ->setError('We were not able to complete your reservation.');
return false;
}
$db ->unlockTables();
}
?>
Вышеупомянутыми таблицами являются InnoDB, #__bookings хранит бронирования, а #__booking_room содержит информацию для выбранных комнат (например, avg_price, количество комнат и т. Д.), И имеет внешний ключ для #__rooms и один для #__bookings
Что меня беспокоит?
1 — я использую другие таблицы во время последней проверки доступности, кроме двух заблокированных, и выдает ошибку.
2 — Когда выполнение второго пользователя достигает точки блокировки таблицы, ожидает ли оно освобождения или вызывает ошибку / исключение? Я мог бы поймать исключение и перенаправить пользователя на страницу ошибки, но таблица медальонов не означает, что резервирование размещено, это может привести к любой проблеме при сохранении информации.
Я надеюсь, что мой рабочий процесс в порядке, пожалуйста, дайте мне знать, если я в порядке, или я должен улучшить свой код.
Спасибо
РЕДАКТИРОВАТЬ:
Следует упомянуть, что бизнес-идея похожа на Expedia, бронирование и т. Д., Где вам разрешено выбирать больше типов номеров и единиц для бронирования. Действительно, мой стол в комнате не для конкретной / реальной комнаты, но предназначен для типа комнаты с фиксированным количеством единиц, доступных каждый день, если они еще не забронированы.
Мой подход был бы проще. В таблице, в которой хранится фактическое бронирование, у меня был бы внешний ключ для конкретного зарезервированного номера, и у меня был бы UNIQUE
индекс для полей roomID, date
, Это гарантирует, что один и тот же номер комнаты не может быть записан дважды для одной и той же даты.
Затем, когда клиент подтверждает бронирование, я выполняю все транзакции, как вы делаете сейчас. Когда код добирается до последнего места и пытается вставить новое бронирование, если за мгновение до того, как другой клиент зарезервировал эту комнату, БД не позволит вам вставить другое бронирование для этой комнаты на эту дату. Вот тогда я бы либо:
roomID
) если есть еще одна комната того же типа еще доступныЯ не могу сказать, является ли ваш рабочий процесс или бизнес-домен правильным выбором, так как это зависит от вашего бизнеса. Но технически вы хотите совершить транзакцию бронирования, которая также предусматривает проверку двойного бронирования. Самым простым способом может быть таблица дат бронирования номеров с уникальным ограничением номера и даты. Таким образом, во время транзакции бронирования вы также вносите каждый день номер комнаты в таблицу, и уникальное ограничение гарантирует, что вторая попытка не удастся.
PS: если таблица заблокирована, PHP просто будет ждать разблокировки. Не будет выброшено исключение (по крайней мере, если соединение не закроется через x секунд)