Tutoriel pour apprendre à utiliser le framework Laravel 5.3

Les bases

Laravel est un Framework PHP libre de droits qui a fait son apparition en 2011. Il est peut-être jeune comparé aux autres de son genre, mais il se démarque par sa facilité, sa syntaxe élégante, et toute sa documentation disponible à tous.

Dans ce tutoriel, première partie d'une série de cours, nous allons nous familiariser avec ce framework en présentant les éléments de base.

4 commentaires Donner une note à l'article (5)

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Présentation générale

Dans ce premier chapitre, je vais évoquer PHP, son historique rapide et sa situation actuelle. Je vais aussi expliquer l'intérêt d'utiliser un framework pour ce langage et surtout pourquoi j'ai choisi Laravel. J'évoquerai enfin le patron MVC et la Programmation Orientée Objet.

I-A. Un framework ?

I-A-1. Approche personnelle

PHP est un langage populaire et accessible. Il est facile à installer et présent chez tous les hébergeurs. C'est un langage riche et plutôt facile à aborder, surtout pour quelqu'un qui a déjà des bases en programmation. On peut réaliser rapidement une application web fonctionnelle grâce à lui. Mais le revers de cette simplicité est que bien souvent le code créé est confus, complexe, sans aucune cohérence. Il faut reconnaître que PHP n'encourage pas à organiser son code et rien n'oblige à le faire.

Lorsqu'on crée des applications PHP on finit par avoir des routines personnelles toutes prêtes pour les fonctionnalités récurrentes, par exemple pour gérer des pages de façon dynamique. Une fois qu'on a créé une fonction ou une classe pour réaliser une tâche il est naturel d'aller la chercher lorsque la même situation se présente. Puisque c'est une bibliothèque personnelle et qu'on est seul maître à bord, il faut évidemment la mettre à jour lorsque c'est nécessaire, et c'est parfois fastidieux.

En général on a aussi une hiérarchie de dossiers à laquelle on est habitué et on la reproduit quand on commence le développement d'une nouvelle application. On se rend compte des fois que cette habitude a des effets pervers parce que la hiérarchie qu'on met ainsi en place de façon systématique n'est pas forcément la plus adaptée.

En résumé l'approche personnelle est plutôt du bricolage à la hauteur de ses compétences et de sa disponibilité.

I-A-2. (Re)découvrir PHP

Lorsque j'ai découvert PHP à la fin du dernier millénaire (ça fait plus impressionnant dit comme ça), il en était à la version 3. C'était essentiellement un langage de script en général mélangé au HTML qui permettait de faire du templating, des accès aux données et du traitement. La version 4 en 2000 a apporté plus de stabilité et une ébauche de l'approche objet. Mais il a fallu attendre la version 5 en 2004 pour disposer d'un langage de programmation à la hauteur du standard existant pour les autres langages.

Cette évolution incite à perdre les mauvaises habitudes si on en avait. Un site comme http://www.phptherightway.com offre des pistes pour mettre en place de bonnes pratiques. Donc si vous êtes un bidouilleur de code PHP, je vous conseille cette saine lecture qui devrait vous offrir un nouvel éclairage sur ce langage et surtout vous permettre de vous lancer de façon correcte dans le code de Laravel.

I-A-3. Un framework

D'après Wikipedia un framework informatique est un « ensemble cohérent de composants logiciels structurels, qui sert à créer les fondations ainsi que les grandes lignes de tout ou d'une partie d'un logiciel ». Autrement dit une base homogène avec des briques toutes prêtes à disposition. Il existe des frameworks pour tous les langages de programmation et en particulier pour PHP. En faire la liste serait laborieux tant il en existe !

L'utilité d'un framework est d'éviter de passer du temps à développer ce qui a déjà été fait par d'autres souvent plus compétents et qui a en plus été utilisé et validé par de nombreux utilisateurs. On peut imaginer un framework comme un ensemble d'outils à disposition. Par exemple je dois faire du routage pour mon site, je prends un composant déjà tout prêt et qui a fait ses preuves et je l'utilise : gain de temps, fiabilité, mise à jour si nécessaire…

Il serait vraiment dommage de se passer d'un framework, alors que le fait d'en utiliser un présente pratiquement uniquement des avantages.

I-B. Pourquoi Laravel ?

I-B-1. Constitution de Laravel

Laravel, créé par Taylor Otwel, initie une nouvelle façon de concevoir un framework en utilisant ce qui existe de mieux pour chaque fonctionnalité. Par exemple toute application web a besoin d'un système qui gère les requêtes HTTP. Plutôt que de réinventer quelque chose, le concepteur de Laravel a tout simplement utilisé celui de Symfony en l'étendant pour créer un système de routage efficace. De la même manière, l'envoi des emails se fait avec la bibliothèque SwiftMailer. En quelque sorte Otwel a fait son marché parmi toutes les bibliothèques disponibles. Nous verrons dans ce cours comment cela est réalisé. Mais Laravel ce n'est pas seulement le regroupement de bibliothèques existantes, c'est aussi de nombreux composants originaux et surtout une orchestration de tout ça.

Vous allez trouver dans Laravel :

  • un système de routage (RESTFul et ressources) ;
  • un créateur de requêtes SQL et un ORM ;
  • un moteur de template ;
  • un système d'authentification pour les connexions ;
  • un système de validation ;
  • un système de pagination ;
  • un système de migration pour les bases de données ;
  • un système d'envoi d'emails ;
  • un système de cache ;
  • un système de gestion des événements ;
  • un système d'autorisations ;
  • une gestion des sessions ;
  • un système de localisation ;
  • un système de notifications…

Et d'autres choses encore que nous allons découvrir ensemble. Il est probable que certains éléments de cette liste ne vous évoquent pas grand-chose, mais ce n'est pas important pour le moment, tout cela deviendra plus clair au fil des chapitres.

I-B-2. Le meilleur de PHP

Plonger dans le code de Laravel, c'est recevoir un cours de programmation tant le style est clair et élégant et le code bien organisé. La version actuelle de Laravel est la 5.3, elle nécessite au minimum la version 5.6.4 de PHP. Pour aborder de façon efficace ce framework, il serait souhaitable que vous soyez familiarisé avec ces notions :

  • les espaces de noms : c'est une façon de bien ranger le code pour éviter des conflits de nommage. Laravel utilise cette possibilité de façon intensive. Tous les composants sont rangés dans des espaces de noms distincts, de même que l'application créée ;
  • les fonctions anonymes : ce sont des fonctions sans nom (souvent appelées closures) qui permettent d'améliorer le code. Les utilisateurs de JavaScript y sont habitués. Les utilisateurs de PHP un peu moins parce qu'elles y sont plus récentes. Laravel les utilise aussi de façon systématique ;
  • les méthodes magiques : ce sont des méthodes qui n'ont pas été explicitement décrites dans une classe, mais qui peuvent être appelées et résolues ;
  • les interfaces : une interface est un contrat de constitution des classes. En programmation objet c'est le sommet de la hiérarchie. Tous les composants de Laravel sont fondés sur des interfaces ;
  • les traits : c'est une façon d'ajouter des propriétés et méthodes à une classe sans passer par l'héritage, ce qui permet de passer outre certaines limitations de l'héritage simple proposé par défaut par PHP.

Un framework n'est pas fait pour remplacer la connaissance d'un langage, mais pour assister celui (ou celle) qui connaît déjà bien ce langage. Si vous avez des lacunes, il vaut mieux les combler pour profiter pleinement de Laravel.

I-C. La documentation

Quand on s'intéresse à un framework il ne suffit pas qu'il soit riche et performant, il faut aussi que la documentation soit à la hauteur. C'est le cas pour Laravel. Vous trouverez la documentation sur le site officiel. Mais il existe de plus en plus de sources d'informations dont voici les principales :

Il existe aussi de bons livres, mais pratiquement tous en anglais.

I-D. MVC ? POO ?

I-D-1. MVC

On peut difficilement parler d'un framework sans évoquer le patron Modèle-Vue-Contrôleur. Pour certains, il s'agit de la clé de voûte de toute application rigoureuse, pour d'autres c'est une contrainte qui empêche d'organiser judicieusement son code. De quoi s'agit-il ? Voici un petit schéma pour y voir clair :

Image non disponible

C'est un modèle d'organisation du code :

  • le modèle est chargé de gérer les données ;
  • la vue est chargée de la mise en forme pour l'utilisateur ;
  • le contrôleur est chargé de gérer l'ensemble.

En général on résume en disant que le modèle gère la base de données, la vue produit les pages HTML et le contrôleur fait tout le reste. Dans Laravel :

  • le modèle correspond à une table d'une base de données. C'est une classe qui étend la classe Model qui permet une gestion simple et efficace des manipulations de données et l'établissement automatisé de relations entre tables ;
  • le contrôleur se décline en deux catégories : contrôleur classique et contrôleur de ressource (je détaillerai évidemment tout ça dans le cours) ;
  • la vue est soit un simple fichier avec du code HTML, soit un fichier utilisant le système de template Blade de Laravel.

Laravel propose ce patron, mais ne l'impose pas. Nous verrons d'ailleurs qu'il est parfois judicieux de s'en éloigner parce qu'il y a des tas de choses qu'on n'arrive pas à caser dans cette organisation. Par exemple si je dois envoyer des emails où vais-je placer mon code ? En général ce qui se produit est l'inflation des contrôleurs auxquels on demande des choses pour lesquelles ils ne sont pas faits.

I-D-2. POO

Laravel est fondamentalement orienté objet. La POO est un design pattern qui s'éloigne radicalement de la programmation procédurale. Avec la POO tout le code est placé dans des classes qui découlent d'interfaces qui établissent des contrats de fonctionnement. Avec la POO on manipule des objets.

Avec la POO, la responsabilité du fonctionnement est répartie dans des classes, alors que dans l'approche procédurale tout est mélangé. Le fait de répartir la responsabilité évite la duplication du code qui est le lot presque forcé de la programmation procédurale. Laravel pousse au maximum cette répartition en utilisant l'injection de dépendances.

L'utilisation de classes bien identifiées, dont chacune a un rôle précis, pilotées par des interfaces claires, dopées par l'injection de dépendances : tout cela crée un code élégant, efficace, lisible, facile à maintenir et à tester. C'est ce que Laravel propose. Alors vous pouvez évidemment greffer là-dessus votre code approximatif, mais vous pouvez aussi vous inspirer des sources du framework pour améliorer votre style de programmation.

L'injection de dépendances est destinée à éviter de rendre les classes dépendantes et de privilégier une liaison dynamique plutôt que statique. Le résultat est un code plus lisible, plus facile à maintenir et à tester. Nous verrons ce mécanisme à l'œuvre dans Laravel.

I-E. En résumé

  • Un framework fait gagner du temps et donne l'assurance de disposer de composants bien codés et fiables.
  • Laravel est un framework novateur, complet, qui utilise les possibilités les plus récentes de PHP et qui est impeccablement codé et organisé.
  • La documentation de Laravel est complète, précise et de nombreux tutoriels et exemples sont disponibles ‌sur la toile.
  • Laravel adopte le patron MVC, mais ne l'impose pas, il est totalement orienté objet.

II. Un environnement de développement

Pour fonctionner, Laravel a besoin d'un certain environnement :

  • un serveur ;
  • PHP ;
  • MySql ;
  • Node ;
  • Composer…

Il y a de plus en plus d'environnements disponibles dans le cloud avec des options gratuites comme chez c9. Mais rien ne vaut un bon système local : c'est rapide, sûr et on peut tout gérer. Mais évidemment il faut se le construire !

Heureusement il existe des solutions toutes prêtes, par exemple pour PHP + MySql : wampserver, xampp, easyphp

Ces solutions sont intéressantes, mais pour ce cours je vous conseille plutôt Laragon. Je l'apprécie de plus en plus en abandonnant peu à peu wamp que j'utilise depuis des années. Il est simple, rapide, convivial, non intrusif, complet, et en plus pensé pour Laravel ! Mais il ne fonctionne que sur Windows.

Pour les utilisateurs de Linux, il faut se tourner vers l'une des solutions évoquées ci-dessus ou alors Homestead qui est l'environnement officiel de Laravel. Pour son installation, il suffit de suivre la procédure décrite dans la documentation. Par contre je déconseille aux utilisateurs de Windows d'installer Homestead, sauf s'ils ont envie de passer de longues heures à configurer leur système.

Donc en résumé :

  • pour Windows : Laragon ;
  • pour Linux ou Mac : Homestead.

II-A. Laragon

Pour récupérer Laragon, il faut se rendre sur le site :

Image non disponible

Il n'y a que cette page, ne cherchez pas de documentation (mais ne vous en faites pas, il n'y en a pas vraiment besoin), il y a juste un bouton pour aller sur le forum de discussion ainsi que deux autres boutons pour le téléchargement.

Je vous conseille la version complète (Download Laragon 2.0.5). Vous aurez sans doute une version ultérieure à celle affichée ici. Vous récupérez ainsi un installeur qu'il suffit de lancer.

Une fois l'installation effectuée vous ouvrez Laragon :

Image non disponible

Vous n'avez plus qu'à cliquer sur « Tout démarrer » pour créer le serveur :

Image non disponible

Vous avez maintenant à disposition :

Avec Homestead, on obtient pratiquement les mêmes possibilités.

Ne vous inquiétez pas si vous ne connaissez pas la moitié de ces outils ! D'une part nous ne les utiliserons pas tous, d'autre part je détaillerai l'utilisation de ceux qui vous seront nécessaires.

II-A-1. Hôte virtuel

Cerise sur le gâteau : Laragon crée automatiquement des hôtes virtuels pour les dossiers qui se trouvent sur le serveur (www). Pour ceux qui ne savent pas de quoi il s'agit, voici un exemple avec justement le cas de Laravel. Le fichier de démarrage de Laravel est placé en www/monsite/public/index.html. Donc à partir de localhost, il faut entrer : localhost/monsite/public. Ce n'est ni pratique ni élégant. Un hôte virtuel permet de définir une adresse simplifiée. Par exemple ici Laragon va définir automatiquement monsite.dev. Avouez que c'est quand même mieux !

Mais ce n'est pas seulement une histoire d'esthétique ou d'économie de clavier. Le fait de disposer d'un hôte virtuel permet d'avoir en local exactement le même comportement que sur le serveur de production. Par exemple si vous avez sur une page HTML une image avec ce genre de référence : /images/bouton.png, vous serez tout à fait heureux d'avoir un hôte virtuel pour que l'image s'affiche !

Créer manuellement un hôte virtuel avec Windows n'est pas difficile, mais un peu laborieux. Il faut modifier le fichier hosts de Windows et httpd-vhosts.conf de Apache. Autant laisser Laragon s'en charger !

II-B. Composer

II-B-1. Présentation

Je vous ai dit dans le précédent chapitre que Laravel utilise des composants d'autres sources. Plutôt que de les incorporer directement, il utilise un gestionnaire de dépendances : composer. D'ailleurs pour le coup les composants de Laravel sont aussi traités comme des dépendances. Mais c'est quoi un gestionnaire de dépendances ?

Imaginez que vous créez une application PHP et que vous utilisez des composants issus de différentes sources : Carbon pour les dates, Redis pour les données… Vous pouvez utiliser la méthode laborieuse en allant chercher tout ça de façon manuelle, et vous allez être confronté à des difficultés :

  • télécharger tous les composants dont vous avez besoin et les placer dans votre structure de dossiers ;
  • traquer les éventuels conflits de nommage entre les bibliothèques ;
  • mettre à jour manuellement les bibliothèques quand c'est nécessaire ;
  • prévoir le code pour charger les classes à utiliser…

Tout ça est évidemment faisable, mais avouez que s'il était possible d'automatiser les procédures ce serait vraiment génial. C'est justement ce que fait un gestionnaire de dépendances !

II-B-2. JSON

Pour comprendre le fonctionnement de composer, il faut connaître le format JSON qui est l'acronyme de JavaScript Object Notation. Un fichier JSON a pour but de contenir des informations de type étiquette-valeur. Regardez cet exemple élémentaire :

 
Sélectionnez
1.
2.
3.
4.
{
  "nom": "Durand",
  "prénom": "Jean"
}

Les étiquettes sont « nom » et « prénom » et les valeurs correspondantes « Durand » et « Jean ». Les valeurs peuvent être aussi des tableaux ou des objets. Regardez ce second exemple :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
{
    "identité1" : {
      "nom": "Durand",
      "prénom": "Jean"
    },
    "identité2" : {
      "nom": "Dupont",
      "prénom": "Albert"
    }   
}

Composer a besoin d'un fichier composer.json associé. Ce fichier contient les instructions pour composer : les dépendances, les classes à charger automatiquement… Par exemple :

 
Sélectionnez
1.
2.
3.
4.
"require": {
    "php": ">=5.6.4",
    "laravel/framework": "5.3.*",
},

Ici on dit qu'on veut (require) que PHP soit au moins en version 5.6.4 et on veut également charger le composant « laravel/framework ».

On verra comment se présente le fichier composer.json de Laravel dans le prochain chapitre.

II-B-3. Packagist

Tous les composants disponibles se trouvent sur le site Packagist :

Image non disponible

Par exemple il me faut un composant pour l'envoi d'email, j'entre ceci dans la zone de recherche :

Image non disponible

J'obtiens une liste assez longue et je n'ai plus qu'à fouiller un peu pour trouver ce que je cherche.

II-B-4. Packalyst

Le site Packalyst est spécialisé dans les composants conçus pour Laravel :

Image non disponible

Là vous êtes sûr que le composant va fonctionner directement dans Laravel ! Il faut toutefois vérifier qu'il correspond au numéro de version que vous utilisez.

Pour aller plus loin avec composer vous pouvez lire cet article.

II-C. Les éditeurs de code

Choisir un éditeur de code n'est pas évident, les critères sont nombreux. Laragon embarque Sublime Text qui jouit d'une grande popularité :

Image non disponible

Son grand avantage est de proposer de très nombreux plugins pour étendre ses possibilités, et il y en a pas mal pour Laravel. Mais pour ça il faut commencer par installer le Package Control. Vous trouvez la procédure d'installation ici.

Ensuite vous pouvez installer tous les plugins que vous voulez. Pour trouver ceux qui concernent Laravel vous avez une zone de recherche :

Image non disponible

Personnellement j'utilise beaucoup Sublime Text en association avec Netbeans qui est quant à lui un IDE assez complet et gratuit.

Dans la communauté Laravel, l'IDE qui a le plus de succès est PhpStorm‌. Il est vraiment puissant et complet, mais il n'est pas gratuit, contrairement à Netbeans.

II-D. En résumé

  • Laravel a besoin d'un environnement de développement complet : PHP, MySql, composer…
  • Il existe des solutions toutes prêtes : Homestead pour Linux et Laragon pour Windows.
  • Composer est le gestionnaire de dépendances utilisé par Laravel.
  • Sublime Text est l'éditeur de code le plus utilisé, il est prévu dans l'installation de Laragon.

III. Installation et organisation

Dans ce chapitre, nous allons voir comment créer une application Laravel et comment le code est organisé dans une application.

Pour utiliser Laravel et suivre ce chapitre et l'ensemble du cours, vous aurez besoin d'un serveur équipé de PHP avec au minimum la version 5.6.4 et aussi de MySQL. Nous avons vu dans le précédent chapitre les différentes possibilités.

Quelle que soit l'application que vous utilisez, vérifiez que vous avez la bonne version de PHP (minimum 5.6.4). D'autre part les extensions PDO, Tokenizer, OpenSSL et Mbstring de PHP doivent être activées.

III-A. Créer une application Laravel

III-A-1. Prérequis

Composer fonctionne en ligne de commande. Vous avez donc besoin de la console (nommée Terminal ou Konsole sur OS X et Linux). Les utilisateurs de Linux sont très certainement habitués à l'utilisation de la console, mais il n'en est généralement pas de même pour les adeptes de Windows. Pour trouver la console sur ce système il faut chercher l'invite de commande :

Image non disponible

Si vous utilisez Laragon, comme je vous le conseille, vous avez une console améliorée (Cmder) accessible avec ce bouton.

Image non disponible

Ce cours a été créé avec la version 5.3.* de Laravel. Lorsque vous créez une nouvelle application, que ce soit avec composer create-project ou avec l'installateur, vous obtenez la dernière version stable. Je m'efforcerai de garder ce cours en phase avec la dernière version de Laravel, mais il y aura toujours un délai entre la sortie d'une nouvelle version et ce cours. Si vous rencontrez des différences de fonctionnement avec les exemples utilisés, vous pouvez toujours, en attendant la mise à niveau du cours, installer la version précédente de Laravel. Il suffit d'utiliser la commande create-project en spécifiant la version comme troisième argument (documentation complète ici).

III-A-2. Installation avec composer

Il y a plusieurs façons de créer une application Laravel. La plus classique consiste à utiliser la commande create-project de composer. Par exemple, je veux créer une application dans un dossier laravel5 à la racine de mon serveur, voici la syntaxe à utiliser :

 
Sélectionnez
composer create-project --prefer-dist laravel/laravel laravel5

L'installation démarre et je n'ai plus qu'à attendre quelques minutes pour que composer fasse son travail jusqu'au bout. Vous verrez s'afficher une liste de téléchargements. Finalement, on se retrouve avec cette architecture :

Image non disponible

On peut vérifier que tout fonctionne bien avec l'URL http://localhost/laravel5/public. Normalement on doit obtenir cette page très épurée :

Image non disponible

Pour les mises à jour ultérieures, il suffit encore d'utiliser composer avec la commande update :

 
Sélectionnez
composer update

III-A-3. Installation avec Laravel Installer

Une autre solution pour installer Laravel consiste à utiliser l'installeur. Il faut commencer par installer globalement l'installeur avec composer :

 
Sélectionnez
composer global require "laravel/installer"

Il faut ensuite informer la variable d'environnement path de l'emplacement du dossier …/composer/vendor/bin.

Pour créer une application, il suffit de taper :

 
Sélectionnez
laravel new monAppli

Laravel sera alors installé dans le dossier monAppli.

III-A-4. Installation avec Laragon

Une façon encore plus simple pour installer Laravel est d'utiliser Laragon avec le menu :

Image non disponible

On vous demande alors le nom du projet (donc du dossier) :

Image non disponible

Et ensuite vous n'avez plus qu'à attendre ! La console s'ouvre et vous pouvez suivre le déroulement des opérations :

Image non disponible

On vous rappelle la commande de composer et en plus une base de données est automatiquement créée avec le même nom !

Si vous installez Laravel en téléchargeant directement les fichiers sur Github et en utilisant la commande composer install il vous faut effectuer deux actions complémentaires. En effet dans ce cas il ne sera pas automatiquement créé de clé de sécurité et vous allez tomber sur une erreur au lancement. Il faut donc la créer avec la commande php artisan key:generate. D'autre part vous aurez à la racine le fichier .env.example que vous devrez renommer en .env (ou en faire une copie) pour que la configuration fonctionne.

III-A-5. Autorisations

Au niveau des dossiers de Laravel, les seuls qui ont besoin de droits d'écriture par le serveur sont storage (et ses sous-dossiers), et bootstrap/cache .

III-A-5-a. Serveur

Pour fonctionner correctement, Laravel a besoin de PHP :

  • Version >= 5.5.9 ;
  • Extension PDO ;
  • Extension Mbstring ;
  • Extension OpenSSL ;
  • Extension Tokenizer ;
  • Extension XML.‌

Laravel est équipé d'un serveur sommaire pour le développement qui se lance avec cette commande :

 
Sélectionnez
php artisan serve

On y accède à cette adresse : http://localhost:8000. Mais évidemment pour que ça fonctionne, il faut que vous ayez PHP installé.

III-A-6. Des URL propres

Pour un serveur Apache il est prévu dans le dossier public un fichier .htaccess avec ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews
    </IfModule>
 
    RewriteEngine On
 
    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)/$ /$1 [L,R=301]
 
    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
 
    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>

Le but est essentiellement d'éviter d'avoir index.php dans l'URL. Mais pour que ça fonctionne, il faut activer le module mod_rewrite.

III-B. Organisation de Laravel

Maintenant qu'on a un Laravel tout neuf et qui fonctionne voyons un peu ce qu'il contient…

III-B-1. Dossier app

Ce dossier contient les éléments essentiels de l'application :

Image non disponible
  • Console : toutes les commandes en mode console ;
  • Http : tout ce qui concerne la communication : contrôleurs, middlewares (il y a trois middlewares de base qui servent à filtrer les requêtes HTTP) et le kernel ;
  • Providers : tous les fournisseurs de services (providers), il y en a déjà cinq au départ. Les providers servent à initialiser les composants ;
  • User : un modèle qui concerne les utilisateurs pour la base de données.

Évidemment tout cela doit vous paraître assez nébuleux pour le moment, mais nous verrons en détail ces éléments au fil du cours. Et on verra d'ailleurs que seront créés bien d'autres dossiers selon nos besoins.

III-B-2. Autres dossiers

Image non disponible

Voici une description du contenu des autres dossiers :

  • bootstrap : scripts d'initialisation de Laravel pour le chargement automatique des classes, la fixation de l'environnement et des chemins, et pour le démarrage de l'application ;
  • public : tout ce qui doit apparaître dans le dossier public du site : images, CSS, scripts…
  • config : toutes les configurations : applications, authentifications, caches, bases de données, espaces de noms, emails, systèmes de fichiers, sessions…
  • database : migrations et populations ;
  • resources : vues, fichiers de langage et assets (par exemple les fichiers LESS ou Sass) ;
  • routes : la gestion des URL d'entrée de l'application ;
  • storage : données temporaires de l'application : vues compilées, caches, clés de session…
  • tests : fichiers de tests unitaires ;
  • vendor : tous les composants de Laravel et de ses dépendances (créé par composer).

III-B-3. Fichiers de la racine

Il y a un certain nombre de fichiers dans la racine dont voici les principaux :

  • artisan : outil en ligne de Laravel pour des tâches de gestion ;
  • composer.json : fichier de référence de composer ;
  • package.json : fichier de référence de npm pour les assets ;
  • phpunit.xml : fichier de configuration de phpunit (pour les tests unitaires) ;
  • .env : fichier pour spécifier l'environnement d'exécution.

Nous verrons tout cela progressivement dans le cours, ne vous inquiétez pas !

III-B-4. Accessibilité

Pour des raisons de sécurité sur le serveur, seul le dossier public doit être accessible :

Image non disponible

Cette configuration n'est pas toujours possible sur un serveur mutualisé, il faut alors modifier un peu Laravel pour que ça fonctionne; j'en parlerai dans le chapitre sur le déploiement.

III-C. Environnement et messages d'erreur

Par défaut, lorsque vous installez Laravel, celui-ci est en mode « debug ». Au niveau de l'affichage des erreurs si vous entrez une URL qui n'est pas prévue vous allez obtenir quelque chose comme ceci :

Image non disponible

Pendant la phase de développement, on a besoin d'obtenir des messages explicites pour traquer les erreurs inévitables que nous allons faire. En mode « production » il faudra changer ce mode, pour cela ouvrez le fichier config/app.php et trouvez cette ligne :

 
Sélectionnez
'debug' => env('APP_DEBUG', false),

Autrement dit on va chercher la valeur dans l'environnement, mais où peut-on le trouver ? Regardez à la racine des dossiers, vous y trouvez le fichier .env :

Image non disponible

Avec ce contenu :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
APP_ENV=local
APP_KEY=base64:N/QruoORs/RqZdvcIWgyB8bVCWIYzMebPmnHX/g/J7A=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=http://localhost
 
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
 
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
 
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
 
MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
 
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_APP_ID=

Vous remarquez que dans ce fichier la variable APP_DEBUG a la valeur true. On va la conserver ainsi puisqu'on veut être en mode « debug ». Vous êtes ainsi en mode débogage avec affichage de messages d'erreur détaillés. Si vous la mettez à false (ou si vous la supprimez), avec une URL non prévue vous obtenez maintenant juste :

Image non disponible

Il ne faudra évidemment pas laisser la valeur true lors d'une mise en production ! On en reparlera lorsqu'on verra la gestion de l'environnement. Vous ne risquerez ainsi plus d'oublier de changer cette valeur parce que Laravel saura si vous êtes sur votre serveur de développement ou sur celui de production.

D'autre part, il y a un fichier qui collecte les erreurs (Log), vous le trouvez ici :

Image non disponible

Par défaut, il n'y a qu'un fichier, mais si vous préférez avoir un fichier par jour par exemple, il suffit d'ajouter cette ligne dans le fichier .env :

 
Sélectionnez
APP_LOG=daily

De la même manière par défaut Laravel stocke toutes les erreurs. C'est pratique dans la phase de développement, mais en production vous pouvez limiter le niveau de sévérité des erreurs retenues (mode debug), par exemple si vous vous contentez des warning :

 
Sélectionnez
APP_LOG_LEVEL=warning

Laravel utilise le composant Monolog pour la gestion des erreurs, reportez-vous à sa documentation si vous avez besoin de plus d'informations. La documentation de Laravel en résume l'essentiel ainsi que son adaptation au framework.

La valeur de APP_KEY qui sécurise les informations est automatiquement générée lors de l'installation avec create-project.

III-D. Le composant Html

Dans la version 4 de Laravel, il y avait directement le composant Html qui permet de créer facilement des formulaires et qui offre un lot d'helpers pour l'écriture du HTML. Dans la version 5, ce composant n'est pas chargé par défaut. Comme nous en aurons besoin dans ce cours, une fois que vous avez réussi à installer une application toute neuve de Laravel, vous allez modifier ainsi le fichier composer.json :

 
Sélectionnez
1.
2.
3.
4.
5.
"require": {
    "php": ">=5.6.4",
    "laravel/framework": "5.3.*",
    "laravelcollective/html": "5.3.*"
},

On demande ainsi à composer de charger le composant laravelcollective/html. Lancez alors une mise à jour :

 
Sélectionnez
composer update

Attention de bien vous positionner dans le dossier racine de l'application !

Attendez la fin du chargement.

Une façon plus synthétique d'installer le composant est d'entrer directement cette commande :

 
Sélectionnez
composer require laravelcollective/html

Le fichier composer.json est automatiquement mis à jour et l'update est effectué. La seule différence c'est que vous ne pouvez pas choisir la version, vous aurez d'office la dernière.

Il faut ensuite ajouter cette ligne dans le fichier config/app.php dans la section des providers :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
<?php
'providers' => [
    // ...
    Collective\Html\HtmlServiceProvider::class,
    // ...
],

Et ces deux lignes dans la section des alias :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
'aliases' => [
    //...
    'Form' => Collective\Html\FormFacade::class,
    'Html' => Collective\Html\HtmlFacade::class,
    //...
],

Ainsi vous allez disposer de ce composant bien utile ! D'autre part vous savez maintenant comment on ajoute un composant à Laravel.

III-E. En résumé

  • Pour son installation et sa mise à jour Laravel utilise le gestionnaire de dépendances composer.
  • La création d'une application Laravel se fait à partir de la console avec une simple ligne de commande.
  • Laravel est organisé en plusieurs dossiers.
  • Le dossier public est le seul qui doit être accessible pour le client.
  • L'environnement est fixé à l'aide du fichier .env.
  • Par défaut Laravel est en mode « debug » avec affichage de toutes les erreurs.
  • Le composant ‌laravelcollective/html n'est pas prévu par défaut, il faut le charger indépendamment.

IV. Le routage

Dans ce chapitre nous allons nous intéresser au devenir d'une requête HTTP qui arrive dans notre application Laravel. Nous allons voir l'intérêt d'utiliser un fichier .htaccess pour simplifier les URL. Nous verrons aussi le système de routage pour trier les requêtes.

IV-A. Les requêtes HTTP

IV-A-1. Petit rappel

On va commencer par un petit rappel sur ce qu'est une requête HTTP. Voici un schéma illustratif :

Image non disponible

Le HTTP (Hypertext Transfer Protocol) est un protocole de communication entre un client et un serveur. Le client demande une page au serveur en envoyant une requête et le serveur réagit en envoyant une réponse, en général une page HTML.

Quand on surfe sur Internet, chacun de nos clics provoque en général cet échange, et plus généralement une rafale d'échanges.

La requête du client comporte un certain nombre d'informations (headers, status code, body…).

Prenons  un exemple avec le site de Laravel. Lorsque je clique sur le lien, voilà toutes les requêtes HTTP qui se produisent :

Image non disponible

En tout 16 requêtes avec la méthode GET. Regardons d'un peu plus près la première :

Image non disponible

On trouve :

  • l'URL : https://laravel.com/;
  • la méthode : GET ;
  • l'adresse IP : 104.20.13.244:443 ;
  • le code : 200 (donc tout s'est bien passé) ;
  • la version du HTTP : 1.1.

Il y a évidemment bien d'autres choses dans les headers (content-type, cookies, encodage…), mais pour le moment on va se contenter de ces informations. Notre navigateur digère tout ça de façon transparente, heureusement pour nous !

Notre application Laravel doit savoir interpréter les informations qui arrivent et les utiliser de façon pertinente pour renvoyer ce que demande le client. Nous allons voir comment cela est réalisé.

Il est souvent utile de générer des requêtes HTTP à partir d'un client, il existe de nombreux outils pour cela, personnellement j'utilise le module de Firefox HttpRequester.

IV-A-2. Les méthodes

Il est indispensable de connaître les principales méthodes du HTTP :

  • GET : c'est la plus courante, on demande une ressource qui ne change jamais, on peut mémoriser la requête, on est sûr d'obtenir toujours la même ressource ;
  • POST : elle est aussi très courante, là, la requête modifie ou ajoute une ressource, le cas le plus classique est la soumission d'un formulaire (souvent utilisé à tort à la place de PUT) ;
  • PUT : on ajoute ou remplace complètement une ressource ;
  • PATCH : on modifie partiellement une ressource (donc à ne pas confondre avec PUT) ;
  • DELETE : on supprime une ressource.

La différence entre PUT et POST est loin d'être évidente.

IV-A-3. .htaccess et index.php

Pour Laravel on veut que toutes les requêtes aboutissent obligatoirement sur le fichier index.php situé dans le dossier public. Pour y arriver, on peut utiliser une URL de ce genre :

http://monsite.fr/index.php/mapage

Mais ce n'est pas très esthétique avec ce index.php au milieu. Si vous avez un serveur Apache lorsque la requête du client arrive sur le serveur où se trouve notre application Laravel elle passe en premier par le fichier .htaccess, s'il existe, qui fixe des règles pour le serveur. Il y a justement un fichier .htaccess dans le dossier public de Laravel avec une règle de réécriture de telle sorte qu'on peut avoir une URL simplifiée :

http://monsite.fr/mapage

Un petit schéma pour visualiser cette action :

Image non disponible

Pour que ça fonctionne, il faut que le serveur Apache ait le module mod_rewrite activé.

IV-B. Le cycle de la requête

Lorsque la requête atteint le fichier public/index.php l'application Laravel est créée et configurée et l'environnement est détecté. Nous reviendrons plus tard plus en détail sur ces étapes. Ensuite le fichier routes/web.php est chargé. Voici l'emplacement de ce fichier :

Image non disponible

Le fichier api.php concerne les routes lorsqu'on crée une API. Le fichier console.php concerne les routes pour les actions en ligne de commande.

C'est avec ce fichier que la requête va être analysée et dirigée. Regardons ce qu'on y trouve au départ :

 
Sélectionnez
Route::get('/', function () {
    return view('welcome');
});

Comme Laravel est explicite, vous pouvez déjà deviner à quoi sert ce code :

  • Route : on utilise le routeur ;
  • get : on regarde si la requête a la méthode « get » ;
  • '/' : on regarde si l'URL comporte uniquement le nom de domaine ;
  • dans la fonction anonyme, on retourne (return) une vue (view) à partir du fichier « welcome ».

Ce fichier « welcome » se trouve bien rangé dans le dossier des vues :

Image non disponible

C'est ce fichier comportant du code HTML qui génère le texte d'accueil que vous obtenez au démarrage initial de Laravel.

Laravel propose plusieurs helpers qui simplifient la syntaxe. Il y a par exemple view pour la classe View comme on l'a vu dans le code ci-dessus.

Visualisons le cycle de la requête :

Image non disponible

Sur votre serveur local vous n'avez pas de nom de domaine et vous allez utiliser une URL de la forme http://localhost/tuto/public en admettant que vous ayez créé Laravel dans un dossier www/tuto. Mais vous pouvez aussi créer un hôte virtuel pour avoir une situation plus réaliste comme déjà évoqué au précédent chapitre.

Laravel accepte les verbes suivants : get, post, put, patch, delete, options, any (on accepte tous les verbes).

IV-C. Plusieurs routes et paramètre de route

À l'installation Laravel a une seule route qui correspond à l'URL de base composée uniquement du nom de domaine. Voyons maintenant comment créer d'autres routes. Imaginons que nous ayons trois pages qui doivent être affichées avec ces URL :

  1. http://monsite.fr/1
  2. http://monsite.fr/2
  3. http://monsite.fr/3

J'ai fait apparaître en gras la partie spécifique de l'URL pour chaque page. Il est facile de réaliser cela avec ce code :

 
Sélectionnez
Route::get('1', function() { return 'Je suis la page 1 !'; });
Route::get('2', function() { return 'Je suis la page 2 !'; });
Route::get('3', function() { return 'Je suis la page 3 !'; });

Cette fois je n'ai pas créé de vue parce que ce qui nous intéresse est uniquement une mise en évidence du routage, je retourne donc directement la réponse au client. Visualisons cela pour la page 1 :

Image non disponible

On a besoin du caractère « / » uniquement dans la route de base.

On peut maintenant se poser une question : est-il vraiment indispensable de créer trois routes alors que la seule différence tient à peu de choses : une valeur qui change ?

On peut utiliser un paramètre pour une route qui accepte des éléments variables en utilisant des accolades. Regardez ce code :

 
Sélectionnez
Route::get('{n}', function($n) {
    return 'Je suis la page ' . $n . ' !'; 
});

Et une visualisation du fonctionnement :

Image non disponible

On dit que la route est paramétrée parce qu'elle possède un paramètre qui peut prendre n'importe quelle valeur.

On peut rendre un paramètre optionnel en lui ajoutant un point d'interrogation, mais il ne doit pas être suivi par un paramètre obligatoire. Dans ce cas pour éviter une erreur d'exécution il faut prévoir une valeur par défaut pour le paramètre, par exemple :

 
Sélectionnez
Route::get('{n?}', function($n = 1) {

Le paramère n est devenu optionnel et par défaut, sa valeur est 1.

IV-D. Erreur d'exécution et contrainte de route

Dans mon double exemple précédent lorsque je dis que le résultat est le même je mens un peu. Que se passe-t-il dans les deux cas pour cette URL ?

http://monsite.fr/4

Dans le cas des trois routes, vous tombez sur une erreur :

Image non disponible

Par contre dans la version avec le paramètre, vous obtenez une réponse valide ! Ce qui est logique parce qu'une route est trouvée. Le paramètre accepte n'importe quelle valeur et pas seulement des nombres. Par exemple avec cette URL :

http://monsite.fr/nimportequoi

Vous obtenez :

Je suis la page nimportequoi !

Ce qui vous l'avouerez n'est pas très heureux !

Pour éviter ce genre de désagrément, il faut contraindre le paramètre à n'accepter que certaines valeurs. On réalise cela à l'aide d'une expression régulière :

 
Sélectionnez
Route::get('{n}', function($n) { 
    return 'Je suis la page ' . $n . ' !'; 
})->where('n', '[1-3]');

Maintenant je peux affirmer que les comportements sont identiques ! Mais il nous faudra régler le problème des routes non prévues.

IV-E. Route nommée

Il est parfois utile de nommer une route, par exemple pour générer une URL ou pour effectuer une redirection. La syntaxe pour nommer une route est celle-ci :

 
Sélectionnez
Route::get('/', function() {
  return 'Je suis la page d\'accueil !';
})->name('home');

Par exemple pour générer l'URL qui correspond à cette route, on peut utiliser l'helper route :

 
Sélectionnez
route('home')

Ce qui va générer l'URL de base du site dans ce cas : http://monsite.

Un avantage à utiliser des routes nommées est qu'on peut réorganiser les URL d'un site sans avoir à modifier beaucoup de code.

Nous verrons des cas d'utilisation de routes nommées dans les prochains chapitres.

IV-F. Ordre des routes

Une chose importante à connaître est l'ordre des routes !

Lisez bien ceci pour vous éviter des heures de recherches et de prises de tête. La règle est :

Les routes sont analysée s dans leur ordre dans le fichier des routes.

Regardez ces deux routes :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
Route::get('{n}', function($n) { 
    return 'Je suis la page ' . $n . ' !'; 
});
 
Route::get('contact', function() {
    return "C'est moi le contact.";
});

Que pensez-vous qu'il va se passer avec http://monsite/contact ?

Je vous laisse deviner et tester et surtout bien retenir ce fonctionnement !

IV-G. Page inconnue (erreur 404)

Il arrive souvent qu'une URL ne corresponde à aucune des routes prévues. On a vu le cas ci-dessus avec l'affichage d'une erreur NotFoundHttpException. C'est parfait lorsqu'on en est au développement, mais pour la production ce n'est pas vraiment ce qu'on attend !

Regardez dans le dossier des ressources :

Image non disponible

On a le dossier des vues (views). On trouve en particulier la vue welcome dont on a déjà parlé ci-dessus (on verra plus tard pourquoi on ajoute le suffixe blade). On a aussi un dossier errors dans les vues. Il comporte seulement une vue 503. Vous pouvez deviner facilement que cette vue correspond à un affichage en cas d'erreur 503 (serveur indisponible).

Lorsqu'une erreur survient, Laravel regarde dans ce dossier s'il trouve un fichier avec comme nom le code de l'erreur, et si c'est le cas il affiche cette vue.

Copiez le fichier 503.blade.php et appelez la copie 404.blade.php. Dans la copie, changez la ligne :

 
Sélectionnez
<div class="title">Be right back.</div>

pour :

 
Sélectionnez
<div class="title">Cette page n'existe pas.</div>

Maintenant utilisez une URL qui ne possède pas de route. Cette fois vous obtenez cette page :

Image non disponible

Si vous ne savez pas trop à quoi correspondent les codes des erreurs HTTP, je vous conseille la lecture de cette page.

Vous commencez à aimer Laravel ? Alors on continue !

IV-H. En résumé

  • Laravel possède un fichier .htaccess pour simplifier l'écriture des URL.
  • Le système de routage est simple et explicite.
  • On peut prévoir des paramètres dans les routes.
  • On peut contraindre un paramètre à correspondre à une expression régulière.
  • On peut nommer une route pour faciliter la génération des URL et les redirections.
  • On peut prévoir une vue spécifique pour chaque erreur HTTP.

V. Les réponses

Nous avons vu précédemment comment la requête qui arrive est traitée par les routes. Voyons maintenant les réponses que nous pouvons renvoyer au client. Nous allons voir le système des vues de Laravel avec la possibilité de transmettre des paramètres. Nous verrons aussi comment créer des templates avec l'outil Blade.

V-A. Les réponses automatiques

Nous avons déjà construit des réponses lorsque nous avons vu le routage au chapitre précédent, mais nous n'avons rien fait de spécial pour cela, juste renvoyé une chaîne de caractères comme réponse. Par exemple si nous utilisons cette route :

 
Sélectionnez
Route::get('test', function () {
    return 'un test';
});

Nous interceptons l'URL http://monsite/test et nous renvoyons la chaîne de caractères « un test ». Mais évidemment Laravel en coulisse construit une véritable réponse HTTP. Voyons cela :

Image non disponible

On se rend compte qu'on a une requête complète avec ses headers, un contenu de type text/html, mais nous ne pouvons pas intervenir sur ces valeurs. Remarquez au passage qu'on a des cookies, on en reparlera lorsque nous verrons les sessions.

Le content-type indique le type MIME du document retourné, pour que le navigateur sache quoi faire du document en fonction de la nature de son contenu. Par exemple :

  • text/html : page Html classique ;
  • text/plain : simple texte sans mise en forme ;
  • application/pdf : fichier pdf ;
  • application/json : données au format JSON ;
  • application/octet-stream : téléchargement de fichier.

Pour une liste exhaustive c'est ici.

Maintenant, voyons ce que ça donne si on renvoie un tableau :

 
Sélectionnez
Route::get('test', function () {
    return ['un', 'deux', 'trois'];
});

Cette fois on reçoit une réponse JSON :

Image non disponible

Donc si vous voulez renvoyer du JSON, il suffit de retourner un tableau et Laravel s'occupe de tout.

V-B. Construire une réponse

Le fonctionnement automatique c'est bien, mais des fois on veut imposer des valeurs. Dans ce cas, il faut utiliser une classe de Laravel pour construire une réponse. Comme la plupart du temps on a un helper qui nous évite de déclarer la classe en question (en l'occurrence c'est une classe de Symfony : Symfony\Component\HttpFoundation\Response).

 
Sélectionnez
Route::get('test', function () {
    return response('un test', 206)->header('Content-Type', 'text/plain');
});

Cette fois j'impose un code (206 : envoi partiel) et un type MIME (text/plain) :

Image non disponible

Dans le protocole HTTP il existe des codes pour spécifier les réponses. Ces codes sont classés par grandes catégories. Voici les principaux :

  • 200 : requête exécutée avec succès ;
  • 403 : ressource interdite ;
  • 404 : la ressource demandée n'a pas été trouvée ;
  • 503 : serveur indisponible.

Pour une liste complète, c'est ici.

En fait vous aurez rarement la nécessité de préciser les headers parce que Laravel s'en charge très bien, mais vous voyez que c'est facile à faire.

On peut aussi ajouter un cookie avec la méthode cookie.

V-C. Les vues

Dans une application réelle, vous retournerez rarement la réponse directement à partir d'une route, vous passerez au moins par une vue. Dans sa version la plus simple, une vue est un simple fichier avec du code HTML :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
<!doctype html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Ma première vue</title>
</head>
<body>
    Je suis une vue !
</body>
</html>

Il faut enregistrer cette vue (j'ai choisi le nom « vue1 ») dans le dossier resources/views avec l'extension php :

Image non disponible

Même si vous ne mettez que du code HTML dans une vue, vous devez l'enregistrer avec l'extension php.

On peut appeler cette vue à partir d'une route avec ce code :

 
Sélectionnez
Route::get('/', function() {
    return view('vue1');
});

Pour que ça fonctionne, commentez ou supprimez la route de base de l'installation.

Je vous rappelle la belle sémantique de Laravel qui se lit comme de la prose : je retourne (return) une vue (view) à partir du fichier de vue « vue1 ».

Voici une illustration du processus :

Image non disponible

V-D. Vue paramétrée

En général, on a des informations à transmettre à une vue, voyons à présent comment mettre cela en place. Supposons que nous voulions répondre à ce type de requête :

http://monsite.fr/article/n

Le paramètre n pouvant prendre une valeur numérique . Voyons comment cette URL est constituée :

Image non disponible
  • la base de l'URL est constante pour le site, quelle que soit la requête ;
  • la partie fixe ici correspond aux articles ;
  • la partie variable correspond au numéro de l'article désiré (le paramètre).

V-D-1. Route

Il nous faut une route pour intercepter ces URL :

 
Sélectionnez
Route::get('article/{n}', function($n) { 
    return view('article')->with('numero', $n); 
})->where('n', '[0-9]+');

On transmet la variable à la vue avec la méthode with.

V-D-2. Vue

Il ne nous reste plus qu'à créer la vue article.php dans le dossier resources/views :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
<!doctype html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Les articles</title>
</head>
<body>
    <p>C'est l'article n° <?php echo $numero ?></p>
</body>
</html>

Pour récupérer le numéro de l'article, on utilise la variable $numero.

Voici une schématisation du fonctionnement :

Image non disponible

Il existe une méthode « magique » pour transmettre un paramètre, par exemple pour transmettre la variable numero comme je l'ai fait ci-dessus, on peut écrire le code ainsi :

 
Sélectionnez
return view('article')->withNumero($n);

Il suffit de concaténer le nom de la variable au mot clé with.

On peut aussi transmettre un tableau comme paramètre :

 
Sélectionnez
return view('article', ['numero' => $n]);

V-E. Blade

Laravel possède un moteur de template élégant nommé Blade qui nous permet de faire pas mal de choses. La première est de nous simplifier la syntaxe. Par exemple au lieu de la ligne suivante que nous avons prévue dans la vue précédente :

 
Sélectionnez
<p>C'est l'article n° <?php echo $numero ?></p>

On peut utiliser cette syntaxe avec Blade :

 
Sélectionnez
<p>C'est l'article n° {{ $numero }}</p>

Tout ce qui se trouve entre les doubles accolades est interprété comme du code PHP. Mais pour que ça fonctionne, il faut indiquer à Laravel qu'on veut utiliser Blade pour cette vue. Ça se fait simplement en modifiant le nom du fichier :

Image non disponible

Il suffit d'ajouter « blade » avant l'extension « php ». Vous pouvez tester l'exemple précédent avec ces modifications et vous verrez que tout fonctionne parfaitement avec une syntaxe épurée.

Il y a aussi la version avec la syntaxe {!! … !!}. La différence entre les deux versions est que le texte entre les doubles accolades est échappé ou purifié. C'est une mesure de sécurité parce qu'un utilisateur pourrait très bien mettre du code malicieux dans l'URL.

V-E-1. Un template

Une fonction fondamentale de Blade est de permettre de faire du templating, c'est-à-dire de factoriser du code de présentation. Poursuivons notre exemple en complétant notre application avec une autre route chargée d'intercepter des URL pour des factures. Voici la route :

 
Sélectionnez
Route::get('facture/{n}', function($n) { 
    return view('facture')->withNumero($n); 
})->where('n', '[0-9]+');

Et voici la vue :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
<!doctype html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Les factures</title>
</head>
<body>
    <p>C'est la facture n° {{ $numero }}</p>
</body>
</html>

On se rend compte que cette vue est pratiquement la même que celle pour les articles. Il serait intéressant de placer le code commun dans un fichier.

C'est justement le but d'un template d'effectuer cette opération.

Voici le template :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
<!doctype html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>@yield('titre')</title>
</head>
<body>
    @yield('contenu')
</body>
</html>

J'ai repris le code commun et prévu deux emplacements repérés par le mot clé @yield et nommés « titre » et « contenu ». Il suffit maintenant de modifier les deux vues. Voilà pour les articles :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
@extends('template')
 
@section('titre')
    Les articles
@endsection
 
@section('contenu')
    <p>C'est l'article n° {{ $numero }}</p>
@endsection

Et voilà pour les factures :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
@extends('template')
 
@section('titre')
    Les factures
@endsection
 
@section('contenu')
    <p>C'est la facture n° {{ $numero }}</p>
@endsection

Dans un premier temps on dit qu'on veut utiliser le template avec @extends et le nom du template « template ». Ensuite, on remplit les zones prévues dans le template grâce à la syntaxe @section en précisant le nom de l'emplacement et en fermant avec @endsection. Voici un schéma pour bien visualiser tout ça avec les articles :

Image non disponible

Au niveau du dossier des vues, on a donc les trois fichiers :

Image non disponible

Blade permet de faire bien d'autres choses, nous verrons cela dans les prochains chapitres.

Lorsqu'elles deviendront nombreuses, on organisera nos vues dans des dossiers. Vous pouvez d'ailleurs remarquer qu'il en existe déjà plusieurs dans l'installation de base.

V-F. Les redirections

Souvent il ne faut pas envoyer directement la réponse, mais rediriger sur une autre URL. Pour réaliser cela, on a l'helper redirect :

 
Sélectionnez
return redirect('facture');

Ici on redirige sur l'URL http://monsite/facture.

On peut aussi rediriger sur une route nommée. par exemple vous avez cette route :

 
Sélectionnez
Route::get('users/action', function() {
    return view('users.action');
})->name('action');

Cette route s'appelle action et elle correspond à l'URL http://monsite/users/action. On peut simplement rediriger sur cette route avec cette syntaxe :

 
Sélectionnez
return redirect()->route('action');

Si la route comporte un paramètre (ou plusieurs) on peut aussi lui assigner une valeur. Par exemple avec cette route :

 
Sélectionnez
Route::get('users/action/{type}', function($type) {
    return view('users.action');
})->name('action');

On peut rediriger ainsi en renseignant le paramètre :

 
Sélectionnez
return redirect()->route('action', ['type' => 'play']);

Des fois on veut tout simplement recharger la même page, par exemple lors de la soumission d'un formulaire avec des erreurs dans la validation des données, il suffit alors de faire :

 
Sélectionnez
return back();

On verra de nombreux exemples de redirections dans les prochains chapitres.

V-G. En résumé

  • Laravel construit automatiquement des réponses HTTP lorsqu'on retourne une chaîne de caractères ou un tableau.
  • Laravel offre la possibilité de créer des vues.
  • Il est possible de transmettre simplement des paramètres aux vues.
  • L'outil Blade permet de créer des templates et d'optimiser ainsi le code des vues.
  • On peut facilement effectuer des redirections avec transmission éventuelle de paramètres.‌

VI. Artisan et les contrôleurs

Nous avons vu le cycle d'une requête depuis son arrivée, son traitement par les routes et sa réponse avec des vues qui peuvent être boostées par Blade. Avec tous ces éléments, vous pourriez très bien réaliser un site web complet, mais Laravel offre encore bien des outils performants que je vais vous présenter.

Pour correctement organiser son code dans une application Laravel, il faut bien répartir les tâches. Dans les exemples vus jusqu'à présent, j'ai renvoyé une vue à partir d'une route, vous ne ferez jamais cela dans une application réelle (même si personne ne vous empêchera de le faire !). Les routes sont juste un système d'aiguillage pour trier les requêtes qui arrivent.

Mais alors qui s'occupe de la suite ?

Et bien ce sont les contrôleurs, le sujet de ce chapitre.

Nous allons aussi découvrir l'outil Artisan qui est la boîte à outils du développeur pour Laravel.

VI-A. Artisan

Lorsqu'on construit une application avec Laravel, on a de nombreuses tâches à accomplir, par exemple créer des classes, vérifier les routes…

C'est là qu'intervient Artisan, le compagnon indispensable. Il fonctionne en ligne de commande, donc à partir de la console. Il suffit de se positionner dans le dossier racine et d'utiliser la commande php artisan pour obtenir la liste de ses possibilités, en voici un extrait :

Image non disponible

Nous verrons peu à peu les principales commandes disponibles. Il y en a une pour connaître les routes disponibles. Voici ce que ça donne avec une nouvelle installation :

Image non disponible

On sait que la seule route au départ est celle-ci :

 
Sélectionnez
Route::get('/', function () {
    return view('welcome');
});

Elle correspond à la première ligne de la liste.

Mais à quoi correspond la seconde route ?

On a vu qu'il y a trois fichiers de routes, en particulier on a celui pour les API :

Image non disponible

Lui aussi comporte une route par défaut :

 
Sélectionnez
Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:api');

On ne va pas s'y intéresser dans ce cours. Vous pouvez commenter ou supprimer cette route pour éviter de polluer la liste.

Je parlerai des middlewares dans un prochain chapitre.

VI-B. Les contrôleurs

VI-B-1. Rôle

La tâche d'un contrôleur est de réceptionner une requête (qui a déjà été sélectionnée par une route) et de définir la réponse appropriée, rien de moins et rien de plus. Voici une illustration du processus :

Image non disponible

VI-B-2. Constitution

Pour créer un contrôleur, nous allons utiliser Artisan. Dans la console, entrez cette commande :

 
Sélectionnez
<samp>php artisan make:controller WelcomeController
</samp>

Si tout se passe bien vous allez trouver le contrôleur ici :

Image non disponible

Avec ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
 
use App\Http\Requests;
 
class WelcomeController extends Controller
{
    //
}

Ajoutez la méthode index :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
<?php 
...
class WelcomeController extends Controller
{
    public function index()
    {
        return view('welcome');
    }
}

Analysons un peu le code :

  • on trouve en premier l'espace de nom (App\Http\Controllers) ;
  • le contrôleur hérite de la classe Controller qui se trouve dans le même dossier et qui permet de factoriser des actions communes à tous les contrôleurs ;
  • on trouve enfin la méthode index qui renvoie quelque chose que maintenant vous connaissez : une vue, en l'occurrence « welcome » dont nous avons déjà parlé. Donc si j'appelle cette méthode je retourne la vue « welcome » au client.

VI-B-3. Liaison avec les routes

Maintenant la question qu'on peut se poser est : comment s'effectue la liaison entre les routes et les contrôleurs ?

Ouvrez le fichier des routes et entrez ce code :

 
Sélectionnez
Route::get('/', 'WelcomeController@index');

Maintenant avec l'URL de base vous devez retrouver la page d'accueil de Laravel :

Image non disponible

Voici une visualisation de la liaison entre la route et le contrôleur :

Image non disponible

On voit qu'au niveau de la route, il suffit de désigner le nom du contrôleur et le nom de la méthode séparés par @.

Si vous êtes attentif au code, vous avez sans doute remarqué qu'au niveau de la route on ne spécifie pas l'espace de noms du contrôleur, on peut légitimement se demander comment on le retrouve. Laravel nous simplifie la syntaxe en ajoutant automatiquement cet espace de nom.

VI-B-4. Route nommée

De la même manière que nous pouvons nommer une route classique, on peut aussi donner un nom à une route qui pointe une méthode de contrôleur :

 
Sélectionnez
Route::get('/', 'WelcomeController@index')->name('home');

Si on utilise Artisan pour lister les routes :

Image non disponible

On voit bien que l'action est faite par le contrôleur avec précision de la méthode à utiliser. On trouve aussi le nom de la route.

VI-C. Utilisation d'un contrôleur

Voyons maintenant un exemple pratique de mise en œuvre d'un contrôleur. On va conserver notre exemple avec les articles, mais maintenant traité avec un contrôleur. On conserve le même template et les mêmes vues :

Image non disponible

On va créer un contrôleur (entraînez-vous à utiliser Artisan) pour les articles :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
 
use App\Http\Requests;
 
class ArticleController extends Controller
{
    public function show($n)
    {
        return view('article')->with('numero', $n);
    }
}
Image non disponible

Dans ce contrôleur, on a une méthode show chargée de générer la vue. Il ne nous reste plus qu'à créer la route :

 
Sélectionnez
Route::get('article/{n}', 'ArticleController@show')->where('n', '[0-9]+');

Voici une illustration du fonctionnement avec ce contrôleur :

Image non disponible

Notez qu'on pourrait utiliser la méthode « magique » pour la transmission du paramètre à la vue :

 
Sélectionnez
return view('article')->withNumero($n);

VI-D. En résumé

  • Les contrôleurs servent à réceptionner les requêtes triées par les routes et à fournir une réponse au client.
  • Artisan permet de créer facilement un contrôleur.
  • Il est facile d'appeler une méthode de contrôleur à partir d'une route.
  • On peut nommer une route qui pointe vers une méthode de contrôleur.‌

VII. Formulaires et middlewares

Dans bien des circonstances, le client envoie des informations au serveur. La situation la plus générale est celle d'un formulaire. Nous allons voir dans ce chapitre comment créer facilement un formulaire avec Laravel, comment réceptionner les entrées et nous améliorerons notre compréhension du routage.

Nous verrons aussi l'importante notion de middleware.

VII-A. Scénario et routes

Nous allons envisager un petit scénario avec une demande de formulaire de la part du client, sa soumission et son traitement :

Image non disponible

On va donc avoir besoin de deux routes :

  • une pour la demande du formulaire avec une méthode get ;
  • une pour la soumission du formulaire avec une méthode post.

On va donc créer ces deux routes dans le fichier routes/web.php :

 
Sélectionnez
Route::get('users', 'UsersController@create');
Route::post('users', 'UsersController@store');

Jusque-là on avait vu seulement des routes avec le verbe get, on a maintenant aussi une route avec le verbe post.

Les URL correspondantes sont donc :

  • http://monsite.fr/users avec la méthode get ;
  • http://monsite.fr/users avec la méthode post.

Donc on a la même URL, seul le verbe diffère. Voici le scénario schématisé avec les URL :

Image non disponible

VII-B. Les middlewares

Les middlewares sont chargés de filtrer les requêtes HTTP qui arrivent dans l'application, ainsi que celles qui en partent (beaucoup moins utilisé). Le cas le plus classique est celui qui concerne la vérification de l'authentification d'un utilisateur pour qu'il puisse accéder à certaines ressources. On peut aussi utiliser un middleware par exemple pour démarrer la gestion des sessions.

Voici un schéma pour illustrer cela :

Image non disponible

On peut avoir en fait plusieurs middlewares en pelures d'oignon, chacun effectue son traitement et transmet la requête ou la réponse au suivant.

Donc dès qu'il y a un traitement à faire à l'arrivée des requêtes (ou à leur départ) un middleware est tout indiqué.

Laravel peut servir comme application « web » ou comme « api ». Dans le premier cas, on a besoin :

  • de gérer les cookies ;
  • de gérer une session ;
  • de gérer la protection CSRF (dont je parle plus loin dans ce chapitre).

Si vous regardez dans le fichier app/Http/Kernel.php :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
<?php
/**
 * The application's route middleware groups.
 *
 * @var array
 */
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
 
    'api' => [
        'throttle:60,1',
        'bindings',
    ],
];

On trouve les deux middlewares de groupes (ils rassemblent plusieurs middlewares) « web » et « api ». On voit que dans le premier cas on active bien les cookies, les sessions et la vérification CSRF.

Par défaut toutes les routes que vous entrez dans le fichier routes/web.php sont incluses dans le groupe « web ». Si vous regardez dans le provider app/Providers/RouteServiceProvider.php vous trouvez cette inclusion :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
<?php
/**
 * Define the "web" routes for the application.
 *
 * These routes all receive session state, CSRF protection, etc.
 *
 * @return void
 */
protected function mapWebRoutes()
{
    Route::group([
        'middleware' => 'web',
        'namespace' => $this->namespace,
    ], function ($router) {
        require base_path('routes/web.php');
    });
}

VII-C. Le formulaire

Pour faire les choses correctement, nous allons prévoir un template resources/views/template.blade.php :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
<!doctype html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
</head>
<body>
    @yield('contenu')
</body>
</html>

Et une vue resources/views/infos.blade.php qui utilise ce template :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
@extends('template')

@section('contenu')
    {!! Form::open(['url' => 'users']) !!}
        {!! Form::label('nom', 'Entrez votre nom : ') !!}
        {!! Form::text('nom') !!}
        {!! Form::submit('Envoyer !') !!}
    {!! Form::close() !!}
@endsection

Cette vue utilise le composant laravelcollective\html dont je vous ai déjà parlé dans le chapitre sur l'installation de Laravel et dont la documentation se trouve ici. Je pars donc du principe que vous l'avez installé. Si ce n'est pas le cas, reportez-vous à ce chapitre pour le faire sinon ce code ne fonctionnera pas.

Nous avons déjà vu comment s'organise une vue avec un template, par contre la création du formulaire mérite quelques commentaires. Pour créer un formulaire avec le composant laravelcollective/html il faut commencer par l'ouvrir :

 
Sélectionnez
{!! Form::open(['url' => 'users']) !!}

La sémantique est simple : on veut pour un formulaire (Form), ouvrir (open) celui-ci, et qu'il pointe vers l'URL « users ».

Ensuite on veut une étiquette (label) :

 
Sélectionnez
{!! Form::label('nom', 'Entrez votre nom : ') !!}

On veut un contrôle de type « text » qui se nomme « nom » :

 
Sélectionnez
{!! Form::text('nom') !!}

On veut enfin un bouton de soumission (submit) avec le texte « Envoyer ! » :

 
Sélectionnez
{!! Form::submit('Envoyer !') !!}

Et finalement on veut clore (close) le formulaire :

 
Sélectionnez
{!! Form::close() !!}

Le code généré pour le formulaire sera alors le suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
<form method="POST" action="<a href="<a href="http://monsite.fr/users">http://monsite.fr/users</a>"><a href="http://monsite.fr/users">http://monsite.fr/users</a></a>" accept-charset="UTF-8">
    <input name="_token" type="hidden" value="pV1vWWdUqFDfYsBjKag43C3NvzbIC0lHtMnv9BpI">    
    <label for="nom">Entrez votre nom : </label>    
    <input name="nom" type="text" id="nom">    
    <input type="submit" value="Envoyer !">   
</form>

Quelques remarques sur cette génération :

  • la méthode par défaut est post, on n'a pas eu besoin de le préciser ;
  • l'action est bien générée ;
  • il y a un contrôle caché (_token) destiné à la protection CSRF dont je parlerai plus loin ;
  • l'étiquette est bien créée avec son attribut for ;
  • le contrôle de texte est du bon type avec le nom correct, il est en plus généré un id pour qu'il fonctionne avec son étiquette ;
  • le bouton de soumission a été généré avec son texte.

Le résultat sera un formulaire sans fioriture :

Image non disponible

Vous n'êtes pas obligé d'utiliser le composant laravelcollective/html pour créer des formulaires, mais je vous y encourage parce qu'il simplifie le codage et je l'utilise dans de ce cours. Par exemple pour créer le formulaire sans l'utiliser il nous faudrait écrire ceci :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
@extends('template')
 
@section('contenu')
    <form method="POST" action="{!! url('users') !!}" accept-charset="UTF-8">
        {!! csrf_field() !!}
        <label for="nom">Entrez votre nom : </label>
        <input name="nom" type="text" id="nom">
        <input type="submit" value="Envoyer !">
    </form>
@endsection

C'est quand même plus simple et lisible avec le composant !

VII-D. Le contrôleur

Il ne nous manque plus que le contrôleur pour faire fonctionner tout ça. Utilisez Artisan pour générer un contrôleur :

 
Sélectionnez
php artisan make:controller UsersController

Vous devez le retrouver ici :

Image non disponible

Modifiez ensuite son code ainsi :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
 
class UsersController extends Controller
{
    public function create()
    {
        return view('infos');
    }
 
    public function store(Request $request)
    {
        return 'Le nom est ' . $request->input('nom'); 
    }
}

Le contrôleur possède deux méthodes :

  • la méthode create qui reçoit l'URL http://monsite.fr/users avec le verbe get et qui retourne le formulaire ;
  • la méthode store qui reçoit l'URL http://monsite.fr/users avec le verbe post et qui traite les entrées.

Pour la première méthode, il n'y a rien de nouveau et je vous renvoie aux chapitres précédents si quelque chose ne vous paraît pas clair. Par contre nous allons nous intéresser à la seconde méthode.

Dans cette seconde méthode, on veut récupérer l'entrée du client. Encore une fois la syntaxe est limpide : on veut dans la requête (request) les entrées (input) récupérer celle qui s'appelle nom.

Si vous faites fonctionner tout ça, vous devez finalement obtenir l'affichage du nom saisi. Voici une schématisation du fonctionnement qui exclut les routes pour simplifier :

Image non disponible

(1) le client envoie la requête de demande du formulaire qui est transmise au contrôleur par la route (non représentée sur le schéma) ;
(2) le contrôleur crée la vue « infos » ;
(3) la vue « infos » crée le formulaire ;
(4) le formulaire est envoyé au client ;
(5) le client soumet le formulaire, le contrôleur reçoit la requête de soumission par l'intermédiaire de la route (non représentée sur le schéma) ;
(6) le contrôleur génère la réponse ;
(7) la réponse est envoyée au client.

VII-E. La protection CSRF

On a vu que le formulaire généré par Laravel comporte un contrôle caché avec une valeur particulière :

 
Sélectionnez
<input name="_token" type="hidden" value="pV1vWWdUqFDfYsBjKag43C3NvzbIC0lHtMnv9BpI">

À quoi cela sert-il ?

Tout d'abord CSRF signifie Cross-Site Request Forgery. C'est une attaque qui consiste à faire envoyer par un client une requête à son insu. Cette attaque est relativement simple à mettre en place et consiste à envoyer à un client authentifié sur un site un script dissimulé (dans une page web ou un email) pour lui faire accomplir une action à son insu.

Pour se prémunir contre ce genre d'attaque Laravel génère une valeur aléatoire (token) associée au formulaire de telle sorte qu'à la soumission cette valeur est vérifiée pour être sûr de l'origine.

Vous vous demandez peut-être où se trouve ce middleware CSRF ?

Il est bien rangé dans le dossier app/Http/Middleware :

Image non disponible

Pour tester l'efficacité de cette vérification essayez un envoi de formulaire sans le token en modifiant ainsi la vue (adaptez la valeur de l'action selon votre contexte) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
@extends('template')
 
@section('contenu')
    <form method="POST" action="{!! url('users') !!}" accept-charset="UTF-8">
        <label for="nom">Entrez votre nom : </label>
        <input name="nom" type="text" id="nom">
        <input type="submit" value="Envoyer !">
    </form>
@endsection

Vous tomberez sur cette erreur (avec un code 500) à la soumission :

Image non disponible

C'est bien pratique que le composant laravelcollective/html le place automatiquement dans les formulaires, du coup on n'a même pas besoin d'y penser. Ça ne sera évidemment plus du tout le cas en cas de soumission avec Ajax par exemple, nous verrons cela plus tard.

VII-F. En résumé

  • Laravel permet de créer des routes avec différents verbes : get, post…
  • Un middleware permet de filtrer les requêtes.
  • Un formulaire peut facilement être créé avec le composant laravelcollective/html.
  • Les entrées du client sont récupérées dans la requête.
  • On peut se prémunir contre les attaques CSRF, cette défense est mise en place automatiquement par Laravel.

VIII. La validation

Nous avons vu dans le chapitre précédent un scénario mettant en œuvre un formulaire. Nous n'avons imposé aucune contrainte sur les valeurs transmises. Dans une application réelle, il est toujours nécessaire de vérifier que ces valeurs correspondent à ce qu'on attend. Par exemple un nom doit comporter uniquement des caractères alphabétiques et avoir une longueur maximale, une adresse email doit correspondre à un certain format.

Il faut donc mettre en place des règles de validation. En général on procède à une première validation côté client pour éviter de faire des allers-retours avec le serveur. Mais quelle que soit la pertinence de cette validation côté client elle n'exonère pas d'une validation côté serveur.

On ne doit jamais faire confiance à des données qui arrivent sur le serveur !

Dans l'exemple de ce chapitre, je ne prévoirai pas de validation côté client, d'une part ce n'est pas mon propos, d'autre part elle masquerait la validation côté serveur pour les tests.

VIII-A. Scénario et routes

Voici le scénario que je vous propose pour ce chapitre :

Image non disponible
  1. Le client demande le formulaire de contact.
  2. Le contrôleur génère le formulaire.
  3. Le contrôleur envoie le formulaire.
  4. Le client remplit le formulaire et le soumet.
  5. Le contrôleur teste la validité des informations et là on a deux possibilités :

    1. En cas d'échec on renvoie le formulaire au client en l'informant des erreurs et en conservant ses entrées correctes,
    2. En cas de réussite on envoie un message de confirmation au client .

VIII-A-1. Routes

On va donc avoir besoin de deux routes :

 
Sélectionnez
Route::get('contact', 'ContactController@create');
Route::post('contact', 'ContactController@store');

On aura une seule URL (avec verbe « get » pour demander le formulaire et verbe « post » pour le soumettre) :

http://monsite.fr/contact

VIII-B. Les vues

VIII-B-1. Le template

Pour ce chapitre, je vais créer un template réaliste avec l'utilisation de Bootstrap pour alléger le code. Voici le code de ce template (resources/views/template.blade.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<!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>
        {!! Html::style('https://netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css') !!}
        <style> textarea { resize: none; } </style>
    </head>
    <body>
        @yield('contenu')
    </body>
</html>

Je rappelle que dans Blade, il y a deux syntaxes : la double accolade permet de sécuriser le code en échappant les caractères spéciaux alors que la syntaxe {!! !!} n'effectue aucun traitement et doit donc être utilisée avec prudence.

Pour la génération des liens vers la bibliothèque CSS, j'ai utilisé la classe Html avec sa méthode style. Il y a un certain nombre de méthodes pratiques dans cette classe que nous découvrirons petit à petit.

J'ai prévu l'emplacement @yield nommé « contenu » pour recevoir les pages du site, pour notre exemple, on aura seulement la page de contact et celle de la confirmation.

VIII-B-2. La vue de contact

La vue de contact va contenir essentiellement un formulaire (resources/views/contact.blade.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
@extends('template')
 
@section('contenu')
    <br>
    <div class="col-sm-offset-3 col-sm-6">
        <div class="panel panel-info">
            <div class="panel-heading">Contactez-moi</div>
            <div class="panel-body"> 
                {!! Form::open(['url' => 'contact']) !!}
                    <div class="form-group {!! $errors->has('nom') ? 'has-error' : '' !!}">
                        {!! Form::text('nom', null, ['class' => 'form-control', 'placeholder' => 'Votre nom']) !!}
                        {!! $errors->first('nom', '<small class="help-block">:message</small>') !!}
                    </div>
                    <div class="form-group {!! $errors->has('email') ? 'has-error' : '' !!}">
                        {!! Form::email('email', null, ['class' => 'form-control', 'placeholder' => 'Votre email']) !!}
                        {!! $errors->first('email', '<small class="help-block">:message</small>') !!}
                    </div>
                    <div class="form-group {!! $errors->has('texte') ? 'has-error' : '' !!}">
                        {!! Form::textarea ('texte', null, ['class' => 'form-control', 'placeholder' => 'Votre message']) !!}
                        {!! $errors->first('texte', '<small class="help-block">:message</small>') !!}
                    </div>
                    {!! Form::submit('Envoyer !', ['class' => 'btn btn-info pull-right']) !!}
                {!! Form::close() !!}
            </div>
        </div>
    </div>
@endsection

Cette vue étend le template vu ci-dessus et renseigne la section « contenu ». Je ne commente pas la mise en forme spécifique à Bootstrap. Le formulaire est généré avec la classe Form que nous avons déjà vue au chapitre précédent.

La structure des méthodes pour générer les contrôles du formulaire est toujours la même. Prenons par exemple l'email :

 
Sélectionnez
{!! Form::email('email', null, ['class' => 'form-control', 'placeholder' => 'Votre email']) !!}

On veut un élément de formulaire (Form) de type « email », avec le nom « email », avec une valeur nulle et avec les attributs « class » et « placeholder » en précisant leur valeur.

Le bouton de soumission ne comporte évidemment pas de valeur et on a donc un paramètre de moins pour lui :

 
Sélectionnez
{!! Form::submit('Envoyer !', ['class' => 'btn btn-info pull-right']) !!}

En cas de réception du formulaire suite à des erreurs, on reçoit une variable $errors qui contient un tableau avec comme clés les noms des contrôles et comme valeurs les textes identifiant les erreurs.

La variable $errors est générée systématiquement pour toutes les vues.

C'est pour cela que je teste la présence d'une erreur pour chaque contrôle en ajustant le style et en affichant le texte de l'erreur si nécessaire avec la méthode first :

 
Sélectionnez
{!! $errors->first('nom', '<small class="help-block">:message</small>') !!}

S'il n'y a aucune erreur rien n'est renvoyé et donc rien n'est affiché, sinon on récupère la première (first) et on respecte le format imposé.

Au départ le formulaire se présente ainsi :

Image non disponible

Après une soumission et renvoi avec des erreurs il pourra se présenter ainsi :

Image non disponible

VIII-B-3. Les messages en français

Par défaut les messages sont en anglais. Pour avoir ces textes en français, vous devez récupérer les fichiers ici. Placez le dossier « fr » et son contenu dans le dossier resources/lang :

Image non disponible

Ensuite, changez cette ligne dans le fichier config/app.php :

 
Sélectionnez
'locale' => 'fr',

Vous devriez avoir votre Laravel en français :

Image non disponible

VIII-B-4. La vue de confirmation

Pour la vue de confirmation (resources/views/confirm.blade.php) le code est plus simple et on utilise évidemment le même template  :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
@extends('template')
 
@section('contenu')
    <br>
    <div class="col-sm-offset-3 col-sm-6">
        <div class="panel panel-info">
            <div class="panel-heading">Contactez-moi</div>
            <div class="panel-body"> 
                Merci. Votre message a été transmis à l'administrateur du site. Vous recevrez une réponse rapidement.
            </div>
        </div>
    </div>
@endsection

Ce qui donne cette apparence :

Image non disponible

VIII-C. La requête de formulaire

Il y a plusieurs façons d'effectuer la validation avec Laravel, mais la plus simple et élégante consiste à utiliser une requête de formulaire (Form request).

Nous avons déjà utilisé Artisan qui permet d'effectuer de nombreuses opérations et nous allons encore avoir besoin de lui pour créer une requête de formulaire :

 
Sélectionnez
php artisan make:request ContactRequest

Comme par défaut le dossier n'existe pas, il est créé en même temps que la classe :

Image non disponible

Voyons le code généré :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
<?php
 
namespace App\Http\Requests;
 
use Illuminate\Foundation\Http\FormRequest;
 
class ContactRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return false;
    }
 
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}

La classe générée comporte deux méthodes :

  • authorize : pour effectuer un contrôle de sécurité éventuel sur l'identité ou les droits de l'émetteur ;
  • rules : pour les règles de validation.

On va arranger le code pour notre cas :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
<?php
 
namespace App\Http\Requests;
 
use Illuminate\Foundation\Http\FormRequest;
 
class ContactRequest 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()
    {
        return [
            'nom' => 'bail|required|between:5,20|alpha',
            'email' => 'bail|required|email',
            'texte' => 'bail|required|max:250'
        ];
    }
}

Au niveau de la méthode rules, on retourne un tableau qui contient des clés qui correspondent aux champs du formulaire. Vous retrouvez le nom, l'email et le texte. Les valeurs contiennent les règles de validation. Comme il y en a chaque fois plusieurs, elles sont séparées par le signe « | ». Voyons les différentes règles prévues :

  • bail : on arrête de vérifier dès qu'une règle n'est pas respectée ;
  • required : une valeur est requise, donc le champ ne doit pas être vide ;
  • between : nombre de caractères entre une valeur minimale et une valeur maximale ;
  • alpha : on n'accepte que les caractères alphabétiques ;
  • email : la valeur doit être une adresse email valide.

Au niveau de la méthode authorize je me suis contenté de renvoyer true parce que nous ne ferons pas de contrôle supplémentaire à ce niveau.

Vous pouvez trouver toutes les règles disponibles dans la documentation. Vous verrez que la liste est longue !

VIII-D. Le contrôleur

On va encore utiliser Artisan pour générer le contrôleur :

 
Sélectionnez
php artisan make:controller ContactController

Modifiez le code par défaut pour en arriver à celui-ci :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<?php 
 
namespace App\Http\Controllers;
 
use App\Http\Requests\ContactRequest;
 
class ContactController extends Controller
{
    public function create()
    {
        return view('contact');
    }
 
    public function store(ContactRequest $request)
    {
        return view('confirm');
    }
}

La méthode create ne présente aucune nouveauté par rapport à ce qu'on a vu au chapitre précédent. On se contente de renvoyer la vue contact qui comporte le formulaire.

La méthode store nécessite quelques commentaires. Vous remarquez le paramètre de type ContactRequest. On injecte dans la méthode une instance de la classe ContactRequest que l'on a précédemment créée. Laravel permet ce genre d'injection de dépendances au niveau d'une méthode. Je reviendrai en détail dans un prochain chapitre sur cette possibilité.

Si la validation échoue parce qu'une règle n'est pas respectée c'est la classe ContactRequest qui s'occupe de tout, elle renvoie le formulaire en complétant les contrôles qui étaient corrects et crée une variable $errors pour transmettre les messages d'erreurs qu'on utilise dans la vue. Vous n'avez rien d'autre à faire !

Vérifiez avec la commande php artisan route:list que tout est correct :

Image non disponible

On a bien nos deux routes avec l'URL correcte, le bon contrôleur avec les méthodes prévues et le middleware web appliqué aux deux routes.

Faites quelques essais avec des erreurs de saisie pour voir le fonctionnement.

VIII-E. D'autres façons d'effectuer la validation

Si vous n'appréciez pas les requêtes de formulaire et leur côté « magique » vous pouvez effectuer la validation directement dans le contrôleur avec la méthode validate. Voici le contrôleur modifié en conséquence :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
<?php 
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
 
class ContactController extends Controller
{
    public function create()
    {
        return view('contact');
    }
 
    public function store(Request $request)
    {
        $this->validate($request, [
            'nom' => 'bail|required|between:5,20|alpha',
            'email' => 'bail|required|email',
            'texte' => 'bail|required|max:250'
        ]);
 
        return view('confirm');
    }
}

Cette fois on injecte dans la méthode store directement la requête (Illuminate\Http\Request). Le fonctionnement est exactement le même.

Si cette méthode validate est encore trop abstraite à votre goût vous pouvez détailler les opérations :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
<?php 
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Validator;
 
class ContactController extends Controller
{
    public function create()
    {
        return view('contact');
    }
 
    public function store(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'nom' => 'bail|required|between:5,20|alpha',
            'email' => 'bail|required|email',
            'texte' => 'bail|required|max:250'
        ]);
 
        if ($validator->fails()) {
            return back()->withErrors($validator)->withInput();
        }
 
        return view('confirm');
    }
}

On utilise la façade Validator en précisant toutes les entrées ($request->all()) et les règles de validation. Ensuite si la validation échoue (fails), on renvoie le formulaire (back) avec les erreurs (withErrors) et les valeurs entrées (withInput) pour pouvoir les afficher dans le formulaire.

Mais pourquoi se compliquer la vie quand on dispose de fonctionnalités plus simples et élégantes ?

VIII-F. En résumé

  • La validation est une étape essentielle de vérification des entrées du client.
  • On dispose de nombreuses règles de validation.
  • Le validateur génère des erreurs explicites à afficher au client.
  • Pour avoir les textes des erreurs en français, il faut aller chercher les traductions et les placer dans le bon dossier.
  • Les requêtes de formulaires (Form request) permettent d'effectuer la validation de façon simple et élégante.
  • Il y a plusieurs façons d'effectuer la validation à adapter selon les goûts‌ et les circonstances.

IX. Envoyer un email

Laravel utilise le célèbre composant SwiftMailer pour l'envoi des emails. Mais il en simplifie grandement l'utilisation. Dans ce chapitre nous allons prolonger l'exemple précédent de la prise de contact en ajoutant l'envoi d'un email à l'administrateur du site lorsque quelqu'un envoie une demande de contact.

On va donc prendre le code tel qu'on l'a laissé lors du précédent chapitre et le compléter en conséquence.

On verra plus tard que Laravel propose aussi un système complet de notification qui permet entre autres l'envoi d'emails.

IX-A. Le scénario

Le scénario est donc le même que pour le précédent chapitre avec l'ajout d'une action :

Image non disponible

On va avoir les mêmes routes et vues, c'est uniquement au niveau du contrôleur que le code va évoluer pour intégrer cette action complémentaire.

IX-B. Configuration

Si vous regardez dans le fichier .env vous trouvez une section qui concerne les emails :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

Les valeurs correspondent au prestataire utilisé.

Au niveau du driver le plus classique est certainement le SMTP, mais vous pouvez aussi utiliser mail, mailgun (gratuit jusqu'à 10 000 envois par mois), ses, sparkpost

Vous devez correctement renseigner les paramètres pour que ça fonctionne selon votre contexte (en local avec le SMTP de votre prestataire, en production avec la fonction mail de PHP ou d'un autre système…).

IX-C. La classe Mailable

Avec Laravel, pour envoyer un email, il faut passer par la création d'une classe « mailable ». Encore une fois c'est Artisan qui va nous permettre de créer notre classe :

 
Sélectionnez
<samp>php artisan make:mail Contact </samp>

Comme le dossier n'existe pas, il est créé en même temps que le fichier de la classe :

Image non disponible

Par défaut on a ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
<?php
 
namespace App\Mail;
 
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
 
class Contact 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');
    }
}

Tout se passe dans la méthode build. On voit qu'on retourne une vue, celle-ci doit comporter le code pour le contenu de l'email.

On commence par changer le nom de la vue :

 
Sélectionnez
return $this->view('emails.contact');

On va créer cette vue (resources/views/emails/contact.blade.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<!DOCTYPE html>
<html lang="fr">
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <h2>Prise de contact sur mon beau site</h2>
    <p>Réception d'une prise de contact avec les éléments suivants :</p>
    <ul>
      <li><strong>Nom</strong> : {{ $contact['nom'] }}</li>
      <li><strong>Email</strong> : {{ $contact['email'] }}</li>
      <li><strong>Message</strong> : {{ $contact['texte'] }}</li>
    </ul>
  </body>
</html>

Pour que ça fonctionne, on doit transmettre à cette vue les entrées de l'utilisateur. Il faut donc passer les informations à la classe Contact à partir du contrôleur.

IX-D. Transmission des informations à la vue

Il y a deux façons de procéder pour transmettre les informations, nous allons utiliser la plus simple. Il suffit de créer une propriété obligatoirement publique dans la classe « mailable » et celle-ci sera automatiquement transmise à la vue. Voici le nouveau code de notre classe :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
<?php
 
namespace App\Mail;
 
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
 
class Contact extends Mailable
{
    use Queueable, SerializesModels;
 
    /**
     * Elements de contact
     * @var array
     */
    public $contact;
 
    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(Array $contact)
    {
        $this->contact = $contact;
    }
 
    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->from('monsite@chezmoi.com')
            ->view('emails.contact');
    }
}

J'en ai aussi profité pour préciser l'adresse de l'expéditeur avec la méthode from. On pourrait attacher un document avec attach, faire une copie avec cc

J'ai créé la propriété publique $contact qui sera renseignée par l'intermédiaire du constructeur.

Il ne reste plus qu'à modifier le contrôleur pour envoyer cet email avec les données nécessaires :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
<?php 
 
namespace App\Http\Controllers;
 
use App\Http\Requests\ContactRequest;
use Illuminate\Support\Facades\Mail;
use App\Mail\Contact;
 
class ContactController extends Controller
{
    public function create()
    {
        return view('contact');
    }
 
    public function store(ContactRequest $request)
    {
        Mail::to('administrateur@chezmoi.com')
            ->send(new Contact($request->except('_token')));
 
        return view('confirm');
    }
}

Tout se passe sur cette ligne :

 
Sélectionnez
Mail::to('administrateur@chezmoi.com')
    ->send(new Contact($request->except('_token')));

On a :

  • l'adresse de l'administrateur (to) ;
  • l'envoi avec la méthode send ;
  • la création d'une instance de la classe Contact avec la transmission des données saisies (mis à part le token pour la protection CSRF qui ne sert pas).

Et si tout se passe bien le message doit arriver jusqu'à l'administrateur :

Image non disponible

IX-E. Envoyer des emails en phase développement

IX-E-1. Le mode Log

Lorsqu'on est en phase de développement, il n'est pas forcément pratique ou judicieux d'envoyer réellement des emails. Une solution simple consiste à passer en mode Log en renseignant le driver dans le fichier .env :

 
Sélectionnez
MAIL_DRIVER=log

Les emails ne seront plus envoyés, mais le code en sera mémorisé dans le fichier des logs :

Image non disponible

Vous allez y trouver par exemple ce contenu :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
[2016-08-30 15:23:20] local.DEBUG: Message-ID: <dd3c75738d7f1b1aa303228bd83b066c@laravel5.dev>
Date: Tue, 30 Aug 2016 15:23:20 +0000
Subject: Contact
From: monsite@chezmoi.com
To: administrateur@chezmoi.com
MIME-Version: 1.0
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable
 
<!DOCTYPE html>
<html lang="fr">
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <h2>Prise de contact sur mon beau site</h2>
    <p>Réception d'une prise de contact avec les éléments suivants :</p>
    <ul>
      <li><strong>Nom</strong> : Durand</li>
      <li><strong>Email</strong> : durand@chezlui.com</li>
      <li><strong>Message</strong> : Je voulais vous dire que votre site est vraiment magnifique !</li>
    </ul>
  </body>
</html>

Ce qui vous permet de vérifier que tout se passe correctement (hormis l'envoi).

IX-E-2. MailTrap

Une autre possibilité très utilisée est MailTrap qui a une option gratuite (une boîte, avec 50 messages au maximum et 2 messages par seconde). Vous avez un tableau de bord et une boîte de messages :

Image non disponible

J'ai fait apparaître les configurations pour Laravel. Il est ainsi facile de renseigner le fichier .env :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=e25fc474aaedf0
MAIL_PASSWORD=fb56a6d137f192
MAIL_ENCRYPTION=null

Avec cette configuration lorsque j'envoie un email je le vois arriver :

Image non disponible

Vous pouvez analyser l'email avec précision, je vous laisse découvrir toutes les options.

IX-F. En résumé

  • Laravel permet l'envoi simple d'email.
  • Il faut configurer correctement les paramètres pour l'envoi des emails.
  • Pour chaque email il faut créer une classe « mailable ».
  • On peut passer des informations à l'email en créant une propriété publique dans la classe « mailable ».
  • On peut passer en mode Log en phase de développement ou alors utiliser MailTrap.

X. Configuration, session et gestion de fichiers

Dans ce chapitre nous verrons la configuration, la gestion des sessions et des fichiers avec un exemple simple d'envoi et d'enregistrement de fichiers images dans un dossier à partir d'un formulaire.

X-A. La configuration

Tout ce qui concerne la configuration de Laravel se trouve dans le dossier config :

Image non disponible

De nombreuses valeurs de configuration sont définies dans le fichier .env.

Les fichiers de configuration contiennent en fait juste un tableau avec des clés et des valeurs. Par exemple pour les vues (view.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
<?php
return [
 
    'paths' => [
        realpath(base_path('resources/views')),
    ],
     
    ...
 
];

On a la clé paths et la valeur : un tableau avec realpath(base_path('resources/views')). Pour récupérer une valeur, il suffit d'utiliser sa clé avec l'helper config :

 
Sélectionnez
config('view.paths');

On utilise le nom du fichier (view) et le nom de la clé (paths) séparés par un point.

On peut aussi changer une valeur :

 
Sélectionnez
Config::set('view.paths', [base_path().'/mes_vues']);

Si je fais effectivement cela, mes vues au lieu d'être cherchées dans le dossier resources/views seront cherchées dans le dossier mes_vues.

Lorsqu'on change ainsi une valeur de configuration, ce n'est valable que pour la requête en cours.

Vous pouvez évidemment créer vos propres fichiers de configuration. Pour l'exemple de ce chapitre, on va avoir besoin justement d'utiliser une configuration. Comme notre application doit enregistrer des fichiers d'images dans un dossier, il faut définir l'emplacement et le nom de ce dossier de destination. On va donc créer un fichier images.php :

Image non disponible

Dans ce fichier, on va définir le nom du dossier :

 
Sélectionnez
return ['path' => 'uploads'];

En production pour gagner en performances, il est conseillé de mettre toute la configuration en cache dans un seul fichier avec la commande php artisan config:cache.

X-B. Les sessions

Étant donné que les requêtes HTTP sont fugitives et ne laissent aucune trace, il est important de disposer d'un système qui permet de mémoriser des informations entre deux requêtes. C'est justement l'objet des sessions.

La configuration des sessions se trouve dans le fichier de configuration session.php. On trouve dans le fichier .env la définition du driver :

 
Sélectionnez
SESSION_DRIVER=file

Par défaut c'est un fichier (dans storage/framework/sessions) qui mémorise les informations des sessions, mais on peut aussi utiliser : les cookies, la base de données, apc, memcached, redis

Quel que soit le driver utilisé l'helper session de Laravel permet une gestion simplifiée des sessions. Vous pouvez ainsi créer une variable de session :

 
Sélectionnez
session(['clé' => 'valeur']);

Vous pouvez aussi récupérer une valeur à partir de sa clé :

 
Sélectionnez
$valeur = session('clef');

Vous pouvez prévoir une valeur par défaut :

 
Sélectionnez
$valeur = session('clef', 'valeur par défaut');

Il est souvent utile (ça sera le cas pour notre exemple) de savoir si une certaine clé est présente en session :

 
Sélectionnez
if (session()->has('error')) ...

Ces informations demeurent pour le même client à travers ses requêtes. Laravel s'occupe de ces informations, on se contente de lui indiquer un couple clé-valeur et il s'occupe de tout.

Ce ne sont là que les méthodes de base pour les sessions, vous trouverez tous les renseignements complémentaires dans la documentation.

X-C. La gestion des fichiers

Laravel utilise Flysystem comme composant de gestion de fichiers. Il en propose une API bien pensée et facile à utiliser. On peut ainsi manipuler fichiers et dossiers de la même manière en local ou à distance, que ce soit en FTP ou sur le cloud.

La configuration du système se trouve dans le fichier config/filesystem.php. Par défaut on est en local :

 
Sélectionnez
'default' => 'local',

Mais on peut aussi choisir ftp, s3 ou rackspace (pour ces deux derniers, il faut installer les composants correspondants). On peut aussi mettre en place un accès à d'autres possibilités et il existe des composants à ajouter, comme celui-ci pour Dropbox.

On peut définir des « disques » (disks) qui sont des cibles combinant un driver, un dossier racine et différents éléments de configuration :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
'disks' => [
 
    'local' => [
        'driver' => 'local',
        'root' => storage_path('app'),
    ],
 
    'public' => [
        'driver' => 'local',
        'root' => storage_path('app/public'),
        'visibility' => 'public',
    ],
 
    's3' => [
        'driver' => 's3',
        'key' => 'your-key',
        'secret' => 'your-secret',
        'region' => 'your-region',
        'bucket' => 'your-bucket',
    ],
 
],

Donc par défaut, c'est le disque local qui est actif et le dossier racine est storage/app.

Autrement dit si j'utilise ce code :

 
Sélectionnez
Storage::disk('local')->put('recettes.txt', 'Contenu du fichier');

Je vais envoyer le fichier recettes.txt dans le dossier storage/app.

Et si j'utilise ce code :

 
Sélectionnez
Storage::disk('public')->put('recettes.txt', 'Contenu du fichier');

Cette fois j'envoie le fichier dans le dossier storage/app/public. Par convention ce dossier doit être accessible depuis le web.

Mais je vous ai dit précédemment que seul le dossier public à la racine doit être accessible. Alors ?

Alors pour que ce soit possible il faut créer un lien symbolique public/storage qui pointe sur storage/app/public. Il y a d'ailleurs une commande d'Artisan pour ça :

 
Sélectionnez
<samp>php artisan storage:link</samp>

Mais franchement je préfère créer directement un dossier dans public. Les motivations avancées (ne pas perdre de fichiers au déploiement) me paraissent trop minces.

Donc rien n'empêche de changer la configuration :

 
Sélectionnez
1.
2.
3.
4.
5.
'public' => [
    'driver' => 'local',
    'root' => public_path(),
    'visibility' => 'public',
],

Ainsi le disque « public » pointe sur le dossier public. C'est d'ailleurs ce qu'on va faire pour l'exemple de ce chapitre.

Il y a de nombreuses méthodes pour manipuler dossiers et fichiers, je ne vais pas développer tout ça pour le moment, vous avez le détail dans la documentation. On va surtout utiliser les possibilités de téléchargement des fichiers dans ce chapitre.

X-D. La requête de formulaire

Nous allons encore avoir besoin d'une requête de formulaire pour la validation. Comme nous l'avons déjà vu, nous utilisons la commande d'Artisan pour la créer :

 
Sélectionnez
<samp>php artisan make:request ImagesRequest </samp>

Vous devez trouver le fichier ici :

Image non disponible

Changez le code pour celui-ci :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
<?php 
 
namespace App\Http\Requests;
 
use Illuminate\Foundation\Http\FormRequest;
 
class ImagesRequest 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()
    {
        return ['image' => 'required|image|dimensions:min_width=100,min_height=100'];
    }
}

On a seulement trois règles pour le champ image :

  • le champ est obligatoire (required) ;
  • ce doit être une image (image) ;
  • l'image doit faire au minimum 100 * 100 pixels (dimensions).

Maintenant notre validation est prête.

X-E. Les routes et le contrôleur

On va avoir besoin de deux routes  :

 
Sélectionnez
Route::get('photo', 'PhotoController@create');
Route::post('photo', 'PhotoController@store');

On utilise Artisan pour créer le contrôleur :

 
Sélectionnez
<samp>php artisan make:controller PhotoController</samp>

Vous devez trouver le fichier ici :

Image non disponible

Changez le code pour celui-ci :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
<?php 
 
namespace App\Http\Controllers;
 
use App\Http\Requests\ImagesRequest;
 
class PhotoController extends Controller 
{
 
    public function create()
    {
        return view('photo');
    }
 
    public function store(ImagesRequest $request)
    {
        $request->image->store(config('images.path'), 'public'));
         
        return view('photo_ok');
    }
}

Donc au niveau des URL :

En ce qui concerne le traitement de la soumission, vous remarquez qu'on récupère le chemin du dossier d'enregistrement qu'on a prévu dans la configuration :

 
Sélectionnez
config('images.path')

La partie intéressante se trouve au niveau de ce code :

 
Sélectionnez
$request->image->store(config('images.path'), 'public')

On récupère l'image avec $request->image. Ce qu'on obtient là est une instance de la classe Illuminate/Http/UploadedFile.

Laravel dispose d'une documentation complète de ses classes, par exemple vous trouvez cette classe ici avec la méthode store documentée.

La méthode store génère automatiquement un nom de fichier basé sur son contenu (hashage MD5) et elle retourne le chemin complet.

X-F. Les vues

On va utiliser le template des chapitres précédents (resources/views/template.blade.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
<!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>
        {!! Html::style('<a href="<a href="https://netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">https://netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css</a>"><a href="https://netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">https://netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css</a></a>') !!}
    </head>
    <body>
        @yield('contenu')
    </body>
</html>

Voici la vue pour le formulaire (resources/views/photo.blade.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
@extends('template')
 
@section('contenu')
    <br>
    <div class="col-sm-offset-4 col-sm-4">
        <div class="panel panel-info">
            <div class="panel-heading">Envoi d'une photo</div>
            <div class="panel-body"> 
                {!! Form::open(['url' => 'photo', 'files' => true]) !!}
                    <div class="form-group {!! $errors->has('image') ? 'has-error' : '' !!}">
                        {!! Form::file('image', ['class' => 'form-control']) !!}
                        {!! $errors->first('image', '<small class="help-block">:message</small>') !!}
                    </div>
                    {!! Form::submit('Envoyer !', ['class' => 'btn btn-info pull-right']) !!}
                {!! Form::close() !!}
            </div>
        </div>
    </div>
@endsection

Avec cet aspect :

Image non disponible

Remarquez comment est créé le formulaire :

 
Sélectionnez
{!! Form::open(['url' => 'photo', 'files' => true]) !!}

Le fait d'ajouter l'attribut files avec la valeur true va avoir pour effet de faire apparaître le type mime nécessaire pour associer un fichier lors de la soumission :

 
Sélectionnez
enctype="multipart/form-data"

En cas d'erreur de validation, le message est affiché et la bordure du champ devient rouge :

Image non disponible

Et voici la vue pour la confirmation en retour (app/views/photo_ok.blade.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
@extends('template')
 
@section('contenu')
    <br>
    <div class="col-sm-offset-3 col-sm-6">
        <div class="panel panel-info">
            <div class="panel-heading">Envoi d'une photo</div>
            <div class="panel-body"> 
                Merci. Votre photo a bien été reçue et enregistrée.
            </div>
        </div>
    </div>
@endsection

Avec cet aspect :

Image non disponible

On retrouve normalement le fichier bien rangé dans le dossier prévu :

Image non disponible

Voici le schéma de fonctionnement :

Image non disponible

X-G. En résumé

  • Les fichiers de configuration permettent de mémoriser facilement des ensembles clé-valeur et sont gérés par l'helper config.
  • Les sessions permettent de mémoriser des informations concernant un client et sont facilement manipulables avec l'helper session.
  • Laravel comporte un système complet de gestion de fichiers en local, à distance ou sur le cloud avec une API commune.
  • Il est facile de créer un système de téléchargement de fichier.

XI. Injection de dépendances, conteneur et façades

Dans ce chapitre nous allons reprendre l'exemple précédent de l'envoi de photos en nous posant des questions d'organisation du code. Laravel ce n'est pas seulement un framework pratique, c'est aussi un style de programmation. Il vaut mieux évoquer ce style le plus tôt possible dans l'apprentissage pour prendre rapidement les bonnes habitudes.

Vous pouvez très bien créer un site complet dans le fichier des routes, vous pouvez aussi vous contenter de contrôleurs pour effectuer tous les traitements nécessaires. Je vous propose une autre approche, plus en accord avec ce que nous offre Laravel.

XI-A. Le problème et sa solution

XI-A-1. Le problème

Je vous ai déjà dit qu'un contrôleur a pour mission de réceptionner les requêtes et d'envoyer les réponses. Entre les deux il y a évidemment du traitement à effectuer, la réponse doit se construire, parfois c'est très simple, parfois plus long et délicat. Mais globalement nous avons pour un contrôleur ce fonctionnement :

Image non disponible

Reprenons la méthode store de notre contrôleur PhotoController du précédent chapitre :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
public function store(ImagesRequest $request)
{
    $request->image->store(config('images.path'), 'public'));
     
    return view('photo_ok');
}

Qu'avons-nous comme traitement ? On récupère l'image transmise et on enregistre cette image.

La question est : est-ce qu'un contrôleur doit savoir comment s'effectue ce traitement ? Si vous avez plusieurs contrôleurs dans votre application qui doivent effectuer le même traitement, vous allez multiplier cette mise en place. Imaginez que vous ayez ensuite envie de modifier l'enregistrement des images, par exemple en les mettant sur le cloud, vous allez devoir retoucher le code de tous vos contrôleurs ! La répétition de code n'est jamais une bonne chose, une saine règle de programmation veut qu'on commence à se poser des questions sur l'organisation du code dès qu'on fait des copies.

Cette préconisation est connue sous l'acronyme DRY (« Don't Repeat Yourself »).

Un autre élément à prendre en compte aussi est la testabilité des classes. Nous verrons cet aspect important du développement trop souvent négligé. Pour qu'une classe soit testable, il faut que sa mission soit simple et parfaitement identifiée et il ne faut pas qu'elle soit étroitement liée avec une autre classe. En effet cette dépendance rend les tests plus difficiles.

XI-A-2. La solution

Alors quelle est la solution ? L'injection de dépendances ! Voyons de quoi il s'agit. Regardez ce schéma :

Image non disponible

Une nouvelle classe entre en jeu pour la gestion, c'est elle qui est effectivement chargée du traitement, le contrôleur fait juste appel à ses méthodes. Mais comment cette classe est-elle injectée dans le contrôleur ? Voici le code du contrôleur modifié :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
<?php 
 
namespace App\Http\Controllers;
 
use App\Http\Requests\ImagesRequest;
use App\Repositories\PhotosRepository;
 
class PhotoController extends Controller 
{
    public function create()
    {
        return view('photo');
    }
 
    public function store(ImagesRequest $request, PhotosRepository $photosRepository)
    {
        $photosRepository->save($request->image);
         
        return view('photo_ok');
    }
}

Vous remarquez qu'au niveau de la méthode store il y a un nouveau paramètre de type App\Repositories\PhotosRepository. On utilise la méthode save de la classe ainsi injectée pour faire le traitement.

De cette façon le contrôleur ignore totalement comment se fait la gestion, il sait juste que la classe PhotosRepository sait la faire. Il se contente d'utiliser la méthode de cette classe qui est « injectée ».

Maintenant vous vous demandez sans doute comment cette classe est injectée là, en d'autres termes comment et où est créée cette instance. Eh bien Laravel est assez malin pour le faire lui-même.

PHP est très tolérant sur les types des variables. Lorsque vous en déclarez une, vous n'êtes pas obligé de préciser que c'est un string ou un array. PHP devine le type selon la valeur affectée. Il en est de même pour les paramètres des fonctions. Mais personne ne vous empêche de déclarer un type comme je l'ai fait ici pour le paramètre de la méthode (malheureusement pour le moment PHP ne reconnaît que les tableaux et les classes). C'est même indispensable pour que Laravel sache quelle classe est concernée. Étant donné que je déclare le type, Laravel est capable de créer une instance de ce type et de l'injecter dans le contrôleur.

Pour trouver la classe, Laravel utilise l'introspection (reflexion en anglais) de PHP qui permet d'inspecter le code en cours d'exécution. Elle permet aussi de manipuler du code et donc de créer par exemple un objet d'une certaine classe. Vous pouvez trouver tous les renseignements dans le manuel PHP‌.

XI-B. La gestion

Maintenant qu'on a dit au contrôleur qu'une classe s'occupe de la gestion, il nous faut la créer. Pour bien organiser notre application ? on crée un nouveau dossier et on place notre classe dedans :

Image non disponible

Le codage ne pose aucun problème parce qu'il est identique à ce qu'on avait dans le contrôleur :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
<?php 
 
namespace App\Repositories;
 
use Illuminate\Http\UploadedFile;
 
class PhotosRepository
{
    public function save(UploadedFile $image)
    {
        $image->store(config('images.path'), 'public');
    }
}

Attention à ne pas oublier les espaces de noms !

Maintenant notre code est parfaitement organisé et facile à maintenir et à tester.

Mais allons un peu plus loin, créons une interface pour notre classe :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
<?php 
 
namespace App\Repositories;
 
use Illuminate\Http\UploadedFile;
 
interface PhotosRepositoryInterface
{
  public function save(UploadedFile $image);
}

Il suffit ensuite d'en informer la classe PhotosRepository :

 
Sélectionnez
class PhotosRepository implements PhotosRepositoryInterface

Ce qui serait bien maintenant serait dans notre contrôleur de référencer l'interface :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
<?php 
 
namespace App\Http\Controllers;
 
use App\Http\Requests\ImagesRequest;
use App\Repositories\PhotosRepositoryInterface;
 
class PhotoController extends Controller 
{
    public function create()
    {
        return view('photo');
    }
 
    public function store(ImagesRequest $request, PhotosRepositoryInterface $photosRepository)
    {
        $photosRepository->save($request->image);
         
        return view('photo_ok');
    }
}

Le souci c'est que Laravel n'arrive pas à deviner la classe à instancier à partir de cette interface :

Image non disponible

Comment s'en sortir ?

Lorsque j'ai présenté la structure de Laravel j'ai mentionné la présence de providers :

Image non disponible

À quoi sert un provider ? Tout simplement à procéder à des initialisations : événements, middlewares, et surtout des liaisons de dépendance. Laravel possède un conteneur de dépendances qui constitue le cœur de son fonctionnement. C'est grâce à ce conteneur qu'on va pouvoir établir une liaison entre une interface et une classe.

Ouvrez le fichier app\Providers\AppServiceProvider.php et ajoutez cette ligne de code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
public function register()
{
    $this->app->bind(
        'App\Repositories\PhotosRepositoryInterface', 
        'App\Repositories\PhotosRepository'
    );
}

La méthode register est activée au démarrage de l'application, c'est l'endroit idéal pour notre liaison. Ici on dit à l'application (app) d'établir une liaison (bind) entre l'interface App\Repositories\PhotosRepositoryInterface et la classe App\Repositories\PhotosRepository. Ainsi chaque fois qu'on se référera à cette interface dans une injection Laravel saura quelle classe instancier. Si on veut changer la classe de gestion ? il suffit de modifier le code du provider.

Maintenant notre application fonctionne.

Si vous obtenez encore un message d'erreur vous disant que l'interface ne peut pas être instanciée, lancez la commande :

 
Sélectionnez
<samp>composer dumpautoload</samp>

XI-C. Les façades

Laravel propose de nombreuses façades pour simplifier la syntaxe. Vous pouvez les trouver toutes déclarées dans le fichier config/app.php :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
'aliases' => [
 
    'App' => Illuminate\Support\Facades\App::class,
    'Artisan' => Illuminate\Support\Facades\Artisan::class,
    'Auth' => Illuminate\Support\Facades\Auth::class,
    'Blade' => Illuminate\Support\Facades\Blade::class,
    'Cache' => Illuminate\Support\Facades\Cache::class,
    'Config' => Illuminate\Support\Facades\Config::class,
    'Cookie' => Illuminate\Support\Facades\Cookie::class,
    'Crypt' => Illuminate\Support\Facades\Crypt::class,
    'DB' => Illuminate\Support\Facades\DB::class,
    'Eloquent' => Illuminate\Database\Eloquent\Model::class,
    'Event' => Illuminate\Support\Facades\Event::class,
    'File' => Illuminate\Support\Facades\File::class,
    'Gate' => Illuminate\Support\Facades\Gate::class,
    'Hash' => Illuminate\Support\Facades\Hash::class,
    'Lang' => Illuminate\Support\Facades\Lang::class,
    'Log' => Illuminate\Support\Facades\Log::class,
    'Mail' => Illuminate\Support\Facades\Mail::class,
    'Notification' => Illuminate\Support\Facades\Notification::class,
    'Password' => Illuminate\Support\Facades\Password::class,
    'Queue' => Illuminate\Support\Facades\Queue::class,
    'Redirect' => Illuminate\Support\Facades\Redirect::class,
    'Redis' => Illuminate\Support\Facades\Redis::class,
    'Request' => Illuminate\Support\Facades\Request::class,
    'Response' => Illuminate\Support\Facades\Response::class,
    'Route' => Illuminate\Support\Facades\Route::class,
    'Schema' => Illuminate\Support\Facades\Schema::class,
    'Session' => Illuminate\Support\Facades\Session::class,
    'Storage' => Illuminate\Support\Facades\Storage::class,
    'URL' => Illuminate\Support\Facades\URL::class,
    'Validator' => Illuminate\Support\Facades\Validator::class,
    'View' => Illuminate\Support\Facades\View::class,
],

Vous trouvez dans ce tableau le nom de la façade et la classe qui met en place cette façade. Par exemple pour les routes, on a la façade Route qui correspond à la classe Illuminate\Support\Facades\Route. Regardons cette classe :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
<?php
 
namespace Illuminate\Support\Facades;
 
/**
 * @see \Illuminate\Routing\Router
 */
class Route extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'router';
    }
}

On se contente de retourner 'router'. Il faut aller voir dans le fichier Illuminate\Routing\RoutingServiceProvider pour trouver l'enregistrement du router :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
protected function registerRouter()
{
    $this->app['router'] = $this->app->share(function ($app) {
        return new Router($app['events'], $app);
    });
}

Les providers permettent d'enregistrer des composants dans le conteneur de Laravel. Ici on déclare « router » et on voit qu'on crée une instance de la classe Router (new Router…). Le nom complet est Illuminate\Routing\Router. Si vous allez voir cette classe vous trouverez les méthodes qu'on a utilisées dans ce chapitre, par exemple get :

 
Sélectionnez
1.
2.
3.
4.
public function get($uri, $action = null)
{
    return $this->addRoute(['GET', 'HEAD'], $uri, $action);
}

Autrement dit, si j'écris en utilisant la façade :

 
Sélectionnez
Route::get('/', function() { return 'Coucou'; });

J'obtiens le même résultat que si j'écris en allant chercher le routeur dans le conteneur :

 
Sélectionnez
$this->app['router']->get('/', function() { return 'Coucou'; });

Ou encore en utilisant un helper :

 
Sélectionnez
app('router')->get('/', function() { return 'Coucou'; });

La différence est que la première syntaxe est plus simple et intuitive, mais certains n'aiment pas trop ce genre d'appel statique.

XI-D. En résumé

  • Un contrôleur doit déléguer toute tâche qui ne relève pas de sa compétence.
  • L'injection de dépendances permet de bien séparer les tâches, de simplifier la maintenance du code et les tests unitaires.
  • Les providers permettent de faire des initialisations, en particulier des liaisons de dépendance entre interfaces et classes.
  • Laravel est équipé de nombreuses façades qui simplifient la syntaxe.
  • Il existe aussi des helpers pour simplifier la syntaxe. ‌

XII. CSS et JavaScript

Laravel est un framework PHP alors pourquoi prévoir un chapitre sur CSS et JavaScript qui concernent le client ?

Tout simplement parce que même si on code côté serveur, on est aussi largement impliqué dans l'aspect client parce qu'il faut générer des pages HTML et assurer le fonctionnement des pages dynamiques.

Laravel n'impose rien à ce niveau, mais propose des solutions. Dans ce chapitre nous allons voir ce qui est prévu de base et la manière de s'en servir, libre à vous ensuite d'utiliser ces possibilités ou pas.

XII-A. Les outils

Par défaut Laravel utilise :

Laravel est équipé de l'outil Elixir qui a pour but de simplifier l'utilisation de Gulp.

Mais c'est quoi Gulp ?

C'est un gestionnaire de tâches. On lui demande de faire des actions (minimiser du CSS ou du JavaScript, compiler du SASS, copier des fichiers, créer un serveur…). Il sait faire beaucoup de choses parce qu'il existe une montagne de plugins pour lui. Il fonctionne avec Node.js.

XII-A-1. Node.js

C'est quoi Node.js ?

C'est un écosystème JavaScript qui permet de pratiquement tout faire côté serveur. On peut par exemple coder complètement un site avec lui et il sera très rapide (utilisation du moteur de Google Chrome et fonctionnement non bloquant) !

Pour le présent chapitre, vous n'avez pas besoin de savoir beaucoup de choses sur Node.js, mais vous avez besoin qu'il soit installé sur votre serveur de développement. Si vous utilisez Laragon, il est déjà installé, de même si vous utilisez Homestead. Sinon vous n'avez plus qu'à l'installer et il vous servira sans doute pour bien d'autres choses !

Node.js comporte le gestionnaire de packages npm. C'est un peu le composer de JavaScript. Le fichier de paramétrage est package.json (l'équivalent de composer.json). Il est déjà présent à la racine de Laravel :

Image non disponible

Voyons le contenu de ce fichier :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
{
  "private": true,
  "scripts": {
    "prod": "gulp --production",
    "dev": "gulp watch"
  },
  "devDependencies": {
    "bootstrap-sass": "^3.3.7",
    "gulp": "^3.9.1",
    "jquery": "^3.1.0",
    "laravel-elixir": "^6.0.0-9",
    "laravel-elixir-vue": "^0.1.4",
    "laravel-elixir-webpack-official": "^1.0.2",
    "lodash": "^4.14.0",
    "vue": "^1.0.26",
    "vue-resource": "^0.9.3"
  }
}

On y trouve les dépendances nécessaires (devDependencies). En particulier y figurent gulp et elixir.

On va installer tout ça avec :

 
Sélectionnez
<samp>npm install</samp>

Avec Windows il vaut mieux utiliser cette commande :

 
Sélectionnez
<samp>npm install --no-bin-links</samp>

Vous allez attendre un bon moment pour que tout s'installe…

Quand c'est fini, vous vous retrouvez avec un nouveau dossier node_modules bien garni :

Image non disponible

Il contient toutes les dépendances nécessaires.

XII-A-2. Gulp

Pour fonctionner, Gulp a besoin du fichier gulpfile.js. Ce fichier est déjà présent à la racine de Laravel :

Image non disponible

Voici son contenu :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
const elixir = require('laravel-elixir');
 
require('laravel-elixir-vue');
 
/*
 |--------------------------------------------------------------------------
 | Elixir Asset Management
 |--------------------------------------------------------------------------
 |
 | Elixir provides a clean, fluent API for defining some basic Gulp tasks
 | for your Laravel application. By default, we are compiling the Sass
 | file for our application, as well as publishing vendor resources.
 |
 */
 
elixir(mix => {
    mix.sass('app.scss')
       .webpack('app.js');
});

Comme je l'ai dit ci-dessus, on n'utilise pas directement Gulp, mais Elixir qui en simplifie l'utilisation.

XII-B. Elixir et css

Alors voyons de plus près cet outil…

Elixir sert à définir des tâches pour Gulp. Il prend en charge la plupart des préprocesseurs (LESS, SASS, JavaScript…). Dans le code de gulp.js on trouve ce code :

 
Sélectionnez
1.
2.
3.
4.
elixir(mix => {
    mix.sass('app.scss')
       .webpack('app.js');
});

XII-B-1. Sass

La méthode sass permet de compiler du code SCSS en CSS. On part du principe qu'il y a un fichier Sass nommé app.scss. Mais où se trouve ce fichier ?

Par défaut les fichiers se trouvent dans resources/assets et pour les fichiers Sass, il y a le dossier sass :

Image non disponible

Voyons son contenu :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
// Fonts
@import url(<a href="https://fonts.googleapis.com/css?family=Raleway:300,400,600">https://fonts.googleapis.com/css?family=Raleway:300,400,600</a>);
 
// Variables
@import "variables";
 
// Bootstrap
@import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap";

On importe une police, on importe un fichier de variables pour Bootstrap, et enfin on importe la bibliothèque Bootstrap au format Sass.

Donc on va modifier Bootstrap avec des valeurs de variables spécifiques pour changer l'aspect par défaut. Voici ces variables (variables.scss) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
// Body
$body-bg: #f5f8fa;
 
// Borders
$laravel-border-color: darken($body-bg, 10%);
$list-group-border: $laravel-border-color;
$navbar-default-border: $laravel-border-color;
$panel-default-border: $laravel-border-color;
$panel-inner-border: $laravel-border-color;
 
// Brands
$brand-primary: #3097D1;
$brand-info: #8eb4cb;
$brand-success: #2ab27b;
$brand-warning: #cbb956;
$brand-danger:  #bf5329;
 
// Typography
$font-family-sans-serif: "Raleway", sans-serif;
$font-size-base: 14px;
$line-height-base: 1.6;
$text-color: #636b6f;
 
// Navbar
$navbar-default-bg: #fff;
 
// Buttons
$btn-default-color: $text-color;
 
// Inputs
$input-border: lighten($text-color, 40%);
$input-border-focus: lighten($brand-primary, 25%);
$input-color-placeholder: lighten($text-color, 30%);
 
// Panels
$panel-default-heading-bg: #fff;

Je ne vais pas entrer dans le détail, mais par exemple :

  • $body-bg : on change la couleur de fond ;
  • $brand-primary : on change la couleur primaire (par défaut un bleu profond) ;
  • $font-size-base : on change la taille de la police.

C'est la méthode classique pour mettre Bootstrap à son goût.

Par défaut le fichier résultant se trouvera ici :

Image non disponible

On peut changer ce comportement par défaut en définissant une autre cible, mais on va garder celle-là. Le fichier s'y trouve d'ailleurs déjà dans l'installation de base :

 
Sélectionnez
1.
2.
3.
4.
@import url(<a href="https://fonts.googleapis.com/css?family=Raleway:300,400,600">https://fonts.googleapis.com/css?family=Raleway:300,400,600</a>);/*!
 * Bootstrap v3.3.7 (<a href="http://getbootstrap.com">http://getbootstrap.com</a>)
 * Copyright 2011-2016 Twitter, Inc.
 ...

Modifiez gulpfile.js ainsi :

 
Sélectionnez
elixir(mix => {
    mix.sass('app.scss');
});

Comme ça, on ne va agir que sur le fichier Sass. Lancez Gulp :

 
Sélectionnez
<samp>gulp</samp>

Normalement tout devrait bien se passer :

Image non disponible

Si on regarde dans le dossier :

Image non disponible

On se retrouve avec deux fichiers cette fois. En effet par défaut le fichier map (qui sert au débogage) est aussi créé. On peut éviter cette génération en ajoutant cette ligne dans gulpfile.js :

 
Sélectionnez
elixir.config.sourcemaps = false;

Si vous regardez maintenant le fichier app.css généré :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
@charset "UTF-8";
@import url(<a href="https://fonts.https://fonts.googleapis.com/css?family=Raleway:300,400,600">https://fonts.googleapis.com/css?family=Raleway:300,400,600</a>);
/*!
 * Bootstrap v3.3.7 (<a href="http://getbootstrap.com">http://getbootstrap.com</a>)
 * Copyright 2011-2016 Twitter, Inc.
 * Licensed under MIT (<a href="https://github.com/twbs/bootstrap/blob/master/LICENSE">https://github.com/twbs/bootstrap/blob/master/LICENSE</a>)
 */
/*! normalize.css v3.0.3 | MIT License | <a href="github.com/necolas/normalize.css">github.com/necolas/normalize.css</a> */
html {
  font-family: sans-serif;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%; }
 
body {
  margin: 0; }
...

Vous constatez qu'il n'est pas minifié. C'est le comportement par défaut, pour avoir en plus la minification, il faut ajouter une option :

 
Sélectionnez
<samp>gulp --production</samp>

On procéderait exactement pareil pour du LESS.

Pour avoir une génération automatique en cas de changement de la source, il faut utiliser cette commande :

 
Sélectionnez
<samp>gulp watch</samp>

C'est quand même bien pratique.

XII-C. Elixir et JavaScript

Elixir sait aussi très bien gérer les fichiers JavaScript. On a souvent besoin de rassembler plusieurs fichiers dans un seul, avec Elixir c'est facile :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
elixir(function(mix) {
    mix.scripts([
        'main.js',
        'module.js'
    ]);
});

Par défaut les fichiers doivent se trouver dans resource/assets/js. Le fichier résultant sera public/js/all.js. Vous pouvez évidemment changer tout ça si vous le voulez.

De la même manière que pour le CSS la minification se produira si on utilise l'option -production.

En plus du JavaScript classique Elixir sait aussi gérer CoffeeScript, Browserify, Babel et bien d'autres ! Vous pouvez donc tranquillement écrire votre JavaScript en ES2015 ou même en TypeScript.

Mais on va s'intéresser à Vue.js parce qu'il est prévu par défaut dans l'installation. Modifiez le code de gulpfile.js ainsi :

 
Sélectionnez
elixir(mix => {
    mix.webpack('app.js')
});

Par défaut on utilise Webpack mais vous pouvez changer pour Rollup.

Le but est de compiler des fichiers écrits en ES2015, donc modulaires et de les rassembler en un seul fichier.

Les sources sont dans resources/assets/js :

Image non disponible

Et le résultat sera dans public/js/app.js.

La commande est évidemment la même :

Image non disponible

Qu'est-ce qu'on a dans le fichier résultant ? Pour le savoir, il faut regarder resources/assets/js/bootstrap.js :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
window._ = require('lodash');
 
/**
 * We'll load jQuery and the Bootstrap jQuery plugin which provides support
 * for JavaScript based Bootstrap features such as modals and tabs. This
 * code may be modified to fit the specific needs of your application.
 */
 
window.$ = window.jQuery = require('jquery');
require('bootstrap-sass');
 
/**
 * Vue is a modern JavaScript library for building interactive web interfaces
 * using reactive data binding and reusable components. Vue's API is clean
 * and simple, leaving you to focus on building your next great project.
 */
 
window.Vue = require('vue');
require('vue-resource');
 
/**
 * We'll register a HTTP interceptor to attach the "CSRF" header to each of
 * the outgoing requests issued by this application. The CSRF middleware
 * included with Laravel will automatically verify the header's value.
 */
 
Vue.http.interceptors.push((request, next) => {
    request.headers['X-CSRF-TOKEN'] = Laravel.csrfToken;
 
    next();
});

On charge :

  • jQuery ;
  • Bootstrap (la bibliothèque JavaScript) ;
  • Vue.js ;
  • Vue-ressource (un composant pour Vue.js qui permet d'utiliser Ajax) ;
  • un intercepteur pour mettre le token de la protection CSRF dans les headers.

D'autre part on a un composant Vue.js (resources/assets/js/components/Example.vue) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
<template>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <div class="panel panel-default">
                    <div class="panel-heading">Example Component</div>
 
                    <div class="panel-body">
                        I'm an example component!
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
 
<script>
    export default {
        ready() {
            console.log('Component ready.')
        }
    }
</script>

Ce composant s'appelle tout simplement example :

 
Sélectionnez
Vue.component('example', require('./components/Example.vue'));

On va s'en servir pour voir si ça fonctionne… Il suffit de créer une vue qui charge le CSS et le JavaScript et qui inclut le composant de Vue.js :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        {!! Html::style('css/app.css') !!}
    </head>
    <body>
        <example></example>
        {!! Html::script('js/app.js') !!}
    </body>
</html>

Et voici le résultat :

Image non disponible

Comme vous le constatez, l'intendance est en place si vous voulez l'utiliser !

XII-D. En résumé

  • Laravel n'impose rien concernant le CSS et le JavaScript, mais il propose des solutions.
  • Elixir est un outil qui permet d'utiliser simplement Gulp.
  • Avec Elixir, on peut compiler du CSS et du JavaScript.‌
  • La bibliothèque JavaScript Vue.js est prévue par défaut.

XIII. Remerciements

Nous remercions Maurice Chavelli qui nous a autorisés à publier ce tutoriel.

Nous tenons également à remercier Winjerome pour la mise au gabarit et Claude Leloup pour la correction orthographique.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2017 Laravel. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.