Projet TODD
Stage LP-MIAW | L2
Sylvain CHAMBON
2 juin 2026
Unilim
  1. Présentation entreprise
  2. Présentation projet
  3. Interface
  4. Modules
  5. Bilan

TIRIA

Présentation de l'entreprise

Tiria

Entreprise

Société de services informatiques

  • Agence Web
  • Éditions de progiciels
  • Applications mobile

Tiria

Équipe

Six membres

  • Fabrice : gérant
  • Martial : direction commmerciale
  • Marlène : assistante de direction
  • Fred et Thibault : développeurs
  • Talina : graphiste

Système TODD

Présentation de ma mission

Système TODD

Qu'est-ce que c'est ?

Système d'affichage digital dynamique

  • Service MyComBox développé par TIRIA
  • Boitier de diffusion
  • Images, Vidéo, Liens créés par applications tierces

Système TODD

Ma mission

Intégration dans le framework maison MMI

  • TODD pensé comme une extension
  • Contenu édité directement depuis l'instance MMI
  • Possibilité de communication directe Boitier MMI

Planning (1)

Semaines Tâches effectuées
1 Observation des services dans l'entreprise
Exploration du système TODD
Propositions d'améliorations (UI et UX)
2 Mise en place de la station de travail (WSL, Docker, TODD)
Débogage
3 Interface de sélection
Création d'un nouveau module Vrai/Faux
4 Nouvelles interface TODD
Création du nouveau module Votes
5 Nouveau dashboard (design + refactorisation)
Conceptualisation et développement de ToddModule (fluent API)
Synchronisation des aperçus

Planning (2)

Semaines Tâches effectuées
6 Veille sur Hidden API ESPN
Nouveau module Pronostics sportifs basé sur ToddModule
Nouveau module Mosaïque basé sur ToddModule
7 Veille API Open-meteo et geo.api.gouv.fr
Nouveau module Météo basé sur ToddModule
Dashboard des boitiers
Découplage SQL
8 Conceptualisation et développement de ToddDevice
API MyComBox
Gestion contenus/boitiers
9 Débogage synchronisation MyComBox
Conception réseau local avec boitiers Raspberry Pi

Interface

Organisation UI/UX de l'extension TODD dans MMI

Tableau de bord : contenus

center

Tableau de bord : création de contenu

center

Tableau de bord : création d'une page météo

center

Tableau de bord : liste des boîtiers

center

Tableau de bord : gestion d'un boîtier particulier

center

Modules

Évolutions dans l'approche de développement des modules

Conception des modules (1/3)

Module
autonome

Exemple : plugins/todd/modules/todd_polls/view.php

todd_polls
├── admin.php
├── dashboard.js
├── form.php
├── handler.php
├── install.php
├── script.js
└── view.php

Exemple : plugins/todd/modules/todd_polls/view.php

<?php
// ...
$votes = $db->getAssocArray("SELECT option_id, count(*) as cnt FROM {DBPFX}todd_polls_votes WHERE poll_id = $poll_id GROUP BY option_id", 'option_id');
$totalVotes = array_sum(array_column($votes, 'cnt'));
?>
<?php ob_start(); ?>

<style>
    .poll-container {
        max-width: 600px;
        margin: 20px auto;
        padding: 20px;
        background: #fff;
        border-radius: 8px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    }

    /* ... */
</style>
<?php $module_head_content = ob_get_clean(); ?>
// ...
<script>
    $(document).ready(function() {
        // Vote Handler
        $('.btn-vote').click(function() {
            var btn = $(this);
            var pollId = btn.data('poll');
            var optionId = btn.data('option');
        // ...
</script>

Conception des modules (2/3)

Module
autonome
Modèle
MVC

Refactorisation de todd_polls (1/2)

todd_poll
├── admin.php
├── assets
│   ├── css
│   │   ├── admin.css
│   │   └── view.css
│   └── js
│       ├── admin.js
│       └── script.js
├── controllers
│   ├── admin.inc.php
│   ├── payload.inc.php
│   └── view.inc.php
├── handler.php
├── install.php
├── modal_add_voter.php
├── modal_import_csv.php
├── models
│   └── ToddPollModel.php
├── script.js
└── view.php

Refactorisation de todd_polls (2/2)

<?php
class ToddPollModel {
    public static function addVoter($contentId, $firstname, $lastname, $email, $pinCode) {
        global $db;
        $sql = "INSERT INTO {DBPFX}todd_poll_voters (content_id, firstname, lastname, email, pin_code)
                VALUES (:content_id, :firstname, :lastname, :email, :pin_code)";

        return $db->dml($sql, [
            ':content_id' => $contentId,
            ':firstname'  => $firstname,
            ':lastname'   => $lastname,
            ':email'      => $email,
            ':pin_code'   => $pinCode
        ]);
    }
// ...
?>

Conception des modules (3/3)

Module
autonome
Modèle
MVC
Classe dédiée

Classe abstraite ToddModule

Propriétés communes aux modules

abstract class ToddModule
{
    protected $instance_id;
    protected $module_code;
    protected $payload = [];

    protected $adminTabs = [];
    protected $adminFields = [];
    protected $currentTabId = null;

    protected $adminViews = [];
  ...
}

Classe abstraite ToddModule

Constructeur

public function __construct($id)
{
    $this->instance_id = (int) $id;

    $row = ToddContentModel::getContentById($this->instance_id);

    if ($row) {
        $this->module_code = $row['module_code'];
        $dbPayload = !empty($row['payload_json']) ? json_decode($row['payload_json'], true) : [];
        $this->payload = array_merge($this->getDefaultPayload(), (is_array($dbPayload) ? $dbPayload : []));
    } else {
        $this->module_code = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', get_class($this)));
        $this->payload = $this->getDefaultPayload();
    }

    $this->setupAdmin();
}

Classe abstraite ToddModule

Fonctions abstraites

abstract protected function getDefaultPayload(): array;

Classe abstraite ToddModule

Fluent API

public function addTab(string $id, string $label, string $icon = 'ph-file'): self
{
    $this->adminTabs[$id] = [
        'id' => $id,
        'label' => $label,
        'icon' => $icon
    ];
    $this->currentTabId = $id;
    return $this;
}

Classe abstraite ToddModule

Fluent API

public function addField(string $name, string $type, array $options = []): self
{
    if (!$this->currentTabId) {
        // Default tab if none added
        $this->addTab('general', 'General', 'ph-gear');
    }

    $this->adminFields[$this->currentTabId][] = [
        'name' => $name,
        'type' => $type,
        'options' => $options
    ];

    return $this;
}

Module todd_bet

Exemple d'utilisation.

├── admin.php
├── assets
│   ├── css
│   │   ├── admin_espn.css
│   │   └── public.css
│   ├── images
│   │   ├── baseball_field.png
│   │   ├── ...
│   └── js
│       ├── admin_espn.js
│       ├── admin.js
│       └── client.js
├── handler.php
├── install.php
├── models
│   ├── ToddBetInstallModel.php
│   └── ToddBetModel.php
├── ToddBet.php
├── view.php
└── views
    ├── modals
    │   └── admin_espn_modal.php
    ├── public_complete.php
    └── public_partial.php

Module todd_bet

Nouvelle classe ToddBet comme extension de ToddModule

class ToddBet extends ToddModule
{
    protected function getDefaultPayload(): array
    {
        return [
            "prono_type" => "1n2",
            "team_a_name" => "",
            "team_a_logo" => "",
            "team_b_name" => "",
            "team_b_logo" => "",
            ...
        ];
    }
    ...
}

Module todd_bet

La fonction setupAdmin() où on construit l'interface.

protected function setupAdmin(): void
{
    $this
        ->addTab("match", "Match", "ph-soccer-ball")
        ->addField("prono_type", "select", [
            "label" => "Mode de jeu",
            "choices" => [
                "1n2" => "1N2 (Anonyme)",
                "score" => "Score Exact (Avec Pseudo)"
            ]
        ])
        ->addRow([
            [
                "name" => "team_a_name",
                "type" => "text",
                "options" => ["label" => "Nom Équipe A"]
            ],
        ...

Module todd_bet

Fichier admin.php

<div class="module-editor hidden" data-module="todd_bet">
<?php
require_once PLUGIN_PATH . 'todd/modules/todd_bet/ToddBet.php';

$toddBetModule = new ToddBet(0);

$reflection = new ReflectionClass($toddBetModule);
$property = $reflection->getProperty('module_code');
$property->setValue($toddBetModule, 'todd_bet');

$toddBetModule->renderAdmin();
?>
</div>

Module todd_bet

Fichier templates/core_admin_shell.php pour générer le HTML de l'interface de création/édition du module.

if (!isset($module) || !($module instanceof ToddModule)) {
    echo '<div class="alert alert-danger">Module configuration error.</div>';
    return;
}

$tabs = $module->getAdminTabs();
$fields = $module->getAdminFields();
$payload = $module->getPayload();
$instance_id = $module->getInstanceId();

Module todd_bet

Fichier templates/core_admin_shell.php

<?php if (!empty($tabs)): ?>
    <ul class="nav nav-tabs mb-3 todd-nav-icons-only" role="tablist">
        <?php $isFirst = true; ?>
        <?php foreach ($tabs as $tabId => $tab): ?>
            <?php
            $tabDomId = $module->getDomId("tab_" . $tabId);
            $paneDomId = $module->getDomId("pane_" . $tabId);
            ?>
            <li class="nav-item" role="presentation">
                <button class="nav-link <?= $isFirst
                    ? "active"
                    ...
                </button>
            </li>
            <?php $isFirst = false; ?>
        <?php endforeach; ?>
    </ul>
<?php endif; ?>

Module ToddMosaic

Module ToddMosaic

ToddModule suffisamment souple pour accepter des surcharges

public function renderAdmin(): void
{
    $this->enqueueAdminAssets();

    $module = $this;

    $templatePath = BASE_PATH . 'plugins/todd/modules/todd_mosaic/views/mosaic_admin_shell.php';

    if (file_exists($templatePath)) {
        require $templatePath;
    } else {
        echo '<div class="alert alert-danger">CMS TODD Fatal Error : Vue administrative Mosaïque introuvable.</div>';
    }
}

Module ToddMosaic

ToddModule suffisamment souple pour accepter des surcharges

public function enqueueAdminAssets(): void
{
    // --- ASSETS JAVASCRIPT ---
    $jsPath = BASE_PATH . 'plugins/todd/modules/todd_mosaic/assets/js/mosaic_admin_shell.js';
    // Utilisation de programmation défensive demandée pour garantir le fonctionnement dev
    $jsVersion = file_exists($jsPath) ? filemtime($jsPath) : '1.0';

    App::registerJS(
        "todd_mosaic_admin_js",
        SITE_URL . "plugins/todd/modules/todd_mosaic/assets/js/mosaic_admin_shell.js?v=" . $jsVersion,
        false,
        false
    );
    App::enqueueJS("todd_mosaic_admin_js");
    ...

Bilan

Points forts & points d'amélioration

Points forts

  • Architecture globale robuste et réutilisable
  • Qualité de l'expérience utilisateur
  • Intégration dans le framework MMI

Points d'améliorations

  • Migration de toutes les modules
  • Code mort
  • Dette technique
  • Tests

Bilan personnel

  • Confrontation au monde réel d'une entreprise de développement logiciel
  • Utilisation de l'IA comme outil de travail

Questions