Creating a Reservation System in Laravel with Lara Reserve: A Step-by-Step Guide
Tags: laravel, php, webdev, tutorial
Published: 2023-05-07 15:59:37

Greetings, and welcome to my first post on this site!

## Introduction:

In this tutorial, I will show you how to create a restaurant reservation system using Laravel and the Lara Reserve package. This package provides a range of valuable features for building reservation systems, making it an ideal choice for this project. Let's dive in!

Link to Lara Reserve Github repository: https://github.com/shayan-yousefi/lara-reserve

read that documentation for more features. I will not use all features in this article.
(🌟 give a star to it if you like that 🌟)

## Let's follow below steps:

### Step 1: Install Lara Reserve.

To start, we need to install the Lara Reserve package. You can do this by running the following command in your terminal:
```shell
composer require shayanys/lara-reserve
```
After Lara Reserve Install, we must run the migrations to create the necessary database tables. To do this, run the following command:
```shell
php artisan migrate
```
Above command will create the Lara Reserve tables in your database, which we'll use later in the tutorial.

Stay tuned for the next step, where we'll build the reservation system!

### Step 2: model and migrations
We need a model, migration, request and controller for restaurant tables. To create these files, run the following command in your terminal:
```shell
php artisan make:model ResturantTable --migration --request --controller
```
Open RestaurantTable Model and implement it from the ReservableInterface interface and use the reservable trait in the Model class:

```php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use ShayanYS\LaraReserve\Interfaces\ReservableInterface;
use ShayanYS\LaraReserve\Traits\Reservable;

class RestaurantTable extends Model implements ReservableInterface
{

    protected $fillable = ['table_number','capacity'];

    use HasFactory, reservable;
}

```

After doing these works now, we go for migration, so open the RestaurantTable migration file and do this:

```php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('restaurant_tables', function (Blueprint $table) {
            $table->id();
            $table->smallInteger('table_number')->unique();
            $table->tinyInteger('capacity');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('resturant_tables');
    }
};

```

I used 'table_number' to identify what table is under reserve in the restaurant and the 'capacity' to specify the count of seats per table. You can customize this migration as you want.
Now you should run migrations by:
```shell
php artisan migrate
```

The last work you should do is open the User Model and implement from the CustomerInterface interface and use the customer trait in the Model class:

```php
<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use ShayanYS\LaraReserve\Interfaces\CustomerInterface;
use ShayanYS\LaraReserve\Traits\Customer;

class User extends Authenticatable implements CustomerInterface
{
    use HasApiTokens, HasFactory, Notifiable, customer;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

```
## Step 3: Controllers and Requests
At this article's beginning, we created a controller for RestaurantTable Model. Now we need another controller named 'ReserveRestaurantTableController'. Run the following command in your terminal:
```shell
php artisan make:request ReserveRestaurantTableController
```

After this, we need a request to validate reserves:
```shell
php artisan make:controller ReserveTableRequest
```

### RestaurantTableController
Open the 'RestaurantTableController' and write the below codes:
```php
namespace App\Http\Controllers;

use App\Http\Requests\StoreRestaurantTable;
use App\Models\RestaurantTable;
use Illuminate\Http\Request;

class RestaurantTableController extends Controller
{
    public function index(){

        $tables = RestaurantTable::paginate(15);
        return view('restaurantTable',compact('tables'));
    }

    public function store(StoreRestaurantRequestTable $request){
        RestaurantTable::create($request->validated());
        return redirect()->back();
    }
}

```
- 'index' -> shows a form to submit new tables and a list of our restaurant tables.
- 'store' -> This will create a new record for restaurant tables.
### StoreRestaurantTableRequest
Now we should write validations for RestaurantTable to store. Open the 'StoreRestaurantTableRequest' and write the following lines:
```php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreRestaurantTableRequest extends FormRequest
{
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
     */
    public function rules(): array
    {
        return [
            'table_number' => 'required|numeric|min:0|max:255,unique:restaurantTable',
            'capacity' => 'required|numeric|min:1|max:12',
        ];
    }
}
```
### ReserveRestaurantTableController
now we should write codes to make reservations in 'ReserveRestaurantTableController':
```php
namespace App\Http\Controllers;

use App\Http\Requests\ReserveTableRequest;
use App\Models\RestaurantTable;
use App\Models\User;
use Carbon\Carbon;
use ShayanYS\LaraReserve\Models\Reserve;

class ReserveRestaurantTableController extends Controller
{
    public function reserveForm(RestaurantTable $resturantTable)
    {
        $users = User::all();
        return view('reserveForm', compact('resturantTable', 'users'));
    }

    public function reserveTable(ReserveTableRequest $request, RestaurantTable $resturantTable)
    {

        $customer = User::find($request->validated('customer'));
        $reserveDate = Carbon::parse($request->validated('reserved_date'));
        $reserveTime = $request->validated('reserved_time');

        $customer->reserve($resturantTable, $reserveDate, $reserveTime);

        return redirect()->route('reserve.index')->with('success','reserved successfully');
    }

    public function index()
    {
        $reserves = Reserve::all();

        return view('reserveIndex',compact('reserves'));
    }
}
```
- 'reserveForm' -> shows a form to submit a reserve. Gets a {restaurantTable} to make a reservation;
- 'reserveTable' -> will create a reservation record.
- 'index' -> will show a list of reservations.

### ReserveTableRequest
we should validate data passed from users (They may have been playful) 😅, so open 'ReserveTableRequest' and write these:
```php
namespace App\Http\Requests;


use Illuminate\Foundation\Http\FormRequest;

class ReserveTableRequest extends FormRequest
{


    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
     */
    public function rules(): array
    {
        return [
            'reserved_date' => 'required|after:yesterday|date',
            'reserved_time' => 'required|date_format:H:i:s',
            'customer' => 'required|exists:users,id',
        ];
    }
}
```

## Step 4: Routes
it's wrong to expect the website without routes 😁, so open the web.php routes file and write the following routes:

```php
Route::group([],function (){

    Route::get('/', [\App\Http\Controllers\RestaurantTableController::class,'index'])->name('table.index');
    Route::post('/store', [\App\Http\Controllers\RestaurantTableController::class,'store'])->name('table.store');

});

Route::name('reserve.')->prefix('/reserve')->group(function (){
    Route::get('/{resturantTable}', [\App\Http\Controllers\ReserveRestaurantTableController::class,'reserveForm'])->name('table.form');
    Route::post('/reserveTable/{resturantTable}', [\App\Http\Controllers\ReserveRestaurantTableController::class,'reserveTable'])->name('reserveTable');
    Route::get('/', [\App\Http\Controllers\ReserveRestaurantTableController::class,'index'])->name('index');
});

```

### Last Step: views
don't think I will leave this article without making views for this project (Although they are simple, they are finally views 😂).

### reserveForm.blade.php
```html
<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>restaurant tables</title>
    </head>
    <body>

        <ul>
            @foreach($errors->all() as $error)
                <li>
                    {{$error}}
                </li>
            @endforeach
        </ul>

        <form action="{{route('reserve.reserveTable',[$resturantTable->id])}}" method="post">
            @csrf
            reserve date: <input type="date" name="reserved_date" value="{{old('reserved_date')}}">
            reserve time: <input type="text" name="reserved_time" value="{{old('reserved_time')}}">
            <select name="customer">
                <option value="">
                    select a user
                </option>
                @foreach($users as $user)
                    <option value="{{$user->id}}" {{old('customer') == $user->id ? 'selected':''}}>{{$user->email}}</option>
                @endforeach
            </select>
            <input type="submit">
            @csrf
        </form>

    </body>
</html>
```

### reserveIndex.blade.php
```html
<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>restaurant tables</title>
    </head>
    <body>


        <p>
            {{session('success')}}
        </p>


        <table style="width: 500px;text-align: center">
            <thead>
            <th>
                id
            </th>

            <th>
                reserve date
            </th>

            <th>
                reserve time
            </th>

            <th>
                table number
            </th>
            </thead>

            <tbody>
            @foreach($reserves as $reserve)
                <tr>
                    <td>
                        {{$reserve->id}}
                    </td>

                    <td>
                        {{$reserve->reserved_date->toDateString()}}
                    </td>

                    <td>
                        {{$reserve->reserved_time}}
                    </td>

                    <td>
                        {{$reserve->reservable->table_number}}
                    </td>
                </tr>
            @endforeach
            </tbody>
        </table>

    </body>
</html>
```

### restaurantTable.blade.php
```html
<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>restaurant tables</title>
    </head>
    <body>

        <ul>
            @foreach($errors->all() as $error)
                <li>
                    {{$error}}
                </li>
            @endforeach
        </ul>

        <form action="{{route('table.store')}}" method="post">
            tabel number:<input type="number" name="table_number" value="{{old('table_number')}}">
            table capacity: <input type="number" name="capacity" value="{{old('capacity')}}">
            <input type="submit">
            @csrf
        </form>

        <table style="width: 500px;text-align: center">
            <thead>
            <th>
                id
            </th>

            <th>
                table number
            </th>

            <th>
                capacity
            </th>

            <th>
                action
            </th>
            </thead>

            <tbody>
            @foreach($tables as $table)
                <tr>
                    <td>
                        {{$table->id}}
                    </td>

                    <td>
                        {{$table->table_number}}
                    </td>

                    <td>
                        {{$table->capacity}}
                    </td>

                    <td>
                        <a href="{{route('reserve.table.form',[$table->id])}}">reserve</a>
                    </td>
                </tr>
            @endforeach
            </tbody>
        </table>

    </body>
</html>
```

## Conclusion
we created a simple reservation system for restaurant tables; you can extend this project as you want, and I hope you like this article. Thanks for reading my article.
**Be sure to read Lara Reserve Documentation in GITHUB. I linked it at the beginning of the article.**