Laravel 5

Créer une application avec Laravel 5.5 – Les vues de l’authentification

Dans ce chapitre nous allons poursuivre le développement de l’application de galerie photos. On l’a laissée en chantier avec un layout adapté mais il faut maintenant qu’on s’occupe de l’apparence des formulaires de l’authentification.

Comme on va avoir des répétitions de code on va un peu mutualiser ça avec ce que nous offre Blade comme possibilités, en l’occurrence les inclusions de vues et les composants.

Enfin on va créer un helper pour rendre actif l’item en cours du menu dans la barre de navigation.

Les formulaires de Bootstrap 4

Si on regarde la documentation de Bootstrap 4 on a une page consacrée aux formulaires. On voit que chaque contrôle est équipé de ce genre de code :

<div class="form-group">
  <label for="exampleInputEmail1">Email address</label>
  <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email">
  <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>

On a aussi tout un passage concernant la validation.

Plutôt que de le reproduire plusieurs fois le même code on va créer une vue partielle à inclure :

En prévoyant toutes les variables nécessaires avec la validation :

<div class="form-group">
    <label for="{{ $name }}">{{ $title }}</label>
    <input id="{{ $name }}" type="{{ $type }}" class="form-control{{ $errors->has($name) ? ' is-invalid' : '' }}" name="{{ $name }}" value="{{ old($name, isset($value) ? $value : '') }}" {{ $required ? 'required' : ''}}>

    @if ($errors->has($name))
        <div class="invalid-feedback">
            {{ $errors->first($name) }}
        </div>
    @endif
</div>

Dans tous les formulaires on va aussi avoir un bouton de soumission. La syntaxe que nous donne la documentation est celle-ci :

<button type="submit" class="btn btn-primary">Submit</button>

Là on va créer un composant :

Avec ce code :

<button type="submit" class="btn btn-primary float-right">
    {{ $slot }}
</button>

Enfin pour faire plus joli on va inclure les formulaires dans un composant card de Bootstrap. On voit dans la documentation la syntaxe de ce composant :

<div class="card">
  <div class="card-header">
    Featured
  </div>
  <div class="card-body">
    <h4 class="card-title">Special title treatment</h4>
    <p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
  </div>
</div>

On va en faire un composant pour notre application :

Avec ce code :

<div class="card text-white bg-dark mb-3">
    <h4 class="card-header">
        {{ $title }}
    </h4>
    <div class="card-body">
        {{ $slot }}
    </div>
</div>

Pour compléter la panoplie on va aussi créer un layout pour tous les formulaires :

@extends('layouts.app')

@section('content')
    <div class="container py-5">
        <div class="row">
            <div class="col-md-6 offset-md-3">
                @yield('card')
            </div>
        </div>
    </div>
@endsection

Maintenant on est bien équipé pour modifier les formulaires existant…

La connexion

Voici le nouveau code pour la vue de connexion (auth/login) :

@extends('layouts.form')

@section('card')

    @component('components.card')

        @slot('title')
            @lang('Connexion')
        @endslot

        <form method="POST" action="{{ route('login') }}">
            {{ csrf_field() }}

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

            @include('partials.form-group', [
                'title' => __('Mot de passe'),
                'type' => 'password',
                'name' => 'password',
                'required' => true,
                ])    

            <div class="custom-control custom-checkbox">
                <input type="checkbox" class="custom-control-input" id="remember" name="remember" {{ old('remember') ? 'checked' : '' }}> 
                <label class="custom-control-label" for="remember"> @lang('Se rappeler de moi')</label>
            </div>

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

            <a class="btn btn-link" href="{{ route('password.request') }}">
                @lang('Mot de passe oublié ?')
            </a>

        </form>

    @endcomponent            

@endsection

Ce qui donne cet aspect :

Pour bien délimiter le formulaire on va dessiner la bordure avec le CSS :

.card {
    border: 4px solid rgba(255, 242, 242, .3);
    border-radius: .5rem;
}

Pensez à activer les modifications avec npm

On vérifie que la validation apparaît bien :

L’inscription

De la même manière on va construire la vue pour l’inscription (auth/register) :

@extends('layouts.form')

@section('card')

    @component('components.card')

        @slot('title')
            @lang('Inscription')
        @endslot

        <form method="POST" action="{{ route('register') }}">
            {{ csrf_field() }}

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

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

            @include('partials.form-group', [
                'title' => __('Mot de passe'),
                'type' => 'password',
                'name' => 'password',
                'required' => true,
                ])

            @include('partials.form-group', [
                'title' => __('Confirmation du mot de passe'),
                'type' => 'password',
                'name' => 'password_confirmation',
                'required' => true,
                ])

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

        </form>

    @endcomponent

@endsection

Le renouvellement du mot de passe

On continue avec la vue pour la demande de renouvellement du mot de passe (auth/email) :

@extends('layouts.form')

@section('card')

    @if (session('status'))
        <div class="alert alert-success" role="alert">
            {{ session('status') }}
        </div>
    @endif

    @component('components.card')

        @slot('title')
            @lang('Renouvellement du mot de passe')
        @endslot

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

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

            @component('components.button')
                @lang('Envoi de la demande')
            @endcomponent
        </form>

    @endcomponent

@endsection

Et enfin la vue pour le renouvellement (auth/reset) :

@extends('layouts.form')

@section('card')

    @component('components.card')

        @slot('title')
            @lang('Renouvellement du mot de passe')
        @endslot

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

            <input type="hidden" name="token" value="{{ $token }}">

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

            @include('partials.form-group', [
                'title' => __('Mot de passe'),
                'type' => 'password',
                'name' => 'password',
                'required' => true,
                ])

            @include('partials.form-group', [
                'title' => __('Confirmation du mot de passe'),
                'type' => 'password',
                'name' => 'password_confirmation',
                'required' => true,
                ])

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

        </form>

    @endcomponent            

@endsection

L’item actif dans le menu

On a bien avancé mais il y a une chose pas très jolie au niveau de la barre de navigation : rien n’indique quel item est actif. On va maintenant arranger ça…

Quand on regarde la documentation de Bootstrap 4 on voit qu’il suffit d’ajouter la classe active à un item pour le distinguer visuellement :

<li class="nav-item active">

Il faut donc qu’on s’arrange pour ajouter cette classe en fonction de la requête en cours. Pour l’occasion on va créer un helper. On commence par créer un fichier (app/helpers.php) :

Et on lui ajoute cette fonction :

<?php

if (!function_exists('currentRoute')) {
    function currentRoute(...$routes)
    {
        foreach($routes as $route) {
            if(request()->url() == $route) {
                return ' active';
            }
        }
    }
}

On prend toujours la précaution de vérifier que la fonction n’existe pas déjà. Pour que Laravel soit au courant que notre fonction existe on va ajouter la référence dans composer.json :

"autoload": {
    "classmap": [
        "database/seeds",
        "database/factories"
    ],
    "files": [
        "app/helpers.php"
    ],
    "psr-4": {
        "App\\": "app/"
    }
},

On relance composer :

composer dumpautoload

Maintenant il ne reste plus qu’à modifier le code de la barre de navigation dans la vue layouts/app :

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

Maintenant quand je vais sur la vue de connexion l’item correspondant s’illumine :

Et ça fonctionne aussi pour l’inscription :

Il n’y a que pour le renouvellement du mot de passe qu’on a rien à rendre actif…

Conclusion

Dans ce chapitre on a vu :

  • comment créer une vue à inclure et des composants pour faciliter l’écriture des vue de formulaire
  • comme styliser les formulaires
  • comment créer un helper pour rendre actif l’item en cours dans la barre de navigation.

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

Print Friendly, PDF & Email

15 commentaires

  • phildid

    bonsoir BESTMOMO

    je suis arrivé au bout çà fonctionne but

    1 / la fonction active su les bouton ne fonctionne pas
    le helpers est bien actif (testé). Vérifier dans vendor/composer/autoload_files.php (OK)
    le composer.json ( bon aussi) et le layouts/app (modifiè)
    j’ai actionné plusieurs fois composer update et dumpload et npm (rien à faire Arg!!!.

    pas moyen de sass –watch. je me suis placé dans le dossier et j’ai essayé plusieurs combinaisons sans success

    sass — watch sass/app.scss:app.css . Là aussi sûrement une histoire de chemin (?)

    Une autre petite chose dans le fichier app.css .j’ai bien ajouté
    body {
    background-color: #343a40;
    padding-top: 70px;
    }
    .card {
    border: 4px solid rgba(255, 242, 242, .3);
    border-radius: .5rem;
    }

    **ça fonctionne** mais lorsque je veux agir sur le bouton submit dans inscription pour le décoller avec un margin-top: Rien à faire
    .btn-primary {

    margin-top:10px;
    }

    Une autre petite question pendant que je suis là SVP
    par quel chemin on arrive à auth/email et auth/reset ?

    J’espère ne pas être trop pénible je fais de mon mieux et je pense que mes questions sont assez sensée du moins je l’espère. merci à vous

    Cordialement PhilDid

    • bestmomo

      Salut,

      Je ne sais pas pourquoi ta fonction ne marche pas, ça doit être une histoire d’autoload. Vérifie que le helper est bien spécifié dans composer.json.

      Pour le watch la syntaxe c’est : npm run watch

      Pour les marges il faut utiliser les classes de Bootstrap

      Pour les chemins en fait c’est password/email et password/reset

      Bon courage 😉

  • Olv

    Bonjour.
    J’ai voulu faire ce tuto après le cours, mais après l’install de make:auth je ne parviens pas à entrer sur login ou register (page ne peut être trouvée)… comme si je n’avais pas confirmeé avec artisan migrate. Marre de chercher dans tous les sens donc je suis reparti de zéro (et plusieurs fois)… je réinstalle un nouveau projet, je vérifie que pas d’authentification. J’utilise Artisan pour l’installer, je vérifie que la base est correcte (3 tables), que le bandeau login et register est bien là, mais pour autant impossible d’entrer sur register, la route ne se fait pas 🙁
    Pourtant tout est là dans web.php, ainsi que les view et controller…
    Qu’est ce que je peux bien vérifier encore ?
    NB: oui je fais bien php artisan migrate après php artisan make:auth.
    Merci de votre aide

  • Kikiro

    Merci BESTMOMO
    pour la promptitude.
    ‘b4e3f29b106af37a2bb239f73cdf68c7’ => $baseDir . ‘/app/helpers.php’ existe bien.

    J’ai mis
    <?php en debut du fichier "app/helpers.php" puis composer dumpautoload et tut semble fonctionner
    url() == $route) {
    return ‘ active’;
    }
    }
    }
    }

    Merci de confirmer.

  • Kikiro

    Bonjour Momo
    Merci pour ce cours que je suis, cependent,
    J’obtiens une erreur dans la dernière étape « layouts/app : » pour illuminer CONNEXION et INSCRIPTION.
    Erreur:
    ErrorException (E_ERROR)
    Call to undefined function currentRoute() (View: E:\laragon\www\album\resources\views\layouts\app.blade.php) (View: E:\laragon\www\album\resources\views\layouts\app.blade.php)

    Merci de m’aider

    • bestmomo

      Bonjour,

      Il faut vérifier que le fichier helpers est bien mentionné dans composer.json.

      Relancer au besoin composer dumpautoload.

      Vérifier dans vendor/composer/autoload_files.php qu’il y a bien cette ligne (la clé peut être différente) :

      'b4e3f29b106af37a2bb239f73cdf68c7' => $baseDir . '/app/helpers.php',

Laisser un commentaire