Réalisation d'un livre d'or avec pdo et approche mvc 1

Réalisation d'un livre d'or avec pdo et approche mvc 1

Description

Les livres d'or font partie des applications dynamiques les plus populaires sur Internet. Ils permettent à aux visiteurs d'un site de déposer une trace écrite de leur passage dans le but d'aider le webmaster à améliorer son site. Nous allons présenter dans ce tutoriel, une manière simple et efficace de développer un livre d'or fonctionnel et sécurisé. Bien entendu, ce dernier s'appuiera sur une base de donnée de type MySQL et son moteur de stockage MyISAM. Dans une optique de structuration de l'application, nous nous appuierons sur le modèle MVC.

Pré-requis

Avant de rentrer dans le vif du sujet, nous considérons que vous êtes déjà à l'aise avec :

  • Les variables, constantes et tableaux,
  • Les fonctions et procédures utilisateurs,
  • Les bases de la programmation orientée objet,
  • L'utilisation minimale (création d'une table, requête SQL SELECT, INSERT) des bases de données. Et notamment avec MySQL.

Vous devrez également vous assurer que votre serveur web fonctionne avec la version 5 de PHP et que l'extension PDO et le driver PDO MySQL sont installés.

Sachez également que ce tutoriel n'a pas l'unique but de vous présenter une solution technique fonctionnelle pour réaliser un livre d'or. Il a également l'ambition (voire même la prétention) de vous apporter des connaissances approfondies de développement PHP5 et de bonnes pratiques.

Présentation et structuration du projet

Un livre d'or est une application dynamique qui permet aux utilisateurs d'un site Internet de déposer des messages d'appréciation. C'est en quelque sorte le recueil des humeurs des visiteurs à l'encontre du site web. Vous l'aurez compris, l'intérêt pour le webmaster est double :

  • Faire remonter des idées et des suggestions pour améliorer son application,
  • Promouvoir son site Internet avec des témoignages positifs d'utilisateurs satisfaits.

L'application que nous allons développer devra répondre aux contraintes suivantes :

  • Poster de nouveaux messages,
  • Contrôler les informations saisies dans le formulaire,
  • Lister les messages,
  • Paginer les résultats,
  • Etre sécurisé contre les injections SQL et les failles XSS,
  • S'appuyer sur le modèle MVC.

Détaillons sommairement ce dernier point.

Qu'est-ce-que le modèle MVC ?

MVC est l'abréviation de «Modèle, Vue, Contrôleur ». C'est est une architecture et une méthode de conception (design pattern) pour le développement d'applications logicielles qui sépare le modèle de données, l'interface utilisateur et la logique de contrôle.

Dans une application web, la couche du modèle est représentée par la base de données, les librairies de fonctions, les classes, les fichiers, les structures de données... Ce sont en fait tous les composants qui permettent de stocker et de manipuler les données.

La vue est la couche logicielle qui assure l'affichage des données à l'utilisateur et l'interface Homme / Machine. Cette couche récupère donc les données brutes du modèle et les formate correctement pour l'utilisateur. Par exemple, le nombre 1 254,67 est ici issu de la vue. Il est en effet formaté pour un site français. Pour un site américain, nous l'aurions écrit 1 254.67. En revanche, dans les deux cas, cette valeur s'écrit 1254.67 dans le modèle (dans une variable de type flottant par exemple).

La dernière couche est le contrôleur. Il s'agit du moteur principal de l'application. Il fait la liaison entre le modèle et la vue. Le contrôleur a la tâche d'analyser la requête de l'utilisateur, d'appeler le modèle adéquat et de retourner la valeur de ce dernier à la vue qui prendra en charge son affichage.

Quel sont les avantages d'utiliser une telle architecture ? Le premier intérêt concerne avant tout la maintenance. En séparant le problème en 3 couches distinctes, l'application deviendra plus facile à maintenir où à faire évoluer. Le second avantage implique la vue. En effet, cet éclatement en 3 couches permet de remplacer la vue aisément sans avoir à toucher au modèle ou bien au contrôleur. Par exemple : changer le wedesign d'un site, proposer différents format d'affichage d'un contenu (XML, XHTML, PDF, image...).

Les besoins

Nous avons défini globalement à quoi correspond le modèle MVC. Arrêtons nous à présent sur la structuration générale de notre livre d'or. De quoi aurons-nous besoin ?

  • Une base de données,
  • Un formulaire,
  • Une page de listing des résultats.

Nous visualisons vaguement deux couches du modèles MVC . Le modèle représenté par la base de données et le formulaire et la page de listing qui feront partie de la vue. Nous construirons le contrôleur petit à petit. Passons désormais à la création de la base de données.

Mise en place de la base de données

Choix du moteur de stockage

Nous utiliserons ici une table MySQL avec un moteur de stockage de type MyISAM. C'est le moteur par défaut de MySQL. Il est rapide, performant et supporte la recherche en texte intégral (fulltext). Néanmoins, ce moteur a le principal défaut de ne pas être conforme à la norme ACID (Atomicité, Cohérence, Isolement, Durabilité) des bases de données. Il ne supporte pas les transactions contrairement au moteur de stockage InnoDB. Malgré tout, nous décidons d'utiliser MyISAM car les enjeux d'intégrité et de cohérence des données ne justifient pas l'emploi d'un moteur de stockage transactionnel. Un livre d'or n'est pas une application critique, contrairement à une application manipulant des données bancaires ou des salaires par exemple.

Structure de la table MySQL

La table MySQL de notre livre d'or accueillera les messages des utilisateurs. Notre formulaire sera composé de 3 champs (pseudo, message et note). Il faut donc au minimum que ces informations soient stockées dans la base de données. Seulement, ces informations ne suffisent pas. Il nous manque tout d'abord la clé primaire de la table. Nous opterons naturellement pour un identifiant unique auto-incrémenté pour chaque nouvel enregistrement. Enfin, nous ajouterons un champ recevant la date d'enregistrement des message afin de pouvoir les ordonner du plus récent au plus ancien à l'affichage.

CREATE TABLE IF NOT EXISTS guestbook (
id INT(7) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
pseudo VARCHAR(20) NOT NULL,
message TEXT NOT NULL,
note TINYINT(2) NOT NULL DEFAULT 5,
creation DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00');

Commencez par exécuter ce code SQL dans votre outil d'administration de votre base de données (PHPMyAdmin, MySQL Query Browser...). Cela aura pour effet immédiat de créer la table "guestbook" dans votre base de données. Nous pouvons à présent nous tourner vers le développement PHP.

Choix du connecteur de base de données

PHP5 propose plusieurs connecteurs capables de se connecter à une base de données MySQL. Le premier et le plus courant reste le driver MySQL de base. C'est le plus simple à utiliser et donc le plus populaire. Il existe également l'extension MySQLI qui n'est autre qu'un driver amélioré de l'extension MySQL de base et qui a la particularité d'être orientée objet. Avec MySQLI, le développeur manipule directement un objet de type MySQLI alors qu'avec le driver MySQL de base il doit fonctionner par appels de procédures et fonctions.

Enfin le dernier connecteur est l'extension PDO qui signifie PHP Data Objects. Cette extension permet de se connecter à de multiples bases de données à condition que les pilotes pour chaque système d'information soit installé sur le serveur Web. PDO a été intégré avec PHP5 et a le principal avantage de faire bénéficier le développeur de certaines fonctionnalités de la base de données en fonction de son pilote. Au même titre que MySQLI, PDO est une interface orientée objet, ce qui est beaucoup plus pratique à utiliser lorsque l'on est à l'aise avec de la programmation orientée objet.

C'est donc le connecteur que nous choisirons pour nous connecter sur la base de données et la manipuler. Notez que nous aurions pu également sélectionner le driver de base ou bien le connecteur MySQLI pour notre projet. Nous avons volontairement choisi PDO comme connecteur pour les raisons évoquées ci-après :

  • Introduire une solution technique purement PHP5
  • Profiter des requêtes préparées pour sécuriser plus efficacement les écritures en base de données
  • Utiliser une solution orientée objet
  • Permettre de déployer plus facilement le livre d'or sur un autre système d'informations (Oracle, Sybase, SQLI, PostgreSQL...) sans avoir à modifier excessivement le code PHP

Nous sommes donc prêts à mettre la main à la pâte et commencer à produire nos premières lignes de code PHP.

Construction du livre d'or

Détermination des fichiers à créer

Notre application se composera de 5 fichiers PHP dont un sera le front-controller ou programme principal qui contiendra toute l'application. Nous appellerons ce fichier guestbook.php. D'un point de vue du code PHP, nous verrons qu'il est relativement limité car il ne contiendra que des appels aux autres fichiers.

Nous développerons également un fichier de configuration (guestbook-config.inc.php) qui contiendra uniquement les informations de configuration de l'application. Il s'agit en fait simplement de constantes définissant les paramètres de connexion sur la base de données ainsi que le nombre de messages à afficher par page.

Puis nous créerons un fichier stockant des procédures / fonctions (ou helpers) utiles à l'application et potentiellement utilisables dans d'autres projets. Ce fichier se nommera guestbook-model.inc.php.

Ensuite nous développerons le coeur même de l'application, c'est-à-dire le contrôleur. Ce fichier PHP contiendra le code PHP qui vérifie et enregistre les données du formulaire en base de données; et récupère une liste de messages en fonction de la pagination. Nous appellerons ce fichier guestbook-controller.inc.php.

Enfin le dernier fichier contiendra le code générant la vue. Il s'agira majoritairement de code XHTML et de quelques appels à des fonctions élémentaires de PHP ainsi qu'à des fonctions utilisateurs du fichier guestbook-model.inc.php. Vous vous en doutez peut-être, ce fichier portera le nom guestbook-view.inc.php.

Fichier de configuration de l'application

<?php

    /*
    - Constantes d'accès à la base de données
    - et de configuration du livre d'or */

    // Adresse du serveur de base de données
    define('DB_SERVEUR', 'localhost');
  
    // Login
    define('DB_LOGIN','root');

    // Mot de passe
    define('DB_PASSWORD','root');

    // Nom de la base de données
    define('DB_NOM','APTutoriels');

    // Nom de la table du livre d'or
    define('DB_GUESTBOOK_TABLE','guestbook');

    // Driver correspondant à la BDD utilisée
    define('DB_DSN','mysql:host='. DB_SERVEUR .';dbname='. DB_NOM);

    // Nombre de messages à afficher par page
    define('MAX_MESSAGES_PAR_PAGE', 1);
?>

Librairie de fonctions

<?php
/* Ce fichier contient toutes les fonctions
utiles à l'application */

/* Fonction de connexion sur la BDD
 Cette fonction utilise l'extension PDO de PHP5
- @param string driver de connexion sur la BDD
- @param string login d'accès à la bdd
- @param string mot de passe d'accès à la base de données
- @return PDO objet de connexion sur la BDD */

function PDOConnect($sDbDsn, $sDbLogin, $sDbPassword) 
{
    $oPDO = new PDO($sDbDsn, $sDbLogin, $sDbPassword);
    $oPDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    return $oPDO;
}

/* Convertit la date au format américain en format français
- @param string la date au format US
- @return string la date au format FR */

function convertirDate($sDateUs)
{
    return strftime('%d/%m/%Y &#224; %H:%M', strtotime($sDateUs));
}

/* Fonction de pagination des résultats
Retourne le code HTML des liens de pagination
- @param integer nombre de résultats total
- @param integer nombre de résultats par page
- @param integer numéro de la page courante
- @param integer nombre de pages avant la page courante
- @param integer nombre de pages après la page courante
- @param integer afficher le lien vers la première page (1=oui / 0=non)
- @param integer afficher le lien vers la dernière page (1=oui / 0=non)
- @return string code html des liens de pagination */

function paginer($nb_results, $nb_results_p_page, $numero_page_courante, $nb_avant, $nb_apres, $premiere, $derniere)
{
    // Initialisation de la variable a retourner
    $resultat = '';
    // nombre total de pages
    $nb_pages = ceil($nb_results / $nb_results_p_page);
    // nombre de pages avant
    $avant = $numero_page_courante > ($nb_avant + 1) ? $nb_avant : $numero_page_courante - 1;
    // nombre de pages après
    $apres = $numero_page_courante <= $nb_pages - $nb_apres ? $nb_apres : $nb_pages - $numero_page_courante;
    // première page
    if($premiere && $numero_page_courante - $avant > 1)
        {
            $resultat .= '<a href="'. htmlspecialchars($_SERVER['PHP_SELF']) .'?numeroPage='. $numero_page_courante .'" title="Première page">««</a>&nbsp;';
        }
    // page précédente
    if($numero_page_courante > 1)
        {
            $resultat .= '<a href="'. htmlspecialchars($_SERVER['PHP_SELF']) .'?numeroPage='. ($numero_page_courante - 1) .'" title="Page précédente '. ($numero_page_courante - 1) . '">«</a>&nbsp;';
        }
    // affichage des numéros de page
    for($i = $numero_page_courante - $avant; $i <= $numero_page_courante + $apres; $i++)
    {
        // page courante
        if($i == $numero_page_courante)
        {
            $resultat .= '&nbsp;[<strong>' . $i . '</strong>]&nbsp;';
        }
        else
        {
            $resultat .= '&nbsp;[<a href="'. htmlspecialchars($_SERVER['PHP_SELF']) .'?numeroPage='. $i .'" title="Consulter la page '. $i . '">' . $i . '</a>]&nbsp;';
        }
    }
    // page suivante
    if($numero_page_courante < $nb_pages)
    {
        $resultat .= '<a href="'. htmlspecialchars($_SERVER['PHP_SELF']) .'?numeroPage='. ($numero_page_courante + 1) .'" title="Consulter la page '. ($numero_page_courante + 1) . ' !">»</a>&nbsp;';
    }
    // dernière page     
    if($derniere && ($numero_page_courante + $apres) < $nb_pages)
    {
        $resultat .= '<a href="'. htmlspecialchars($_SERVER['PHP_SELF']) .'?numeroPage='. $nb_pages .'" title="Dernière page">»»</a>&nbsp;';
    }

    // On retourne le résultat
    return $resultat;
}
?>

Contrôleur de l'application

<?php
/* Contrôleur de l'application. Ce fichier 
- traite le formulaire
- Enregistre les informations en base de données
- Affiche une liste paginée de résultats */

/* ----- Déclaration des variables globales ----*/ 

// Objets de connexion et de manipulation de la BDD
$oPDO = null;
$oPDOStatement = null;

// Tableau stockant les informations du livre d'or
$aInfosGuestbook = array();

// Tableau stockant les messages récupérés de la BDD
$aListeMessages = array();

// Tableau stockant les erreurs générées
$aListeErreurs = array();

// Nombre de messages enregistrés dans la BDD
$iNombreDeMessages = 0;

// Numéro de la page courante
$iNumeroDePageCourante = 1;

// Offset à partir duquel on récupère les messages dans la BDD
$iOffsetSelection = 0;

// Note moyenne du site
$fNoteMoyenne = 0;


/* -----Contrôle de la pagination ---- */

if(!empty($_GET['numeroPage' * ) 
    && is_numeric($_GET['numeroPage' * ) 
    && ($_GET['numeroPage' * >1))
    {
       $iNumeroDePageCourante = intval($_GET['numeroPage']); 
        $iOffsetSelection = ($iNumeroDePageCourante - 1) * MAX_MESSAGES_PAR_PAGE; 
    }


 /* ----- Initialisation de la connexion avec la base de données ---- */

$oPDO = PDOConnect(DB_DSN, DB_LOGIN, DB_PASSWORD);


/* ----- Contrôle du formulaire ---- */

if(!empty($_POST))
{
    // Récupération et nettoyage des données
    $_POST['pseudo'] = trim($_POST['pseudo']);
    $_POST['message'] = trim($_POST['message']);
    $_POST['note'] = trim($_POST['note']);
       
    // Le pseudo est-il rempli ?
   if(empty($_POST['pseudo']))
         {
             $aListeErreurs[] = 'Veuillez indiquer votre pseudo';
         }
         else
         {
             // Le pseudo est-il compris entre 2 et 20 caractères ?
             if(strlen($_POST['pseudo']) < 2)
             {
                 $aListeErreurs[] = 'Votre pseudo est trop court';
             }
             if(strlen($_POST['pseudo']) > 20)
             {
                 $aListeErreurs[] = 'Votre peudo est trop long';
             }
         }
     // Le message est-il rempli ?
     if(empty($_POST['message']))
         {
             $aListeErreurs[] = 'Veuillez indiquer votre message';
         }
    // La note est-elle correcte ?
     if(empty($_POST['note'])
             || !is_numeric($_POST['note'])
             || ($_POST['note'] < 1)
             || ($_POST['note']>10))
         {
             $aListeErreurs[] = 'Veuillez choisir une note';
         }
    // Si aucune erreur n'a été générée

    // On enregistre le message dans la BDD
         if(0 === sizeof($aListeErreurs))
         {
             try
             {
                 // Création d'une requête préparée
                 $oPDOStatement = $oPDO->prepare('INSERT INTO '. DB_GUESTBOOK_TABLE .' (pseudo, message, note, creation) VALUES(:pseudo, :message, :note, NOW())');
                 // Ajout de chaque paramètre à la requête
                 // Les paramètres sont automatiquement protégés par l'objet PDO
                 $oPDOStatement->bindParam(':pseudo', $_POST['pseudo'], PDO::PARAM_STR);
                 $oPDOStatement->bindParam(':message', $_POST['message'], PDO::PARAM_STR);
                 $oPDOStatement->bindParam(':note', $_POST['note'], PDO::PARAM_INT);
                 // Execution de la requête préparée
                 $oPDOStatement->execute();
             }
             catch(PDOException $oPdoException)
             {
                 $aListeErreurs[] = 'Une erreur est survenue et a empêché l\'enregistrement de votre message';
             }
        }
    }


/* ----- Comptage du nombre de messages en base de données 
et calcul de la note moyenne ----*/

    $oPDOStatement = $oPDO->query('SELECT COUNT(1) AS nombreMessages, SUM(note) AS noteTotale FROM '. DB_GUESTBOOK_TABLE);
    $oPDOStatement->setFetchMode(PDO::FETCH_ASSOC);
    $aInfosGuestbook = $oPDOStatement->fetch();
    $iNombreDeMessages = intval($aInfosGuestbook['nombreMessages']);
    // Calcul de la note moyenne
    if($iNombreDeMessages > 0)
    {
        $fNoteMoyenne = round(intval($aInfosGuestbook['noteTotale']) / $iNombreDeMessages, 2);
    }
    $oPDOStatement = null;


/** ----- Récupération des messages en fonction de la pagination ---- */

    if(sizeof($iNombreDeMessages)>0)
    {
        $oPDOStatement = $oPDO->prepare('SELECT pseudo, message, creation FROM '. DB_GUESTBOOK_TABLE .' ORDER BY creation DESC LIMIT :offset, '. MAX_MESSAGES_PAR_PAGE);
        $oPDOStatement->bindParam(':offset', $iOffsetSelection, PDO::PARAM_INT);
        $oPDOStatement->execute();

        // Récupération des résultats sélectionnés dans le tableau $aListeMessages
        $aListeMessages = $oPDOStatement->fetchAll(PDO::FETCH_OBJ);
    }
  // Fermeture de la connexion SQL
  $oPDOStatement = null;
  $oPDO = null;
?>

Affichages des messages et du formulaire

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
    <head>
        <title>Mon livre d'or</title>
        <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    </head>

    <body>
        <h1>Mon livre d'or</h1>
            <h2>Poster un message</h2>

            <?php /** Affichage des erreurs générées **/ ?>
            <?php if(sizeof($aListeErreurs) > 0) : ?>
                <ul>
                    <?php foreach($aListeErreurs as $sErreur) : ?>
                    <li><?php echo htmlspecialchars($sErreur); ?>
                    <?php endforeach; ?>
                </ul>
            <?php endif; ?>

            <form action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" method="post">
                <div>
                    <label for="pseudo">Pseudo :</label>
                    <input type="text" name="pseudo" id="pseudo" value="<?php if(!empty($_POST['pseudo'])) : echo htmlspecialchars($_POST['pseudo']); endif; ?>" />
                </div>

                <div>
                    <label for="message">Message :</label>
                    <textarea name="message" id="message" rows="10" cols="45"><?php if(!empty($_POST['message'])) : echo htmlspecialchars($_POST['message']); endif; ?></textarea>
                </div>

                <div>
                    <label for="note">Note :</label>
                    <select name="note" id="note">
                        <?php for($i=1; $i<11; $i++) : ?>
                        <option value="<?php echo $i; ?>"<?php if(!empty($_POST['note']) && ($_POST['note'] == $i)) : echo ' selected="selected"'; endif; ?>><?php echo $i; ?></option>
                        <?php endfor; ?>
                    </select>
                </div>

                <div>
                    <input type="submit" name="envoyer" value="Soumettre" />
                </div>
            </form>

            <h2>Liste des messages</h2>

            <?php /** Affichage des messages **/ ?>
            <?php if($iNombreDeMessages > 0) : ?>

                <ul>
                    <li><?php echo $iNombreDeMessages; ?> message<?php if($iNombreDeMessages > 1) : ?>s<?php endif; ?></li>
                    <li>Note moyenne : <?php echo $fNoteMoyenne; ?> / 10
                </ul>

                <?php foreach($aListeMessages as $oMessage) : ?>
                    <div>
                        <p>
                            Par <?php echo htmlspecialchars($oMessage->pseudo); ?>, le <?php echo convertirDate($oMessage->creation); ?>
                        </p>
                        <blockquote>
                            <p>
                                <?php echo nl2br(htmlspecialchars($oMessage->message)); ?>
                            </p>
                        </blockquote>
                    </div>

                    <hr/>
                <?php endforeach; ?>
                
                <?php /** Affichage de la pagination si nécessaire **/ ?>
                <?php if($iNombreDeMessages > MAX_MESSAGES_PAR_PAGE) : ?>
                <div class="pagination">
                    <?php echo paginer($iNombreDeMessages, MAX_MESSAGES_PAR_PAGE, $iNumeroDePageCourante, 4, 4, 1, 1); ?>
                </div>
                <?php endif; ?>
            <?php else : ?>
            <p>
                Aucun message enregistré
            </p>

            <?php endif; ?>
    </body>
</html>

Programme principal

<?php
/* Programme principal
Construit la page à partir de tous les fichiers*/
    require(dirname(__FILE__).'/guestbook-config.inc.php');
    require(dirname(__FILE__).'/guestbook-model.inc.php');
    require(dirname(__FILE__).'/guestbook-controller.inc.php');
    require(dirname(__FILE__).'/guestbook-view.inc.php');
?>

Utilisation du livre d'or

Pour tester le livre d'or, il vous suffit simplement de placer tous les fichiers dans le même répertoire et d'appeller le script principal dans votre navigateur.
Par exemple : http://monsite.com/guestbook/guestbook.php

Téléchargement et licence des fichiers sources

Les fichiers sources du programme sont disponibles en téléchargement libre. Vous pouvez les modifier et les commercialiser librement.
http://www.apprendre-php.com/downloads/Guestbook/APGuestbook.zip

Améliorations possibles

Ce livre d'or reste malgré tout sommaire et il ne tient qu'à vous à présent de l'améliorer. Voici quelques idées pour agrémenter ce programme de nouvelles fonctionnalités :

  • Ajout d'un captcha visuel ou image pour empêcher le spamm par des robots
  • Ajout de nouveaux champs : email, site web...
  • Utilisation d'un éditeur WYSIWYG ou de tags BBCode pour personnaliser les messages
  • Développement du module d'administration des messages

...

Un petit mot sur le portage de l'application sur un autre SGBDR

Du fait de l'implémentation de l'objet PDO, cette application devient plus aisément portable sur un autre système de bases de données relationnelles. Si vous comptez utiliser ce livre d'or sur un système Oracle par exemple, il ne vous suffira qu'à changer la constante DB_DSN du fichier de configuration, et modifier les requête SQL du contrôleur en conséquence. Vous n'aurez nul besoin de toucher aux autres fichiers. C'est là tout l'avantage du modèle MVC comme nous l'avons expliqué plus haut.

Conclusion

Nous sommes arrivés au terme de ce tutoriel. Nous avons pu découvir progressivement comment réaliser une application Web PHP5 structurée et sécurisée s'appuyant sur le modèle MVC. Bien entendu ce n'est pas la seule et unique façon de procéder. Il en existe beaucoup d'autres mais cela vous donnera probablement de nouvelles idées pour vos prochains développements...

Ce document intitulé « Réalisation d'un livre d'or avec pdo et approche mvc 1 » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous