Les débuts peuvent souvent représenter un véritable défi. Même pour des développeurs expérimentés comme nous, se plonger dans de nouvelles technologies s’accompagne fréquemment de difficultés. Comprendre les principes qui sous-tendent le développement d’un plugin Contao et les exploiter pour enrichir son site Contao avec des plugins créatifs demande du temps. Bien que la documentation offre une bonne vue d’ensemble, on se heurte rapidement à certaines limites dès que les questions deviennent plus spécifiques.
C’est pour cette raison que nous avons décidé de créer un guide expliquant comment développer un simple plugin Contao « Hello World ». Cet article se veut accessible et fournit les bases nécessaires pour créer vos propres plugins Contao – aussi bien pour le frontend que pour le développement backend.
Dans cet article, vous apprendrez :
- comment poser les bases d’un plugin
- comment afficher un texte « Hello World » dans le frontend
- quelle structure adopter pour les données
- comment établir une connexion à la base de données
À la fin, vous aurez programmé un plugin Contao « Hello World » capable d’afficher des messages définis dans le backend.
Si vous avez besoin d’aide pour le développement ou le support de vos sites web, n’hésitez pas à consulter nos solutions CMS ou notre support pour sites web et applications web. Nous serons ravis de vous accompagner.
Le code du plugin est disponible sur GitHub via ce lien. Vous pouvez bien entendu l’utiliser à des fins de test.
La structure de fichiers du plugin Contao
La structure de fichiers d’un plugin Contao doit impérativement être respectée afin que le Contao Manager reconnaisse le plugin comme tel et le charge correctement.
Plugin Contao : structure de base
Dans un premier temps, il est nécessaire de configurer le plugin Contao de manière à ce qu’il soit reconnu et installé par le Contao Manager. Pour cela, les classes suivantes doivent être créées et adaptées en conséquence.
Configuration de Composer
Contao utilise le PHP Composer pour gérer les modules, extensions et plugins. C’est pourquoi notre plugin Contao nécessite également un fichier de configuration Composer, situé à la racine du plugin. Si vous utilisez le fichier JSON d’exemple de Composer, veillez à adapter correctement toutes les valeurs marquées par un #.
./composer.json
{
"name": "time4digital/dylans-hello-world-bundle",
"description": "Dylan's Hello World Plugin",
"license": "LGPL-3.0-or-later",
"type": "contao-bundle",
"version": "0.0.4",
"authors": [
{
"name": "Ribeiro de Serra Dylan",
"homepage": "https://www.time4digital.lu"
}
],
"homepage": "https://contao.org",
"support": {
"issues": "https://github.com/ridy01-backup/contao-plugins/issues",
"source": "https://github.com/ridy01-backup/contao-plugins"
},
"require": {
"php": "^8.1",
"contao/core-bundle": "^4.13 || ^5.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.5",
"contao/manager-plugin": "^2.0",
"phpunit/phpunit": "^9.5",
"symfony/phpunit-bridge": "^6.1"
},
"conflict": {
"contao/manager-plugin": "<2.0 || >=3.0"
},
"autoload": {
"psr-4": {
"Time4digital\\\\DylansHelloWorldBundle\\\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Time4digital\\\\DylansHelloWorldBundle\\\\Tests\\\\": "tests/"
}
},
"config": {
"allow-plugins": {
"bamarni/composer-bin-plugin": true,
"contao-components/installer": true,
"contao/manager-plugin": true
}
},
"extra": {
"bamarni-bin": {
"bin-links": false,
"target-directory": "tools"
},
"contao-manager-plugin": "Time4digital\\\\DylansHelloWorldBundle\\\\ContaoManager\\\\Plugin"
},
"scripts": {
"all": [
"@unit-tests",
"@ecs",
"@phpstan"
],
"ecs": "@php tools/ecs/vendor/bin/ecs check src tests --config ecs.php --fix --ansi",
"phpstan": "@php tools/phpstan/vendor/bin/phpstan analyze --ansi",
"unit-tests": "@php vendor/bin/phpunit --colors=always"
}
}
Structure de la classe Bundle
La classe Bundle sert à regrouper vos ressources.
Elle ne nécessite aucune configuration particulière : il suffit simplement
d’étendre la classe Symfony Bundle.
./src/DylansHelloWorldBundle.php
<?php
namespace Time4digital\DylansHelloWorldBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class DylansHelloWorldBundle extends Bundle
{
}
Structure de la classe Plugin de Contao
La classe Plugin permet de charger le bundle afin que le Contao Manager
sache qu’il s’agit bien d’un plugin Contao.
./src/ContaoManager/Plugin.php
<?php
declare(strict_types=1);
namespace Time4digital\DylansHelloWorldBundle\ContaoManager;
use Contao\ManagerPlugin\Bundle\BundlePluginInterface;
use Contao\ManagerPlugin\Bundle\Config\BundleConfig;
use Contao\ManagerPlugin\Bundle\Parser\ParserInterface;
use Contao\CoreBundle\ContaoCoreBundle;
use Time4digital\DylansHelloWorldBundle\DylansHelloWorldBundle;
class Plugin implements BundlePluginInterface
{
/**
* {@inheritdoc}
*/
public function getBundles(ParserInterface $parser)
{
return [
BundleConfig::create(DylansHelloWorldBundle::class)
->setLoadAfter([ContaoCoreBundle::class]),
];
}
}
Structure de la classe Dependency
La classe Dependency permet de charger les fichiers de configuration et de services.
./src/DependencyInjection/DylansHelloWorldExtension.php
<?php
declare(strict_types=1);
namespace Time4digital\DylansHelloWorldBundle\ContaoManager;
use Contao\ManagerPlugin\Bundle\BundlePluginInterface;
use Contao\ManagerPlugin\Bundle\Config\BundleConfig;
use Contao\ManagerPlugin\Bundle\Parser\ParserInterface;
use Contao\CoreBundle\ContaoCoreBundle;
use Time4digital\DylansHelloWorldBundle\DylansHelloWorldBundle;
class Plugin implements BundlePluginInterface
{
/**
* {@inheritdoc}
*/
public function getBundles(ParserInterface $parser)
{
return [
BundleConfig::create(DylansHelloWorldBundle::class)
->setLoadAfter([ContaoCoreBundle::class]),
];
}
}
Les fichiers de configuration et de services
Les fichiers de configuration et de services servent à relier les classes que vous créez au sein du bundle, afin qu’elles soient chargées et utilisables lors de l’installation.
./src/Resources/config/config.yml
imports:
- { resource: services.yml }
./src/Resources/config/services.yml
services:
_defaults:
autowire: true
autoconfigure: true
La base du plugin est ainsi mise en place. Vous pourriez maintenant compresser le plugin,
le téléverser via le Contao Manager et l’installer.
Mais cela ne suffit pas encore pour afficher « Hello World » dans le frontend.
Structure du module frontend « Hello World » de Contao
Dans cette section, nous créons un module frontend qui affichera « Hello World »
dans le frontend.
Le module charge également un fichier CSS et un fichier JS.
Structure de la classe du module frontend
La classe du module sert à créer un module dans le backend, qui pourra ensuite être intégré dans un article ou un autre élément de contenu.
./src/Module/DylanHelloWorldModule.php
<?php
namespace Time4digital\DylansHelloWorldBundle\Module;
use Contao\Module;
// Déclaration des fichiers CSS et JS.
// Format : bundles/nomDuBundle/nomDuFichier.extension
$GLOBALS['TL_CSS'][] = 'bundles/dylanshelloworld/styles.css';
$GLOBALS['TL_JAVASCRIPT'][] = 'bundles/dylanshelloworld/scripts.js';
class DylanHelloWorldModule extends Module
{
// Définition du nom du template.
// Le nom doit correspondre au template situé dans
// ./src/Resources/contao/templates
protected $strTemplate = 'mod_helloWorld';
protected function compile()
{
// Avec $this->Template->nomVariable, vous créez des variables
// qui pourront ensuite être utilisées dans le template.
$this->Template->message = 'Hello World!';
}
}
Ajouter le module frontend au backend
Le fichier de configuration PHP sert à rendre les modules disponibles dans le backend.
./src/Resources/contao/config/config.php
<?php
use Time4digital\DylansHelloWorldBundle\Module\DylanHelloWorldModule;
// Modules frontend
// Sous « miscellaneous », un nouvel onglet nommé
// « Hello World Plugin » sera créé et chargera notre module frontend.
$GLOBALS['FE_MOD']['miscellaneous']['Hello World Plugin'] = DylanHelloWorldModule::class;
Fichier de template pour le module frontend
Le fichier de template sert de conteneur pour le module. Vous y intégrez les données définies dans le module et construisez ainsi votre structure HTML.
./src/Resources/contao/templates/modules/mod_helloWorld.html5
<?php $this->extend('block_searchable'); ?>
<?php $this->block('content'); ?>
<!-- Ici, nous utilisons la valeur de la variable message -->
<div class="dylan-hello-world-container">
<?= $this->message; ?>
</div>
<?php $this->endblock(); ?>
Intégrer du CSS et du JS dans le module Contao
Vous pouvez intégrer vos propres fichiers CSS et JS dans le module Contao, comme défini ci-dessus. Il suffit de respecter la structure de fichiers suivante (le nom des fichiers n’a pas d’importance) :
./src/Resources/public/scripts.js
./src/Resources/public/styles.css
… et voilà ! Vous pouvez maintenant créer un nouveau module dans le backend sous Thèmes > Modules frontend et choisir simplement le plugin Contao « Hello World » comme type de module.
Ensuite, rendez-vous dans un article (ou un autre élément de contenu) et sélectionnez le module que vous venez de créer.
Ouf… c’était déjà pas mal de travail ! Mais ce n’est pas encore tout. Certes, notre module frontend suffit pour afficher quelque chose de visuel dans le frontend avec des fonctionnalités PHP. Mais que se passe-t-il si nous voulons également charger des données depuis la base de données et les gérer via le backend Contao ?
C’est précisément ce point que nous allons aborder en détail dans la section suivante.
Module backend Contao : structure et intégration de la base de données
Dans cette section, nous créons un module backend qui charge des données depuis
une table nommée tl_messages et permet également de les modifier.
Le tout sera visible dans le backend Contao via un point de menu dédié.
Nous étendrons également le module frontend afin qu’il puisse lire les données
issues du backend.
Création de la table de base de données avec DCA
À l’aide du fichier DCA PHP, vous pouvez indiquer à Contao
que vous souhaitez créer une nouvelle table de base de données.
Cela vous permet de configurer la structure de la table et de définir
la manière dont les données seront affichées.
./src/Resources/contao/dca/tl_messages.php
<?php
use Contao\DC_Table;
// Nom de la table ; le fichier PHP doit être nommé en conséquence.
$GLOBALS['TL_DCA']['tl_messages'] = [
// Définition de ce qui est affiché dans le backend Contao :
'palettes' => [
'default' => '{messages_legend},message;',
],
// Définition des champs SQL :
'fields' => [
// Ce champ est obligatoire.
'id' => [
'sql' => "int(10) unsigned NOT NULL auto_increment",
],
// Ce champ est obligatoire.
'tstamp' => [
'sql' => "int(10) unsigned NOT NULL default '0'",
'label' => 'TS',
],
'message' => [
'inputType' => 'text',
'eval' => [
'tl_class' => 'w50',
'maxlength' => 255,
],
'sql' => "varchar(255) NOT NULL default ''",
],
],
// Définition des clés et autres attributs.
'config' => [
'dataContainer' => DC_Table::class,
'sql' => [
'keys' => [
'id' => 'primary',
],
],
],
// Définition de l’affichage dans le backend Contao.
'list' => [
'sorting' => [
'mode' => 1,
],
'operations' => [
'edit',
'delete',
],
'label' => [
'fields' => ['id', 'message'],
'showColumns' => true,
],
],
];
Traductions des champs
À l’aide du fichier de traduction XLF, vous pouvez définir les libellés
et descriptions des champs pour différentes langues.
./src/Resources/contao/languages/en/tl_messages.xlf
<?xml version="1.0" ?>
<xliff version="1.1">
<!-- Format : contao/languages/LANGUE/TABLE.php -->
<file
datatype="php"
original="contao/languages/en/tl_messages.php"
source-language="en"
>
<body>
<!-- Nom de la légende dans le backend Contao -->
<trans-unit id="tl_messages.messages_legend">
<source>Messages</source>
</trans-unit>
<!-- Libellé du champ « Message » -->
<trans-unit id="tl_messages.message.0">
<source>Message</source>
</trans-unit>
<!-- Description du champ « Message » -->
<trans-unit id="tl_messages.message.1">
<source>Your individual message.</source>
</trans-unit>
</body>
</file>
</xliff>
Ajouter le module backend au backend Contao
Le fichier de configuration PHP doit maintenant être adapté, comme pour le module frontend.
./src/Resources/contao/config/config.php
<?php
use Time4digital\DylansHelloWorldBundle\Module\DylanHelloWorldModule;
// Modules frontend
// Sous « miscellaneous », un nouvel onglet nommé
// « Hello World Plugin » sera créé et chargera notre module frontend.
$GLOBALS['FE_MOD']['miscellaneous']['Hello World Plugin'] = DylanHelloWorldModule::class;
// Modules backend
// Sous la catégorie de menu « content », une nouvelle entrée
// nommée « Messages » apparaîtra et permettra de gérer la table tl_messages.
$GLOBALS['BE_MOD']['content']['Messages'] = [
'tables' => ['tl_messages'],
];
Afficher les données du backend dans le frontend
Pour cela, le template et le module frontend doivent être adaptés.
./src/Module/DylanHelloWorldModule.php
<?php
namespace Time4digital\DylansHelloWorldBundle\Module;
use Contao\Module;
// Déclaration des fichiers CSS et JS.
// Format : bundles/nomDuBundle/nomDuFichier.extension
$GLOBALS['TL_CSS'][] = 'bundles/dylanshelloworld/styles.css';
$GLOBALS['TL_JAVASCRIPT'][] = 'bundles/dylanshelloworld/scripts.js';
class DylanHelloWorldModule extends Module
{
// Définition du nom du template.
// Le nom doit correspondre au template situé dans
// ./src/Resources/contao/templates
protected $strTemplate = 'mod_helloWorld';
protected function compile()
{
// Avec $this->Template->nomVariable, vous créez des variables
// qui pourront ensuite être utilisées dans le template.
$this->Template->message = 'Hello World!';
// La classe Module nous permet de charger rapidement
// des entrées de base de données via la classe Database de Contao.
// Ici, nous récupérons les données via une requête SQL.
try {
$objEntries = $this->Database->execute(
"SELECT * FROM tl_messages"
);
$this->Template->entries = $objEntries->fetchAllAssoc();
} catch (\Exception $e) {
// En cas d’erreur, un tableau vide est simplement
// transmis au template.
$this->Template->entries = [];
}
}
}
./src/Resources/contao/templates/modules/mod_helloWorld.html5
<?php $this->extend('block_searchable'); ?>
<?php $this->block('content'); ?>
<!-- Ici, nous utilisons la valeur de la variable message -->
<div class="dylan-hello-world-container">
<?= $this->message; ?>
</div>
<!--
Ici, nous parcourons le tableau entries et récupérons
pour chaque élément la valeur du message
-->
<div class="dylans-hello-world-live-container">
<ul>
<?php foreach ($this->entries as $entry): ?>
<li><?= $entry["message"]; ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php $this->endblock(); ?>
Enfin, nous y sommes arrivés ! Après de nombreux hauts et bas,
nous avons atteint le sommet. Place à la satisfaction ! ;-)
Après la réinstallation du plugin Contao, un nouveau point de menu
nommé « Messages » devrait désormais être disponible.
Il permet de créer de nouveaux messages, qui seront ensuite
affichés par notre module frontend.
Nous espérons que cet article de blog vous a permis d’en apprendre davantage sur le développement de plugins Contao.
Il est important de préciser qu’il existe plusieurs manières de mettre en œuvre ce plugin et que cet article ne représente qu’un exemple. Vous êtes entièrement libre de développer votre plugin Hello World comme vous le souhaitez, tant que la base est correcte et que le Contao Manager le reconnaît et l’installe comme plugin.
Vous trouverez plus d’informations ici.
Vous pouvez également télécharger le plugin directement via ce lien et bien sûr le faire évoluer.
Merci beaucoup d’avoir lu cet article ! J’espère que vous y avez pris du plaisir et que vous avez appris quelque chose. Bonne continuation et amusez-vous bien en développant !
L’article peut également être lu en anglais sur Medium.