Laravel Boilerplate

Lorsqu’on utilise fréquemment Laravel on est amené à effectuer des tâches répétitives et à utiliser une certain nombre de classes et fonctionnalités à travers différents projets. On pourrait ainsi imaginer une trame de base comportant tout ce qu’on utilise habituellement. C’est en gros ce qui est réalisé par Laravel Boilerplate. Voyons un peu ce qui se cache dans cette librairie qui a obtenu quand même plus de 2600 stars sur Github…

Installation

On dispose d’un site plutôt bien fait :

Il y a une page de démarrage rapide (Quick Start). Là il y a la liste des fonctionnalités, des copies d’écran, des explications pour le téléchargement et l’installation.

Comme c’est juste un Laravel amélioré l’installation est assez classique. On va commencer par récupérer le dépôt :

git clone https://github.com/rappasoft/laravel-5-boilerplate.git boilerplate

On trouve un fichier .env.example plutôt bien garni par rapport à celui de base de Laravel, il faut le renommer en .env.

Ensuite il n’y a plus qu’à lancer l’installation :

cd boilerplate
composer install

Ça dure un moment parce qu’il y a pas mal de package prévus…

Il faut ensuite générer une clé pour le cryptage :

php artisan key:generate

Ensuite on crée un base de données et on renseigne .env :

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

On peut alors lancer les migrations et la population :

php artisan migrate --seed

On se retrouve avec 12 tables :

Avec 3 utilisateurs par défaut :

Pour le frontend on a le choix entre npm et yarn. Personnellement j’utilise npm, donc il faut installer :

npm install

Les 1432 packages mettent un petit moment à s’installer…

On trouve aussi de nombreux tests qu’on peut lancer avec phpUnit :

Il faut prévoir l’extension pdo_sqlite de PHP et également renseigner la configuration des mails pour éviter de tomber sur une erreur.

État des lieux

Tous semble correct alors je lance :

Une page d’accueil sommaire avec une barre de navigation, un accès au login et à l’enregistrement, un large choix de langues, une page de contact avec un formulaire et la barre de débogage.

On peut accéder à l’administration avec :

Username: admin@admin.com

Password: 1234

Le template d’administration est CoreUI dans sa version gratuite, c’est à dire pratiquement sans plugins. Personnellement je préfère AdminLTE.

On trouve une gestion des utilisateurs :

Une gestion des rôles :

Une visualisation des logs :

Pour en apprendre plus il faut plonger dans la documentation ou fouiller un peu le code…

Les contrôleurs

On trouve de nombreux contrôleur rangés dans leur dossier et sous-dossiers :

Les validations sont systématiquement réalisées par des Form Request et la gestion des données au travers de repositories. Du coup le code est propre :

/**
 * @param ManageUserRequest $request
 *
 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 */
public function index(ManageUserRequest $request)
{
    return view('backend.auth.user.index')
        ->withUsers($this->userRepository->getActivePaginated(25, 'id', 'asc'));
}

Les middlewares

On trouve ces middlewares :

On remarque l’ajout de :

  • LocaleMiddleware pour la gestion des locales associé à la configuration config/locale.php
  • PasswordExpires si on veut une expiration du mot de passe au bout d’un certain délai

D’autre part on a aussi les middlewares du package spatie/laravel-permission pour la gestion des rôles :

  • RoleMiddleware 
  • PermissionMiddleware

L’ajout de ces middlewares permet de protéger facilement les routes :

Route::group([
    'middleware' => 'role:administrator',
], function () {

Les modèles

Les modèles se trouvent dans le dossier app/Models :

On trouve de nombreux traits. Ils servent pour gérer les attributs, les relations, les scopes et les méthodes spécifiques. Ça fait au final pas mal de fichiers. Par exemple pour le modèle User on trouve une rafale de traits :

class User extends Authenticatable
{
    use HasRoles,
        Notifiable,
        SendUserPasswordReset,
        SoftDeletes,
        UserAttribute,
        UserMethod,
        UserRelationship,
        UserScope,
        Uuid;

C’est un choix architectural…

Les routes

Là aussi on trouve de nombreux fichiers :

Pour comprendre l’organisation il faut regarder le code dans routes/web.php :

/*
 * Frontend Routes
 * Namespaces indicate folder structure
 */
Route::group(['namespace' => 'Frontend', 'as' => 'frontend.'], function () {
    include_route_files(__DIR__.'/frontend/');
});

L’espace de nom est analogue au chemin des dossiers. Ici on va dans le dossier frontend. Dans ce dossier dans le fichier auth.php on trouve par exemple :

Route::group(['namespace' => 'Auth', 'as' => 'auth.'], function () {

Du coup les routes seront préfixées par frontend.auth :

Il faut juste un peu s’habituer au système…

Les providers

On trouve ces providers :

On a donc l’ajout de :

  • BladeServiceProvider pour ajouter des directives Blade
  • ComposerServiceProvider pour ajouter des composeurs de vues

Il y a quelques réglages dans AppServiceProvider : la locale, le forçage éventuel en HTTPS, les templates pour Bootstrap 4…

Les assets

Les assets sont fournis :

On peut mieux comprendre l’organisation en allant jeter un œil dans webpack.mix.js :

mix.sass('resources/assets/sass/frontend/app.scss', 'public/css/frontend.css')
    .sass('resources/assets/sass/backend/app.scss', 'public/css/backend.css')
    .js('resources/assets/js/frontend/app.js', 'public/js/frontend.js')
    .js([
        'resources/assets/js/backend/before.js',
        'resources/assets/js/backend/app.js',
        'resources/assets/js/backend/after.js'
    ], 'public/js/backend.js');

if (mix.inProduction() || process.env.npm_lifecycle_event !== 'hot') {
    mix.version();
}

Conclusion

Ce boilerplate est bien structuré et codé, c’est une bonne base de départ pour un projet à condition d’accepter l’organisation imposée. Il peut faire gagner du temps au niveau de la constitution du backend. Il est équipé d’une batterie complète de tests. Je regrette juste le choix de CoreUI.




Exemple en Laravel 5.5

Laravel 5.5 vient de sortir et pour fêter ça je propose un exemple complet disponible sur Github.

Cet exemple servira de base pour une nouvelle série d’initiation que je compte démarrer bientôt pour cette nouvelle version.




PHP 7 ?

Faut-il passer à PHP 7 (en fait 7.1) ? La question devient pertinente pour les utilisateurs de Laravel parce que la version 5.5 de ce framework imposera cette version de PHP. Alors je vous propose de faire un peu le point de ce que nous apporte ce nouveau PHP…

Déjà on va y gagner en performances puisqu’on nous annonce un gain en 25% et 70%. Bon la marge est grande !

Mais au niveau des fonctionnalités ? Vous pouvez tout trouver dans le manuel.

Pour les allergiques à l’anglais voyons un peu ça en me limitant à ce qui me semble le plus important…

Groupement des « use »

L’utilisation des espaces de noms nous amène à effectuer de nombreuse déclarations. On va pouvoir maintenant les regrouper. prenez par exemple ce code :

use App\Http\Controllers\Controller;
use App\Http\Requests\CommentRequest;
use App\Models\Post;
use App\Models\Comment;

On a autant de lignes que de déclarations. On va pouvoir simplifier ainsi :

use App\Http\{
    Controllers\Controller,
    Requests\CommentRequest
};
use App\Models\{
    Post,
    Comment
};

On y gagne en clarté !

Type des paramètres

Paramètres des fonctions

Avec PHP 5 on peut déclarer le type des paramètres pour les interface, les classes et les tableaux. Avec la version 7 on peut enfin aussi déclarer les types scalaires (int, float, string, bool).

Considérez par exemple cette fonction :

/**
 * Get the next comments for the specified post.
 *
 * @param  \App\Models\Post  $post
 * @param  integer $page
 * @return array
 */
public function comments(Post $post, $page)
{
    ...
}

On a le type du premier paramètre parce que c’est un objet, mais le second, qui est un entier, on ne peut pas le déclarer…

Avec PHP 7 on peut écrire :

public function comments(Post $post, int $page)

Mais PHP ne va pas vraiment vérifier que le type est bon (allez savoir pourquoi…), il va juste s’efforcer de transformer la valeur pour la rendre conforme au type, s’il y arrive… D’ailleurs il vaut mieux se méfier de la transformation des flottants en entiers qui peuvent réserver des surprises !

Mais on peut lui forcer la main pour qu’il devienne plus pointilleux :

declare(strict_types = 1);

Obligatoirement sur la première ligne du fichier PHP du code appelant.

Et là il ne laissera rien passer ! Bon, du coup c’est pas mal après d’intercepter l’erreur qui est de type TypeError.

Avec la version 7.1 est même apparu un pseudo type : iterable. Il arrive souvent qu’on transmettre un paramètre qui implémente l’interface Transversable, autrement dit qui acceptent l’utilisation de foreach. Dans cette catégorie on a les tableaux et les objets. On peut désormais indiquer à la fonction qu’on va lui transmettre ce genre de chose :

function action(iterable $elements)

Et rien n’empêche évidement de déclarer une valeur par défaut :

function action(iterable $elements = [])

Type du retour

La possibilité du typage s’étend à la valeur du retour de la fonction.

Voilà par exemple une fonction qui renvoie un string :

/**
 * Get .env element.
 *
 * @param string $key
 * @return string
 */
public function get($key)
{
    return $this->env->get ($key);
}

On ne précise pas ici le type de la valeur qu’on renvoie, juste dans les commentaires. Avec PHP 7 on peut écrire :

public function get($key) : string

Là aussi si on veut un comportement « strict » il faut le préciser de la même manière qu’on a vue ci-dessus.

On peut aussi déclarer le type de retour iterable.

Les opérateurs

L’opérateur de coalescence

Je suppose que vous avez souvent l’occasion d’écrire du code dans ce genre :

$category = isset($request->category) ? $request->category : 'aucune';

PHP 7 nous offre un nouvel opérateur :

$category = isset($request->category) ?? 'aucune';

Le résultat est le même mais c’est plus concis !

L’opérateur « spaceship »

Prenons ce code qui permet de classer des éléments :

usort($element, function ($a, $b) {
  return ($a < $b) ? -1 : (($a > $b) ? 1 : 0);
});

On a :

  • a < b : -1
  • a = b : 0
  • a > b : 1

Avec PHP 7 on peut simplifier la syntaxe :

usort($element, function ($a, $b) {
  return $a <=> $b;
});

Pratique ! Par contre ça me fait pas vraiment penser à un vaisseau spatial mais bon…

Classe anonyme

Avec PHP 5 on a hérité des fonctions anonymes qui sont il faut l’avouer bien pratiques surtout comme fonctions de rappel.

Avec PHP 7 ce sont maintenant les classes qui peuvent devenir anonymes. Autrement dit on n’a pas besoin de faire toutes les déclarations. Voici un exemple :

$maClasseAnonyme = new class($parametre) {
   private $parametre;
   public function __construct($parametre) {
     $this->parametre = $parametre;
   }
};

A part d’être anonymes on les utilise exactement comme les autres : implémentation d’interfaces, héritages, traits…

J’avoue que je ne vais pas m’en servir tous les jours…

Délégation des générateurs

Si vous n’êtes pas familiarisé avec les générateurs alors commencez par vous renseigner à leur sujet. C’est une façon de fournir des éléments d’un ensemble (itérateur) sans être obligé de construire au départ cet ensemble, donc de ne pas affecter outre mesure la mémoire.

Ce n’est pas clair ? Voici un exemple :

function entiers() {
    for ($i = 0; $i <= 10; ++$i) {
        yield $i;
    }
}

foreach (entiers() as $number) {
    echo "$number ";
}

// Résultat : 0 1 2 3 4 5 6 7 8 9 10

Oui c’est un peu idiot comme exemple mais c’est pour le principe !

La délégation c’est le fait pour un générateur d’en appeler un autre :

function entiers() {
    for ($i = 0; $i <= 5; ++$i) {
        yield $i;
    }
}

function nombres() {
    yield 100;
    yield from entiers ();
    yield 101;
    yield from entiers ();
}

foreach (nombres() as $number) {
    echo "$number ";
}

// Résultat : 100 0 1 2 3 4 5 101 0 1 2 3 4 5

Bon ce n’est pas plus malin comme exemple mais au moins ça donne le principe.

Les exceptions

Les exceptions ont été apportées à PHP à partir de la version 5. Mais ça ne concerne que les objets et toutes les autres erreurs de PHP ne passent pas par là. D’autre part il est en général fait usage d’un gestionnaire par défaut avec la fonction set_exception_handler.

PHP 7 apporte l’interface Throwable qui concerne les exceptions et, c’est nouveau, les erreurs, parce qu’elle se place en tête de la hiérarchie.

Prenons ce code :

function test($object)
{
  return $object->riendutout();
}
test(null);
echo 'Je marche encore !';

Si je l’exécute je vais tomber immanquablement sur une erreur fatale : Call to a member function riendutout() on null…

On va maintenant intercepter cette erreur :

function test($object)
{
  return $object->riendutout();
}
try
{
  test(null);
} catch(Error $e)
{
  echo $e->getMessage(), PHP_EOL;
}
echo 'Je marche encore !';

A l’exécution (PHP 7) on obtient :

Call to a member function riendutout() on null
Je marche encore !

On peut ainsi tout intercepter, même les divisions par zéro !

Syntaxe uniforme des variables

Avec PHP on peut utiliser des variables de variables, même si ce n’est pas très indiqué parce que ça complique la lecture du code et on peut en général facilement s’en passer.

Regardez ce code :

$identite = ['nom' => 'Pierre'];
$Pierre = 'Paul';
echo $$identite['nom'];

Avec PHP 5 vous obtenez : Pierre.

Avec PHP 7 vous obtenez plein d’erreurs : Array to string conversion…

Que se passe-t-il ?

PHP 7 apporte la « syntaxe uniforme des variables », un truc qui couvait depuis quelques années avec pas mal d’opposition à cause du cassage du code antérieur. Mais finalement c’est passé dans cette version.

Normalement l’interprétation se fait de gauche à droite, donc on devrait commencer par $$identite, puis ensuite prendre l’offset. Mais PHP 5 ne fait pas ça, il commence par traiter $identite[‘nom’]. Par contre comme PHP 7 va scrupuleusement de gauche à droite il y a un gros souci. Pour que ça fonctionne on doit écrire ${$identite[‘nom’]}. Et là maintenant ça fonctionne !

Ce changement n’est pas anodin si vous voulez migrer du code en PHP 7 !

Conclusion

On voit qu’il y a pas mal de nouveautés dans cette version, et je n’ai pas parlé des choses plus ou moins anodines que vous pouvez trouver dans le manuel…

 

 




reCAPTCHA2

D’après Wikipedia « Le terme CAPTCHA est une marque commerciale de l’université Carnegie-Mellon désignant une famille de tests de Turing permettant de différencier de manière automatisée un utilisateur humain d’un ordinateur ».

En gros on veut savoir si on a affaire à un humain ou une machine. Sur Internet ce n’est pas si facile à déterminer mais c’est souvent essentiel pour éviter de se faire spammer ou attaquer en brute force.

Les première versions étaient assez laborieuses et finalement pas si efficaces que ça. Mais notre « ami » Google nous propose désormais une version simple, efficace et plutôt facile à mettre en place. L’objet de cet article est de présenter cette nouvelle version. Je vais le faire en PHP de base pour simplifier la démarche.

Imaginons un site où on veut que les utilisateurs puissent s’inscrire à une lettre d’information. On a bien tout mis en place, que ce soir avec du code propre ou par l’intermédiaire de l’une des nombreuses propositions qui existent en ligne. On crée un joli formulaire et là on ne veut pas qu’un méchant robot vienne saboter notre édifice. Alors on décide de placer un reCAPTCHA et évidemment notre choix se porte sur celui de Google :

La documentation de Google

On trouve une documentation simple mais suffisante sur le site :

Récupérer les clés

Pour utiliser le service il faut une paire de clés en se rendant sur cette page en étant connecté à soin compte Google :

Il faut choisir le type de CAPTCHA qui peut être visible ou invisible :

Pour cet article je vais utiliser le plus classique, le visible.

Quand on a validé on se retrouve avec ses clés :

La mise en place côté client

Elle peut se faire automatiquement (le plus simple) ou de façon explicite si on a une situation particulière. Je vais dans cet article utiliser la première version.

Sur la page où on récupère ses clés on a aussi un résumé de l’intégration côté client :

La mise en place côté serveur

La documentation décrit l’API qui est toute simple et qu’on va voir en action bientôt.

Sur la page où on récupère ses clés on a aussi un résumé de l’intégration côté serveur :

La page HTML et le Javascript

Maintenant qu’on a tous les éléments nécessaires voyons notre exemple d’application.

Voici le code complet :

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta content="text/html; charset=UTF-8" http-equiv="content-type">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0">
    <title>Tes du reCAPTCHA2</title>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.2/css/materialize.min.css">
    <link href="https://fonts.googleapis.com/css?family=PT+Sans+Narrow" rel="stylesheet">
    <style>
        body {
            PT Sans Narrow', sans-serif;
            font-size: 18px;
            background-color: #5179d0;
        }
        #captcha {
            margin: 30px 0 20px;
        }
    </style>
</head>
<body>

  <div id="newsletter" class="modal">
      <div class="modal-content">
          <h5 class="center">Pour vous abonner (ou désabonner) à notre lettre d'information :</h5>
          <div class="row">
              <form id="newsform" class="col s12" action="mail.php" method="post">
                  <div class="row">
                      <div class="input-field col s12">
                          <input placeholder="Votre adresse mail" id="email" type="email" name="email" class="validate" required>
                      </div>
                      <div class="input-field col s12">
                          <input name="action" type="radio" value="subscribe" id="subscribe" checked>
                          <label for="subscribe">S'abonner</label>
                          <input name="action" type="radio" value="unsubscribe" id="unsubscribe">
                          <label for="unsubscribe">Se désabonner</label>
                      </div>
                      <div id="captcha" class="input-field col s12 center">
                        <div class="g-recaptcha" data-sitekey="6LeCNyMUAAAAAFXF-onjzpzk7q-XBWDWzpsVkOWM"></div>
                        <div id="error" class="left red-text text-darken-2" style="display: none">
                            Veuillez cliquer sur le Captcha, merci.
                        </div>
                      </div>
                      <button class="btn waves-effect waves-light right" type="submit">Envoyer
                          <i class="material-icons right">send</i>
                      </button>
                  </div>
              </form>
          </div>
      </div>
  </div>

  <div id="erreurmodal" class="modal">
      <div class="modal-content">
          <h5 class="center">Erreur dans la procédure d'abonnement</h5>
          <p class="center-align">Nous sommes désolés mais une erreur inattendue est survenue lors de votre demande d'abonnement. Veuillez s'il vous plait renouveller la procédure dans quelques minutes.</p><p class="center-align">Merci.</p>
      </div>
  </div>

  <div id="subscribemodal" class="modal">
      <div class="modal-content">
          <h5 class="center">Procédure d'abonnement achevée</h5>
          <p class="center-align">Votre demande a bien été prise en compte.</p>
          <p class="center-align">Vous allez recevoir un e-mail de vérification dans un instant. Il contient un lien sur lequel vous devez cliquer afin de confirmer votre inscription.</p>
          <p class="center-align">Merci.</p>
      </div>
  </div>

  <div id="unsubscribemodal" class="modal">
      <div class="modal-content">
          <h5 class="center">Procédure de désabonnement achevée</h5>
          <p class="center-align">Votre demande a bien été prise en compte.</p>
          <p class="center-align">Nous sommes désolés de vous voir partir.</p>
          <p class="center-align">Merci.</p>
      </div>
  </div>

  <div id="intro" class="section scrollspy no-pad-bot center orange-text">
    <div class="container">
      <h1 class="header">Exemple d'utilisation du reCAPTCHA2</h1>
      <div class="row center">
          <p><a id="modalcommand" href="#" class="btn"><i class="material-icons right">email</i>La lettre d'information</a></p>
       </div>
    </div>
  </div>

  <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.2/js/materialize.min.js"></script>
  <script src="https://www.google.com/recaptcha/api.js" async defer></script>
  <script>
      $(function(){

          $('#newsletter').modal()
          $('#subscribemodal').modal()
          $('#unsubscribemodal').modal()
          $('#erreurmodal').modal()

          $('#newsform').submit (function(event) {
              event.preventDefault()
              $.post(
                  $(this).attr('action'),
                  $(this).serialize()
              )
              .done(function(data) {
                  if (data == 200) {
                    $('#newsform').modal('close')
                    if ($('input[name=action]:checked', '#newsform').val() == 'subscribe') {
                        $('#subscribemodal').modal('open')
                    } else {
                        $('#unsubscribemodal').modal('open')
                    }
                  } else if (data == 'nocaptcha') {
                    $('#error').show('slow')
                  } else {
                    $('#newsletter').modal('close')
                    $('#erreurmodal').modal('open')
                  }
              })
          })

          $('#modalcommand').click(function (event) {
              event.preventDefault()
              $('#newsletter').modal('open')
              $('#error').hide()
          })
      })
  </script>

  </body>
</html>

Pour l’esthétique j’ai utilisé Materialize que j’aime bien :

L’intégration du reCAPTCHA se fait en deux emplacements :

  • l’appel de la librairie Javascript :
<script src='https://www.google.com/recaptcha/api.js'></script>
  • l’ajout d’un champ dans le formulaire :
 <div id="captcha" class="input-field col s12 center">
     <div class="g-recaptcha" data-sitekey="6LeCNyMUAAAAAFXF-onjzpzk7q-XBWDWzpsVkOWM"></div>
     <div id="error" class="left red-text text-darken-2" style="display: none">
         Veuillez cliquer sur le Captcha, merci.
     </div>
 </div>

Là je prévoie le signalement de l’erreur au départ invisible si la case n’est pas cochée. Et qui apparaît lorsque ça devient utile :

Lorsqu’on clique sur le bouton le formulaire apparaît dans une fenêtre modale :

Lorsqu’on clique le reCAPTCHA on a évidemment l’apparence classique :

Pour la gestion j’utilise Ajax, donc la soumission et le traitement de la réponse se font avec JQuery :

$('#newsform').submit (function(event) {
  event.preventDefault()
  $.post(
      $(this).attr('action'),
      $(this).serialize()
  )
  .done(function(data) {
      if (data == 200) {
        $('#newsform').modal('close')
        if ($('input[name=action]:checked', '#newsform').val() == 'subscribe') {
            $('#subscribemodal').modal('open')
        } else {
            $('#unsubscribemodal').modal('open')
        }
      } else if (data == 'nocaptcha') {
        $('#error').show('slow')
      } else {
        $('#newsletter').modal('close')
        $('#erreurmodal').modal('open')
      }
  })
})

J’ai prévu 3 modales pour l’abonnement, le désabonnement et l’erreur :

Le traitement côté serveur

Côté serveur on a du simple PHP. Pour la gestion de la requête HTTP j’ai utilisé Guzzle parce que c’est une librairie parfaite pour ne pas se soucier des détails d’implémentation.

Evidemment il faut l’installer :

composer require guzzlehttp/guzzle

Ce qui crée les dossiers avec toutes les dépendances :

Dans notre code il suffira de charger la librairie :

require 'vendor/autoload.php';

Voici le code PHP complet (fichier nommé mail.php) :

<?php

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    require 'vendor/autoload.php';

    $email = null;
    $action = null;
    $captcha = null;

    if (isset($_POST['g-recaptcha-response'])) {
        $captcha = $_POST['g-recaptcha-response'];
    }
    if (!$captcha) {
        echo 'nocaptcha';
        return;
    }

    if (isset($_POST['email'])) {
        $email = $_POST['email'];
    }
    if (isset($_POST['action'])) {
        $action = $_POST['action'];
    }

    $client = new \GuzzleHttp\Client();

    $response = $client->request (
        'POST',
        'https://www.google.com/recaptcha/api/siteverify', [
        'form_params' => [
            'secret' => 'ma_cle_secrete_cote_serveur',
            'response' => $captcha,
        ]
    ]);

    $responses = json_decode ($response->getBody ());

    if ($responses->success) {
        // Actualisation de l'abonnement sur le serveur
        echo 200;
    } else {
        echo 'fail';
    }
}

Dans le fonctionnement on récupère la valeur du captcha issue du formulaire :

$captcha = $_POST['g-recaptcha-response'];

Si c’est vide on renvoie une erreur :

if (!$captcha) {
     echo 'nocaptcha';
     return;
}

Si c’est bon on envoie la requête chez Google :

$client = new \GuzzleHttp\Client();
$response = $client->request (
     'POST',
     'https://www.google.com/recaptcha/api/siteverify', [
         'form_params' => [
         'secret' => 'ma_cle_secrete_cote_serveur',
         'response' => $captcha,
     ]
]);

$responses = json_decode ($response->getBody ());

if ($responses->success) {
    // Actualisation de l'abonnement sur le serveur
    echo 200;
} else {
    echo 'fail';
}

Il faut évidemment renseigner la clé secrète là.

On récupère la réponse et on adapte le retour en conséquence. Le traitement effectif de l’abonnement se ferait ici.

Conclusion

On voit que l’intégration de reCAPTCHA ne pose aucun problème technique et qu’il serait dommage de ne pas l’utiliser. Mon exemple est assez sommaire côté serveur pour se limiter à l’essentiel de la gestion.




Laravel 5.5 : les nouveautés

A chaque semestre sa nouvelle version importante de Laravel. Certains disent que c’est trop rapide, mais personne n’est obligé de suivre ce rythme ! Alors voici que se profile la version 5.5 qui est annoncée pour le mois de juillet et sera LTS. La copie est assez avancée pour pouvoir faire un peu le point. Si vous aimez la langue anglaise vous avez une description de toutes les nouveautés sur le site officiel. Sinon je vous en fais ici un petit résumé dans notre langue préférée !

PHP 7

La première grande nouveauté est la version minimale de PHP qui est désormais la 7 :

"require": {
    "php": ">=7.0.0",
    "laravel/framework": "5.5.*",
    ...

Donc si vous voulez passer à cette version de Laravel il faudra peut-être mettre à jour votre serveur !

Le principal intérêt se trouve au niveau des performances parce que cette version de PHP est vraiment plus rapide. Mais ce n’est pas le seul avantage, vous trouvez la liste des nouveautés dans le manuel PHP.

Les erreurs

Les pages d’erreurs

L’aspect des pages d’erreur par défaut (donc pour 404, 419, 500 et 503) ont été relookées. C’est un changement purement cosmétique mais ça fait du bien aux yeux :

Mais on peut évidemment créer son propre aspect comme dans les versions précédentes en créant un dossier resources/views/errors et en nommant les fichiers blade avec le numéro de l’erreur.

Les helpers throw_if et throw_unless

Deux nouveaux helpers apparaissent :

$foo = false;
throw_if($foo, new MonException('Foo est faux'));
// ou
throw_if($foo, MonException::class, 'Foo est faux');

$foo = true;
throw_unless($foo, new MonException('Foo est faux'));
// ou
throw_unless($foo, MonException::class, 'Foo est faux);

Retour des données de la validation

Laravel offre la puissante méthode validate pour valider les données :

public function store(Request $request)
{
    $this->validate($request, [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);

    return Post::create($request->all());
}

La méthode jusque là ne retournait aucune valeur. Désormais elle va retourner les données de la requête. Du coup on pourra écrire :

public function store()
{
    $data = $this->validate(request(), [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);

    return Post::create($data);
}

Il faut évidemment que toutes les données passent par la validation, quitte à ne préciser aucune règle.

Personnellement j’utilise principalement des requêtes de formulaire et cette possibilité me laisse assez indifférent. D’autre part elle ne me paraît pas être d’un très grand intérêt.

La commande migrate:fresh

Voici l’annonce officielle de cette commande.

Vous utilisez sans doute souvent la commande migrate:refresh. Elle permet de revenir à zéro pour repartir sur une migration toute fraîche. Mais pour que ça fonctionne il faut prévoir des méthodes down dans les migrations pour définir le retour en arrière.

Avec la commande migrate:fresh plus besoin de méthode down, les tables sont purement et simplement supprimées et les migrations relancées. On peut lui adjoindre l’option –seed pour remplir les tables.  Voilà une commande que je vais beaucoup utiliser !

La commande vendor:publish

Voilà une commande qui m’a souvent agacé parce que telle qu’elle elle publie tout ce qu’elle trouve et ce n’est pas toujours judicieux !

Désormais lorsque vous utiliserez cette commande sans préciser de provider vous aurez la liste de tous les providers et vous pourrez choisir celui que vous voulez publier !

Voici une animation bien faite pour cette commande :

Les mails

Les thèmes

La gestion des mails a bien progressé dans les dernières versions de Laravel, par exemple avec la possibilité d’utiliser le marquage Markdown. Cette fois on va pouvoir utiliser des thèmes. Dans la version 5.4 il y a un thème par défaut. On n’est pas obligé de l’utiliser mais ça oblige à quelques changements : créer évidement son propre CSS :

resources/views/vendor/mail/html/themes/mon-theme-a-moi.css

et aussi informer Laravel (config/mail.php) :

/*
|--------------------------------------------------------------------------
| Markdown Mail Settings
|--------------------------------------------------------------------------
|
| If you are using Markdown based email rendering, you may configure your
| theme and component paths here, allowing you to customize the design
| of the emails. Or, you may simply stick with the Laravel defaults!
|
*/

'markdown' => [
    'theme' => 'mon-theme-a-moi',

    'paths' => [
        resource_path('views/vendor/mail'),
    ],
],

Ce n’est pas bien compliqué mais la version 5.5 nous simplifie un peu plus la vie !

Il faut encore évidemment créer le fichier CSS mais pour sa déclaration ça se passe directement dans la classe Mailable :

class MonMailAMoi extends Mailable
{
    protected $theme = 'mon-theme-a-moi';
    ...
}

Le rendu dans le navigateur

Lorsqu’on développe un thème ça dure évidemment un moment et on fait plein de réglage. On ne va pas chaque fois envoyer réellement un mail pour vérifier le rendu. Il existe des solutions mais aucune de vraiment simple et directe.

Avec Laravel 5.5 on peut visualiser le mail directement dans le navigateur. Il suffit d’ajouter une route :

Route::get('/demo', function () {
    return new App\Mail\MonMailAMoi();
});

Et c’est tout !

Ca peut d’ailleurs donner d’autres idées d’utilisation…

Le frontend

Le frontend a connu une évolution un peu houleuse au cours des versions. Avec Laravel 5.5 ça devient plus modulable et rien n’est imposé. On a toujours Bootstrap et Vue.js par défaut mais on peut facilement (avec Artisan) changer pour React. Je suppose que ça va encore évoluer avec d’autres propositions.

Whoops

Ceux qui ont utilisé Laravel 4 doivent se rappeler de la fenêtre d’affichage des erreurs Whoops :

Et bien on va retrouver cette sympathique librairie dans la version 5.5 !

Chargement des packages

Taylor a présenté récemment une nouvelle fonctionnalité pour l’installation des packages. On est tous habituer à effectuer toujours les mêmes opérations pour installer un package :

  • utilisation de composer
  • déclaration du service providers dans la configuration
  • déclaration éventuelle de la façade dans la configuration

Avec la version 5.5 on pourra se contenter de composer, le reste sera reconnu automatiquement si le package est configuré pour ça !

Conclusion

Laravel 5.5 ne nous apporte pas des changements très importants mais se présente plutôt comme l’aboutissement de la série 5.

 




Laravel 5.4 : les nouveautés

La sortie de la vesion 5.4 de Laravel est prévue en janvier 2017 et elle aura besoin de 6 mois pour avoir une élimination des tous les bugs. On peut donc aller voir ce qui nous attend très bientôt dans cette nouvelle version !

La page officielle de présentation des nouveautés est ici. Mais pour ceux qui préfèrent une présentation française je vais détailler un peu tout ça dans cet article.

Pour faire des essais il faut aller chercher la version développement sur Github et l’installer.

Blade : component et slot

La présentation officielle de cette nouveauté est ici. On va avoir plus de possibilités pour organiser nos vues orchestrées par Blade.

Etat actuel

En général on utilise un template (layout.blade.php). Voici un exemple standard avec initialisation de bootstrap :

<!DOCTYPE html>
<html lang="fr">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Mon joli site</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div class="container">
      @yield('contenu')
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
  </body>
</html>

On prévoit un emplacement pour gérer le contenu avec :

@yield('contenu')

Ensuite dans nos pages on référence le template et l’emplacement prévu (home.blade.php) :

@extends('layout')
@section('contenu')
    <h1>Ma page d'accueil !</h1>
@endsection

Tout ça fonctionne très bien et jusque là on s’en est contentés !

Maintenant si on veut insérer une barre d’alerte dans la vue on peut utiliser une inclusion avec passage d’un paramètre :

@extends('layout')
@section('contenu')
    <h1>Ma page d'accueil !</h1>
    @include('alerte', ['texte' => 'Texte de l\'alerte'])
@endsection

Avec ce fichier pour l’alerte (alerte.blade.php) :

<div class="alert alert-success" role="alert">
    {{ $texte }}
</div>

Là aussi ça fonctionne bien…

Un composant

Avec la version 5.4 on dispose pour faire la même chose de la possibilité de créer un composant :

@extends('layout')
@section('contenu')
    <h1>Ma page d'accueil !</h1>
    @component('alerte')
        Le message d'alerte ici !
    @endcomponent
@endsection

On utilise la nouvelle instruction @component et on nomme la vue en relation. On ne nomme pas de paramètre mais on met le texte de l’alerte directement.

Voici le code de l’alerte maintenant :

<div class="alert alert-success" role="alert">
    {{ $slot }}
</div>

Le texte de l’alerte va occuper la place de $slot.

Le résultat est le même mais c’est quand même plus propre et élégant !

Un autre aspect intéressant de $slot c’est que toute variable déclarée dans le composant est utilisable dans la vue principale. C’est pas clair ? Alors un petit exemple… on utilise ce code dans le template :

<div class="container">
  <h1>{{ $titre }}</h1>
  {{ $slot }}
</div>

On attend une variable $titre ainsi qu’un contenu pour le $slot. Donc dans notre template on aura forcément un composant qui lui-même peut utiliser d’autres composants.

Voici le code de la page d’accueil :

@component('layout')
    @slot('titre')
        Ma page d'accueil !
    @endslot
    @component('alerte')
        Le message d'alerte ici !
    @endcomponent
@endcomponent

Le résultat est encore le même. Regardez comment est renseignée la variable titre pour le layout. On peut ainsi créer autant de variables qu’on a besoin.

Ca nous donne une autre façon d’envisager l’organisation de nos vues avec des composants, elle s’inspire de Vue.js.

Façade automatique

La présentation officielle de cette nouveauté est ici.

Etat actuel

Imaginez que vous ayez créé la librairie mathématique du siècle :

<?php

namespace App\Services;

class Maths 
{
    public function double($i)
    {
        return 2 * $i;
    }
}

Pour l’utiliser vous pouvez l’injecter dans un contrôleur :

<?php

namespace App\Http\Controllers;

use App\Services\Maths;

class TestController
{
    public function test(Maths $maths, $i)
    {
        echo $maths->double($i);
    }
}

Il suffit d’avoir une bonne route :

Route::get('test/{i}', 'TestController@test');

Et le tour est joué ! Pour :

.../test/8

Vous allez obtenir  le résultat :

16

Et puis vous vous dites qu’une façade ça serait bien pour une si belle librairie !

Alors vous créez la façade :

<?php
 
namespace App\Services;

use Illuminate\Support\Facades\Facade;
 
class MathsFacade extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'maths';
    }
}

Vous déclarez votre classe dans AppServiceProvider :

public function register()
{
    $this->app->bind('maths', function ($app) {
        return new \App\Services\Maths;
    });
}

Puis vous déclarez la façade dans config/app.php :

'Maths' => App\Services\MathsFacade::class,

Et là magie de Laravel dans le contrôleur la façade fonctionne :

<?php

namespace App\Http\Controllers;

use Maths;

class TestController
{
    public function test($i)
    {
        echo Maths::double($i);
    }
}

La façade automatique

Bon, on peut trouver que la mise en place est un peu laborieuse. Avec la version 5.4 ça sera beaucoup plus facile : oubliées la création et la déclaration de la façade !

Vous avez juste à faire ça dans le contrôleur :

<?php

namespace App\Http\Controllers;

use Facades\ { App\Services\Maths };

class TestController
{
    public function test($i)
    {
        echo Maths::double($i);
    }
}

Et ça fonctionne ! Laravel devient de plus en plus magique !

Les routes

La présentation officielle des nouveautés sont ici.

On a une amélioration du cache. d’autre part on peut déclarer le nom d’une route avec une syntaxe simplifiée.

La syntaxe actuelle est celle-ci :

Route::get('test/{i}', 'TestController@test')->name('maths');

Avec la version 5.4 on pourra écrire :

Route::name('maths')->get('test/{i}', 'TestController@test');

On peut traiter de la même manière les middlewares.

Bon c’est pas l’évolution la plus géniale il faut l’avouer ! Mais bon, on prend quand même…

Le multilanguage

La présentation officielle des nouveautés sont ici.

Etat actuel

Pour faire du multilangue on dispose de l’helper trans et on doit créer autant de fichiers que de langues prévues. Par exemple on a un fichier en/test.php pour l’anglais :

<?php

return [
    'test' => 'The english version of test',
];

Et le même pour le français fr/test.php :

<?php

return [
    'test' => 'La version française de test',
];

Si dans un contrôleur j’utilise :

echo trans('test.test');

J’aurais selon la locale :

The english version of test

ou

La version française de test

Ca fonctionne plutôt bien mais c’est vrai que ça devient vite laborieux pour un gros site.

D’autre part on a la possibilité d’utiliser des paramètres, par exemple :

<?php

return [
    'test' => 'The english version of :name',
];

Et pour l’appel :

echo trans('test.test', ['name' => 'Toto']);

Utilisation de fichiers JSON

Avec la version 5.4 de Laravel on a une amélioration avec l’utilisation d’un fichier JSON par langue :

Voici celui de l’anglais pour notre exemple (en.json) :

{"test:": "The english version of test"}

Et celui du français (fr.json) :

{"test:": "La version française de test"}

Maintenant la syntaxe appelante est celle-ci :

echo __('test:');

Le fait de disposer de fichiers en JSON qui est un language utilisé universellement devrait simplifier les traductions et donner lieu à l’émergence d’outils pratiques.

On peut aussi utiliser des paramètres comme dans la version actuelle :

{"test:": "The english version of :name"}

Avec cet appel :

echo __('test:', ['name' => 'Toto']);

 

Higher Order Messaging (HOM)

La présentation officielle est ici.

Il s’agit d’un design pattern destiné à simplifier la syntaxe lorsqu’on doit effectuer des boucles pour des langages orientés objet. La bonne nouvelle c’est qu’il est appliqué aux collections de Laravel dans la version 5.4 !

Par exemple on a cette classe :

class Personne
{
    protected $name;
    public function __construct($name)
    {
        $this->name = $name;
    }
    public function afficheNom()
    {
        echo $this->name . '<br>';
    }
}

On crée une collection de personnes :

$collection = collect([
    new Personne('Dupont'),
    new Personne('Fremoulle'),
    new Personne('Tartignole')
]);

Pour afficher tous les noms on fait classiquement ceci :

$collection->each(function($item) {
    $item->afficheNom();
});

Avec le nouveau design pattern on pourra écrire avec cette syntaxe simplifiée :

$collection->each->afficheNom();

Bon… c’est du détail mais ça participe à l’élégance de Laravel.

Des emails avec Markdown

Avec Laravel 5.3 on dispose de Mailable pour créer facilement des emails. Il suffit d’utiliser cette commande d’artisan :

php artisan make:mail monMail

Et la magie opère, un dossier et un fichier sont créés :

Avec ce code :

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;

class monMail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('view.name');
    }
}

Il ne reste plus qu’à créer la vue et le tour est joué et au besoin jouer avec quelques méthodes pour savoir qui envoie, transmettre des paramètree… Mais la vue utilise du HTML ou à la rigueur du texte simple.

Avec Laravel 5.4 la commande d’artisan s’enrichit pour le markdown :

php artisan make:mail MonMail --markdown=mon_mail

On a encore création du dossier et du fichier comme avant mais la syntaxe change un peu pour la méthode build :

public function build()
{
    return $this->markdown('mon_mail');
}

On ne retroune plus avec view mais avec markdown et en plus une vue est créée :

Avec ce code de base :

@component('mail::message')
# Introduction

The body of your message.

@component('mail::button', ['url' => ''])
Button Text
@endcomponent

Thanks,<br>
{{ config('app.name') }}
@endcomponent

La syntaxe est fondée sur le nouveau composant de Blade dont j’ai parlé ci-dessus.

Il paraît qu’on dispose de ces composants :

  • button
  • footer
  • header
  • layout
  • message
  • panel
  • promotion
  • subcopy
  • table

Ca permet de faire pas mal de choses !

Si on envoie tel quel voici le résultat obtenu :

Pour vérifier si ça marche je tente un tableau :

On va tenter un tableau :
@component('mail::table')
Item     | Valeur
-------- | ---
Fraises  | 12 €
Banane   | 3 €
Pipe     | 50 €
@endcomponent

Avec ce résultat :

Je n’ai pas essayé les autres composants mais c’est très prometteur !

On a les mêmes possibilités avec les notifications.

Laravel Dusk

Pour terminer on va évoquer les tests. Laravel permet de mettre en place des tests puissants d’application, on peut ainsi remplir un formulaire, cliquer sur un lien… Tout se passe bien tant que Javascript ou encore pire Ajax ne s’en mêlent pas parce que ce qui est utilisé c’est le BrowserKit de Symfony.

Dusk arrive à digérer le Javascript, il offre les mêmes possibilités que le BrowserKit et reconnait même les glisser-déposer !

Pour le moment il en est à la phase alpha. Pour se faire une idée il y a un exemple sur Github.

Laravel Mix

Si vous utilisez Laravel vous connaissez forcément Elixir qui facilité l’utilisation de Gulp pour gérer CSS et Javascript. Avec Laravel 5.4 on abandonne gulp en faveur de Webpack et pour l’occasion le nom change aussi pour Laravel Mix. En attendant d’avoir plus d’informations on peut déjà se documenter sur Webpack si on le connaît peu ou pas.

Deux nouveaux middlewares

Trim Strings

Avec ce middleware non actif par défaut on n’aura plus de souci concernant la saisie d’espaces parasites surout en fin de chaîne de caractères. Les espaces avant et arrières seront éradiqués.

Convert Empty Strings to Null

On a parfois des soucis avec des colonnes nullables lorsque rien n’est saisit dans un formulaire. Avec ce middleware non actif par défaut on aura une transformation en valeur nulle donc directement envoyable dans la base.

Conclusion

Les nouveautés ne sont pas aussi radicales qu’avec la version 5.3 mais il y a des choses bien intéressantes ! Si vous voulez donner votre avis sur le sujet…