Laravel 5

Changer le framework CSS bis

Il y a un peu plus d’un an j’ai publié un article qui montre comment remplacer Bootstrap qui est utilisé par défaut par Materialize. Un commentaire récent pour cet article m’a montré que les choses évoluent très vite et que ce que j’avais dit était devenu obsolète. Entre temps Laravel a changé et Materialize est enfin passé à la version 1. Je vous présente donc dans le présent article la procédure actualisée.

Pour vous faciliter la vie le projet complet est téléchargeable ici.

Installation par défaut

Partez d’une nouvelle installation de Laravel :

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

Puis générez les vue pour l’authentification :

php artisan make:auth

Renseignez le fichier .env pour la connexion à la base puis générez les migrations :

php artisan migrate

Si tout se passe bien vous avez 3 tables :

Ensuite installez les modules avec npm :

npm install

Lancez en mode développement pour voir si ça fonctionne :

npm run dev

Vous devez avoir la compilation des assets dans les fichiers public/css/app.css et public/js/app.js comme c’est prévu dans le fichier webpack.mix.js :

mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css');

Pour mémoire les sources des assets sont ici :

Pour le CSS on voit que dans le fichier resources/sass/app.scss on importe bootstrap :

@import '~bootstrap/scss/bootstrap';

Et qu’on le modifie un peu en chargeant de nouvelles variables :

@import "variables";

Pour le Javascript on voit que dans le fichier resources/assets/js/bootstrap.js on charge bootstrap :

require('bootstrap-sass');

D’autre part on charge aussi lodash, jQuery et Vue.js avec un composant par défaut mais ce n’est pas l’objet du présent article.

Voilà donc un petit état des lieux à l’installation de Laravel et on se retrouve pour les vues de l’authentification avec un aspect typique de Bootstrap :

On va maintenant procéder aux modifications pour utiliser non plus Bootstrap mais Materialize que j’aime bien. Mais les explications restent valables pour n’importe quel framework CSS.

On passe à Materialize

L’intendance

Sur le site de npm on cherche le module :

Et comme on veut ce module uniquement pour le développement on va utiliser cette syntaxe :

npm install materialize-css --save-dev

Si tout va bien le fichier package.json est actualisé :

"devDependencies": {
    ...
    "materialize-css": "^1.0.0",
    ...
}

On va maintenant mettre à jour le fichier resources/sass/app.scss pour importer Materialize plutôt que Bootstrap :

// Fonts
//@import url('https://fonts.googleapis.com/css?family=Nunito');
@import url("https://fonts.googleapis.com/icon?family=Material+Icons");

// Variables
//@import 'variables';

// Bootstrap
//@import '~bootstrap/scss/bootstrap';

//Materialize
@import "~materialize-css/sass/materialize.scss";

.card { margin-top: 80px; };

Et pour le Javascript ça se passe dans le fichier resources/js/bootstrap.js, on va se contenter de ça :

window.$ = window.jQuery = require('jquery');
require('materialize-css');

On va lancer en mode développement avec actualisation automatique :

npm run watch

Si on regarde maintenant la page de login on va lui trouver évidemment un salle aspect. C’est normal parce qu’on a les classes de Bootstrap déclarées et qu’on a plus ce framework disponible. Il nous reste maintenant à modifier les vues avec les classes de Materialize.

Layout

Voici la nouvelle vue resources/views/layouts/app.blade.php :

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/>

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Styles -->
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    @yield('css')
</head>
<body>
    <div id="app">

        <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
            @csrf
        </form>

        @auth
            <ul id="dropdown1" class="dropdown-content">
                <li><a href="{{ route('logout') }}" class="logout">{{ __('Logout') }}</a></li>
            </ul>
        @endauth

        <nav>
            <div class="nav-wrapper">
                <a href="{{ url('/') }}" class="brand-logo">&nbsp{{ config('app.name', 'Laravel') }}</a>
                <a href="#" data-target="mobile-demo" class="sidenav-trigger"><i class="material-icons">menu</i></a>
                @guest
                    <ul class="right hide-on-med-and-down">
                        <li><a href="{{ route('login') }}">{{ __('Login') }}</a></li>
                        <li><a href="{{ route('register') }}">{{ __('Register') }}</a></li>
                    </ul>
                @else
                    <ul class="right hide-on-med-and-down">
                        <li><a class="dropdown-trigger" href="#!" data-target="dropdown1">{{ Auth::user()->name }}<i class="material-icons right">arrow_drop_down</i></a></li>
                    </ul>
                @endguest
            </div>
        </nav>


        <ul class="sidenav" id="mobile-demo">
            @guest
                <li><a href="{{ route('login') }}">{{ __('Login') }}</a></li>
                <li><a href="{{ route('register') }}">{{ __('Register') }}</a></li>
            @else
                <li><a href="{{ route('logout') }}" class="logout">{{ __('Logout') }}</a></li>
            @endguest
        </ul>

        @yield('content')
    </div>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}"></script>

    <script>
        $(document).ready(function(){
            $('.sidenav').sidenav();
            $('.dropdown-trigger').dropdown();
            $('.logout').click(function(e) {
                e.preventDefault();
                $('#logout-form').submit();
            });
        });
    </script>
</body>
</html>

J’ai changé essentiellement la barre de navigation.

Il faut maintenant modifier les autres vues…

Login

Voici la nouvelle vue de login (resources/views/auth/login.blade.php) :

@extends('layouts.app')

@section('content')
<div class="container">

    <div class="row">
        <div class="col s12 m10 offset-m1 l8 offset-l2">
            <div class="card">
            <form  method="POST" action="{{ route('login') }}">
                <div class="card-content">
                    {{ csrf_field() }}
                    <span class="card-title">{{ __('Login') }}</span>

                    <hr>

                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">mail</i>
                            <input id="email" type="email" name="email" value="{{ old('email') }}" class="{{ $errors->has('email') ? 'invalid' : '' }}" required autofocus>
                            <label for="email">{{ __('E-Mail Address') }}</label>
                            <span class="red-text">{{ $errors->has('email') ? $errors->first('email'): '' }}</span>
                        </div>
                    </div>

                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">lock</i>
                            <input id="password" type="password" name="password" class="{{ $errors->has('password') ? 'invalid' : '' }}" required>
                            <label for="password">{{ __('Password') }}</label>
                            <span class="red-text">{{ $errors->has('password') ? $errors->first('password'): '' }}</span>
                        </div>
                    </div>

                    <p>
                        <label>
                            <input type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
                            <span>{{ __('Remember Me') }}</span>
                        </label>
                    </p>

                    <p>
                        <button class="btn waves-effect waves-light right" type="submit" name="action">{{ __('Login') }}
                            <i class="material-icons right">lock_open</i>
                        </button>
                    </p>

                    <br>

                </div>
                <div class="card-action">
                    @if (Route::has('password.request'))
                        <a href="{{ route('password.request') }}">{{ __('Forgot Your Password?') }}</a>
                    @endif
                </div>
            </form>
        </div>
        </div>
    </div>
</div>
@endsection

Avec cet aspect :

C’est quand même présentable !

Register

Voici la nouvelle vue d’enregistrement (resources/views/auth/register.blade.php) :

@extends('layouts.app')

@section('content')
<div class="container">

    <div class="row">
        <div class="col s12 m10 offset-m1 l8 offset-l2">
            <div class="card">
            <form  method="POST" action="{{ route('register') }}">
                <div class="card-content">
                    {{ csrf_field() }}
                    <span class="card-title">{{ __('Register') }}</span>

                    <hr>

                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">person</i>
                            <input id="name" type="text" name="name" value="{{ old('name') }}" class="{{ $errors->has('name') ? 'invalid' : '' }}" required autofocus>
                            <label for="email">{{ __('Name') }}</label>
                            <span class="red-text">{{ $errors->has('name') ? $errors->first('name'): '' }}</span>
                        </div>
                    </div>

                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">mail</i>
                            <input id="email" type="email" name="email" value="{{ old('email') }}" class="{{ $errors->has('email') ? 'invalid' : '' }}" required>
                            <label for="email">{{ __('E-Mail Address') }}</label>
                            <span class="red-text">{{ $errors->has('email') ? $errors->first('email'): '' }}</span>
                        </div>
                    </div>

                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">lock</i>
                            <input id="password" type="password" name="password" class="{{ $errors->has('password') ? 'invalid' : '' }}" required>
                            <label for="password">{{ __('Password') }}</label>
                            <span class="red-text">{{ $errors->has('password') ? $errors->first('password'): '' }}</span>
                        </div>
                    </div>

                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">lock</i>
                            <input id="password-confirm" type="password" name="password_confirmation" required>
                            <label for="password-confirm">{{ __('Confirm Password') }}</label>
                        </div>
                    </div>

                    <p>
                        <button class="btn waves-effect waves-light" type="submit" name="action">{{ __('Register') }}
                            <i class="material-icons right">create</i>
                        </button>
                    </p>

                </div>
            </form>
        </div>
        </div>
    </div>
</div>
@endsection

Avec cet aspect :

Email

Voici la nouvelle vue de demande de renouvellement du mot de passe (resources/views/auth/passwords/email.blade.php) :

@extends('layouts.app')

@section('content')
<div class="container">

    @if (session('status'))
        <div class="card">
            <div class="card green darken-1">
                <div class="card-content white-text">
                    {{ session('status') }}
                </div>
            </div>
        </div>
    @endif

    <div class="row">
        <div class="col s12 m10 offset-m1 l8 offset-l2">
            <div class="card">

            <form  method="POST" action="{{ route('password.email') }}">
                <div class="card-content">
                    {{ csrf_field() }}
                    <span class="card-title">{{ __('Reset Password') }}</span>

                    <hr>

                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">mail</i>
                            <input id="email" type="email" name="email" value="{{ old('email') }}" class="{{ $errors->has('email') ? 'invalid' : '' }}" required autofocus>
                            <label for="email">{{ __('E-Mail Address') }}</label>
                            <span class="red-text">{{ $errors->has('email') ? $errors->first('email'): '' }}</span>
                        </div>
                    </div>

                    <p>
                        <button class="btn waves-effect waves-light right" type="submit" name="action">{{ __('Send Password Reset Link') }}
                            <i class="material-icons right">lock_open</i>
                        </button>
                    </p>

                    <br><br>

                </div>
            </form>
        </div>
        </div>
    </div>
</div>
@endsection

Avec cet aspect :

Reset

Voici la nouvelle vue de de renouvellement du mot de passe (resources/views/auth/passwords/reset.blade.php) :

@extends('layouts.app')

@section('content')
<div class="container">

    <div class="row">
        <div class="col s12 m10 offset-m1 l8 offset-l2">
            <div class="card">
            <form  method="POST" action="{{ route('password.update') }}">
                <div class="card-content">
                    {{ csrf_field() }}
                    <input type="hidden" name="token" value="{{ $token }}">

                    <span class="card-title">{{ __('Reset Password') }}</span>

                    <hr>

                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">mail</i>
                            <input id="email" type="email" name="email" value="{{ old('email') }}" class="{{ $errors->has('email') ? 'invalid' : '' }}" required>
                            <label for="email">{{ __('E-Mail Address') }}</label>
                            <span class="red-text">{{ $errors->has('email') ? $errors->first('email'): '' }}</span>
                        </div>
                    </div>

                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">lock</i>
                            <input id="password" type="password" name="password" class="{{ $errors->has('password') ? 'invalid' : '' }}" required>
                            <label for="password">{{ __('Password') }}</label>
                            <span class="red-text">{{ $errors->has('password') ? $errors->first('password'): '' }}</span>
                        </div>
                    </div>

                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">lock</i>
                            <input id="password-confirm" type="password" name="password_confirmation" required>
                            <label for="password-confirm">{{ __('Confirm Password') }}</label>
                        </div>
                    </div>

                    <p>
                        <button class="btn waves-effect waves-light" type="submit" name="action">{{ __('Reset Password') }}
                            <i class="material-icons right">lock_open</i>
                        </button>
                    </p>

                </div>
            </form>
        </div>
        </div>
    </div>
</div>
@endsection

Avec cet aspect :

Home

Pour finir maintenant le code pour la vue resources/views/home.blade.php :

@extends('layouts.app')

@section('content')
<div class="container">
  <div class="row">
    @if (session('status'))
        <div class="card green darken-1">
            <div class="card-content white-text">
                {{ session('status') }}
            </div>
        </div>
    @endif
      <div class="card red lighten-2">
        <div class="card-content white-text">

            <span class="card-title">Dashboard</span>

            You are logged in!
        </div>
      </div>
    </div>
</div>
@endsection

Avec cet aspect sur mobile :

La vérification de l’email

Laravel offre la possibilité de faire une vérification de l’email lorsque quelqu’un s’enregistre. Il faut le préciser dans les routes de l’authentification :

Auth::routes(['verify' => true]);

Ensuite on définit les routes à protéger, par exemple celle de la vue home :

Route::get('/home', 'HomeController@index')->name('home')->middleware('verified');

Enfin il faut dans le modèle User implémenter l’interface :

class User extends Authenticatable implements MustVerifyEmail

On va aussi adapter la vue resources/views/auth/verify.blade.php pour Materialize :

@extends('layouts.app')

@section('content')
<div class="container">
  <div class="row">
    @if (session('resent'))
        <div class="card green darken-1">
            <div class="card-content white-text">
                {{ __('A fresh verification link has been sent to your email address.') }}
            </div>
        </div>
    @endif
    <div class="card">
        <div class="card-content">
            <div class="card-title">{{ __('Verify Your Email Address') }}</div>
            <hr>
            {{ __('Before proceeding, please check your email for a verification link.') }}
            {{ __('If you did not receive the email') }}, <a href="{{ route('verification.resend') }}">{{ __('click here to request another') }}</a>.
        </div>
      </div>
    </div>
</div>
@endsection

Avec cet aspect :

Vous voyez que le changement de framework CSS n’est pas bien difficile mais il demande un peu d’attention au niveau de Laravel Mix si on veut profiter de cet outil…

Print Friendly, PDF & Email

2 commentaires

Laisser un commentaire