
Un site de réservation avec la TALL stack (partie 1)
Il est de plus en plus évident que la TALL stack, dont j’ai déjà parlé dans cet article, se présente comme une sérieuse alternative aux approches classiques. On peut ne pas aimer la verbosité de Tailwind, et j’en fais partie, ou le mélange des genres de Livewire, et j’en fais encore partie, il faut reconnaître à ces technologies une redoutable efficacité.
J’ai déjà évoqué plusieurs fois Livewire dans le présent blog, une fois pour en faire une introduction, une autre fois pour découvrir un datatable fondé sur Livewire, et enfin une implémentation de FullCalendar avec le même Livewire.
Dans cette série d’articles je vous propose de faire une synthèse de tout ça avec un site très simplifié (on ne va gérer que la partie utilisateur et oublier l’administration du site) de réservation de gîtes avec l’utilisation de FullCalendar, de datatable et d’autres packages de Livewire. Le but est de voir comment organiser le codage avec ces nouvelles technologies. On oublie les contrôleurs et autres éléments classiques de Laravel pour utiliser des composants qui vivent leur vie à cheval entre le client et le serveur. C’est un peu dépaysant, mais on s’y habitue vite parce que le codage est rapide et très simplifié.
Dans ce premier article on va s’occuper principalement de l’intendance et on rentrera dans le vif du sujet dans le prochain.
Vous pouvez télécharger le code final de cet article ici.
Installation
Laravel
On fait une nouvelle installation de Laravel :
composer create-project laravel/laravel mesvacances --prefer-dist
On crée une base de données et on informe le fichier .env :
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=mesvacances DB_USERNAME=root DB_PASSWORD=
Breeze
On va utiliser Breeze pour l’authentification :
composer require laravel/breeze --dev php artisan breeze:install npm install npm run dev
Maintenant on a l’authentification en place et par la même occasion on a installé Tailwind.
Livewire
On installe aussi Livewire :
composer require livewire/livewire
On a maintenant notre Laravel tout prêt !
Les données
homes
Pour notre site de réservation de gîtes on va avoir une première tables homes pour mémoriser les informations des gîtes :
php artisan make:model Home -ms
Dans la migration on supprime les timestamps et on prévoit ces colonnes :
public function up() { Schema::create('homes', function (Blueprint $table) { $table->id(); $table->string('title'); $table->string('text1'); $table->string('text2'); $table->string('text3'); $table->string('image'); }); }
Donc un titre, quelques textes de présentation et le lien vers une image (vous pouvez récupérer les images dans le dossier ZIP joint). Récupérez ces trois images en créant le dossier :
Dans le modèle Home on précise qu’on n’a plus les timestamps et on renseigne $fillable :
public $timestamps = false; protected $fillable = ['id', 'text1', 'text2', 'text3', 'image'];
Pour la population (HomeSeeder) on prévoit deux gîtes :
public function run() { Home::insert([ [ 'id' => 1, 'title' => 'Les rondins du lac', 'text1' => 'Un emplacement de rêve', 'text2' => 'Une vue inégalée', 'text3' => 'Un calme absolu', 'image' => 'gite1.jpg', ], [ 'id' => 2, 'title' => 'L\'avenir est déjà là', 'text1' => 'Une architecture unique', 'text2' => 'Une piscine langoureuse', 'text3' => 'Une lumière enchanteresse', 'image' => 'gite2.jpg', ], ]); }
events
On crée ensuite la table des réservations :
php artisan make:model Event -ms
Là aussi on oublie les timestamps :
public function up() { Schema::create('events', function (Blueprint $table) { $table->id(); $table->date('start'); $table->date('end'); $table->date('limit'); $table->boolean('rented')->default(false); $table->foreignId('user_id')->constrained(); $table->foreignId('home_id')->constrained(); }); }
On a ces colonnes :
- start : date de départ de la réservation
- end : date de la réservation
- limit : date limite de paiement
- rented : indique si la réservation est effectuée
- user_id : clé étrangère pour les utilisateurs (un utilisateur a plusieurs réservations)
- home_id : clé étrangère pour les gîtes (un gîte a plusieurs réservations)
Dans le modèle Event on renseigne $timestamps et $fillable :
public $timestamps = false; protected $fillable = ['user_id', 'home_id', 'start', 'rented', 'end', 'limit'];
Pour la population (EventSeeder) on prévoit 6 réservations :
<?php namespace Database\Seeders; use Illuminate\Database\Seeder; use App\Models\Event; use Illuminate\Support\Str; use Carbon\Carbon; class EventSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Event::insert([ [ 'user_id' => 1, 'home_id' => 1, 'rented' => false, 'limit' => Carbon::now()->addDays(4)->toDateTimeString(), 'start' => Carbon::now()->addDays(5)->toDateTimeString(), 'end' => Carbon::now()->addDays(6)->toDateTimeString(), ], [ 'user_id' => 1, 'home_id' => 1, 'rented' => true, 'limit' => Carbon::now()->addDays(3)->toDateTimeString(), 'start' => Carbon::now()->addDays(10)->toDateTimeString(), 'end' => Carbon::now()->addDays(11)->toDateTimeString(), ], [ 'user_id' => 2, 'home_id' => 1, 'rented' => true, 'limit' => Carbon::now()->addDays(9)->toDateTimeString(), 'start' => Carbon::now()->addDays(16)->toDateTimeString(), 'end' => Carbon::now()->addDays(18)->toDateTimeString(), ], [ 'user_id' => 1, 'home_id' => 2, 'rented' => false, 'limit' => Carbon::now()->addDays(4)->toDateTimeString(), 'start' => Carbon::now()->addDays(5)->toDateTimeString(), 'end' => Carbon::now()->addDays(6)->toDateTimeString(), ], [ 'user_id' => 1, 'home_id' => 2, 'rented' => true, 'limit' => Carbon::now()->addDays(4)->toDateTimeString(), 'start' => Carbon::now()->addDays(11)->toDateTimeString(), 'end' => Carbon::now()->addDays(15)->toDateTimeString(), ], [ 'user_id' => 2, 'home_id' => 2, 'rented' => true, 'limit' => Carbon::now()->addDays(9)->toDateTimeString(), 'start' => Carbon::now()->addDays(16)->toDateTimeString(), 'end' => Carbon::now()->addDays(17)->toDateTimeString(), ], ]); } }
payments
Pour finir on ajoute une table pour mémoriser les paiements :
php artisan make:model Payment -m
Ça sera assez symbolique parce qu’on va se contenter d’une clé étrangère pour faire le lien avec les réservations :
public function up() { Schema::create('payments', function (Blueprint $table) { $table->id(); $table->timestamps(); $table->foreignId('event_id')->constrained(); }); }
On ne prévoit pas de population pour les paiements.
Par contre, il faut renseigner DatabaseSeeder pour activer les seeders qu’on a créés et on va par ailleurs utiliser le factory des users pour en créer 5 :
<?php namespace Database\Seeders; use Illuminate\Database\Seeder; use App\Models\User; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { User::factory()->count(5)->create(); $this->call([HomeSeeder::class,]); $this->call([EventSeeder::class,]); } }
On finit en créant les tables et en les remplissant :
php artisan migrate --seed
Vérifiez dans la base que vous avez les 7 tables :
Et aussi qu’elles sont bien remplies.
La page d’accueil
Sue la page d’accueil du site on veut une présentation générale, une description de chaque gîte avec sa photo, un calendrier pour chacun d’eux pour effectuer les réservations. Pour le moment on va oublier les calendriers et crée l’infrastructure globale.
Contrôleur et route
Pour ouvrir la page d’accueil en envoyant les informations des gîtes on crée un contrôleur :
php artisan make:controller HomeController --invokable
Avec ce simple code :
<?php namespace App\Http\Controllers; use App\Models\Home; class HomeController extends Controller { public function __invoke() { $homes = Home::all(); return view('home', compact('homes')); } }
On ajoute la route (en supprimant la route de base pour la racine du site) :
use App\Http\Controllers\HomeController; Route::get('/', HomeController::class)->name('home');
La vue
On ajoute la vue :
Vous pouvez aussi supprimer dashboard et welcome qui ne nous serviront pas.
Pour pas me compliquer la vie, je suis parti d’un template gratuit que j’ai allégé et adapté à la situation. Comme le code est assez long je ne le copie pas ici, vous pouvez récupérer le fichier dans le ZIP.
On a une partie supérieure avec titre, illustration, bouton de connexion ou inscription :
Ensuite je me suis un peu amusé avec du texte est des images SVG :
On a ensuite les deux gîtes, voici par exemple le premier :
En bas de page, il y a quelques liens classiques.
On ajoutera les calendriers dans le prochain article.
Les pages de l’authentification
Pour le moment on a les pages standards qui arrivent avec Brezze. On va un peu les customiser pour les faire cadrer avec l’aspect du site. D’autre part tout y est en anglais alors on va arranger ça.
Les langues
Déjà dans config.app on change la locale :
'locale' => 'fr',
Ensuite on va chercher les fichiers de langue ici et on copie le dossier du français au bon endroit :
D’autre part allez récupérer dans le ZIP le fichier JSON qui comporte plein de traductions pour l’ensemble du projet :
Les vues
Pour les vues on a un fond grisâtre et le logo de Laravel :
Pour le logo ça se passe dans ce composant :
On change le SVG :
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" {{ $attributes }}> <path d="M312.638,378.443c-1.499-5.581-3.861-13.796-6.783-23.958c-49.456-171.839-45.944-205.524-39.593-210.25 c20.202-5.073,52.617,39.627,65.101,62.07c1.532,2.742,4.48,4.188,7.641,4.236c3.145-0.169,5.927-2.105,7.165-5.002 c21.226-49.524-20.798-92.038-41.758-109.306c16.697-6.315,30.883-1.935,31.556-1.724c3.46,1.169,7.29-0.105,9.387-3.105 c2.093-3.01,1.96-7.024-0.327-9.889C316.46,45.814,256.875,49,232.246,52.113c3.226-15.468,12.98-25.565,13.077-25.661 c2.625-2.619,3.177-6.675,1.347-9.909c-1.823-3.226-5.565-4.863-9.19-3.935c-26.855,6.71-48.282,23.339-57.197,31.111 l-44.46-22.232c-2.992-1.494-6.573-1.048-9.105,1.145c-2.524,2.194-3.48,5.685-2.423,8.853l13.44,40.325 c-70.815,14.895-71.669,96.587-71.669,97.452c0,4.242,3.218,7.798,7.444,8.218c4.181,0.363,8.077-2.435,8.911-6.597 c3.573-17.865,24.214-29.226,43.964-35.807c-32.673,46.339-3.464,124.081-1.964,127.992c1.496,3.893,5.669,6.048,9.718,5.04 c4.048-1.016,6.706-4.887,6.186-9.032c-6.061-48.486,48.899-88.744,70.169-102.379c18.526,55.411,26.693,185.076,28.565,219.262 C103.701,379.774,0,429.102,0,491.359c0,4.564,3.698,8.292,8.258,8.292h495.484c4.56,0,8.258-3.728,8.258-8.292 C512,435.345,428.055,389.792,312.638,378.443z M222.298,141.236c-0.915-2.139-2.689-3.79-4.887-4.557 c-2.194-0.742-4.613-0.558-6.657,0.557c-3.081,1.685-60.786,33.702-80.718,81.887c-5.214-31.718-4.552-74.47,30.56-92.026 c3.577-1.79,5.359-5.885,4.226-9.724c-1.129-3.831-4.827-6.3-8.831-5.873c-2.29,0.258-40.548,4.766-66.637,23.322 c0.569-1.532,1.181-3.081,1.847-4.637c11.073-25.831,30.706-40.482,58.355-43.554c2.48-0.274,4.702-1.661,6.044-3.758 c1.343-2.105,1.665-4.696,0.879-7.064l-10.214-30.639l31.718,15.863c3.193,1.607,7.024,0.97,9.532-1.548 c0.173-0.167,13.056-12.911,32.153-22.572c-2.831,7.01-4.96,15.451-4.96,24.992c0,2.476,1.109,4.815,3.024,6.389 c1.911,1.565,4.423,2.168,6.855,1.71c0.637-0.145,53.669-10.405,88.681,7.03c-8.77,1.236-18.79,4.349-28.819,11.034 c-2.298,1.532-3.678,4.113-3.678,6.869c0,2.758,1.379,5.339,3.678,6.871c0.605,0.403,53.181,36.043,50.79,79.317 c-15.488-23.194-44.968-59.928-72.988-52.903c-3.113,0.774-7.5,2.877-10.722,8.468c-14.141,24.54,5.145,106.637,38.448,222.363 c2.017,7.008,3.757,13.05,5.13,17.946c-12.726-0.845-25.783-1.284-39.11-1.284c-0.135,0-0.268,0-0.402,0.002 C253.685,339.651,244.688,193.471,222.298,141.236z M17.383,483.135C27.907,433.586,132.899,392.296,256,392.296 s228.093,41.29,238.617,90.839H17.383z" /> </svg>
Ca fait déjà plus vacances :
On va maintenant homogénéiser le fond avec celui du site. On va commencer par enlever le gris du fond, ça se passe dans le composant auth-card.blade.php :
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0"> ...
Ensuite on met le dégradé dans le layout guest.blade.php :
<body class="bg-gradient-to-r from-indigo-900 to-indigo-300">
On a bien le dégradé mais notre logo doit changer de couleur pour être visible :
Malheureusement la couleur du texte pour ce logo est définie dans toutes les vues de l’authentification, ce qui est un peu dommage :
<x-application-logo class="w-20 h-20 fill-current text-gray-500" />
On doit donc les passer une par une pour changer la couleur :
<x-application-logo class="w-20 h-20 fill-current text-white" />
Et ça devrait être bon :
On peut finir en faisant du ménage dans les vues :
Conclusion
C’était un peu laborieux mais maintenant on va pouvoir utiliser Livewire pour ajouter les calendriers pour les deux gîtes dans le prochain article.


6 commentaires
christophe
bonjour,
Juste après le « php artisan migrate –seed », j’ai cette erreur :
Illuminate\Database\QueryException
SQLSTATE[HY000] [1049] Unknown database ‘mesvacances’ (SQL: select * from information_schema.tables where table_schema = mesvacances and table_name = migrations and table_type = ‘BASE TABLE’)
Pourtant dans mysql la base ‘mesvacances’ apparait, et j’ai une date de dernière connexion.
Une piste peut-être ?
Merci 🙂
prebag
Dans la première partie, /register est 404
Peut-être que je dois attendre la deuxième partie ?
prebag
Autant pour moi, j’ai oublié dans DatabaseSeeder d’inclure cette ligne:
use App\Models\User;
et dans HomeSeeder ( ligne manquante pour moi)
use App\Models\Home;
et dans EventSeeder (ligne manquante pour moi)
use App\Models\Event;
use Illuminate\Support\Str;
use Carbon\Carbon;
prebag
Bonjour,
Dans la partie données, vous avez oublié volontairement la création de User ?
php artisan make:model User -ms
Merci,
prebag
Je me suis trompé, il cherche plutot la classe Class ‘Database\Seeders\User’
$ php artisan migrate –seed
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (24.58ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (23.75ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (25.55ms)
Migrating: 2021_05_22_205724_create_homes_table
Migrated: 2021_05_22_205724_create_homes_table (12.07ms)
Migrating: 2021_05_22_210156_create_events_table
Migrated: 2021_05_22_210156_create_events_table (101.35ms)
Migrating: 2021_05_22_212557_create_payments_table
Migrated: 2021_05_22_212557_create_payments_table (49.54ms)
Error
Class ‘Database\Seeders\User’ not found
at database/seeders/DatabaseSeeder.php:17
13▕ */
14▕ public function run()
15▕ {
16▕ // \App\Models\User::factory(10)->create();
➜ 17▕ User::factory()->count(5)->create();
18▕ $this->call([HomeSeeder::class,]);
19▕ $this->call([EventSeeder::class,]);
20▕ }
21▕ }
+35 vendor frames
36 artisan:37
Illuminate\Foundation\Console\Kernel::handle()
erikelcath
Salut,
User est installé et initialisé via breeze, tu n’as pas besoin de faire ‘php artisan make:model User -ms’ et toutes les config comme pour Home, Event et Payment.