Skip to content

shirokovnv/innkeeper

Repository files navigation

Innkeeper

ci.yml Latest Version on Packagist Total Downloads

The library for your next Laravel booking app.

Supported Laravel versions: >=9.x

Installation

  1. Install package via composer
$ composer require shirokovnv/innkeeper
  1. Run migrations
$ php artisan migrate
  1. 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:

  1. Define hash function
function generateBookingHash(int $room_id, string $started_at, string $ended_at) {
    return $room_id . $started_at . $ended_at;
}
  1. 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.

About

The library for your next laravel booking app

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •