11 avril 2023   |   De Jérôme Borg   |    Laravel

Le Crud

Le Crud

Le CRUD représente toutes les opérations que nous pouvons effectuer sur un modèle.

Créer : Create or Add
Lire : Read or Browse
Modifier : Update ou Edit
Supprimer : Delete

Laravel, offre une fonctionnalité spécifique pour le CRUD, les contrôleurs de types ressources. Ce sont des contrôleurs qui ont toutes les méthodes nécessaires pour le CRUD, à savoir 7

index -> liste tous les éléments
show -> affiche un élément
edit -> renvoie vers un formulaire avec un élément pour une modification
update -> met à jour cette élément
create  -> renvoie vers un formulaire vide pour une création
store -> sauve cette élément
delete -> supprime cette élément

Pour les API, les méthodes edit et create ne sont pas présentes

Nous allons créer un CRUD pour le modèle Post

php artisan make:model Post -mcrR

Grace a cette commande, on a créer le modèle, sa migration -m, son contrôleur -c, de type resource -r, et les forms Request -R

Nous allons également ajouter les routes nécessaires, dans ce cas de figure, il suffit d'en ajouter une, Laravel se charge du reste

Route::resource('/posts', PostController::class);

En effet, pour une route de type resource, Laravel créer automatiquent toutes les routes, basées sur les actions, et verbes, lancer la commande 

php artisan route:list

Comme nous le voyons, chaque route est constituée d'une url et d'un verbe. Pour index et store, ce sont les mêmes urls, mais par le même verbe.

A présent, créons le modèle, pour la démonstration, nous allons faire très simple, un titre et une description, 

dans la migration database/migrations/Y_m_d_xxxxx_create_posts_table.php

Schema::create('posts', function (Blueprint $table) {
           $table->id();
           $table->string('title');
           $table->text('description');
           $table->timestamps();
});

dans le modèle app/Models/Post.php

protected $fillable = ['title' , 'description'];

dans le contrôleur, app/Http/Controllers/PostController.php, pour la méthode index, on récupère tous les posts que l'on envoie a la vue index, dans le dossiers resources/views/posts

public function index()
{
           $posts = Post::all();  
           return view('posts.index', compact('posts'));  
}

Afin de tester tous ceci, nous allons créer de fausses données, commençons par créer une factory

php artisan make:factory PostFactory
public function definition(): array  {
           return [
                      'title' => fake()->sentence(6, true),
                      'description' => fake()->paragraph( 3, true)
           ];
}

Laravel a intégré a ses dépendances, depuis quelques versions, la bibliothèque de Francois Zaninotto, faker qui permet de créer du contenu qui a du sens.

Lancons la migration, 

php artisan migrate

et injectons des données, dans notre table posts, laravel propose un outil "tinker", qui permet d'écrire du code en ligne de commande

php artisan tinker
App\Models\Post::factory(10)->create();

Ici on fait appel a la méthode static factory du trait HasFactory, on lui passe en paramètre le nombre d'élément à créer, puis la méthode create, qui par défaut va prendre les éléments définis dans PostFactory.

On peut surcharger, comme par exemple

App\Models\Post::factory(10)->create(['title' => 'Un titre']);

Nous avons bien une table contenant 10 éléments

 

index()

A présent, passons a la vue index, dans le dossiers resources/views/posts/, créer un fichier index.blade.php, nous allons tous simplement lister les titres de post

<table>
     <thead>
          <th>Id</th>
          <th>Title</th>
          <th>Action</th>
     </thead>
     <tbody>
          @foreach($posts as $post)
               <tr>
                    <td>{{ $post->id }}</td>
                    <td>{{ $post->title }}</td>
                    <td></td>
               </tr>
          @endforeach
     </tbody>
</table>
        

 

show()

Passons a la méthode show, modifions un peu le fichier index.blade.php

 <table>  
    <thead>  
        <th>Id</th>  
        <th>Title</th>  
        <th>Action</th>  
    </thead>  
    <tbody>  
    @foreach($posts as $post)  
        <tr>  
            <td><a href="{{ route('posts.show') }}" title="show">{{ $post->id }}</a></td>  
            <td>{{ $post->title }}</td>  
            <td></td>  
        </tr>  
    @endforeach  
    </tbody>  
</table>

En cliquant sur l'id, nous allons afficher l'élément, pour cela, on passe dans l'url en get, le model, grace au "Model Binding", laravel va retrouver automatiquent l'id de l'élément, et injecter dans le contrôleur le modèle directement. Concernant l'url, j'utilise les routes nommées, qui sont extrêmement pratique, pour les obtenir, il faut lancer la commande php artisan route:list

Dans la méthode show du contrôleur

public function show(Post $post)  
    {  
        return view('posts.show', compact('post'));  
    }

Sans "model binding"

public function show($id)  
{  
    $post = Post::findOrFail($id);  
      
    return view('posts.show', compact('post'));  
}

Créons à présent la vue de consultation, resources/views/posts/show.blade.php

id: {{ $post->id }}

title: {{ $post->title }}

description: {{ $post->description }}

 

create()

Pour la création d'un nouvel élément, il faut un formulaire vide, et un lien nous renvoyant vers cette vue, dans index.blade.php

remplaçons

<th>Action</th>

par

 <th><a href="{{ route('posts.create') }}" title="new">New</a></th>

Dans la méthode create() du controller

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

et enfin, notre formulaire vide, avec la gestion des erreurs

<form action="{{ route('posts.store') }}" method="POST">  
    @csrf  
    <div>  
        <label for="title">Title</label><br>  
        <input type="text" name="title" id="title" value="{{ old('title','') }}">  
        @error('title')<br><small style="color:#f00">{{ $message }}</small>@enderror  
    </div>  
    <div>  
        <label for="description">Description</label><br>  
        <textarea name="description" id="description">{{ old('description','') }}</textarea>  
        @error('description')<br><small style="color:#f00">{{ $message }}</small>@enderror  
    </div>  
    <div>  
        <button type="submit" name="Send">Send</button>  
    </div>  
</form>

C'est un simple formulaire html sans aucune class, ce n'est pas le sujet de cette article.

 

On passe en route au formulaire, la route nommée vers la méthode store, puis que le verbe est POST, et enfin le CSRF qui est un élément indispensable de sécurité pour laravel.

Enfin pour chaque input, on peut faire appel a l'helper old, qui va afficher dans le champ la précédente saisie en cas d'erreur.

On fait également appel a l'helper @error de blade, qui va afficher l'erreur trouvée, si dans le tableau $errors, il trouve par exemple le champ 'title'

 

store()

A présent dans la méthode store du controller

public function store(StorePostRequest $request)  
    {  
    }

Laravel  a directement injecté $request, qui contient toutes les variables de Post et Get, il spécifie également, qu'elles doivent être de type StorePostRequest

dans le fichier StorePostRequest, commençons par autorisé l'utilisation

public function authorize(): bool  
{  
    return true;  
}

puis écrivons nos règles de validation (voir article sur la validation)

public function rules(): array  
{  
    return [  
        'title' => 'required|string|max:255',  
        'description' => 'required|string|max:5000',  
    ];  
}

On renvoie un tableau associatif, ayant pour clé le nom du champ a valider, et pour valeur un tableau de règles de validation, comme par exemple pour "title", "requis", "chaine de caractère" et "maximum 255 caractères". Lorsque que l'on soumet le formulaire, il va être envoyé a cette class de validation, qui va vérifier les règles de validation. Si une ou plusieurs règles ne sont pas respectées, il renvoie à la vue, avec les données saisies et un tableau d'erreur.

si par contre les données, sont validées, elles seront retournées grâce à la méthode $request->validated(), et nous pourrons procéder a l'enregistrement

public function store(StorePostRequest $request)  
{  
    Post::create($request->validated());  
  
    return redirect()->route('posts.index');  
}

Puis nous rédigeons l'utilisateur vers la page d'index, ou se trouve notre nouveau Post

 

edit()

Passons à présent a l'édition, pour cela modifions un peu notre fichier index.blade.php, dans la colonne en dessous de new, rajoutons un bouton permettant d'éditer notre modèle

<td><a href="{{ route('posts.edit') }}" title="edit">Edit</a></td>

Dans la méthode edit du controller

public function edit(Post $post)  
    {  
        return view('posts.editForm', compact('post'));  
    }

Créons le formulaire, nous allons copier/coller createForm.blade.php en editForm.blade.php, et le modifier

 <form action="{{ route('posts.update',$post) }}" method="POST">  
    @method('PUT')  
    @csrf  
    <div>  
        <label for="title">Title</label><br>  
        <input type="text" name="title" id="title" value="{{ old('title',$post->title) }}">  
        @error('title')<br><small style="color:#f00">{{ $message }}</small>@enderror  
    </div>  
    <div>  
        <label for="description">Description</label><br>  
        <textarea name="description" id="description">{{ old('description',$post->description) }}</textarea>  
        @error('description')<br><small style="color:#f00">{{ $message }}</small>@enderror  
    </div>  
    <div>  
        <button type="submit" name="Send">Send</button>  
    </div>  
</form>

La route a changé, de plus on lui passe en paramètre le modèle, le verbe imposé pour de l'update est PUT ou PATCH, on applique donc la méthode souhaitée avec directive @method('PUT')

pour les inputs, si on a la valeur, on l'affiche "$post->title"

 

update()

dans le contrôleur, commençons par la form Request, ce sont les même règles que pour la création, et pensons a autoriser l'utilisation

public function rules(): array  
{  
    return [  
        'title' => 'required|string|max:255',  
        'description' => 'required|string|max:5000',  
    ];  
}
public function update(UpdatePostRequest $request, Post $post)  
{  
    $post->update($request->validated());  
  
    return redirect()->route('posts.index');  
}

Comme pour la création,  si les données sont validées, elles seront retournées grâce à la méthode $request->validated(), et nous pouvons procéder a la mise à jour

 

delete()

Enfin passons à la suppression, nous allons ajouter un bouton de suppression dans l'index. Comme le verbe imposé est "delete", nous allons devoir passé par un formulaire. De plus lors d'actions "potentiellement risquées", il est bien vu de demander une confirmation à l'utilisateur, ajoutons ce code dans la page index.blade.php a coté de Edit

<td><a href="{{ route('posts.edit') }}" title="edit">Edit</a>   
                <a href="" title="delete" onclick="event.preventDefault();let response = confirm('Are You Sure?'); 
                           if (response) {  document.getElementById('delete{{ $post->id }}').submit() }">Delete</a>  
                <form id="delete{{ $post->id }}" action="{{ route('posts.destroy') }}" method="POST">  
                    @csrf  
                    @method('DELETE')  
                </form>  
            </td>

Via un href, et javascript confirm, on demande a l'utilisateur s'il valide la suppression, On stocke sa réponse dans response, si oui on recherche le formulaire de cet élément avec document.getElementById('delete{{ $post->id }}'), et on appelle sa méthode submit, ce qui va appeler la méthode destroy de notre contrôleur

public function destroy(Post $post)  
{  
    $post->delete();  
  
    return redirect()->route('posts.index');  
}

enfin on fait appel a la méthode delete de la class Model, et on redirige vers l'index

Nous venons de développer un CRUD pour le modèle Post

 

De Jérôme Borg
Le 11 avril 2023
Temps de lecture : 21 min
Jérôme Borg
Jérôme Borg

Développeur fullstack laravel/VueJs, formateur

Tous les articles de cet auteur
Articles recommandés
Le fichier d'environnement .env
Le fichier d'environnement .env
Jérôme Borg De Jérôme Borg | 23 février 2023 | Lu : 5min
Homestead
Homestead
Jérôme Borg De Jérôme Borg | 20 avril 2023 | Lu : 10min