Mis à jour le 08/01/2018
  • 30 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

TP : un système de news

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

La plupart des sites web dynamiques proposent un système d'actualités. Je sais par conséquent que c'est l'exemple auquel la plupart d'entre vous s'attend : nous allons donc réaliser ici un système de news ! Cela sera le dernier petit TP permettant de clarifier vos acquis avant celui plus important qui vous attend. Ce TP est aussi l'occasion pour vous de voir un exemple concret qui vous sera utile pour votre site !

Ce que nous allons faire

Cahier des charges

Commençons par définir ce que nous allons faire et surtout, ce dont nous allons avoir besoin.

Ce que nous allons réaliser est très simple, à savoir un système de news basique avec les fonctionnalités suivantes :

  • Affichage des cinq premières news à l'accueil du site avec texte réduit à 200 caractères.

  • Possibilité de cliquer sur le titre de la news pour la lire entièrement. L'auteur et la date d'ajout apparaîtront, ainsi que la date de modification si la news a été modifiée.

  • Un espace d'administration qui permettra d'ajouter / modifier / supprimer des news. Cet espace tient sur une page : il y a un formulaire et un tableau en-dessous listant les news avec des liens modifier / supprimer. Quand on clique sur « Modifier », le formulaire se pré-remplit.

Pour réaliser cela, nous allons avoir besoin de créer une table news dont la structure est la suivante :

CREATE TABLE `news` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `auteur` varchar(30) NOT NULL,
  `titre` varchar(100) NOT NULL,
  `contenu` text NOT NULL,
  `dateAjout` datetime NOT NULL,
  `dateModif` datetime NOT NULL,
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

Concernant l'organisation des classes, nous allons suivre la même structure que pour les personnages, à savoir :

  • Une classe News qui contiendra les champs sous forme d'attributs. Son rôle sera de représenter une news.

  • Une classe NewsManager qui gérera les news. C'est elle qui interagira avec la BDD.

Cependant, je voudrais vous enseigner quelque chose de nouveau. Je voudrais que la classe NewsManager ne soit pas dépendante de PDO. Nous avons vu dans le chapitre précédent que l'injection de dépendances pouvait être intéressante mais nous compliquait trop la tâche concernant l'adaptation des DAO. Au lieu d'injecter la dépendance, nous allons plutôt créer des classes spécialisées, c'est-à-dire qu'à chaque API correspondra une classe. Par exemple, si nous voulons assurer la compatibilité de notre système de news avec l'API PDO et MySQLi, alors nous aurons deux managers différents, l'un effectuant les requêtes avec PDO, l'autre avec MySQLi. Néanmoins, étant donné que ces classes ont une nature en commun (celle d'être toutes les deux des managers de news), alors elles devront hériter d'une classe représentant cette nature.

Voir le résultat que vous devez obtenir

Retour sur le traitement des résultats

Je vais vous demander d'essayer de vous rappeler le dernier TP, celui sur les personnages, et, plus précisément, la manière dont nous récupérions les résultats. Nous obtenions quelque chose comme ça :

<?php
// On admet que $db est une instance de PDO

$q = $db->prepare('SELECT id, nom, degats FROM personnages WHERE nom <> :nom ORDER BY nom');
$q->execute([':nom' => $nom]);

while ($donnees = $q->fetch(PDO::FETCH_ASSOC))
{
  $persos[] = new Personnage($donnees);
}

Comme vous pouvez le constater, on récupère la liste des personnages sous forme de tableau grâce à la constante PDO::FETCH_ASSOC, puis on instancie notre classe Personnage en passant ce tableau au constructeur. Je vais vous dévoiler un moyen plus simple et davantage optimisé pour effectuer cette opération. Je ne l'ai pas abordé précédemment car je trouvais qu'il était un peu tôt pour vous en parler dès la première partie.

Avec PDO (comme avec MySQLi, mais nous le verrons plus tard), il existe une constante qui signifie « retourne-moi le résultat dans un objet » (au même titre que la constante PDO::FETCH_ASSOC signifie « retourne-moi le résultat dans un tableau »). Cette constante est tout simplement PDO::FETCH_CLASS. Comment s'utilise-t-elle ? Comme vous vous en doutez peut-être, si nous voulons que PDO nous retourne les résultats sous forme d'instances de notre classe News, il va falloir lui dire ! Or, nous ne pouvons pas lui dire le nom de notre classe directement dans la méthode fetch() comme nous le faisons avec PDO::FETCH_ASSOC. Nous allons nous servir d'une autre méthode, setFetchMode(). Si vous avez lu les toutes premières lignes de la page pointée par le lien que j'ai donné, vous devriez savoir utiliser cette méthode :

<?php
// On admet que $db est une instance de PDO

$q = $db->prepare('SELECT id, nom, degats FROM personnages WHERE nom <> :nom ORDER BY nom');
$q->execute([':nom' => $nom]);

$q->setFetchMode(PDO::FETCH_CLASS, 'Personnage');

$persos = $q->fetchAll();

Comme vous le voyez, puisque nous avons dit à PDO à la ligne 7 que nous voulions une instance de Personnage en guise de résultat, il n'est pas utile de spécifier de nouveau quoi que ce soit lorsqu'on appelle la méthode fetchAll().

Vous pouvez essayer ce code et vous verrez que ça fonctionne. Maintenant, je vais vous demander d'effectuer un petit test. Affichez la valeur des attributs dans le constructeur de News (avec des simples echo, n'allez pas chercher bien loin). Lancez de nouveau le script. Sur votre écran vont alors s'afficher les valeurs que PDO a assignées à notre objet. Rien ne vous titille ? Dois-je vous rappeler que, normalement, le constructeur d'une classe est appelé en premier et qu'il a pour rôle principal d'initialiser l'objet ? Pensez-vous que ce rôle est accompli alors que les valeurs ont été assignées avant même que le constructeur soit appelé ? Si vous aviez un constructeur qui devait assigner des valeurs par défaut aux attributs, celui-ci aurait écrasé les valeurs assignées par PDO.

Pour résumer, mémorisez qu'avec cette technique, l'objet n'est pas instancié comme il se doit. L'objet est créé (sans que le constructeur soit appelé), puis les valeurs sont assignées aux attributs par PDO, enfin le constructeur est appelé.

Pour remettre les choses dans l'ordre, une autre constante est disponible : PDO::FETCH_PROPS_LATE. Elle va de paire avec PDO::FETCH_CLASS. Essayez dès à présent cette modification :

<?php
// On admet que $db est une instance de PDO

$q = $db->prepare('SELECT id, nom, degats FROM personnages WHERE nom <> :nom ORDER BY nom');
$q->execute([':nom' => $nom]);

$q->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Personnage');

$persos = $q->fetchAll();

Affichez avec des echo la valeur de quelques attributs dans le constructeur et vous verrez qu'ils auront tous une valeur nulle, pour la simple et bonne raison que cette fois-ci, le constructeur a été appelé avant que les valeurs soient assignées aux attributs par PDO.

Maintenant, puisque le cahier des charges vous demande de rendre ce script compatible avec l'API MySQLi, je vais vous dire comment procéder. C'est beaucoup plus simple : au lieu d'appeler la méthode fetch_assoc() sur le résultat, appelez tout simplement la méthode fetch_object('NomDeLaClasse').

Mes attributs sont privés ou protégés, pourquoi PDO et MySQLi peuvent-ils modifier leur valeur sans appeler les setters ?

Procéder de cette façon, c'est de la "bidouille" de bas niveau. PDO et MySQLi sont des API compilées avec PHP, elles ne fonctionnent donc pas comme les classes que vous développez. En plus de vous dire des bêtises, cela serait un hors sujet de vous expliquer le pourquoi du comment, mais sachez juste que les API compilées ont quelques super pouvoirs de ce genre. ;)

Gérons les dates comme il faut

Nous savons gérer les dates avec MySQL en utilisant le type DATETIME. Par contre, du côté de PHP, si on récupère la date sous sa forme habituelle (c'est-à-dire sous la forme d'une chaine de caractères du type ANNEE-MOIS-JOUR HEURE-MINUTE-SECONDE), on aurait un peu de mal à nous en servir. Heureusement, PHP met à notre disposition une classe s'occupant de leur gestion : DateTime. Lorsque vous récupérerez vos news depuis la base de données, il faudra donc penser à parcourir toutes vos news afin de modifier les attributs dateAjout et dateModif de vos objets. Bien entendu, cela implique de modifier votre classe News afin que les setters des attributs dateAjout et dateModif demandent des instances de DateTime.

Correction

Diagramme UML

Avant de donner une correction du code, je vais corriger la construction des classes en vous donnant le diagramme UML représentant le module (voir la figure suivante).

Diagramme modélisant le système de news
Diagramme modélisant le système de news

Le code du système

Pour des raisons d'organisation, j'ai décidé de placer les quatre classes dans un dossier lib.

<?php
/**
 * Classe représentant une news, créée à l'occasion d'un TP du tutoriel « La programmation orientée objet en PHP » disponible sur http://www.openclassrooms.com/
 * @author Victor T.
 * @version 2.0
 */
class News
{
  protected $erreurs = [],
            $id,
            $auteur,
            $titre,
            $contenu,
            $dateAjout,
            $dateModif;
  
  /**
   * Constantes relatives aux erreurs possibles rencontrées lors de l'exécution de la méthode.
   */
  const AUTEUR_INVALIDE = 1;
  const TITRE_INVALIDE = 2;
  const CONTENU_INVALIDE = 3;
  
  
  /**
   * Constructeur de la classe qui assigne les données spécifiées en paramètre aux attributs correspondants.
   * @param $valeurs array Les valeurs à assigner
   * @return void
   */
  public function __construct($valeurs = [])
  {
    if (!empty($valeurs)) // Si on a spécifié des valeurs, alors on hydrate l'objet.
    {
      $this->hydrate($valeurs);
    }
  }
  
  /**
   * Méthode assignant les valeurs spécifiées aux attributs correspondant.
   * @param $donnees array Les données à assigner
   * @return void
   */
  public function hydrate($donnees)
  {
    foreach ($donnees as $attribut => $valeur)
    {
      $methode = 'set'.ucfirst($attribut);
      
      if (is_callable([$this, $methode]))
      {
        $this->$methode($valeur);
      }
    }
  }
  
  /**
   * Méthode permettant de savoir si la news est nouvelle.
   * @return bool
   */
  public function isNew()
  {
    return empty($this->id);
  }
  
  /**
   * Méthode permettant de savoir si la news est valide.
   * @return bool
   */
  public function isValid()
  {
    return !(empty($this->auteur) || empty($this->titre) || empty($this->contenu));
  }
  
  
  // SETTERS //
  
  public function setId($id)
  {
    $this->id = (int) $id;
  }
  
  public function setAuteur($auteur)
  {
    if (!is_string($auteur) || empty($auteur))
    {
      $this->erreurs[] = self::AUTEUR_INVALIDE;
    }
    else
    {
      $this->auteur = $auteur;
    }
  }
  
  public function setTitre($titre)
  {
    if (!is_string($titre) || empty($titre))
    {
      $this->erreurs[] = self::TITRE_INVALIDE;
    }
    else
    {
      $this->titre = $titre;
    }
  }
  
  public function setContenu($contenu)
  {
    if (!is_string($contenu) || empty($contenu))
    {
      $this->erreurs[] = self::CONTENU_INVALIDE;
    }
    else
    {
      $this->contenu = $contenu;
    }
  }
  
  public function setDateAjout(DateTime $dateAjout)
  {
    $this->dateAjout = $dateAjout;
  }
  
  public function setDateModif(DateTime $dateModif)
  {
    $this->dateModif = $dateModif;
  }
  
  // GETTERS //
  
  public function erreurs()
  {
    return $this->erreurs;
  }
  
  public function id()
  {
    return $this->id;
  }
  
  public function auteur()
  {
    return $this->auteur;
  }
  
  public function titre()
  {
    return $this->titre;
  }
  
  public function contenu()
  {
    return $this->contenu;
  }
  
  public function dateAjout()
  {
    return $this->dateAjout;
  }
  
  public function dateModif()
  {
    return $this->dateModif;
  }
}
<?php
abstract class NewsManager
{
  /**
   * Méthode permettant d'ajouter une news.
   * @param $news News La news à ajouter
   * @return void
   */
  abstract protected function add(News $news);
  
  /**
   * Méthode renvoyant le nombre de news total.
   * @return int
   */
  abstract public function count();
  
  /**
   * Méthode permettant de supprimer une news.
   * @param $id int L'identifiant de la news à supprimer
   * @return void
   */
  abstract public function delete($id);
  
  /**
   * Méthode retournant une liste de news demandée.
   * @param $debut int La première news à sélectionner
   * @param $limite int Le nombre de news à sélectionner
   * @return array La liste des news. Chaque entrée est une instance de News.
   */
  abstract public function getList($debut = -1, $limite = -1);
  
  /**
   * Méthode retournant une news précise.
   * @param $id int L'identifiant de la news à récupérer
   * @return News La news demandée
   */
  abstract public function getUnique($id);
  
  /**
   * Méthode permettant d'enregistrer une news.
   * @param $news News la news à enregistrer
   * @see self::add()
   * @see self::modify()
   * @return void
   */
  public function save(News $news)
  {
    if ($news->isValid())
    {
      $news->isNew() ? $this->add($news) : $this->update($news);
    }
    else
    {
      throw new RuntimeException('La news doit être valide pour être enregistrée');
    }
  }
  
  /**
   * Méthode permettant de modifier une news.
   * @param $news news la news à modifier
   * @return void
   */
  abstract protected function update(News $news);
}
<?php
class NewsManagerPDO extends NewsManager
{
  /**
   * Attribut contenant l'instance représentant la BDD.
   * @type PDO
   */
  protected $db;
  
  /**
   * Constructeur étant chargé d'enregistrer l'instance de PDO dans l'attribut $db.
   * @param $db PDO Le DAO
   * @return void
   */
  public function __construct(PDO $db)
  {
    $this->db = $db;
  }
  
  /**
   * @see NewsManager::add()
   */
  protected function add(News $news)
  {
    $requete = $this->db->prepare('INSERT INTO news(auteur, titre, contenu, dateAjout, dateModif) VALUES(:auteur, :titre, :contenu, NOW(), NOW())');
    
    $requete->bindValue(':titre', $news->titre());
    $requete->bindValue(':auteur', $news->auteur());
    $requete->bindValue(':contenu', $news->contenu());
    
    $requete->execute();
  }
  
  /**
   * @see NewsManager::count()
   */
  public function count()
  {
    return $this->db->query('SELECT COUNT(*) FROM news')->fetchColumn();
  }
  
  /**
   * @see NewsManager::delete()
   */
  public function delete($id)
  {
    $this->db->exec('DELETE FROM news WHERE id = '.(int) $id);
  }
  
  /**
   * @see NewsManager::getList()
   */
  public function getList($debut = -1, $limite = -1)
  {
    $sql = 'SELECT id, auteur, titre, contenu, dateAjout, dateModif FROM news ORDER BY id DESC';
    
    // On vérifie l'intégrité des paramètres fournis.
    if ($debut != -1 || $limite != -1)
    {
      $sql .= ' LIMIT '.(int) $limite.' OFFSET '.(int) $debut;
    }
    
    $requete = $this->db->query($sql);
    $requete->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'News');
    
    $listeNews = $requete->fetchAll();

    // On parcourt notre liste de news pour pouvoir placer des instances de DateTime en guise de dates d'ajout et de modification.
    foreach ($listeNews as $news)
    {
      $news->setDateAjout(new DateTime($news->dateAjout()));
      $news->setDateModif(new DateTime($news->dateModif()));
    }
    
    $requete->closeCursor();
    
    return $listeNews;
  }
  
  /**
   * @see NewsManager::getUnique()
   */
  public function getUnique($id)
  {
    $requete = $this->db->prepare('SELECT id, auteur, titre, contenu, dateAjout, dateModif FROM news WHERE id = :id');
    $requete->bindValue(':id', (int) $id, PDO::PARAM_INT);
    $requete->execute();
    
    $requete->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'News');

    $news = $requete->fetch();

    $news->setDateAjout(new DateTime($news->dateAjout()));
    $news->setDateModif(new DateTime($news->dateModif()));
    
    return $news;
  }
  
  /**
   * @see NewsManager::update()
   */
  protected function update(News $news)
  {
    $requete = $this->db->prepare('UPDATE news SET auteur = :auteur, titre = :titre, contenu = :contenu, dateModif = NOW() WHERE id = :id');
    
    $requete->bindValue(':titre', $news->titre());
    $requete->bindValue(':auteur', $news->auteur());
    $requete->bindValue(':contenu', $news->contenu());
    $requete->bindValue(':id', $news->id(), PDO::PARAM_INT);
    
    $requete->execute();
  }
}
<?php
class NewsManagerMySQLi extends NewsManager
{
  /**
   * Attribut contenant l'instance représentant la BDD.
   * @type MySQLi
   */
  protected $db;
  
  /**
   * Constructeur étant chargé d'enregistrer l'instance de MySQLi dans l'attribut $db.
   * @param $db MySQLi Le DAO
   * @return void
   */
  public function __construct(MySQLi $db)
  {
    $this->db = $db;
  }
  
  /**
   * @see NewsManager::add()
   */
  protected function add(News $news)
  {
    $requete = $this->db->prepare('INSERT INTO news(auteur, titre, contenu, dateAjout, dateModif) VALUES(?, ?, ?, NOW(), NOW())');
    
    $requete->bind_param('sss', $news->auteur(), $news->titre(), $news->contenu());
    
    $requete->execute();
  }
  
  /**
   * @see NewsManager::count()
   */
  public function count()
  {
    return $this->db->query('SELECT id FROM news')->num_rows;
  }
  
  /**
   * @see NewsManager::delete()
   */
  public function delete($id)
  {
    $id = (int) $id;
    
    $requete = $this->db->prepare('DELETE FROM news WHERE id = ?');
    
    $requete->bind_param('i', $id);
    
    $requete->execute();
  }
  
  /**
   * @see NewsManager::getList()
   */
  public function getList($debut = -1, $limite = -1)
  {
    $listeNews = [];
    
    $sql = 'SELECT id, auteur, titre, contenu, dateAjout, dateModif FROM news ORDER BY id DESC';
    
    // On vérifie l'intégrité des paramètres fournis.
    if ($debut != -1 || $limite != -1)
    {
      $sql .= ' LIMIT '.(int) $limite.' OFFSET '.(int) $debut;
    }
    
    $requete = $this->db->query($sql);
    
    while ($news = $requete->fetch_object('News'))
    {
      $news->setDateAjout(new DateTime($news->dateAjout()));
      $news->setDateModif(new DateTime($news->dateModif()));

      $listeNews[] = $news;
    }
    
    return $listeNews;
  }
  
  /**
   * @see NewsManager::getUnique()
   */
  public function getUnique($id)
  {
    $id = (int) $id;
    
    $requete = $this->db->prepare('SELECT id, auteur, titre, contenu, dateAjout, dateModif FROM news WHERE id = ?');
    $requete->bind_param('i', $id);
    $requete->execute();
    
    $requete->bind_result($id, $auteur, $titre, $contenu, $dateAjout, $dateModif);
    
    $requete->fetch();
    
    return new News([
      'id' => $id,
      'auteur' => $auteur,
      'titre' => $titre,
      'contenu' => $contenu,
      'dateAjout' => new DateTime($dateAjout),
      'dateModif' => new DateTime($dateModif)
    ]);
  }
  
  /**
   * @see NewsManager::update()
   */
  protected function update(News $news)
  {
    $requete = $this->db->prepare('UPDATE news SET auteur = ?, titre = ?, contenu = ?, dateModif = NOW() WHERE id = ?');
    
    $requete->bind_param('sssi', $news->auteur(), $news->titre(), $news->contenu(), $news->id());
    
    $requete->execute();
  }
}

Pour accéder aux instances de PDO et MySQLi, nous allons nous aider du design pattern factory. Veuillez donc créer une simple classe DBFactory.

<?php
class DBFactory
{
  public static function getMysqlConnexionWithPDO()
  {
    $db = new PDO('mysql:host=localhost;dbname=news', 'root', '');
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    return $db;
  }
  
  public static function getMysqlConnexionWithMySQLi()
  {
    return new MySQLi('localhost', 'root', '', 'news');
  }
}

Nous allons créer deux pages : index.php qui sera accessible au grand public et listera les news, ainsi que admin.php qui nous permettra de gérer les news. Dans ces deux pages, nous aurons besoin d'un autoload. Nous allons donc créer un fichier autoload.php dans le dossier lib qui contiendra notre autoload. Il s'agit d'un simple fichier, voyez par vous-même :

<?php
function autoload($classname)
{
  if (file_exists($file = __DIR__ . '/' . $classname . '.php'))
  {
    require $file;
  }
}

spl_autoload_register('autoload');

Maintenant que nous avons créé la partie interne, nous allons nous occuper des pages qui s'afficheront devant vos yeux. Il s'agit bien entendu de la partie la plus facile, le pire est derrière nous. ;)

Commençons par la page d'administration :

<?php
require 'lib/autoload.php';

$db = DBFactory::getMysqlConnexionWithPDO();
$manager = new NewsManagerPDO($db);

if (isset($_GET['modifier']))
{
  $news = $manager->getUnique((int) $_GET['modifier']);
}

if (isset($_GET['supprimer']))
{
  $manager->delete((int) $_GET['supprimer']);
  $message = 'La news a bien été supprimée !';
}

if (isset($_POST['auteur']))
{
  $news = new News(
    [
      'auteur' => $_POST['auteur'],
      'titre' => $_POST['titre'],
      'contenu' => $_POST['contenu']
    ]
  );
  
  if (isset($_POST['id']))
  {
    $news->setId($_POST['id']);
  }
  
  if ($news->isValid())
  {
    $manager->save($news);
    
    $message = $news->isNew() ? 'La news a bien été ajoutée !' : 'La news a bien été modifiée !';
  }
  else
  {
    $erreurs = $news->erreurs();
  }
}
?>
<!DOCTYPE html>
<html>
  <head>
    <title>Administration</title>
    <meta charset="utf-8" />
    
    <style type="text/css">
      table, td {
        border: 1px solid black;
      }
      
      table {
        margin:auto;
        text-align: center;
        border-collapse: collapse;
      }
      
      td {
        padding: 3px;
      }
    </style>
  </head>
  
  <body>
    <p><a href=".">Accéder à l'accueil du site</a></p>
    
    <form action="admin.php" method="post">
      <p style="text-align: center">
<?php
if (isset($message))
{
  echo $message, '<br />';
}
?>
        <?php if (isset($erreurs) && in_array(News::AUTEUR_INVALIDE, $erreurs)) echo 'L\'auteur est invalide.<br />'; ?>
        Auteur : <input type="text" name="auteur" value="<?php if (isset($news)) echo $news->auteur(); ?>" /><br />
        
        <?php if (isset($erreurs) && in_array(News::TITRE_INVALIDE, $erreurs)) echo 'Le titre est invalide.<br />'; ?>
        Titre : <input type="text" name="titre" value="<?php if (isset($news)) echo $news->titre(); ?>" /><br />
        
        <?php if (isset($erreurs) && in_array(News::CONTENU_INVALIDE, $erreurs)) echo 'Le contenu est invalide.<br />'; ?>
        Contenu :<br /><textarea rows="8" cols="60" name="contenu"><?php if (isset($news)) echo $news->contenu(); ?></textarea><br />
<?php
if(isset($news) && !$news->isNew())
{
?>
        <input type="hidden" name="id" value="<?= $news->id() ?>" />
        <input type="submit" value="Modifier" name="modifier" />
<?php
}
else
{
?>
        <input type="submit" value="Ajouter" />
<?php
}
?>
      </p>
    </form>
    
    <p style="text-align: center">Il y a actuellement <?= $manager->count() ?> news. En voici la liste :</p>
    
    <table>
      <tr><th>Auteur</th><th>Titre</th><th>Date d'ajout</th><th>Dernière modification</th><th>Action</th></tr>
<?php
foreach ($manager->getList() as $news)
{
  echo '<tr><td>', $news->auteur(), '</td><td>', $news->titre(), '</td><td>', $news->dateAjout()->format('d/m/Y à H\hi'), '</td><td>', ($news->dateAjout() == $news->dateModif() ? '-' : $news->dateModif()->format('d/m/Y à H\hi')), '</td><td><a href="?modifier=', $news->id(), '">Modifier</a> | <a href="?supprimer=', $news->id(), '">Supprimer</a></td></tr>', "\n";
}
?>
    </table>
  </body>
</html>

Et enfin, la partie visible à tous vos visiteurs :

<?php
require 'lib/autoload.php';

$db = DBFactory::getMysqlConnexionWithPDO();
$manager = new NewsManagerPDO($db);
?>
<!DOCTYPE html>
<html>
  <head>
    <title>Accueil du site</title>
    <meta charset="utf-8" />
  </head>
  
  <body>
    <p><a href="admin.php">Accéder à l'espace d'administration</a></p>
<?php
if (isset($_GET['id']))
{
  $news = $manager->getUnique((int) $_GET['id']);
  
  echo '<p>Par <em>', $news->auteur(), '</em>, le ', $news->dateAjout()->format('d/m/Y à H\hi'), '</p>', "\n",
       '<h2>', $news->titre(), '</h2>', "\n",
       '<p>', nl2br($news->contenu()), '</p>', "\n";
  
  if ($news->dateAjout() != $news->dateModif())
  {
    echo '<p style="text-align: right;"><small><em>Modifiée le ', $news->dateModif()->format('d/m/Y à H\hi'), '</em></small></p>';
  }
}

else
{
  echo '<h2 style="text-align:center">Liste des 5 dernières news</h2>';
  
  foreach ($manager->getList(0, 5) as $news)
  {
    if (strlen($news->contenu()) <= 200)
    {
      $contenu = $news->contenu();
    }
    
    else
    {
      $debut = substr($news->contenu(), 0, 200);
      $debut = substr($debut, 0, strrpos($debut, ' ')) . '...';
      
      $contenu = $debut;
    }
    
    echo '<h4><a href="?id=', $news->id(), '">', $news->titre(), '</a></h4>', "\n",
         '<p>', nl2br($contenu), '</p>';
  }
}
?>
  </body>
</html>

Vous êtes demandeur d'emploi ?
Sans diplôme post-bac ?

Devenez Développeur web junior

Je postule
Formation
en ligne
Financée
à 100%
Exemple de certificat de réussite
Exemple de certificat de réussite