Se familiariser avec un univers de programmation prend toujours un certain temps. Bien qu’excellente, la documentation de ProcessWire n’offre pas un portrait global de la structure d’un site. Comme j’ai dû maintes fois expliquer la « philosophie » et les « comportements » du CMS, j’ai pensé rédiger cette introduction.
Le lecteur est bien entendu invité à parcourir la documentation officielle et à se joindre aux groupes de discussion. Il existe également une infolettre hebdomadaire. La grande simplicité du CMS/CMF cache une puissance qu’il faut apprendre à harnacher.
Je ne prétends pas tout connaître des engrenages en jeu lorsqu’on appelle une page dans ProcessWire. Cet article est, pour ainsi dire, le tour du propriétaire.
La structure des fichiers
Le diagramme ci-dessous illustre la structure de base des fichiers et répertoire. Cette structure laisse entrevoir le chemin parcouru avant de pouvoir afficher une page.
Deux répertoires principaux : wire
et site
.
Le répertoire wire
contient le code de ProcessWire. On n’y touche évidemment pas. Lors d’une mise à jour, ce répertoire est automatiquement renommé en .wire-version-number
et le nouveau code est copié dans un nouveau wire
. C’est le principe de toute mise à jour dans ce CMS. Ainsi, si on veut revenir en arrière, il suffit de détruire la version actuelle et renommer le répertoire précédent avec son nom d’origine. (Pour les modules, on ne fait qu’enlever le point. Par exemple, en mettant à jour le module TracyDebugger, l’ancien code sera dorénavant dans .TracyDebugger
. ProcessWire ne lira pas le code dans ce type de répertoire ainsi nommé). On veillera, au besoin, à exclure du GIT ces répertoires d’archive.
À la racine du site se trouve le seul fichier central index.php
. La présence d’un fichier composer.json indique qu’on peut greffer, grâce à Composer, d’autres librairies PHP qui seront placées dans le répertoire vendor
. Ce sera au programmeur d’accéder à ces librairies à travers les différents fichiers décrits plus bas.
Toute la programmation d’un site se déroule dans le répertoire site
. On y reviendra.
La base de données
L’interface d’administration permet de créer les champs et les modèles de page, ainsi que d’installer des modules. Très peu est inscrit pour ProcessWire dans cette base de données. Chaque champ possédera sa table. Les tables de ProcessWire sont principalement cache
(que l’on peut vider), pages
, modules
, templates
.
Généralement, on ne s’occupe pas trop de la base de données et on n’a pas vraiment à s’inquiéter si on met à jour ProcessWire, car le CMS ne change au grand jamais la structure initiale. Cela n’empêche pas de procéder à des sauvegardes avant de passer à une version supérieure, ce que ProcessWire proposera. On peut dormir tranquille avec ProcessWire. Une mise à jour n’exigera jamais que l’on récrive son code.
ProcessWire possède beaucoup de fonctions pour réunir l’information d’une page (pages()->find(selectors)
est un exemple) et la tâche pourra paraître dantesque d’y aller avec des requêtes SQL directes. Cependant, il existe quelques modules, surtout l’excellent RockFinder3, qui facilitent de tels appels. Le programmeur se verra d’ailleurs confronté aux nombreuses requêtes à la base de données. En réunissant tous les éléments d’une page dans la variable $page
, ProcessWire doit en effet créer un objet complexe dans lequel toutes les transformations auront été faites. Il y a moyen de contourner ces appels par des caches et des modules et améliorer la performance.
On ne peut préparer d’avance une base de données, comme on le ferait avec le framework YII. Les relations entre les tables sont d’un ordre différent de ce que l’on s’attend d’une base de données relationnelle classique. Toutefois, plus on travaille avec ProcessWire, plus on se rend compte que le concept de « page » dans une arborescence permet de lier entre elles les données. Voir l’exemple plus bas. Le contenu devient en soi une base de données relationnelle manipulable à même l’administration du site. La documentation de ProcessWire est à lire pour bien comprendre cela.
Les globales
Avant même de voir apparaître une page, ProcessWire prépare de nombreuses variables globales. Ces variables possèdent des méthodes. L’API les décrit en détail. Elles sont toujours « visibles » dans le code et on peut les appeler de deux manières. Par exemple, l’ensemble des pages du site est pris en compte par la classe $pages et ses méthode. On pourra ainsi rechercher toutes les pages d’un modèle X de cette manière. Les sélecteurs de recherche sont variés et polyvalents :
$result = $pages->find(‘template=x’);
ou
$result = pages()->find(‘template=x’);
Cette deuxième manière permet d’avoir accès dans un éditeur tel PHPStorm à l’ensemble des composantes de l’objet $pages
. La page à afficher est, bien entendu, $page
au singulier (ou page()
).
Notons quelques-unes des autres globales importantes :
- cache() : la cache peut être programmée, catégorisée dans des
namespace
, crée, détruite à volonté. - config() : Il existe deux fichiers
config.php
, un dans le répertoirewire
et l’autre danssite
. L’ensemble des configurations est rassemblé dans cette variable. Notez la présence deconfig.env.php
qui est appelé parconfig.php
. Ce fichier est la configuration locale, jamais mise en GIT. Elle n’est pas obligatoire et nullement documentée dans ProcessWire. C’est la façon chez Spiria de travailler avec des sites de développement lorsque nous sommes plusieurs sur un même projet. On y met surtout les coordonnées de la base de données locale ainsi que certains paramètres de débogage. - session() : L’équivalent sur stéroïdes de la variable
$_SESSION
. D’ailleurs, plusieurs variables PHP sont ainsi rehaussées par ProcessWire. Il en va ainsi deinput()
(qui rassemble tout ce qui est$_GET
et$_POST
). - sanitizer() : Cette classe importante permet d’assainir les entrées de
input()
.
Quand une page est appelée
Supposons la visite de https://www.site.com/unePage
. Nous faisons abstraction pour le moment de tout segment supplémentaire tels ?id=1234&u=abssd
, qui est traité par input()
et les segments d’URL. Nous faisons également abstraction de la lecture préalable des modules actifs.
ProcessWire a déjà reconnu le modèle (template
) associé à l’URL. Dans notre exemple, nous l’avons appelé monModele
. Avant d’accéder à ce modèle, ProcessWire lit le fichier init.php
. À ce stade, l’objet $page
n’est pas complet. On n’a pas encore accès aux champs. Peut être placé dans ce fichier les premiers appels tels la détection de la langue du navigateur ou toute autre considération de programmation. Ce fichier n’est pas obligatoire. ProcessWire ne cherchera à le lire que s’il est présent.
Ensuite, ProcessWire vérifie la présence d’une classe associée au modèle de page monModele
, dans le répertoire classes
. Ceci est un ajout récent à ProcessWire. Si un fichier DefaultPage.php
est présent, chaque page du site recevra les fonctions incluses dans cette classe comme si ces méthodes étaient inhérentes au modèle. Très pratique ! Voir les explications des classes personnalisées.
class DefaultPage extends Page
{
...
}
On peut, pour chaque modèle, étendre la classe DefaultPage:
class MyModelPage extends DefaultPage{
...
}
Ou on peut l’ignorer carrément:
class MyModelPage{
...
}
Le but de ces classes est de généraliser du code. Le dernier exemple est peut-être superflu. Puisque la classe n’est pas rattachée à la précédente, tout aussi bien mettre les fonctions de celles-ci… dans le modèle lui-même, situé dans le répertoire templates
.
Mais nous ne sommes pas encore prêts à visiter ce répertoire. Il nous reste à rencontrer le fichier ready.php
. Comme son nom le suggère, l’objet page()
est maintenant présent à être traité ! On a accès aux champs, aux méthodes. Cela nous permet alors de créer des hooks aux différents modules actifs, mais aussi aux processus déclenchés lors de l’affichage ou l’édition d’une page.
On pourrait, par exemple, intercepter la sauvegarde d’une page avant qu’elle ne se produise, afin d’y faire quelques transformations.
Notons au passage qu’il est possible de créer un ou des modules qui auront une fonction similaire aux fichiers init.php
et ready.php
, car chaque module créé possédera une fonction init()
et ready()
.
Finalement, pour l’affichage de notre page, on arrive à ce qui se trouve dans templates
! Notre modèle monModele.php
doit obligatoirement y être. L’objet $page
est prêt à être consommé par ce fichier. Exemples :
<h1><?= page()->title ?><h1>
<div class="summary"><?= page()->title ?></div>
<div class="photoGallery"><?= page()->getGallery() ?></div>
La variable $user
Il ne faudrait pas oublier cette importante variable. $user
et sa compagne $users
comprennent l’information du visiteur actuel, l’ensemble des usagers inscrits. Si l’usager est anonyme, c’est-à-dire non connecté, il aura le rôle guest
. À la lecture de la page décrite plus haut, on pourrait ainsi détecter la langue en usage de cette simple manière:
$lang = user()->language->name
Un exemple
Prenons comme exemple le site d’un auteur. Le site comprend la description de ses livres publiés, des critiques, une page sur lui ainsi qu’un blogue. La structure des fichiers s’apparente à celle-ci :
Le répertoire view
est optionnel, car il dépend de la stratégie d’organisation du code que vous choisirez. J’ai décrit dans un précédent article les stratégies de rendu d’une page.
Cette structure est grosso modo celle de mon site personnel. L’administration est ainsi construite (décrite ici partiellement):
Vous remarquerez la présence de pages, les critiques, ne possédant pas de fichier PHP correspondant ! C’est toute la beauté de ProcessWire qui entre en jeu ici. Des modèles de pages peuvent coexister sans fichiers puisque leurs champs sont inscrits dans la base de données. Cela permet toutes sortes de stratégies de relations dont celle présente sur mon site.
Chaque livre publié a reçu quelques critiques. J’ai donc créé un modèle de page qui permet de rassembler celles-ci. Outre le texte, le modèle comprend un champ pour la date, l’auteur de la critique, etc.
Pour afficher les critiques d’un livre, on cherche les enfants de la page. La variable $critiques
contient dorénavant toutes les critiques sous forme d’objets:
$critiques = page()->children();
Les afficher pourrait se faire ainsi:
<?php foreach($critiques as $critique): ?>
<div class="critique">
<div class="critiqueText"><?=$critique->body?></div>
<p class="critiqueAuthor">
<?=$critique->author?> • <?=$critique->date?>
</p>
</div>
<?php endforeach; ?>
En conclusion
Cet article est sûrement trop bref pour tout comprendre de ProcessWire. Le concepteur du CMS a pris grand soin de traiter tous les aspects d’une publication Web, que ce soit la pagination, les appels ajax, curl, mail, etc.
Une fois la structure de base comprise, la liberté est de mise dans la programmation, ce qui permet la construction de sites du simple au complexe.