Créer une application avec Laravel 5.5 – Les catégories 1/2

image_pdfimage_print

On va poursuivre la création de notre galerie photos en nous intéressant dans ce chapitre aux catégories. En effet pour organiser un peu les photos de la galerie on les classe en catégories. Seul un administrateur peut créer, modifier ou supprimer une catégorie.

On va voir dans ce chapitre comment on crée une catégorie. Pour ça on va devoir déjà compléter le menu de la barre de navigation, créer un formulaire, les routes, le contrôleur et même un middleware et un événement…

Un middleware

Pour vérifier qu’on a affaire à un administrateur le plus simple est de créer un middleware :

php artisan make:middleware Admin

On va changer ainsi la fonction handle :

public function handle($request, Closure $next)
{
    $user = $request->user();

    if ($user && $user->role === 'admin') {
        return $next($request);
    }

    return redirect()->route('home');
}

Si c’est un administrateur on continue, sinon on renvoie sur la page d’accueil.

On le déclare dans app/Http/Kernel.php :

protected $routeMiddleware = [
    ...
    'admin' => \App\Http\Middleware\Admin::class,
];

On va en profiter pour ajouter une directive à Blade dans AppServiceProvider :

use Illuminate\Support\Facades\Blade;

...


public function boot()
{
    Blade::if('admin', function () {
        return auth()->check() && auth()->user()->role === 'admin';
    });

}

On pourra ainsi écrire @admin dans les vues !

Le contrôleur

Pour gérer les catégories on va créer un contrôleur :

php artisan make:controller CategoryController --resource

Le fait d’utiliser l’option –resource a généré les 7 méthodes de base. On va toutes les conserver sauf show.

Les routes

Pour les routes on va ajouter ça :

Route::middleware('admin')->group(function () {

    Route::resource ('category', 'CategoryController', [
        'except' => 'show'
    ]);

});

Le groupe nous servira plus tard quand on ajoutera d’autres routes.

On vérifie :

php artisan route:list

On a bien nos 6 routes pour notre contrôleur.

Le menu

Pour accéder au formulaire de création d’une catégorie on va devoir compléter la barre de navigation dans views/layouts/app :

<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-dark">
    <a class="navbar-brand" href="{{ route('home') }}">{{ config('app.name', 'Album') }}</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
            @admin
            <li class="nav-item dropdown">
                <a class="nav-link dropdown-toggle{{ currentRoute( route('category.create') )}}" href="#" id="navbarDropdownGestCat" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                    @lang('Administration')
                </a>
                <div class="dropdown-menu" aria-labelledby="navbarDropdownGestCat">
                    <a class="dropdown-item" href="{{ route('category.create') }}">
                        <i class="fa fa-plus fa-lg"></i> @lang('Ajouter une catégorie')
                    </a>
                </div>
            </li>
            @endadmin
        </ul>
        <ul class="navbar-nav">
            @guest
                <li class="nav-item{{ currentRoute(route('login')) }}"><a class="nav-link" href="{{ route('login') }}">@lang('Connexion')</a></li>
                <li class="nav-item{{ currentRoute(route('register')) }}"><a class="nav-link" href="{{ route('register') }}">@lang('Inscription')</a></li>
            @else
                <li class="nav-item">
                    <a id="logout" class="nav-link" href="{{ route('logout') }}">@lang('Déconnexion')</a>
                    <form id="logout-form" action="{{ route('logout') }}" method="POST" class="hide">
                        {{ csrf_field() }}
                    </form>
                </li>
            @endguest
        </ul>
    </div>
</nav>

La partie ajoutée est celle-ci :

@admin
<li class="nav-item dropdown">
    <a class="nav-link dropdown-toggle{{ currentRoute( route('category.create') )}}" href="#" id="navbarDropdownGestCat" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
        @lang('Administration')
    </a>
    <div class="dropdown-menu" aria-labelledby="navbarDropdownGestCat">
        <a class="dropdown-item" href="{{ route('category.create') }}">
            <i class="fa fa-plus fa-lg"></i> @lang('Ajouter une catégorie')
        </a>
    </div>
</li>
@endadmin

Si on a affaire à un administrateur (@admin) on crée un menu déroulant. Pour le moment on déroule un seul item mais on complétera plus tard.

Dans le code j’ai prévu une icône de Font Awesome mais elle n’apparaît pas parce qu’on a pas chargé la librairie ni les icônes. On peut charger tout ça ici. On ajoute le fichier CSS :

Les icônes :

On déclare la librairie CSS dans webpack.mix.js :

mix.styles([
    'resources/assets/css/bootstrap.css',
    'resources/assets/css/font-awesome.css',
    'resources/assets/css/app.css'
], 'public/css/app.css')
.scripts([
    'resources/assets/js/jquery-3.2.1.js',
    'resources/assets/js/popper.js',
    'resources/assets/js/bootstrap.js'
], 'public/js/app.js');

Et on lance mix :

npm run dev

Et voilà une icône dans le menu déroulant :

La vue de création

On crée un dossier pour les catégories et une vue pour la création :

Pour cette vue on utilise l’intendance qu’on a précédemment mise en place :

@extends('layouts.form')

@section('card')

    @component('components.card')

        @slot('title')
            @lang('Ajouter une catégorie')
        @endslot

        <form method="POST" action="{{ route('category.store') }}">
            {{ csrf_field() }}

            @include('partials.form-group', [
                'title' => __('Nom'),
                'type' => 'text',
                'name' => 'name',
                'required' => true,
                ])

            @component('components.button')
                @lang('Envoyer')
            @endcomponent

        </form>

    @endcomponent            

@endsection

L’affichage du formulaire

Il nous faut maintenant coder la gestion de tout ça dans le contrôleur CategoryController.

Déjà il faut afficher le formulaire :

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

On vérifie avec le menu que ça marche :

La validation

Pour la validation on crée une requête de formulaire :

php artisan make:request CategoryRequest

Et on change ainsi le code :

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CategoryRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        $id = $this->category ? ',' . $this->category->id : '';

        return $rules = [
            'name' => 'required|string|max:255|unique:categories,name' . $id,
        ];
    }
}

C’est déjà préparé pour gérer la validation de la modification. Dans ce cas on sait qu’il faut arranger un peu la règle d’unicité.

La soumission

A la soumission on arrive dans la méthode store du contrôleur. On va la coder ainsi :

use App\Http\Requests\CategoryRequest;
use App\Models\Category;

...

public function store(CategoryRequest $request)
{
    Category::create($request->all());

    return redirect()->route('home')->with('ok', __('La catégorie a bien été enregistrée'));
}

Mais évidemment ça ne va pas encore fonctionner :

En effet on a pas donné de valeur au slug. Comme on va en avoir besoin dans le cas de la création et de la modification on va se servir d’un événement.

Un événement

On crée le listener :

php artisan make:listener CategorySaving

Et l’événement :

php artisan make:event CategorySaving

Dans l’événement on se contente de transmettre le modèle :

<?php

namespace App\Events;

use Illuminate\ {
    Queue\SerializesModels,
    Database\Eloquent\Model
};

class CategorySaving
{
    use SerializesModels;

    public $model;

    public function __construct(Model $model)
    {
        $this->model = $model;
    }
}

Et dans le listener on code le slug :

<?php

namespace App\Listeners;

use App\Events\CategorySaving as EventCategorySaving;

class CategorySaving
{
    public function handle(EventCategorySaving $event)
    {
        $event->model->slug = str_slug($event->model->name, '-');
    }
}

Merci à l’helper str_slug de Laravel !

On établit la liaison entre event et listener dans EventServiceProvider :

protected $listen = [
    'App\Events\CategorySaving' => [
        'App\Listeners\CategorySaving',
    ],
];

Il ne reste plus qu’à propager l’événement à partir du modèle Category :

use App\Events\CategorySaving;

...

protected $dispatchesEvents = [
    'saving' => CategorySaving::class,
];

Le message

Dans le contrôleur on renvoie une information de réussite en flash session. Il faut prévoir dans le layout (views/layouts/app) de quoi l’afficher :

@if (session('ok'))
    <div class="container">
        <div class="alert alert-dismissible alert-success fade show" role="alert">
            {{ session('ok') }}
            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                <span aria-hidden="true">&times;</span>
            </button>
        </div>
    </div>
@endif

@yield('content')

Et maintenant ça doit fonctionner :

Conclusion

Dans ce chapitre on a :

  • créé un middleware pour l’administration
  • créé une directive Blade
  • créé routes, contrôleur et requête de formulaire pour la création d’un catégorie
  • complété la barre de navigation
  • créé un événement pour la création du slug
  • modifié le layout pour afficher un message

Pour vous simplifier la vie vous pouvez charger le projet dans son état à l’issue de ce chapitre.

11 commentaires sur “Créer une application avec Laravel 5.5 – Les catégories 1/2

  1. J’ai réussi à régler le problème:
    1. je me suis rendu sur un bootstrap starter template (https://getbootstrap.com/docs/4.0/examples/starter-template/)
    2. j’ai récupérer dans le code les .js et je les ai téléchargés
    3. je les ai introduits dans le webpack.mix.js dans le même ordre que dans le template:
    ‘resources/assets/js/jquery-3.2.1.slim.min.js’,
    ‘resources/assets/js/popper.min.js’,
    ‘resources/assets/js/bootstrap.min.js’,

    Il doit y avoir un problème entre certaines versions beta du boostrap 4 et de popper… d’ailleurs j’ai toujours une erreur que je ne comprend pas (mais elle n’a pas d’influence visible… pour l’instant):
    Erreur dans les liens source : request failed with status 404
    URL de la ressource : http://phonemia.oo/js/app.js
    URL du lien source : bootstrap.min.js.map

    Mais je ne suis apparemment pas le seul: https://github.com/twbs/bootstrap/issues/23381

    En espérant que cela puisse aider quelqu’un!

    1. Salut,

      Merci pour la description détaillée. Le souci vient du fait que lorsque j’ai rédigé l’article, et donc écrit le code, Bootstrap en était à la version beta2 et que maintenant il en est à la beta3. Donc pour suivre le tuto je pense que le plus simple est d’utiliser la beta2 parce sinon il doit y avoir des incohérence au niveau du balisage et peut-être aussi avec popper. Lorsque Bootstrap passera en version définitive, ce qui ne saurait tarder, je reprendrai le tuto pour que ça fonctionne mais je dois tout repasser en revue parce que j’ai mis pour chaque article un ZIP à charger…

  2. Bonjour,

    J’ai créé le code avec la version beta2 de Bootstrap qui en est maintenant à la beta3, il y a donc peut-être un souci si vous utilisez cette version par rapport au code que j’ai prévu dans les vues.

  3. Bonjour, j’ai terminé mais quand je clique sur envoyer pour ajouter la catégorie ca me donne une page blanche avec cette erreur : javascript?v=1505741566:3 Uncaught TypeError: Cannot read property ‘style’ of undefined

  4. Salut BESTMOMO
    Mille merci pour ce cours qui m’apprend beaucoup.
    Le code fonctionne parfaitement mais je n’arrive pas à obtenir le Dropdown du menu « Ajouter une catégorie ».
    je ne vos que « Administration » et lorsque je clic rien ne fait Dropdown.
    J’ai même copier le code source de ton fichier « views/layouts/app » et aussi le contenu de « resources\assets » puis « npm run dev » mais c’est pareille
    Merci de m’aider

    1. Bonjour,

      Il faudrait vérifier que le fichier public/js/app.js est bien généré par npm et qu’il comporte bien les 3 librairies nécessaires. D’autre part vérifier aussi qu’il est bien déclaré dans le layout. Pour la forme vérifier aussi dans un autre navigateur des fois qu’il y ait un souci avec Javascript…

          1. Salut,
            Un énorme merci pour ces précieuses ressources!!! Je débute avec Laravel et c’est vraiment très utile.
            J’ai également le même problème… je n’ai pas encore trouvé la solution. Apparemment c’est quelque chose avec Popper.js..!? peut-être à cet endroit-là: export default Popper;
            C’est la seule erreur indiquée par la console.
            D’ailleurs le menu en responsive ne fonctionne pas (l’icône du menu « smartphone » s’affiche mais rien ne se passe lorsque je clic dessus).
            @kikiro : qu’as-tu fait pour que ça fonctionne?

Laisser un commentaire