Créer un module - Étape 1 : Les bases du module

Dans ce tutoriel, nous allons apprendre à créer un nouveau module sur Contao.

Nous allons prendre comme exemple la gestion d'une bibliothèque de CD.

Prérequis

Les compétences suivantes sont requises pour réaliser de ce tutoriel :

  • Bon niveau en PHP (notamment les tableaux)
  • Connaissance de l'architecture MVC
  • Une première approche du back-office de Contao (voir la documentation)

Concept

L'idée du module est donc de s'imaginer que nous avons besoin d'un module pour gérer nos CD de musique. Nous voulons afficher la liste des CD que nous avons et pouvoir en ajouter ou en supprimer.

L'affichage de la liste va se faire sur la partie front-office du site tandis que la gestion des CD va se faire sur la partie back-office.

Contao différencie distinctement ces deux parties, nous aurons donc besoin de deux modules.

Le module back-office est en réalité une table d'enregistrements, souvent reliés à une base de données, sur laquelle on peut effectuer les opérations de gestion classiques (création, modification, duplication, suppression).

Le module front-office est habituellement utilisé pour afficher les données stockées avec le module back-office. Chaque module front-office est construit selon l'architecture Modèle Vue Contrôleur où le modèle représente les appels à la base de données, le contrôleur les différentes actions, fonctions et variables à passer à la vue qui affiche le résultat au format HTML.

Préparer Contao pour le développement

Pour que toute modification du code soit bien prise en compte, il est nécessaire de désactiver le cache interne du back-office. Pour se faire, nous allons dans le menu Configuration, à l'onglet Configuration globale, cocher la case Contourner le cache interne.

Créer le module

Contao possède un outil de création de module qui va créer pour nous les fichiers de bases d'un module. Nous allons utiliser cet outil qui se trouve dans le menu Outils de développement → Créateur d'extension.

Le champ Nom du groupe est utilisé pour identifier un module dans la liste des extensions créées, nous allons mettre Gestion des CD.

Le champ Nom du répertoire est utilisé pour créer le dossier qui va contenir les différents fichiers du module, nous allons mettre cd_collection.

Pour les champs Auteur, Copyright et Licence, nous allons mettre notre nom, l'année de création du module et la licence du module (par exemple LGPL).

Le champ Paquet est utilisé pour le namespace du module. Généralement, nous allons mettre le nom du répertoire mais écrit en CamelCase, donc ici CdCollection.

Nous allons ajouter un module back-office en cochant la case Ajouter un module back-office. Ici, seul le champ Tables du back-office va nous intéresser pour la création du module, nous ajouterons les autres fichiers manuellement plus tard. Nous allons mettre dans le champ tl_cd. A noter que toutes les tables dans Contao doivent êtres préfixées par tl_.

Nous créerons les fichiers du module front-office manuellement plus tard dans le tutoriel.

Nous allons ajouter deux packs de langue, nous allons donc mettre dans le champ Langues la valeur fr,en.

Nous allons ensuite créer les fichiers de l'extension à l'aide du bouton Créer les fichiers.

Le répertoire de notre module est maintenant disponible dans /system/modules/ et est construit comme ci dessous.

arborescence_cd_collection.png

Configuration - Ajout du module dans le menu

Nous allons ajouter notre module dans le menu du back-office de Contao.

Cette configuration va se dérouler dans le fichier config/config.php.

Nous allons ajouter une entrée au tableau contenant les modules back-office.

Dans Contao, la grande majorité des configurations se fait dans le tableau $GLOBALS.

$GLOBALS['BE_MOD']['content']['cd_collection'] = array(
    'tables' => array('tl_cd'),
    'icon'   => 'system/modules/cd_collection/assets/icon.png'
);

Nous renseignons pour le module cd_collection que la table tl_cd sera utilisée et le chemin de l'icône affichée dans le menu.

Notre module est maintenant visible dans le menu Contenu du back-office.

menu_cd_collection.png

Création du module back-office : le fichier DCA

Nous allons maintenant développer notre module back-office. Tout va se faire dans un seul fichier : le DCA. Il a été créé par le créateur d'extension et se situe dans cd_collection/dca/tl_cd.php.

Comme nous pouvons le voir en modifiant le fichier, le DCA est composé de 4 parties importantes : Config, List, Palettes et Fields que nous allons détailler :

Config

    // Config
    'config' => array
    (
        'dataContainer'               => 'Table',
        'enableVersioning'            => true,
        'sql' => array
        (
            'keys' => array
            (
                'id' => 'primary'
            )
        )
    ),

Ici, la valeur de dataContainer est Table, ce qui signifie que les valeurs de notre DCA seront sauvegardées dans une table de la base de données. C'est la valeur qui sera utilisée pour la très grande majorité des cas. Il est tout de même utile de savoir que l'on peut sauvegarder nos données dans un fichier.

La propriété enableVersioning est utile pour afficher par qui et quand une modification a été apportée dans les données de notre table. On peut ainsi revenir en arrière en cas de mauvaise saisie.

Nous reparlerons de la propriété sql quand nous en serons à la partie Fields du DCA.

List

    // List
    'list' => array
    (
        'sorting' => array
        (
            'mode'                    => 1,
            'fields'                  => array(''),
            'flag'                    => 1
        ),
        'label' => array
        (
            'fields'                  => array(''),
            'format'                  => '%s'
        ),
        'global_operations' => array
        (
            'all' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['MSC']['all'],
                'href'                => 'act=select',
                'class'               => 'header_edit_all',
                'attributes'          => 'onclick="Backend.getScrollOffset();" accesskey="e"'
            )
        ),
        'operations' => array
        (
            'edit' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['tl_cd']['edit'],
                'href'                => 'act=edit',
                'icon'                => 'edit.gif'
            ),
            'copy' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['tl_cd']['copy'],
                'href'                => 'act=copy',
                'icon'                => 'copy.gif'
            ),
            'delete' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['tl_cd']['delete'],
                'href'                => 'act=delete',
                'icon'                => 'delete.gif',
                'attributes'          => 'onclick="if(!confirm(\'' . $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] . '\'))return false;Backend.getScrollOffset()"'
            ),
            'show' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['tl_cd']['show'],
                'href'                => 'act=show',
                'icon'                => 'show.gif'
            )
        )
    ),

La propriété sorting définit la façon et l'ordre dans lesquels les données de notre table seront affichées. La façon de lister les données sera définie par mode et l'ordre par flag. Ici, nous allons garder les valeurs par défaut, soit 1 et 1. Les différentes valeurs possibles sont décrites ici https://docs.contao.org/books/api/dca/reference.html#sorting. La propriété fields correspond au champ de référence du mode et de l'ordre d'affichage des données. Nous le compléterons une fois que nous aurons renseigné les champs de notre DCA dans la partie Fields.

La propriété label définit comment chaque ligne, donc chaque donnée, est affichée dans la liste. Elle reprend le ou les champs à écrire et leur format.

La propriété global_operations définit la liste des actions globales du module. Par défaut, l'opération all est définie. Il s'agit de l'édition multiple. Il nous sera possible par la suite de créer nos propres opérations globales.

La propriété operations définit la liste des actions propres à chaque ligne de la liste des données. On retrouve ici par exemple les boutons pour modifier (edit), dupliquer (copy), supprimer (delete) ou encore afficher les détails de la ligne (show).

Palettes

    // Palettes
    'palettes' => array
    (
        '__selector__'                => array(''),
        'default'                     => '{title_legend},title;'
    ),

Les palettes définissent quels champs seront affichés dans le formulaire de création/modification d'un enregistrement; mais surtout dans quel ordre et dans quel onglet ils seront affichés.

Il nous est possible de créer plusieurs palettes et également des sous-palettes mais pour cet exemple nous allons rester avec une seule palette default. Les champs peuvent donc être regroupés dans différentes onglets. Chaque onglet est caractérisé par une légende (le titre de l'onglet) et dans la palette, une légende est entouré d’accolades et possède le suffixe _legend. Dans notre exemple, {title_legend} est un titre d'onglet. Tout ce qui suit fera partie de cet onglet, donc ici title. Les champs sont séparés par une virgule et les onglets par un point-virgule. Voici un exemple simple de deux onglets :

{onglet1_legend},onglet1_champ1,onglet1_champ2;{onglet2_legend},onglet2_champ1;

Fields

    // Fields
    'fields' => array
    (
        'id' => array
        (
            'sql'                     => "int(10) unsigned NOT NULL auto_increment"
        ),
        'tstamp' => array
        (
            'sql'                     => "int(10) unsigned NOT NULL default '0'"
        ),
        'title' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cd']['title'],
            'exclude'                 => true,
            'inputType'               => 'text',
            'eval'                    => array('mandatory'=>true, 'maxlength'=>255),
            'sql'                     => "varchar(255) NOT NULL default ''"
        )
    )

Nous arrivons à la partie la plus importante du DCA : les champs de notre table !

C'est ici que nous allons définir tous nos champs, leurs types, leurs caractéristiques, leurs options

Deux champs sont obligatoires : id et tstamp. id n'a pas besoin d'explication, tstamp est utile pour enregistrer la date à laquelle une ligne est ajoutée/supprimée. Ce champ est très utile car il est géré directement par Contao et est nécessaire pour l'option enableVersioning.

Pour chaque champ, nous devons définir le type sql du champ en base de données. Ensuite, de nombreuses options sont disponibles. Voir la liste ici https://docs.contao.org/books/api/dca/reference.html#fields.

Nous pouvons retenir label qui va servir pour le libellé du champ (et ses traductions), inputType pour afficher le champ dans le formulaire, eval et notamment la propriété mandatory pour rendre le champ obligatoire à la saisie.

Nous connaissons maintenant les principales parties qui composent le fichier DCA et allons l'adapter pour notre module.

Nous avons concrètement besoin, pour la définition d'un CD, des champs suivants : le titre du cd, l'artiste, le genre, l'année, une photo de la pochette, une description. Nous pourrons rajouter par la suite d'autres champs comme la durée, le nombre de chansons, etc.

Nous avons donc la partie fields du fichier DCA suivante :

    // Fields
    'fields' => array
    (
        'id' => array
        (
            'sql'                     => "int(10) unsigned NOT NULL auto_increment"
        ),
        'tstamp' => array
        (
            'sql'                     => "int(10) unsigned NOT NULL default '0'"
        ),
        'title' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cd']['title'],
            'inputType'               => 'text',
            'eval'                    => array('mandatory'=>true, 'maxlength'=>128, 'tl_class'=>'w50'),
            'sql'                     => "varchar(128) NOT NULL default ''"
        ),
        'artist' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cd']['artist'],
            'inputType'               => 'text',
            'eval'                    => array('mandatory'=>true, 'maxlength'=>128, 'tl_class'=>'w50'),
            'sql'                     => "varchar(128) NOT NULL default ''"
        ),
        'genre' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cd']['genre'],
            'inputType'               => 'text',
            'eval'                    => array('mandatory'=>true, 'maxlength'=>32, 'tl_class'=>'w50'),
            'sql'                     => "varchar(32) NOT NULL default ''"
        ),
        'year' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cd']['year'],
            'inputType'               => 'text',
            'eval'                    => array('mandatory'=>true, 'rgxp'=>'digit', 'maxlength'=>4, 'tl_class'=>'w50'),
            'sql'                     => "varchar(4) NOT NULL default ''"
        ),
        'cover' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cd']['cover'],
            'inputType'               => 'fileTree',
            'eval'                    => array('fieldType'=>'radio', 'files'=>true, 'filesOnly'=>true, 'extensions'=>$GLOBALS['TL_CONFIG']['validImageTypes']),
            'sql'                     => "int(10) unsigned NOT NULL default '0'"
        ),
        'description' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cd']['description'],
            'inputType'               => 'textarea',
            'eval'                    => array('rte'=>'tinyMCE'),
            'sql'                     => "text NULL"
        )
    )

Par convention, nous définirons toujours nos noms de champs en anglais.

La plupart des types de champs parlent d'eux-mêmes sauf pour le champ coverfileTree est un sélecteur de fichiers propre à Contao.

Attention : après l'ajout, la modification ou la suppression d'un champ d'un DCA, il est nécessaire d'indiquer à Contao que la base de données est à mettre à jour. Pour ce faire, nous allons nous rendre dans le menu Système → Package management puis cliquer sur l'opération globale Update database puis mettre à jour la base de données. Notre base de données contient désormais les champs définis dans notre fichier DCA.

Nous pouvons maintenant revenir aux précédentes parties de notre fichier DCA dans lesquelles il était nécessaire de définir préalablement les champs de notre module.

Concernant la partie list, nous allons trier les données selon le titre de l'album (il est possible de trier les données sur n'importe quel champ, par exemple l'année).

Nous allons afficher les données comme ceci : Titre de l'album, Artiste.

Nous obtenons donc le code suivant :

        'sorting' => array
        (
            'mode'                    => 1,
            'fields'                  => array('title'),
            'flag'                    => 1
        ),
        'label' => array
        (
            'fields'                  => array('title', 'artist'),
            'format'                  => '%s, %s'
        ),

Concernant la partie palette nous allons ordonner notre formulaire sur deux onglets : dans un premier temps le titre, l'artiste, le genre et l'année puis dans un deuxième temps l'image de la pochette et la description.

Nous obtenons donc le code suivant :

    'palettes' => array
    (
        'default'                     => '{title_legend},title,artist,genre,year;{cover_legend},cover,description'
    ),

Notre formulaire est donc prêt à être affiché et nous pouvons ajouter, depuis le back office, notre premier CD. A un détail près ... Si l'on regarde le formulaire ci dessous, nous pouvons constater que les champs ne sont pas traduits. Bien entendu, il est quand même fonctionnel mais nous pouvons le rendre plus esthétique et plus compréhensible en ajoutant des messages d'aide aux champs.

contao_formulaire.png

La traduction

Traduction de la configuration du module

Cette traduction a son utilité pour ne pas afficher dans le menu le libellé de la configuration du module mais un texte mis en forme (majuscules, accents, etc ..).

Cette traduction se met en place dans le fichier modules.php du répertoire correspondant au code de la langue voulue. Exemple pour français : languages/fr/modules.php.

Nous allons ajouter la ligne suivante dans ce fichier :

$GLOBALS['TL_LANG']['MOD']['cd_collection'] = array('Gestion des CD', 'Gestion de la bibliothèque de CD');

La première valeur sera affichée dans le menu et la deuxième lors du passage de la souris sur le lien.

Traduction des champs du module

Cette traduction se fait dans un fichier qui porte le même nom que notre DCA. Dans notre exemple et pour la langue française, dans le fichier languages/fr/tl_cd.php.

Nous pouvons écrire le code suivant dans ce fichier qui va s'occuper de la traduction et des messages d'aides des champs, des légendes d'onglets et des boutons d'action :

<?php

/**
 * Fields
 */
$GLOBALS['TL_LANG']['tl_cd']['title']       = array('Titre', 'Veuillez saisir un titre.');
$GLOBALS['TL_LANG']['tl_cd']['artist']      = array('Artiste', 'Veuillez saisir le nom de l\'artiste.');
$GLOBALS['TL_LANG']['tl_cd']['genre']       = array('Genre', 'Veuillez saisir le genre de l\'album.');
$GLOBALS['TL_LANG']['tl_cd']['year']        = array('Année', 'Veuillez saisir l\'année de l\'album.');
$GLOBALS['TL_LANG']['tl_cd']['cover']       = array('Image de la pochette', 'Veuillez sélectionner l\'image de la pochette.');
$GLOBALS['TL_LANG']['tl_cd']['description'] = array('Description', 'Veuillez saisir la description de l\'album.');


/**
 * Legends
 */
$GLOBALS['TL_LANG']['tl_cd']['title_legend'] = 'Titre et artiste';
$GLOBALS['TL_LANG']['tl_cd']['cover_legend'] = 'Image et description';


/**
 * Buttons
 */
$GLOBALS['TL_LANG']['tl_cd']['new']    = array('Nouvel album', 'Ajouter un nouvel album');
$GLOBALS['TL_LANG']['tl_cd']['show']   = array('Détails', 'Voir les détails de l\'album ID %s');
$GLOBALS['TL_LANG']['tl_cd']['edit']   = array('Modifier', 'Modifier l\'album ID %s');
$GLOBALS['TL_LANG']['tl_cd']['copy']   = array('Dupliquer', 'Dupliquer l\'album ID %s');
$GLOBALS['TL_LANG']['tl_cd']['delete'] = array('Supprimer', 'Supprimer l\'album ID %s');

Conclusion

Notre module back office est maintenant fonctionnel et traduit en français.

Il est accessible depuis le menu du back office et nous pouvons ajouter/modifier/dupliquer/supprimer des CD dans notre bibliothèque.

La deuxième étape du module va consister à mettre en place un module front office pour afficher la liste de nos CD sur le site et accessible à tout le monde.

Retour

comments powered by Disqus