Créer un module - Étape 3 : Table enfant

Dans ce tutoriel, nous allons créer une table enfant pour notre module de gestion de collection de CD. Nous allons répondre à un besoin simple : comment gérer les pistes des albums qui dépendent d'un CD ?

Pour répondre à ce besoin, nous allons avoir besoin d'une table enfant, qui contiendra uniquement le titre d'une piste ainsi que sa durée. La table sera liée à la table tl_cd créée précédemment à l'aide d'une clé étrangère.

Cette table sera nommée tl_cd_song

Prérequis

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

Configuration

La configuration du module doit être mise à jour pour utiliser la table que nous allons créer ensuite. Nous avons jusqu'à maintenant le code suivant dans notre fichier config/config.php :

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

Nous allons modifier la deuxième ligne de ce fichier pour ajouter notre nouvelle table :

    'tables' => array('tl_cd', 'tl_cd_song'),

Table parent

Nous allons modifier la table parent, c'est-à-dire tl_cd, pour indiquer l'existence d'une table enfant. Pour se faire, nous allons rajouter, dans la partie config du fichier DCA la ligne suivante :

'ctable'                      => array('tl_cd_song')

Aussi, il va être nécessaire de pouvoir faire le lien entre un enregistrement de la table parent (un album) avec des enregistrements de la table enfant (des pistes). Nous allons donc ajouter une opération, qui va être un bouton d'action pour chaque ligne de la liste, situé au même endroit que les boutons de modification, suppression, etc .. Nous allons ajouter le code suivant :

    'songs' => array
    (
        'label'               => &$GLOBALS['TL_LANG']['tl_cd']['songs'],
        'href'                => 'table=tl_cd_song',
        'icon'                => 'system/modules/cd_collection/assets/songs.png'
    )

Nous n'oublierons pas d'ajouter l'icône et de faire la traduction pour cette opération.

Table enfant

Nous devons maintenant créer notre table enfant destinée à gérer les pistes d'un album. Nous allons créer le fichier tl_cd_song dans le répertoire de notre module.

Notre fichier DCA sera composé du code suivant :

<?php

/**
 * Table tl_cd_song
 */
$GLOBALS['TL_DCA']['tl_cd_song'] = array
(

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

    // List
    'list' => array
    (
        'sorting' => array
        (
            'mode'                    => 4,
            'fields'                  => array('title'),
            'headerFields'            => array('title', 'artist', 'year', 'genre'),
            'child_record_callback'   => array('tl_cd_song', 'generateSongRow')
        ),
        '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_song']['edit'],
                'href'                => 'act=edit',
                'icon'                => 'edit.gif'
            ),
            'copy' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['tl_cd_song']['copy'],
                'href'                => 'act=paste&mode=copy',
                'icon'                => 'copy.gif'
            ),
            'cut' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['tl_cd_song']['cut'],
                'href'                => 'act=paste&mode=cut',
                'icon'                => 'cut.gif'
            ),
            'delete' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['tl_cd_song']['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_song']['show'],
                'href'                => 'act=show',
                'icon'                => 'show.gif'
            )
        )
    ),

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

    // Fields
    'fields' => array
    (
        'id' => array
        (
            'sql'                     => "int(10) unsigned NOT NULL auto_increment"
        ),
        'pid' => array
        (
            'sql'                     => "int(10) unsigned NOT NULL default '0'"
        ),
        'tstamp' => array
        (
            'sql'                     => "int(10) unsigned NOT NULL default '0'"
        ),
        'title' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cd_song']['title'],
            'inputType'               => 'text',
            'eval'                    => array('mandatory'=>true, 'maxlength'=>128, 'tl_class'=>'w50'),
            'sql'                     => "varchar(128) NOT NULL default ''"
        ),
        'duration' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cd_song']['duration'],
            'inputType'               => 'text',
            'eval'                    => array('mandatory'=>true, 'maxlength'=>8, 'tl_class'=>'w50'),
            'sql'                     => "varchar(8) NOT NULL default ''"
        )
    )
);


/**
 * Provide miscellaneous methods that are used by the data configuration array
 */
class tl_cd_song extends Backend
{

    /**
     * Generate a song row and return it as HTML string
     * @param array
     * @return string
     */
    public function generateSongRow($arrRow)
    {
        return '<div>' . $arrRow['title'] . ' <span style="padding-left:3px;color:#b3b3b3;">[' . $arrRow['duration'] . ']</span></div>';
    }
}

Nous allons détailler quelques points de ce fichier.

'ptable'                      => 'tl_cd',

Nous avons ajouter la correspondance avec la table parent sur le même principe que la description de la table enfant précédemment.

            'mode'                    => 4,
            'fields'                  => array('title'),
            'headerFields'            => array('title', 'artist', 'year', 'genre'),
            'child_record_callback'   => array('tl_cd_song', 'generateSongRow')

Nous avons défini le mode d'affichage 4 qui, selon la documentation de Contao, correspond à l'affichage des données selon une table parent.

Nous trions les données selon le champs title.

La propriété headerFields va afficher au dessus des enregistrements les champs de la table parent.

La propriété child_record_callback va nous permettre de choisir quels champs vont être affichés dans la liste des enregistrements et comment les mettre en forme. Elle correspond aux propriétés fields et format pour une table parent.

    'pid' => array
        (
            'sql'                     => "int(10) unsigned NOT NULL default '0'"
        ),

Nous avons ajouté le champs pid qui va correspondre à l'identifiant du CD de la table parent. Ce champ est obligatoire dans la définition d'une table enfant.

    public function generateSongRow($arrRow)
    {
        return '<div>' . $arrRow['title'] . ' <span style="padding-left:3px;color:#b3b3b3;">[' . $arrRow['duration'] . ']</span></div>';
    }

Pour finir, nous avons ajouté à notre fichier une classe qui hérite de Backend contenant la fonction appelée avec la propriété child_record_callback qui prend en paramètre un enregistrement et affiche le contenu structuré comme voulu.

Il nous reste à mettre à jour la base de données pour y ajouter les champs de notre table tl_cd_song puis faire la traduction des champs.

Module front office

Nous pouvons maintenant, grâce à la table enfant tl_cd_song gérer les pistes d'un CD dans le back office de Contao mais l'idéal serait de pouvoir afficher sur le front office la liste des pistes pour chaque CD. Nous allons pour cela ajouter un lien sur chaque titre d'album et renvoyer vers une page avec la liste des pistes rattachées à cet album.

Nous allons pour cela modifier le module front office de la liste des CD en apportant des modification sur la vue (pour ajouter le lien sur le titre du CD) mais il va être nécessaire auparavant de renseigner dans le module quelle page correspond à la liste des pistes d'un CD.

Nous allons donc créer dans le back office une nouvelle page intitulée Liste des pistes d'un album dans laquelle nous ajouterons le module front office que nous allons créer maintenant.

Nous allons, comme pour le module de la liste des CD, ajouter au fichier config/config.php la ligne suivante :

$GLOBALS['FE_MOD']['miscellaneous']['cd_list_song'] = 'ModuleCdListSong';

avec son fichier de langue correspondant (languages/fr/modules.php) :

$GLOBALS['TL_LANG']['FMD']['cd_list_song'] = array('Liste des pistes d\'un CD', 'Ajoute une liste des pistes d\'un CD au site.');

Nous allons ensuite ajouter à la palette de notre module Liste de CD le champ qui va définir la page correspondant à la liste des pistes. Nous allons pour cela créer le fichier dca/tl_module.php pour surcharger la table tl_module.

<?php

/**
 * Add palettes to tl_module
 */
$GLOBALS['TL_DCA']['tl_module']['palettes']['cd_list']   = '{title_legend},name,headline,type,jumpTo;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID,space';

Nous rajoutons à la palette de notre module le champ jumpTo qui est un sélecteur de page de Contao. Ce champ existe déjà dans d'autres modules, nous n'avons donc pas besoin de redéfinir son type, label, sql, etc .. et nous n'avons pas non plus besoin de mettre à jour la base de données.

Nous mettons ensuite à jour le module ModuleCdList.phppour ajouter dans la fonction compile la ligne suivante :

$this->Template->childLink = $this->generateFrontendUrl(\PageModel::findByPk($this->jumpTo)->row());

Avec ce code, nous passons à la vue une variable childLink qui construit une url de type front end basée sur la page sélectionnée dans la configuration de notre module.

Nous pouvons donc mettre à jour la template templates/mod_cdlist.html5 en modifiant la ligne

<h3><?php echo $cd->title ?></h3>

par

<h3><a href="<?php echo $this->childLink ?>?id=<?php echo $cd->id ?>"><?php echo $cd->title ?></a></h3>

Ainsi, nous avons donc, pour chaque CD dans la liste, un lien correspondant à la page des pistes d'un CD et avec comme paramètre d'URL l'ID du CD. Nous pouvons donc mettre en place le module de la liste des pistes en créant le fichier modules/ModuleCdListSongqui va contenir le code suivant :

<?php

namespace CdCollection;

/**
 * Class ModuleCdListSong
 *
 * Front end module "cd list song".
 */
class ModuleCdListSong extends \Module
{

    /**
     * Template
     * @var string
     */
    protected $strTemplate = 'mod_cdlistsong';

    /**
     * Display a wildcard in the back end
     * @return string
     */
    public function generate()
    {
        if (TL_MODE == 'BE')
        {
            $objTemplate = new \BackendTemplate('be_wildcard');

            $objTemplate->wildcard = '### ' . utf8_strtoupper($GLOBALS['TL_LANG']['FMD']['cd_list_song'][0]) . ' ###';
            $objTemplate->title = $this->headline;
            $objTemplate->id = $this->id;
            $objTemplate->link = $this->name;
            $objTemplate->href = 'contao/main.php?do=themes&table=tl_module&act=edit&id=' . $this->id;

            return $objTemplate->parse();
        }

        return parent::generate();
    }

    /**
     * Generate the module
     */
    protected function compile()
    {
        $id_cd = \Input::get('id');
        $this->Template->songs = \CdSongModel::findByPid($id_cd);
    }
}

Il est nécessaire au préalable de créer le fichier models/CdSongModel et de le compléter sur le même modèle que CdModel à condition bien sûr de changer le nom de la table. Nous remarquons dans notre module l'utilisation d'une classe de Contao Input qui va récupérer les données en GET ou en POST. L'équivalent ici aurait été de faire $id_cd = $_GET['id'];.

Ensuite, nous récupérons la liste des listes du CD définit par son identifiant. Pour cela nous utilisons la méthode magique des Modèles de Contao findByChamp où Champ est remplacé par le champ voulu de notre table et structuré en CamelCase. Donc ici , pid devient Pid. Cette méthode retourne, comme findAll, une collection.

Nous pouvons ensuite créer notre vue et afficher la liste des pistes du CD correspondant en créant le fichier templates/mod_cdlistsong.html5 et y ajouter le code suivant :

<div class="songs">
    <?php if ($this->songs): ?>
        <?php foreach ($this->songs as $song): ?>
            <div class="song">
                <div class="title"><?php echo $song->title ?></div>
                <div class="duration"><?php echo $song->duration ?></div>
            </div>
        <?php endforeach; ?>
    <?php endif; ?>
</div>

Nous n'oublierons pas de créer les autoloads pour les fichiers que nous venons d'ajouter et notre module est maintenant terminé. Nous avons donc, sur le front office, la liste de nos CD ainsi que les pistes correspondantes que nous pouvons gérer sur le back office.

Améliorations possibles

Ce module est très basique et de nombreuses améliorations sont possibles. En voici quelques unes :

  • Afficher les détails de l'album dans la page de la liste des pistes
  • Ajouter un lien de retour dans cette même page
  • Afficher un message s'il n'y a pas de CD / de pistes
  • Ajouter un contrôle sur l'identifiant passé en paramètre sur la page de la liste des pistes et afficher un message d'erreur si le CD n'existe pas

Retour

comments powered by Disqus