XXVII. Chapitre 27 : Cache et configuration▲
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 :
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.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
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.
Vous trouvez aussi la localisation du fichier (par défaut app/storage/cache) ; ainsi que : le nom de la base et de la table à utiliser dans le cas de stockage en base de données, le paramétrage de memcached, ; le préfixe pour APC ou Memcached.
Quelle que soit la méthode choisie, la classe Cache de Laravel offre les mêmes possibilités de base.
XXVII-A. Les commandes de base▲
Avec une nouvelle installation de Laravel 4 entrez ce code dans le fichier des routes :
Résultat :
1
La 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, et 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 :
Vous pouvez aussi récupérer une valeur dans le cache et la mémoriser si elle n'est pas présente :
XXVII-A-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.
XXVII-A-1-a. 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
'
);
XXVII-A-1-b. Le contrôleur▲
Voici le code du contrôleur app/controllers/DocController.php :
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.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
<?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 lien 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…
XXVII-A-1-c. Les vues▲
Créez le template app/views/layout/master.blade.php :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
<!
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.
On crée ensuite la vue app/views/main.blade.php qui utilise ce template :
2.
3.
4.
5.
6.
7.
@section
('navigation'
)
{{
$nav
}}
@stop
@section
('content'
)
{{
$content
}}
@stop
XXVII-A-1-d. 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 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
body {
background-color:
#ccc
;
}
.container
{
margin-top:
20
px;
}
pre {
background-color:
white
;
}
ul {
list-style-type:
none
;
}
nav ul:
first-child
{
font-size:
20
px;
padding:
10
px;
color:
#27a
;
}
Et voici l'apparence du résultat :
Les liens fonctionnent et la présentation est agréable ( à mon goût, mais vous pouvez changer tout ce que vous voulez ).
XXVII-A-2. 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 :
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.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
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.
XXVII-A-3. 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 :
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.
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 :
Et le contrôleur :
Les paramètres sont ainsi centralisés dans un fichier dédié .