Logo Spiria

Tutoriel : la structure des fichiers dans un site ProcessWire

27 mai 2021.

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.

decorative

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.

decorative

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 :

  1. cache() : la cache peut être programmée, catégorisée dans des namespace, crée, détruite à volonté.
  2. config() : Il existe deux fichiers config.php, un dans le répertoire wire et l’autre dans site. L’ensemble des configurations est rassemblé dans cette variable. Notez la présence de config.env.php qui est appelé par config.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.
  3. 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 de input() (qui rassemble tout ce qui est $_GET et $_POST).
  4. 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 :

decorative

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):

decorative

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.