Voyons aujourd'hui la mise en cache à la sauce Laravel 4. Je rappelle qu'un cache est destiné à accélérer la génération des pages en gardant en mémoire des informations. Le cas le plus classique est celui de requêtes sur une base de données. Si les données ne changent pas fréquemment il est plus efficace de mettre en cache leur résultat plutôt que d'aller chaque fois interroger la base. On peut aussi carrément mettre en cache des pages complètes.
Laravel 4 propose un système de cache simple et efficace qui mémorise par défaut les informations dans un fichier. Regardez le fichier app/config/cache.php :
return array( /* |-------------------------------------------------------------------------- | Default Cache Driver |-------------------------------------------------------------------------- | | This option controls the default cache "driver" that will be used when | using the Caching library. Of course, you may use other drivers any | time you wish. This is the default when another is not specified. | | Supported: "file", "database", "apc", "memcached", "redis", "array" | */ 'driver' => 'file', /* |-------------------------------------------------------------------------- | File Cache Location |-------------------------------------------------------------------------- | | When using the "file" cache driver, we need a location where the cache | files may be stored. A sensible default has been specified, but you | are free to change it to any other place on disk that you desire. | */ 'path' => storage_path().'/cache', /* |-------------------------------------------------------------------------- | Database Cache Connection |-------------------------------------------------------------------------- | | When using the "database" cache driver you may specify the connection | that should be used to store the cached items. When this option is | null the default database connection will be utilized for cache. | */ 'connection' => null, /* |-------------------------------------------------------------------------- | Database Cache Table |-------------------------------------------------------------------------- | | When using the "database" cache driver we need to know the table that | should be used to store the cached items. A default table name has | been provided but you're free to change it however you deem fit. | */ 'table' => 'cache', /* |-------------------------------------------------------------------------- | Memcached Servers |-------------------------------------------------------------------------- | | Now you may specify an array of your Memcached servers that should be | used when utilizing the Memcached cache driver. All of the servers | should contain a value for "host", "port", and "weight" options. | */ 'memcached' => array( array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100), ), /* |-------------------------------------------------------------------------- | Cache Key Prefix |-------------------------------------------------------------------------- | | When utilizing a RAM based store such as APC or Memcached, there might | be other applications utilizing the same cache. So, we'll specify a | value to get prefixed to all our keys so we can avoid collisions. | */ 'prefix' => 'laravel', );
Vous trouvez toutes les possibilités de configuration du cache. Vous voyez en premier la façon dont les informations sont stockées :
- fichier (valeur par défaut)
- base de données
- APC
- Memcached
- Redis
- array
Les commandes de base
Avec une nouvelle installation de Laravel 4 entrez ce code dans le fichier des routes :Route::get('/', function() { Cache::put('un', '1', 2); echo Cache::get('un'); });Résultat :
1La méthode put permet de mémoriser une information, le premier paramètre est la clé, le second la valeur et le troisième la durée de conservation de l'information. La méthode get est l'inverse, elle permet de retrouver une information mémorisée à partir de sa clé. Regardons maintenant le fichier créé : Et son contenu :
1368103994s:1:"1";Il est possible de conserver indéfiniment une information en utilisant la méthode forever :
Cache::forever('un', '1');Et la méthode forget pour en supprimer une :
Cache::forget('un');On a aussi la possibilité d'affecter une valeur par défaut en cas d'inexistence de la valeur :
Cache::get('deux', function() { return '2'; });Vous pouvez aussi récupérer une valeur dans le cache et la mémoriser si elle n'est pas présente :
$value = Cache::remember('un', 1, function() { return 1; });
Un cas d'application
Voyons maintenant un cas d'application qui justifie l'utilisation d'un cache. Si vous allez sur cette page vous trouvez la documentation de Laravel 4. Cette application a été réalisée par Dayle Rees et le code se trouve ici. Je me suis dit qu'il y avait une façon plus simple de procéder en allant directement chercher les informations sur la page du projet. Surtout qu'il y a une page avec la table des matières.
La route
Partez d'une nouvelle installation de Laravel. Dans le fichier des routes mettez juste ce code pour appeler un contrôleur :
Route::get('/{name?}', 'DocController@show');
Le contrôleur
Voici le code du contrôleur app/controllers/DocController.php :
<?php class DocController extends BaseController { /** * Le template de base */ protected $layout = 'layout.master'; /** * Récupération du HTML d'un URL */ private function getHtml($url) { $curl = curl_init($url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); $output = curl_exec($curl); curl_close($curl); return $output; } /** * Récupération d'une balise dans du HTML */ private function getTag($html, $tag, $exclu = null) { $start = strpos($html, '<' . $tag); $end = strpos($html, '</' . $tag); if($exclu == null) { return substr($html, $start, $end - $start + strlen($tag) + 2); } else { $start = strpos($html, '>', $start) + 1; return substr($html, $start, $end - $start); } } /** * Affichage de la page. */ public function show($name = 'introduction') { // Récupération de la page d'index $index = $this->getHtml('https://github.com/laravel/docs/blob/master/documentation.md'); // Sélection de l'index $index = $this->getTag($index, 'article', true); // Création du DOM libxml_use_internal_errors(true); $doc = new DOMDocument; $doc->loadHTML($index); // Changement des liens $nodes = $doc->getElementsByTagName('a'); foreach ($nodes as $node) { $link = $node->getAttribute('href'); $node->setAttribute('href', basename($link)); $node->setAttribute('class', 'btn btn-small'); } // Création du Html élagué return $this->getTag($doc->saveHTML(), 'body', true); // Récupération du contenu $content = $this->getHtml('https://github.com/laravel/docs/blob/master/' . $name . '.md'); // Sélection du texte $content = $this->getTag($content, 'article'); // Création du DOM $doc->loadHTML($content); // Ajout de la classe pour le prettify $nodes = $doc->getElementsByTagName('pre'); foreach ($nodes as $node) { $node->setAttribute('class', 'prettyprint'); } // Ajout classes pour les tableaux $nodes = $doc->getElementsByTagName('table'); foreach ($nodes as $node) { $node->setAttribute('class', 'table table-bordered table-condensed'); } // Création du Html élagué $content = $this->getTag($doc->saveHTML(), 'body', true); // Affichage de la vue $this->layout->content = View::make('main', array('nav' => $nav, 'content' => $content)); } }
En gros le code permet d'aller récupérer le Html de la documentation, de sélectionner juste ce qui est utile, de modifier les liens et d'ajouter quelques classes pour la présentation. Je laisse des classes devenue inutiles, les puristes peuvent les supprimer...
Les vues
Créez le template app/views/layout/master.blade.php :<!DOCTYPE html> <head> <meta charset="utf-8"> <title>Laravel documentation</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> {{ HTML::style('assets/css/bootstrap.min.css') }} {{ HTML::style('assets/css/bootstrap-responsive.min.css') }} {{ HTML::style('assets/css/main.css') }} </head> <body> <div class="container"> <h1>Documentation de Laravel 4</h1> <hr> <div class="row"> <nav class="span3"> @yield('navigation') </nav> <section class="span9"> @yield('content') </section> </div> </div> {{ HTML::script('assets/javascript/run_prettify.js') }} </body>
On charge Bootstrap pour la mise en forme, on prévoit deux localisations pour la navigation et le contenu et on charge Prettify pour la coloration syntaxique du code.
Créez ensuite la vue app/views/main.blade.php qui utilise ce template :
@section('navigation') {{ $nav }} @stop @section('content') {{ $content }} @stop
Les assets
Il ne nous manque plus que les assets : Pour télécharger Bootstrap c'est ici. Et pour Prettify c'est ici. Et voici le code de main.css :body { background-color: #ccc; } .container { margin-top: 20px; } pre { background-color: white; } ul { list-style-type: none; } nav ul:first-child { font-size: 20px; padding: 10px; color: #27a; }Et voici l'apparence du résultat : Les liens fonctionnent et la présentation est agréable (bon enfin c'est à mon goût vous pouvez changer tout ce que vous voulez ).
Mise en cache pour l'application
Maintenant que nous avons une application plutôt laborieuse parce qu'elle doit envoyer des requêtes HTTP pour récupérer des informations, nous pouvons utiliser un cache pour la rendre bien plus rapide. Il faut mettre en cache d'une part la table des matières, et ensuite chacun des chapitres au fur et à mesure qu'ils sont affichés. Voici le contrôleur modifié en conséquence :
class DocController extends BaseController { /** * Le template de base */ protected $layout = 'layout.master'; /** * Récupération du HTML d'un URL */ private function getHtml($url) { $curl = curl_init($url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); $output = curl_exec($curl); curl_close($curl); return $output; } /** * Récupération d'une balise dans du HTML */ private function getTag($html, $tag, $exclu = null) { $start = strpos($html, '<' . $tag); $end = strpos($html, '</' . $tag); if($exclu == null) { return substr($html, $start, $end - $start + strlen($tag) + 2); } else { $start = strpos($html, '>', $start) + 1; return substr($html, $start, $end - $start); } } /** * Affichage de la page */ public function show($name = 'introduction') { // Récupération de l'index $nav = Cache::remember('nav', 60, function() { // Récupération de la page d'index $index = $this->getHtml('https://github.com/laravel/docs/blob/master/documentation.md'); // Sélection de l'index $index = $this->getTag($index, 'article', true); // Création du DOM libxml_use_internal_errors(true); $doc = new DOMDocument; $doc->loadHTML($index); // Changement des liens $nodes = $doc->getElementsByTagName('a'); foreach ($nodes as $node) { $link = $node->getAttribute('href'); $node->setAttribute('href', basename($link)); $node->setAttribute('class', 'btn btn-small'); } // Création du Html élagué return $this->getTag($doc->saveHTML(), 'body', true); }); // Récupération du contenu $content = Cache::remember($name, 60, function() use ($name) { $content = $this->getHtml('https://github.com/laravel/docs/blob/master/' . $name . '.md'); // Sélection du texte $content = $this->getTag($content, 'article'); // Création du DOM libxml_use_internal_errors(true); $doc = new DOMDocument; $doc->loadHTML($content); // Ajout de la classe pour le prettify $nodes = $doc->getElementsByTagName('pre'); foreach ($nodes as $node) { $node->setAttribute('class', 'prettyprint'); } // Ajout classes pour les tableaux $nodes = $doc->getElementsByTagName('table'); foreach ($nodes as $node) { $node->setAttribute('class', 'table table-bordered table-condensed'); } // Création du Html élagué return $this->getTag($doc->saveHTML(), 'body', true); }); // Affichage de la vue $this->layout->content = View::make('main', array('nav' => $nav, 'content' => $content)); } }
Maintenant notre application devient performante. J'ai réglé la durée de mémorisation à 60 minutes, ce qui semble très raisonnable dans ce cas parce que la documentation change rarement.
Configuration
On peut améliorer l'ergonomie de notre application en utilisant un fichier de configuration pour la durée de mémorisation et le titre de la page. Créez un fichier app/config/doc.php avec ce code :
return array( /* |-------------------------------------------------------------------------- | Titre |-------------------------------------------------------------------------- | | Titre qui apparaît en haut de la page | | Défaut : 'Documentation de Laravel 4' | */ 'title' => 'Documentation de Laravel 4', /* |-------------------------------------------------------------------------- | Durée |-------------------------------------------------------------------------- | | Durée de mémorisation pour le cache en minutes | | Défaut : 60 | */ 'timer' => 60 );
Il ne reste plus qu'à modifier le template app/views/layout/master.blade.php :
<h1>{{ Config::get('doc.title') }}</h1>
Et le contrôleur :
$nav = Cache::remember('nav', Config::get('doc.timer'), function()
$content = Cache::remember($name, Config::get('doc.timer'), function() use ($name)
Les paramètres sont ainsi centralisés dans un fichier dédié .
Par bestmomo
Nombre de commentaires : 3