Laravel 8

Ma première application Laravel 8

Je vous propose dans cet article de voir comment créer une simple application Laravel en détaillant toutes les étapes. Je l’avais déjà fait récemment mais pour la version 7, cet article est une actualisation pour Laravel 8. Il s’adresse donc aux débutants qui désirent découvrir ce framework et peut-être aux moins débutants qui aimeraient se rafraichir un peu les idées !

Évidemment je ne vais pas exposer tous les aspects de Laravel ici mais juste les éléments essentiels à prendre en compte. Pour une présentation détaillée de Laravel 8 il faut aller voir mon cours. Toutefois on arrivera à une application totalement fonctionnelle.

On va ainsi créer un simple gestionnaire de tâches.

Vous pouvez télécharger le code final de l’article (sans les dossiers vendor et node_modules).

Les prérequis

Laravel, qui en est actuellement à sa version 8, a besoin de quelques éléments côté serveur :

  • PHP >= 7.3  c’est la moindre des choses pour un framework PHP !
  • des extensions PHP (que du classique) :
    • BCMath
    • Ctype
    • Fileinfo
    • JSON
    • Mbstring
    • OpenSSL
    • PDO
    • Tokenizer
    • XML

Vous pouvez trouver tout ça facilement sur un serveur local comme WAMPP ou XAMPP mais franchement si vous ne voulez pas vous compliquer la vie utilisez Laragon !

On va aussi utiliser MySQL comme serveur de données.

Vous aurez aussi besoin de Composer pour gérer les librairies PHP.

Enfin pour le frontend il vous faudra node.js.

Vous avez tout ça ? Alors c’est parti !

Installation de Laravel

Si vous utilisez Laragon l’installation est d’une extrême simplicité :

Il suffit ensuite de préciser le nom :

Et c’est parti ! Vous obtenez :

  • un nouveau dossier todolist avec une installation fraîche de Laravel dernière version
  • une base de données MySQL du même nom
  • et un hôte local todolist.oo (chez moi c’est oo mais peut-être que pour vous ce sera une autre extension comme test)

Si vous n’utilisez pas Laragon c’est plus laborieux, dans la console entrez :

composer create-project --prefer-dist laravel/laravel todolist

Et évidemment il vous faudra créer manuellement la base. D’autre part vous n’aurez pas automatiquement un hôte local.

Laravel dispose d’un serveur PHP local léger qu’on démarre en utilisant l’outil Artisan de Laravel :

php artisan serve

Mais il vaut mieux utiliser un serveur plus complet comme celui proposé par Laragon.

Si tout se passe bien vous devez arriver sur cette page avec todolist.oo (ou l’extension que vous utilisez) :

Base de données

Vous avez créé la base de données mais il faut renseigner Laravel pour qu’il s’en serve. Pour ça on va utiliser le fichier de configuration .env qui se situe à la racine :

Si pour une raison quelconque ce fichier n’existe pas faite une copie de .env.example pour le créer.

Dans ce fichier on va préciser le nom de la base et les identifiants pour y accéder :

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=todolist
DB_USERNAME=root
DB_PASSWORD=

Pour l’instant la base est vide. Laravel dispose d’un outil de migration pour créer des tables, les modifier, créer des index…

On trouve ça dans le dossier database :

On y trouve trois fichiers de migration :

  • pour la table users
  • pour la table password_resets
  • pour la table failed_jobs

Comme on ne se servira pas de la troisième on va supprimer ce fichier. Il ne va donc plus nous rester que la migration pour la table users et celle pour le renouvellement des mots de passe. Si on regarde un peu le code de celle des users on a une fonction up :

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}

C’est ici qu’on a le code pour créer la table et ses colonnes. Je ne vais pas entrer dans le détail mais le code est explicite.

Pour créer les table à partir de ces migrations on utilise encore Artisan :

Si on regarde dans la base on se retrouve avec 3 tables :

On a bien nos tables users et password_resets mais aussi un table migrations qui mémorise les actions de migration de Laravel. C’est une table d’intendance que vous n’avez pas à toucher.

On a dans la table users les colonnes telles que définies dans la migration :

L’authentification

Laravel n’est pas équipé à la base d’un système complet d’authentification mais on peut l’ajouter avec un package complémentaire (Jetsream), c’est ce qu’on va faire en utilisant Composer :

On voit que ce package en installe aussi un certain nombre pour ses propres besoins.

Composer a ajouté la librairie dans le fichier composer.json :

"require": {
    ...
    "laravel/jetstream": "^1.3",
    ...
},

Et l’a chargée dans le dossier vendor :

Ce package ajoute une commande à Artisan :

On a deux possibilités pour le frontend (Livewire + Blade ou Inertia.js + Vue), on va faire simple avec la première possibilité :

On voit qu’on a encore l’installation de deux packages (sanctum et livewire).

On va suivre maintenant installer les librairie du frontend compiler les assets avec npm :

npm install
npm run dev

On va avoir la création d’un dossier node_modules avec toutes les dépendances. Ça prend un certain temps parce qu’il y a beaucoup de dépendances.

D’autre part le package ajoute des migrations et on va créer les tables correspondantes :

On a maintenant 5 tables dans la base :

On a en plus une page pour le login :

Et une pour l’inscription :

Il est d’autre part possible de réinitialiser le mot de passe. L’utilisateur a aussi accès à un profil avec pas mal de possibilités. Vous pouvez explorer tout ça…

Un peu de ménage

Comme on ne va pas se servir de tout on va déjà supprimer la possibilité de créer des tokens d’API et on va se passer de la photo profil. Ouvrez le fichier config/jetstream.php et commentez ces lignes :

'features' => [
    // Features::profilePhotos(),
    // Features::api(),
    // Features::teams(),
],

On va aussi se passer le l’authentification à deux facteurs, cette fois ça se passe dans config/fortify.php :

'features' => [
    Features::registration(),
    Features::resetPasswords(),
    // Features::emailVerification(),
    Features::updateProfileInformation(),
    Features::updatePasswords(),
    // Features::twoFactorAuthentication([
    //     'confirmPassword' => true,
    // ]),
],

Les langues

Évidemment à la base tout est en anglais mais Laravel sait très bien gérer l’aspect linguistique. Vous avez toutes les traductions de base ici.

Vous pouvez récupérer le dossier fr et le copier là :

Ensuite vous changez la locale dans config/app.php :

'locale' => 'fr',

Vous aurez ainsi en français les messages pour l’authentification et la validation. Mais ça ne va pas changer automatiquement les textes dans les formulaires. Voyons déjà où sont ces formulaires. Toutes les vues (fichiers qui génèrent les pages HTML) de Laravel sont dans le dossier resources/views :

La package qu’on a installé à ajouté ici pas mal de vues. regardons un peu le code, par exemple dans la vue du login on trouve cette ligne :

<x-jet-label value="{{ __('Password') }}" />

Qui correspond au titre de ce champ du formulaire :

Je peux faire un changement brutal :

<x-jet-label value="Mot de passe" />

Après tout si je veux mon site exclusivement en français ça ne pose aucun problème. Mais si je veux conserver la possibilité d’avoir plusieurs langue je dois trouver une méthode plus élégante…

Je crée un fichier resources/lang/fr.json :

Avec ce code :

{
  "Password": "Mot de passe" 
}

On obtient le même résultat mais maintenant le site est multilingues !

Il faut évidemment passer en revue tous les textes à traduire et compléter le fichier JSON. C’est un peu laborieux à faire mais heureusement il existe des packages qui facilitent la vie, comme par exemple celui-ci !

Pour ne pas alourdir cet article on va garder les textes en anglais dans l’authentification mais rien ne vous empêche de les modifier !

Les données

Il est maintenant temps de se poser la question des données nécessaires pour notre application. On va partir sur cette base, pour chaque tâche on a avoir :

  • un titre (« Tondre la pelouse« )
  • un texte de détail (« Ne pas oublier de demander la tondeuse au voisin la veille »)
  • une date de création
  • une date de modification
  • un état (a priori deux états : à faire et fait)

On va donc créer une table pour mémoriser tout ça. On a vu qu’avec Laravel on utilise une migration pour ça. d’autre part Laravel est équipé d’un ORM efficace : Eloquent. Chaque table est représenté par une classe qui permet de manipuler les données. On dispose ainsi d’un modèle pour chaque table à manipuler. On va demander à Artisan de créer à la fois une table et son modèle Eloquent associé :

On trouve le modèle ici :

On a déjà le modèle par défaut User pour gérer les données de la table users.

La migration a été créée ici :

Par défaut on a ce code :

public function up()
{
    Schema::create('tasks', function (Blueprint $table) {
        $table->id();
        $table->timestamps();
    });
}

On a :

  • une clé id
  • deux colonnes (created_at et updated_at) créée par la méthode timestamps.

On va ajouter les colonnes nécessaires :

Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->timestamps();
    $table->string('title');
    $table->text('detail');
    $table->boolean('state')->default(false);
});

Et on lance la migration :

On doit trouver la table dans la base avec ses colonnes :

Le contrôleur et les routes

Laravel est basé sur le modèle MVC :

  • Modèle : c’est Eloquent qui se charge de cet aspect et on a créé un modèle (Task) pour nos tâches,
  • Vue : on a déjà des vues pour la partie authentification, on a devoir en créer aussi pour nos tâches,
  • Contrôleur : c’est le chef d’orchestre de l’application, on va créer à présent un contrôleur pour gérer toutes les actions nécessaires pour les tâches.
php artisan make:controller TaskController --resource

On trouve ce contrôleur ici :

C’est un contrôleur de ressource, ce qui signifie qu’il est déjà équipé de 7 fonctions pour les actions suivantes :

Verbe URI Action Route
GET /tasks index tasks.index
GET /tasks/create create tasks.create
POST /tasks store tasks.store
GET /tasks/{task} show tasks.show
GET /tasks/{task}/edit edit tasks.edit
PUT/PATCH /tasks/{task} update tasks.update
DELETE /tasks/{task} destroy tasks.destroy

On va créer aussi les routes pour accéder à ces actions dans le fichier routes/web.php :

use App\Http\Controllers\TaskController;
Route::resource('tasks', TaskController::class)->middleware('auth');

Si vous utilisez la commande php artisan route:list -c vous obtenez toutes les routes de l’application et en particulier les 7 pour le contrôleur :

Maintenant que tout ça est en place on va coder ces actions et créer les vues correspondantes !

Un petit mot sur le middleware auth que j’ai ajouté pour ces routes. Un middleware est une étape pour la requête HTTP qui arrive, ça permet d’accomplir des vérifications, ici on demande que l’accès à ces routes soir limitées aux utilisateurs authentifiés. Ceux qui ne le sont pas seront automatiquement renvoyés à la page de connexion. Pour la suite de ce tutoriel vous devrez donc créer un utilisateur avec les formulaires qu’on a déjà mis en place.

Créer une tâche

Lorsqu’on installe Jetstream pour bénéficier de l’authentification, de la gestion du profil et d’autres choses on fait aussi automatiquement le chois d’utiliser Tailwind et Liveware. Dans les vues que je vais créer j’utiliserai donc Tailwind puisqu’il est disponible même si ce n’est pas une librairie de j’affectionne parce que je la trouve trop verbeuse. Mais évidemment ce n’est pas une obligation, on peut adapter tout ça selon ses goût mais évidemment ça peut demander plus de travail.

Le formulaire

Pour la création d’une tâche on va avoir besoin d’un formulaire. On crée un dossier tasks et la vue create :

Avec ce code :

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            Création d'une tâche
        </h2>
    </x-slot>    

    <div class="py-12">
      <div class="flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100">
        <div class="w-full sm:max-w-lg mt-6 px-6 py-4 bg-white shadow-md overflow-hidden sm:rounded-lg">
          <x-jet-validation-errors class="mb-4" />
          @if (session()->has('message'))
            <div class="flex items-center bg-green-500 text-white text-sm font-bold px-4 py-3">
              {{ session('message') }}
            </div>
          @endif
          <form method="POST" action="{{ route('tasks.store') }}">
              @csrf

              <div class="mt-4">
                  <x-jet-label value="Titre" />
                  <x-jet-input class="block mt-1 w-full" type="text" id=title name="title" :value="old('title')" placeholder="Titre de la tâche" required autofocus />
              </div>

              <div class="mt-4">
                  <x-jet-label value="Détail" />
                  <textarea class="form-input rounded-md shadow-sm mt-1" style="width: 100%" id="detail" name="detail" placeholder="Détail de la tâche">{{ old('detail') }}</textarea>
              </div>

              <div class="flex items-center justify-end mt-4">
                  <x-jet-button class="ml-4">
                      Envoyer
                  </x-jet-button>
              </div>
          </form>
        </div>
      </div>
    </div>
  </div>  
</x-app-layout>

Il y a beaucoup à dire sur ce code mais je ne vais pas entrer dans les détails.

On a une vue de base layouts.app qui comporte la structure de base de la page. Elle est considérée par Laravel comme un composant géré par cette classe :

La syntaxe <x-app-layout> permet de dire qu’on utilise ce composant. Laravel utilise Blade comme gestionnaire de template. Toute vue qui utilise Blade doit comporter blade dans son nom.

Le formulaire est défini avec ce code :

<form action="{{ route('tasks.store') }}" method="post">

On utilise l’helper route qui, à partir du nom de la route, génère l’url. Pour insérer des éléments avec PHP on utilise des accolades.

On a ensuite cette commande Blade :

@csrf

On demande ici à Blade d’insérer un token dans un champ caché qui va permettre à Laravel de contrer les attaques CSRF, le code généré ressemble à ça :

<input type="hidden" name="_token" value="sUTuW5dYrt2l2iPgAJ5EVXn5UwMjEGFLAZaH4jHz">

C’est automatique et on n’a pas besoin de s’en préoccuper ensuite. Un middleware se charge à l’arrivée de ce contrôle de sécurité.

On va compléter le code de la fonction create du contrôleur TaskController pour appeler la vue :

public function create()
{
    return view('tasks.create');
}

Maintenant avec l’url todolist.oo/tasks/create on obtient la page avec le formulaire (si on est connecté !) :

La soumission

Pour créer effectivement la tâche on va devoir gérer la soumission du formulaire dans la méthode store du contrôleur :

use App\Models\Task;

...

public function store(Request $request)
{
    $data = $request->validate([
        'title' => 'required|max:100',
        'detail' => 'required|max:500',
    ]);

    $task = new Task;
    $task->title = $request->title;
    $task->detail = $request->detail;
    $task->save();

    return back()->with('message', "La tâche a bien été créée !");
}

On commence par vérifier les entrées avec la validation. Les deux champs sont requis et on vérifie une longueur maximale. On utilise un composant de jetstream pour l’afficher dans la vue :

<x-jet-validation-errors class="mb-4" />

Ensuite on utilise Eloquent pour créer la tâche.

Pour finir on renvoie dans la vue.

Vous pouvez vérifier que la validation fonctionne  :

Si la validation est bonne, la tâche est créée et on retourne la même vue (back) mais cette fois on flashe une information en session (ce qui signifie qu’elle ne sera valable que pour la prochaine requête) avec with :

return back()->with('message', "La tâche a bien été créée !");

Dans la vue on vérifie s’il y a une information en session et si c’est le cas on l’affiche :

@if (session()->has('message'))
  <div class="flex items-center bg-green-500 text-white text-sm font-bold px-4 py-3">
    {{ session('message') }}
  </div>
@endif

On trouve les données dans la table :

Par défaut l’état est à 0 (false). Remarquez aussi le renseignement automatique des deux dates.

Modifier une tâche

Le formulaire

Pour la modification d’une tâche on va avoir besoin d’un formulaire. On crée la vue edit :

Évidemment cette vue va beaucoup ressembler à celle de la création mais il va falloir renseigner les champs et on va ajouter la possibilité de modifier l’état :

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            Modification d'une tâche
        </h2>
    </x-slot>    

    <div class="py-12">
      <div class="flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100">
        <div class="w-full sm:max-w-lg mt-6 px-6 py-4 bg-white shadow-md overflow-hidden sm:rounded-lg">
          <x-jet-validation-errors class="mb-4" />
          @if (session()->has('message'))
            <div class="flex items-center bg-green-500 text-white text-sm font-bold px-4 py-3">
              {{ session('message') }}
            </div>
          @endif
          <form action="{{ route('tasks.update', $task->id) }}" method="post">
              @csrf
              @method('put')
              <div class="mt-4">
                  <x-jet-label value="Titre" />
                  <x-jet-input class="block mt-1 w-full" type="text" id=title name="title" :value="old('title', $task->title)" placeholder="Titre de la tâche" required autofocus />
              </div>

              <div class="mt-4">
                  <x-jet-label value="Détail" />
                  <textarea class="form-input rounded-md shadow-sm mt-1" style="width: 100%" id="detail" name="detail" placeholder="Détail de la tâche">{{ old('detail', $task->detail) }}</textarea>
              </div>

              <div class="block mt-4">
                  <label class="flex items-center">
                      <input type="checkbox" class="form-checkbox" id="state" name="state" @if(old('state', $task->state)) checked @endif>
                      <span class="ml-2 text-sm text-gray-600">Tâche accomplie</span>
                  </label>
              </div>

              <div class="flex items-center justify-end mt-4">
                  <x-jet-button class="ml-4">
                      Envoyer
                  </x-jet-button>
              </div>
          </form>
        </div>
      </div>
    </div>
  </div>  
</x-app-layout>

Dans la déclaration du formulaire on a changé l’url :

<form action="{{ route('tasks.update', $task->id) }}" method="post">

On précise la route et il faut ajouter l’identifiant de la tâche à modifier ($task->id). Remarquez que la route attend une méthode PUT mais que là on déclare une méthode POST. C’est parce que les navigateurs gèrent encore très mal ce genre de méthode alors on déclare un POST mais après on précise qu’on veut un PUT :

@method('put')

Ca crée un champ caché qui va servir à Laravel pour savoir de quelle méthode il s’agit :

<input type="hidden" name="_method" value="put">

Pour le reste ça ne change pas beaucoup de la création sauf la case à cocher ajoutée.

Dans le contrôleur on appelle la vue :

public function edit(Task $task)
{
    return view('tasks.edit', compact('task'));
}

Remarquez le paramètre qui est du type du modèle Task. C’est une façon de dire à Laravel : le paramètre transmis est un nombre mais en fait c’est l’identifiant d’une instance de Task. Du coup Eloquent peut aller chercher dans la table la tâche correspondante. Il suffit ensuite de transmettre ça à la vue.

Maintenant avec une url de la forme todolist.oo/tasks/1/edit on atteint le formulaire.

La soumission

Pour modifier effectivement la tâche on va devoir gérer la soumission du formulaire dans la méthode update du contrôleur :

public function update(Request $request, Task $task)
{
    $data = $request->validate([
        'title' => 'required|max:100',
        'detail' => 'required|max:500',
    ]);

    $task->title = $request->title;
    $task->detail = $request->detail;
    $task->state = $request->has('state');
    $task->save();

    return back()->with('message', "La tâche a bien été modifiée !");        
}

On a la même validation que pour la création. D’ailleurs il faudrait pour bien faire mutualiser ce code pour éviter cette redondance qui n’est jamais une bonne chose (principe DRY). Laravel dispose d’une stratégie élégante pour la validation avec les Form Request, vous pouvez trouver les détails dans la documentation.

On met à jour les valeurs de la tâche. Pour la case à cocher il faut savoir qu’on ne retrouve une valeur que si elle est cochée. Donc on va vérifier si la valeur existe pour mettre à jour dans la table.

Voir une tâche

On va partir du principe qu’on aura un tableau avec juste le titre des tâches et pas le détail. Il faut donc prévoir de pouvoir afficher chaque tâche.

On crée la vue :

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            Fiche d'une tâche
        </h2>
    </x-slot>    

    <div class="py-12">
      <div class="flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100">
        <div class="w-full sm:max-w-lg mt-6 px-6 py-4 bg-white shadow-md overflow-hidden sm:rounded-lg">
          <p class="text-2xl">Titre</p>
          <p>{{ $task->title }}</p>
          <p class="text-2xl">Détail</p>
          <p>{{ $task->detail }}</p>
          <p class="text-2xl">Etat</p>
          <p>
            @if($task->state)
              La tâche a été accomplie !
            @else
              La tâche n'a pas encore été accomplie.
            @endif
          </p>
          <p class="text-2xl">Date de création</p>
          <p>{{ $task->created_at->format('d/m/Y') }}</p>
          @if($task->created_at != $task->updated_at)
            <p class="text-2xl">Dernière mise à jour</p>
            <p>{{ $task->updated_at->format('d/m/Y') }}</p>
          @endif
        </div>
      </div>
    </div>
  </div>  
</x-app-layout>

On complète le contrôleur :

public function show(Task $task)
{
    return view('tasks.show', compact('task'));
}

Et avec une url de la forme todolist.oo/tasks/1 on affiche les éléments d’une tâche :

On affiche la date de mise à jour que si elle est différente de celle de la création.

Liste des tâches

Maintenant qu’on sait créer, modifier et afficher des tâches on va voir comment en afficher la liste en prévoyant des boutons pour les différentes actions.

Au niveau du contrôleur on va récupérer toutes les tâches et les envoyer dans une vue :

public function index()
{
    $tasks = Task::all();

    return view('tasks.index', compact('tasks'));
}

Comme il n’y aura pas de très nombreuses tâches on ne prévoie pas de pagination mais Laravel sait très bien s’occuper de ça également.

On crée la vue :

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            Liste des tâches        
          <a href="{{ route('tasks.create') }}" role="button" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded float-right">Créer une tâche</a>
        </h2>
    </x-slot>    

    <div class="py-12">
      <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
          <div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
            <table class="table-fixed">
              <thead>
                <tr>
                  <th class="px-4 py-2 w-1/4">Titre</th>
                  <th class="px-4 py-2 w-1/4">Etat</th>
                  <th class="px-4 py-2 w-1/6"></th>
                  <th class="px-4 py-2 w-1/6"></th>
                  <th class="px-4 py-2 w-1/6"></th>
                </tr>
              </thead>
              <tbody>
                @foreach($tasks as $task)
                  <tr>
                    <td class="px-4 py-3">{{ $task->title }}</td>
                    <td class="px-4 py-3">@if($task->state) Accomplie @else A faire @endif</td>
                    <td class="px-4 py-3"><a href="{{ route('tasks.show', $task->id) }}" role="button" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Voir</a></td>
                    <td class="px-4 py-3"><a href="{{ route('tasks.edit', $task->id) }}" role="button" class="bg-yellow-400 hover:bg-yellow-600 text-white font-bold py-2 px-4 rounded">Modifier</a></td>
                    <td class="px-4 py-3">
                      <form id="destroy{{ $task->id }}" action="{{ route('tasks.destroy', $task->id) }}" method="POST">
                        @csrf
                        @method('DELETE')                      
                        <a role="button" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
                          onclick="event.preventDefault();
                          this.closest('form').submit();">
                          Supprimer
                        </a>
                      </form>
                    </td class="px-4 py-3">
                  </tr>
                @endforeach
              </tbody>
            </table>
        </div>
      </div>
    </div>
  </div>  
</x-app-layout>

On se retrouve avec un tableau des tâches et un bouton pour en créer une avec l’url todolist.oo/tasks :

Les boutons permettent d’accéder aux tâches qu’on a codées précédemment sauf la suppression. On va coder pour cela cette fonction dan sle contrôleur :

public function destroy(Task $task)
{
    $task->delete();
    return back();
}

Je n’ai pas prévu de boîte de dialogue d’avertissement avant la suppression mais ça serait à ajouter dans une application réelle. Pour cette suppression j’ai prévu un formulaire caché pour chaque tâche et un peu de javascript pour la soumission. ce n’est évidemment pas la seule façon de faire mais ça ne concerne pas directement Laravel.

Organisation

Pour le moment l’url de base envoie sur la page par défaut de Laravel. D’autre part une fois qu’un utilisateur se connecte il est envoyé aussi sur une vue par défaut, en l’occurence le dashboard.

Ce qu’on voudrait c’est que si l’utilisateur est conecté il arrive sur la liste des tâches et sinon sur la page de connexion.

Dans le provider RouteServiceProvider on va changer le code ainsi :

public const HOME = '/tasks';

Maintenant à l’issue de la connexion on arrive directement dans les tâches.

Les tests

Laravel permet de faire facilement de tests. Il utilise PHPUnit et on trouve par défaut le fichier de configuration phpunit.xml à la racine. Par défaut la base de donnée en sqlite et en mémoire est commentée :

<!-- <server name="DB_CONNECTION" value="sqlite"/> -->
<!-- <server name="DB_DATABASE" value=":memory:"/> -->

Il faut décommenter ces lignes pour les activer.

Les fichiers de test se trouvent dans le dossier tests :

Supprimez le fichier Unit/ExampleTest.php. Il ne reste donc plus que Feature/ExampleTest.php. Lancez les tests :

php artisan test

   PASS  Tests\Feature\ExampleTest
  ✓ basic test

  Tests:  1 passed
  Time:   0.23s

Regardons le code du test :

class ExampleTest extends TestCase
{
    public function testBasicTest()
    {
        $response = $this->get('/');
        $response->assertStatus(200);
    }
}

On ouvre la page d’accueil et on attend un code 200.

Mais comment faire pour avoir la page pour un utilisateur connecté ? Il faut créer la base, créer un utilisateur, le connecter… Avec Laravel c’est facile, changez ainsi le code :

use App\Models\User;

use RefreshDatabase;

public function testBasicTest()
{
    $user = User::factory()->create();
    $response = $this->actingAs($user)->get('/');
    $response->assertStatus(200);
}

 

On a bien un code 200 avec un utilisateur connecté !

On ne va pas tester toute l’application mais par exemple la création d’une tâche. On crée d’abord la classe de test :

php artisan make:test CreateTaskTest

On va prévoir cette fonction dans la classe :

...

use App\Models\User;

class CreateTaskTest extends TestCase
{
    use RefreshDatabase;
    public function test_auth_can_create_task()
    {
        $user = User::factory()->create();
        $response = $this->actingAs($user)->post('/tasks', [
            'title' => 'Ma nouvelle tâche',
            'detail' => 'Tous les détails de ma nouvelle tâche',
        ]);
        $this->assertDatabaseHas('tasks', [
            'title' => 'Ma nouvelle tâche'
        ]);
        $this->get('/tasks')
             ->assertSee('Ma nouvelle tâche');
    }
}

On fait les tests suivants :

  • un utilisateur authentifié soumet le fomulaire
  • la tâche est bien dans la table
  • on trouve la tâche dans la liste des tâches

On peut tester ainsi tous les aspects de l’application, je vous renvoie à la documentation détaillée pour tous les détails.

Conclusion

J’espère que ce petit exemple pourra donner envie de découvrir ce framework. Il est à la fois très chargé pour un débutant et frustrant pour quelqu’un de plus avancé mais son seul objectif est de permettre la découverte de Laravel au travers d’un exemple léger mais réaliste.

 

 

 

Print Friendly, PDF & Email

30 commentaires

  • Frederick_Dikaprio

    Bonjour s’il vous plait, je suis nouveau dans l’utilisation de Laravel et des frameswork, j’étais totalement natif j’usqu’ici donc je ne comprend presque rien… et excusez mes bizarres questions à la longue…

    voila, mon gros soucis ici c’est que j’arrive pas à afficher la vue des formulaires tel que présenté, une erreur m’est toujours renvoyé, c’est quand j »essaie de mettre dans des balises HTML que ça passe….

    maintenant j’aimerai savoir s’il ya tout un code CSS que je dois écrire derriere pour que ces formulaires apparaissent ainsi? sinon, merci je m’orienté pasà pascomment afficher cela… merci.

    NB: j’ai aimé le tuto vraiment j’ai étét enrichi, Merci

  • fabBlab

    Hello,

    Ce cas pratique est sympa et permet de retrouver une partie ce qui a été présenté dans les cours.

    Cependant, je rencontre une difficulté en essayant de traduire les textes des vues affichant les formulaires liés à Jetstream.

    Le fichier « resources/lang/fr.json » qu’il est proposé de créer dans l’article est déjà fourni par Laravel-Lang.
    Dans un dossier « packages » on trouve aussi des fichiers json spécifiques à jetstream, fortify, etc.
    On y trouve les diverses expressions utilisées dans les vues des formulaires, par exemple :
    « Password »: « Mot de passe »,
    Mais bien que présents dans le répertoire /resources/lang/fr/, ces fichiers semblent ignorés par Laravel.
    J’ai pourtant bien précisé « fr » (‘locale’ => ‘fr’) comme langue principale dans le fichier de configuration de l’application.
    J’ai essayé de déplacer le fichier jetstream.json de son sous-réepertoire vers « /lang/fr/ » et même de rafraichir le cache des vues, cela n’est pas pris en compte.
    Idem en écrivant directement les traductions dans fr.json comme indiqué dans l’article.
    J’ai relu le cours sur les localisations, mais le cas de ces fichiers n’est pas abordé.
    J’ai loupé quelque chose ?

    Sinon, moins gênant, mais pour une raison mystérieuse certaines classes telles « bg-blue-500 », « bg-blue-500 »… sont absentes du app.css généré lors de l’installation de Jetstream.
    Ces classes existent pourtant bien : https://tailwindcss.com/docs/background-color
    Cela rend invisible certains boutons ou encore les messages informant que les enregistrements se sont bien passés. Mais je ne suis pas fan non plus de tailwindcss, donc j’essayerai de m’en passer dans l’avenir…

    • bestmomo

      Salut,

      Oui apparemment les fichiers de langue ont été complétés récemment. Il faut placer le fichier fr.json dans le dossier lang, pas dans la locale.

      Pour les classes manquantes je pense que relancer la compilation avec npm run dev devrait arranger les choses.

      • fabBlab

        Ok impeccable !

        Je comprends mieux. Il n’y a donc qu’un seul fichier JSON par langue, contrairement aux tableaux de traduction PHP.

        Je viens de tester et en suivant la procédure d’installation de Laravel-Lang via composer, on reçoit en fait un fichier JSON comprenant toutes les traductions pour jetstream, etc. : https://github.com/Laravel-Lang/lang/tree/master/docs/installation

        Et ok aussi pour les classes CSS manquantes.
        npm run dev a bien réglé le problème.

        Encore merci !

        PS : rien à voir avec Laravel mais plutôt WordPress. Lorsque je coche le case permettant demandant à être prévenu des nouveaux commentaires, une fois sur deux le lien que je reçois par mail ne fonctionne pas. Le token n’est pas reconnu. Pourtant je ne traine pas à cliquer.

  • gil

    Hello à toi,
    Groumpf, j’avais abandonné les devs en 2016 avec laravel 5.2, il y a quand même du changement. On s’y retrouve vite question organisation, pas de révolution sur le système route, controllers et vues, mais plus de dépendances, pas mal de changements côté blade avec la métho (héritage ou insertion pour les layouts) et la syntaxe, et aussi gros changements pour les choix authent de base & front. J’ai eu un peu de mal à retrouver d’ou sortaient tes « x-jet-button » par exemple (mais où que c’est donc défini où donc ? :p) , même si le « jet » indique en partie la source… Je n’ai quand même pas pu éviter la doc (x- pour composant blade, donc recherche fichier jet-button, ah non, jet aussi doit être un préfixe, ouf, c’est un composant button.blade de chez jetstream).
    Mais justement, est-ce que tu utilises parce que tu as vu que c’était pratique pour avoir le même look, ou parce que l’usage en est documenté par jetstream ? Je n’ai pas trouvé dans leur doc (peut-être mal regardé).
    En tout cas, avant de me lancer dans un truc bien plus ambitieux qu’il y a cinq ans (et ce qui me fait le plus peur, ce sont les IHM d’admin de multiples relations de type many-to-many avec des données pivot…), il va me falloir rebaigner dans toutes ces nouveautés en parcourant la doc officielle, refaire quelques autres « projets tuto » et suivre des vidéos laracast…

    • bestmomo

      Salut et bon retour !

      C’est vrai qu’au niveau de Blade c’est un peu délicat mais en fait c’est juste qu’on dispose de nombreuses possibilités. On a la méthode ancienne avec les layouts et les inclusions. Mais on a ausis la méthode plus récente issue de la philosophie Vue.js avec des composants, et là on peut utiliser des classes ou pas… Il faut parcourir la documentation et essayer les différentes possibilités pour finir par opter pour ce qui plait le plus. Personnellement je navigue d’une possibilité à une autre en fonction de l’humeur ou de l’adéquation ponctuelle.

      En ce qui concerne Jetstream je ne l’utilise pas et je me suis plutôt orienté sur Breeze, je récupère les contrôleurs et je change les vues pour éviter Tailwind que je n’aime pas du tout. Mais là aussi c’est une histoire de goût personnel.

      • gil

        J’ai suivi tailwind, pas trop fana non plus, mais pourquoi pas pour un petit projet.
        Quelque chose que j’aime bien, dont j’ai suivi le développement aussi, c’est patternfly. Plein de bonnes idées mais orienté gros projet pour être très propre au niveau IHM. Pas utilisé non plus, mais j’envisage d’en faire au moins un essai.

        > Il faut parcourir la documentation et essayer les différentes possibilités pour finir par opter pour ce qui plait le plus
        C’est clair, c’est pourquoi je me suis prévu plusieurs semaines de remises à niveau et essais avant de commencer le « vrai travail » 😀

  • code

    Salut!! J’ai un souci concernant ma commande tinker, en fait lorsque j’essaie de saisir des données et que j’utilise le fonction save pour sauvegarde on me renvoie un message d’err qui suit SQLSTATE [42s22] column not found: 1054 champ ‘updated_at’ inconnu field list

  • Yoh

    Super tuto ! Merci !
    Comme le cours d’ailleurs 🙂

    Petite question : Si je souhaite, après avoir modifié la tâche, redirigé l’utilisateur vers le listing des tâches en ajoutant un message d’erreur. Vous feriez comment ?

    J’ai fait ça et cela fonctionne mais je ne suis pas convaincu que cela soit la meilleure façon :

    public function update(TaskRequest $request, Task $task)
    {

    [...]

    $tasks = Task::all();
    return view('tasks.index', [
    'tasks' => $tasks,
    'message' => __('Task updated with success'),
    'state' => 'success'
    ]);
    }

    J’ai bien essayé avec des redirect()->route(tasks.index)->with(…) mais je ne récupère jamais le contenu du with … Merci !

  • code

    Salut !
    En fait moi je suis débutant c’est maintenant que j’ai commencé à coder vus que je code en PHP on m’a donc conseillé d’utiliser LARAVEL mais je rencontre des problèmes pour son installation j’ai vus plein de tuto pour l’installer et j’ai procédé de la même manière mais sans succès donc une aide de votre part m’aiderai beaucoup encore merci d’avance

      • gil

        Pour info, il y a du avoir quelques changements de dépendances depuis; avec des Laravel & Laragon « tout frais », il y a quelques updates indispensables : PHP évidemment, mais aussi composer et nodejs au moins. Loin d’être si simple pour un débutant complet qui n’a jamais manipulé npm ou update-node (et galère aussi pour un vieux qui s’y remet :D). Dommage qu’il n’y ait pas moyen de pousser ces updates de versions dans le laragon à télécharger.
        A part ça, barryvdh/laravel-debugbar toujours aussi utile !

  • larakof

    Bonjour et merci pour ce super tutoriel!
    Votre tutoriel fonctionne parfaitement sous environnement laragon mais dès que je le déplace sur un environnement linux à part le dashboard , j’ai une erreur 500.
    Le controleur Task et toutes ses routes ne fonctionnent pas .
    laravel ramene une erreur controleur not found , j’ai meme pris votre dossier zippé pour déployer sur un environnement linux pareil , j’ai toujours la même erreur .
    Pouvez vous me conseiller une piste de correction
    Merci

Laisser un commentaire