Laravel 8

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.

Print Friendly, PDF & Email

5 commentaires

  • 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

      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()

Laisser un commentaire