IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Cours pour apprendre à utiliser le framework Laravel 5.5

Plus loin avec Laravel


précédentsommairesuivant

VIII. Les fichiers et le cache

On a vu comment utiliser une base de données pour gérer des informations, mais ce n'est pas la seule façon de les mémoriser et de les retrouver. De plus, il y a bien d'autres éléments à gérer, en particulier des fichiers. Dans ce chapitre, on va voir comment Laravel permet avec élégance de gérer des fichiers, ainsi que le système de cache qui peut booster vos applications.

VIII-A. Le système de gestion des fichiers

Laravel fournit un système de gestion des fichiers suffisamment abstrait pour s'adapter à toutes les situations, que ce soit en local ou sur un nuage (cloud). Sous le capot, c'est le package flysystem qui est utilisé. Les avantages de ce package peuvent se résumer ainsi :

  • on dispose d'une API unique pour tous les systèmes de gestion (local ou nuage) ;
  • on peut utiliser un cache ;
  • permet de gérer de gros fichiers (stream) ;
  • facile à tester.

Par défaut on dispose de nombreux drivers, en particulier :

  • local ;
  • Azure ;
  • AWS S3 ;
  • Dropbox ;
  • FTP ;
  • Rackspace…

VIII-A-1. La configuration

La configuration se trouve dans le fichier config/filesystem.php avec ce code par défaut :

 
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.
'disks' => [

    'local' => [
        'driver' => 'local',
        'root' => storage_path('app'),
    ],

    'public' => [
        'driver' => 'local',
        'root' => storage_path('app/public'),
        'url' => env('APP_URL').'/storage',
        'visibility' => 'public',
    ],

    's3' => [
        'driver' => 's3',
        'key' => env('AWS_KEY'),
        'secret' => env('AWS_SECRET'),
        'region' => env('AWS_REGION'),
        'bucket' => env('AWS_BUCKET'),
    ],

],

On raisonne en « disques » (disks), chacun utilisant un driver et un emplacement :

  • local : interagit avec les fichiers locaux et présume qu'ils se trouvent dans le dossier storage/app ;
  • public : interagit avec les fichiers locaux qui doivent être accessibles par les clients. Le dossier par défaut est storage/app/public, mais ce dossier n'est pas accessible par défaut. Il faut alors ajouter un lien symbolique (symlink) pour pointer quelque part dans le dossier public. On dispose d'une commande d'Artisan pour le faire :
 
Sélectionnez
php artisan storage:link

La motivation principale est d'avoir les fichiers accessibles publiquement dans un dossier géré par les outils de déploiement comme Envoyer. A vous de voir si vous voulez suivre cette procédure ou tout simplement diriger tout sur le dossier public.

  • s3 : c'est un exemple de configuration d'un nuage avec renseignement des paramètres. Pour que ça fonctionne, il faut installer le package correspondant. Par exemple pour s3, c'est league/flysystem-aws-s3-v3 ~1.0.

Le disque par défaut est précisé dans le même fichier de configuration :

 
Sélectionnez
'default' => env('FILESYSTEM_DRIVER', 'local'),

VIII-A-2. La façade

Une fois que vous avez défini vos disques dans la configuration, vous pouvez utiliser la façade Storage pour gérer les fichiers. Il suffit de préciser le disque concerné par la manipulation (s'il n'est pas précisé, ça sera le disque par défaut défini dans la configuration). Par exemple, avec :

 
Sélectionnez
Storage::disk('s3')->get('image.png');

on va aller chercher dans le nuage s3 l'image image.png.

On dispose de nombreuses méthodes pour manipuler les fichiers, voici les principales :

  • get : récupération d'un fichier comme on l'a vu ci-dessus ;
  • put : sauvegarde d'un contenu dans un fichier put('fichier.txt', $contenu) ;
  • putFile : sauvegarde le contenu d'un fichier dans un emplacement putFile('dossier', $fichier) ;
  • exists : détermine si un fichier existe exists('dossier', $fichier) ;
  • delete : supprime un fichier delete('fichier.txt') ;
  • deleteDirectory : supprime un dossier deleteDirectory('dossier') ;
  • copy : copie un fichier copy('fichier.txt', 'nouveaufichier.txt') ;
  • move : déplace un fichier move('fichier.txt', 'nouveaufichier.txt') ;
  • files : retourne un tableau avec les noms des fichiers files('dossier') ;
  • directories : retourne un tableau avec les noms des dossiers directories('dossier') ;
  • prepend : ajoute un contenu au début d'un fichier prepend('fichier.txt', 'texte') ;
  • append : ajoute un contenu à la fin d'un fichier append('fichier.txt', 'texte').

VIII-B. Un exemple

Dans l'application d'exemple, on a cette 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.
23.
24.
25.
26.
27.
28.
29.
'disks' => [

    'local' => [
        'driver' => 'local',
        'root' => storage_path('app'),
    ],

    'public' => [
        'driver' => 'local',
        'root' => storage_path('app/public'),
        'url' => env('APP_URL').'/storage',
        'visibility' => 'public',
    ],

    'files' => [
        'driver' => 'local',
        'root' => public_path() . ('/files'),
        'visibility' => 'public',
    ],

    'thumbs' => [
        'driver' => 'local',
        'root' => public_path() . ('/thumbs'),
        'visibility' => 'public',
    ],

    ...

],

On a les valeurs par défaut pour local et public, et on a deux disques supplémentaires :

  • files : pour les médias ;
  • thumbs : pour les petites images des articles dans l'administration.

On voit que les deux sont placés dans public :

Image non disponible

VIII-B-1. Les médias

Dans l'application, les médias sont gérés par elFinder :

Image non disponible

par l'intermédiaire de ce package pour l'adaptation à Laravel.

La configuration du package se situe dans ce fichier :

Image non disponible

J'ai juste ajouté ces middlewares parce qu'il faut être au moins rédacteur pour accéder à la gestion des médias :

 
Sélectionnez
1.
2.
3.
4.
'route' => [
    'prefix' => 'elfinder',
    'middleware' => ['web', 'redac'],
],

Un administrateur a accès à tous les médias mais un rédacteur doit être limité à ses propres médias pour éviter qu'ils aillent perturber les autres.

Si vous regardez le contenu du dossier public/files, vous trouverez ceci :

Image non disponible

Chaque rédacteur a un dossier de la forme user{id}. Par défaut, on a un seul rédacteur avec l'identifiant « 2 », et donc son dossier est user2 :

Image non disponible

Dans le modèle app\Models\User, on trouve cette méthode :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
/**
 * Get user files directory
 *
 * @return string|null
 */
public function getFilesDirectory()
{
    if ($this->role === 'redac') {
        $folderPath = 'user' . $this->id;
        if (!in_array($folderPath , Storage::disk('files')->directories())) {
            Storage::disk('files')->makeDirectory($folderPath);
        }
        return $folderPath;
    }
    return null;
}

Elle retourne le nom du dossier du rédacteur et au besoin on le crée s'il n'existe pas. On voit l'utilisation de la méthode directories de Storage sur le disque files pour trouver tous les dossiers :

 
Sélectionnez
Storage::disk('files')->directories()

Si le dossier n'existe pas, alors on le crée avec makeDirectory :

 
Sélectionnez
Storage::disk('files')->makeDirectory($folderPath);

VIII-B-2. Les thumbs

Lorsqu'on liste les articles dans l'administration, on visualise une miniature de l'image d'illustration de l'article :

Image non disponible

Il serait lourd de charger les images en haute résolution, même s'il ne s'agit que de l'administration, alors l'application génère des miniatures dans le dossier thumbs :

Image non disponible

J'ai déjà évoqué ce sujet dans le chapitre sur les événementsLes événements. J'ai alors dit que c'est le service app\Services\Thumb qui est chargé de la génération des miniatures lorsqu'on crée ou modifie un article :

Image non disponible

Voici la principale méthode de ce service :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
public static function makeThumb(Model $model)
{
    if ($model instanceof Post) {
        $path = $model->image;
        $dir = dirname ($path);
        if ($dir != '\files') {
            $dir = substr_replace ($dir, '', 0, 7);
            if (!in_array($dir , Storage::disk('thumbs')->directories())) {
                Storage::disk('thumbs')->makeDirectory($dir);
            }
        }
        $image = Image::make(url($model->image))->widen(100);
        Storage::disk('thumbs')->put(substr_replace (self::makeThumbPath($path), '', 0, 7), $image->encode());
    }
}

On retrouve la routine de création du dossier telle qu'on l'a vue pour les médias puisque la structure de thumbs doit être la même que celle de files :

 
Sélectionnez
if (!in_array($dir , Storage::disk('thumbs')->directories())) {
    Storage::disk('thumbs')->makeDirectory($dir);
}

On crée la miniature avec le superbe package intervention/image :

 
Sélectionnez
$image = Image::make(url($model->image))->widen(100);

Ensuite on utilise la méthode put de Storage pour mémoriser la miniature sur le disque thumbs :

 
Sélectionnez
Storage::disk('thumbs')->put(substr_replace (self::makeThumbPath($path), '', 0, 7), $image->encode());

VIII-C. Le cache

Laravel fournit une API unique pour tous les drivers de cache gérés comme Memcached ou Redis. Ça fonctionne un peu comme les sessions avec un système clé-valeur. Par défaut le cache utilise un fichier.

VIII-C-1. La configuration

Le fichier de configuration est ici :

Image non disponible

On voit que par défaut on utilise un fichier (file) :

 
Sélectionnez
'default' => env('CACHE_DRIVER', 'file'),

Ensuite vous avez tous les systèmes disponibles :

 
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.
41.
42.
43.
44.
45.
46.
'stores' => [

    'apc' => [
        'driver' => 'apc',
    ],

    'array' => [
        'driver' => 'array',
    ],

    'database' => [
        'driver' => 'database',
        'table' => 'cache',
        'connection' => null,
    ],

    'file' => [
        'driver' => 'file',
        'path' => storage_path('framework/cache/data'),
    ],

    'memcached' => [
        'driver' => 'memcached',
        'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
        'sasl' => [
            env('MEMCACHED_USERNAME'),
            env('MEMCACHED_PASSWORD'),
        ],
        'options' => [
            // Memcached::OPT_CONNECT_TIMEOUT  => 2000,
        ],
        'servers' => [
            [
                'host' => env('MEMCACHED_HOST', '127.0.0.1'),
                'port' => env('MEMCACHED_PORT', 11211),
                'weight' => 100,
            ],
        ],
    ],

    'redis' => [
        'driver' => 'redis',
        'connection' => 'default',
    ],

],

Donc, par défaut, ça aboutit ici :

Image non disponible

VIII-C-2. Utilisation

Vous disposez de la façade Cache et de l'helper cache() pour utiliser le cache.

VIII-C-2-a. Mettre en cache

Pour mettre en cache, on utilise la méthode put :

 
Sélectionnez
Cache::put('clé', 'valeur', $minutes);
// ou
cache(['clé' => 'valeur'], $minutes);

Pour vérifier que la clé n'existe pas déjà, et risquer de l'écraser, utilisez add au lieu de put.

Il faut obligatoirement indiquer une durée en minutes. Si vous voulez une durée infinie, alors utilisez la méthode forever :

 
Sélectionnez
Cache::forever('clé', 'valeur');
ou cache()->forever('clé', 'valeur');
VIII-C-2-b. Récupérer une valeur en cache

Pour récupérer une valeur en cache, on utilise get :

 
Sélectionnez
$valeur = Cache::get('clé');
// ou
$valeur = cache('clé');

Si la clé n'existe pas, la méthode retourne null, ce qui peut être parfois gênant. Il est possible d'indiquer une valeur par défaut :

 
Sélectionnez
$valeur = Cache::get('clé', 'défaut'); 
// ou
$valeur = cache('clé', 'défaut');

Mais vous pouvez avoir envie, si la clé n'existe pas, de mémoriser une valeur. Voici la syntaxe à utiliser :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
$value = Cache::remember('clé', $minutes, function () {
    return genereCle();
});
// ou
$value = cache()->remember('clé', $minutes, function () {
    return genereCle();
});

Et si vous avez envie de récupérer la valeur et de la supprimer en même temps, alors utilisez « pull ».

VIII-C-2-c. Supprimer une valeur du cache

Pour supprimer une valeur du cache on utilise forget :

 
Sélectionnez
Cache::forget('clé');
// ou
cache()->forget('clé');

et si vous voulez carrément vider tout le cache, utilisez flush :

 
Sélectionnez
Cache::flush();
// ou
cache()->flush();
VIII-C-2-d. Vérifier qu'une clé existe

Enfin, on peut vérifier l'existence d'une clé avec has :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
if (Cache::has('clé')) {
    //
}
// ou
if (cache()->has('clé')) {
    //
}

VIII-C-3. Un exemple

Vous pouvez trouver un exemple expliqué avec une démonstration en ligne ici. L'article date un peu mais reste pertinent.

Il y a encore pas mal de possibilités avec ce cache, et vous pouvez trouver tout ça dans la documentation.

VIII-C-4. Les routes et la configuration

VIII-C-4-a. Les routes

À chaque requête, Laravel doit parcourir le fichier des routes pour trouver la bonne. Cette action peut être accélérée si on crée un cache des routes :

 
Sélectionnez
php artisan route:cache

Le fichier est créé ici :

Image non disponible

Mais attention ! Il ne faut pas qu'il y ait de closure dans votre fichier des routes sinon ça ne fonctionnera pas !

Maintenant, votre application sera plus rapide mais… si vous apportez une modification au fichier des routes, celle-ci ne sera plus prise en compte puisque c'est le cache qui sert de référence. Vous avez alors le choix entre supprimer le cache :

 
Sélectionnez
php artisan route:clear

et tout simplement le récréer directement avec la commande vue plus haut.

De toute façon, je ne vois pas trop l'intérêt d'utiliser un cache de routes en cours de développement : réservez donc cette action pour la production parce que là, vous n'aurez normalement plus de modification à apporter à vos routes. Toutefois, si vous devez ensuite faire une mise à jour, n'oubliez pas ce cache !

VIII-C-4-b. La configuration

Ce que j'ai dit ci-dessus pour les routes est tout aussi valable pour la configuration, et on dispose de ces deux commandes :

 
Sélectionnez
config:cache
config:clear

VIII-D. En résumé

  • Laravel est équipé d'un système de gestion de fichiers unifié qui permet des manipulations en local ou sur le cloud.
  • Laravel est équipé d'un système de cache unifié.
  • Laravel autorise la mise en cache des routes.

précédentsommairesuivant

Copyright © 2018 Maurice Chavelli. Aucune reproduction, même partielle, ne peut être faite de ce site ni 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.