shirokovnv / innkeeper
The library for your next Laravel booking app
Requires
- php: >=8.0
- illuminate/database: ~9|~10
- illuminate/support: ~9|~10
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.9
- orchestra/testbench: ~7|~8
- phpstan/phpstan: ^1.0
- phpunit/phpunit: ^9.6
This package is auto-updated.
Last update: 2024-11-11 08:01:51 UTC
README
The library for your next Laravel booking app.
Supported Laravel versions: >=9.x
Installation
- Install package via composer
$ composer require shirokovnv/innkeeper
- Run migrations
$ php artisan migrate
- Done!
Usage
Add bookable functionality to your eloquent model
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Shirokovnv\Innkeeper\Contracts\Bookable; use Shirokovnv\Innkeeper\Traits\HasBooking; class Room extends Model implements Bookable { use HasBooking; }
That's basically it. Now your rooms can be booked.
Checking opportunity for the booking:
The Innkeeper
service provides functionality to check wether its possible to book something in a date range:
use \Shirokovnv\Innkeeper\Contracts\Innkeepable; $room = \App\Models\Room::find(1); $innkeeper = app()->make(Innkeepable::class); // Has bookings in a date range ? $has_bookings = $innkeeper->exists( $room, new DateTime('2022-08-01 15:00'), new DateTime('2022-08-07 12:00') );
Create a new booking
Creating a new booking is straight forward and could be done the following way:
use \Shirokovnv\Innkeeper\Contracts\Innkeepable; $room = \App\Models\Room::find(1); $innkeeper = app()->make(Innkeepable::class); $booking_hash = generateBookingHash(); $innkeeper->book( $room, $booking_hash, new DateTime('2022-08-01 15:00'), new DateTime('2022-08-07 12:00') );
Or if you like facades, you can do it like this:
use Shirokovnv\Innkeeper\Facades\Innkeeper; Innkeeper::book( $room, $booking_hash, new DateTime('2022-08-01 15:00'), new DateTime('2022-08-07 12:00') );
Notes:
Why we need
booking_hash
and what is it ?
- The hash is a field in a database with a unique constraint
- It serves purpose to prevent some duplicate bookings without explicitly locking tables
Let me show you and example:
Suppose, you have a few visitors on your booking site. And all the visitors asks for booking the same room at a time.
In this particular case, you probably need to assign the room to the first customer and notify others the room already booked.
You can do it by these simple steps:
- Define hash function
function generateBookingHash(int $room_id, string $started_at, string $ended_at) { return $room_id . $started_at . $ended_at; }
- In your business logic code
use Illuminate\Database\QueryException; $room = \App\Models\Room::find(1); $booking_hash = generateBookingHash($room->id, $started_at, $ended_at); try { $innkeeper->book($room, $booking_hash, $started_at, $ended_at); } catch (QueryException $exception) { // Catch SQLSTATE[23000]: Integrity constraint violation: 19 UNIQUE constraint failed: bookings.hash // show user popup with apologies or // redirect to another free room or ... }
This example covers only the case, when you have deterministic booking schedule without intersections. Like this:
- 09:00 - 10:00
- 10:00 - 11:00
- 11:00 - 12:00
- ...
If you have some intersections, like:
- 09:00 - 10:00
- 09:30 - 10:30
you may still have a problem with duplicates.
Of course, you can check the availability of the room:
$can_be_booked = ! $innkeeper->exists($room, $started_at, $ended_at); if ($can_be_booked) { $innkeeper->book($room, $booking_hash, $started_at, $ended_at); } else { // show user an error message }
But it doesn't guarantee resolving concurrent requests for your schedule.
So, please, let me know what can be a solution if it is your case.
Query bookings
use \Shirokovnv\Innkeeper\Contracts\Innkeepable; $room = \App\Models\Room::find(1); $innkeeper = app()->make(Innkeepable::class); // All the bookings for the room $bookings = $innkeeper->all($room); // All the bookings for the room in a range. $bookings = $innkeeper->allInRange($room, $started_at, $ended_at); // The first started booking $first_booking = $innkeeper->first($room); // The last ended booking $last_booking = $innkeeper->last($room);
Delete bookings
$room = \App\Models\Room::find(1); $booking_hash = 'some hash'; // Delete by predefined hash $innkeeper->deleteByHash($room, $booking_hash); $started_at = new DateTime('2022-08-01 09:00'); $ended_at = new DateTime('2022-08-02 09:00'); // Delete by specific date range. $innkeeper->deleteByRange($room, $started_at, $ended_at);
Change log
Please see the changelog for more information on what has changed recently.
Testing
$ composer test
Contributing
Please see contributing.md for details and a todolist.
Security
If you discover any security related issues, please email shirokovnv@gmail.com instead of using the issue tracker.
Credits
License
MIT. Please see the license file for more information.