Chapitre 38 : Projet final – Blog dynamique

Créez un système de blog complet en PHP avec une interface publique et une interface d'administration

1. Objectif du projet

Développer un blog dynamique avec :

  • Gestion complète des articles (CRUD)
  • Système de commentaires
  • Espace d'administration sécurisé
  • Interface responsive

2. Structure de la base de données

-- Table des articles
CREATE TABLE `articles` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `titre` VARCHAR(255) NOT NULL,
  `slug` VARCHAR(255) NOT NULL UNIQUE,
  `contenu` LONGTEXT NOT NULL,
  `auteur` VARCHAR(100) NOT NULL,
  `date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP,
  `date_modification` DATETIME ON UPDATE CURRENT_TIMESTAMP,
  `image` VARCHAR(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Table des commentaires
CREATE TABLE `commentaires` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `article_id` INT NOT NULL,
  `auteur` VARCHAR(100) NOT NULL,
  `email` VARCHAR(255) NOT NULL,
  `contenu` TEXT NOT NULL,
  `date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP,
  `approuve` TINYINT(1) DEFAULT 0,
  FOREIGN KEY (`article_id`) 
    REFERENCES `articles`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Table des utilisateurs (pour l'administration)
CREATE TABLE `utilisateurs` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `email` VARCHAR(180) NOT NULL UNIQUE,
  `roles` JSON NOT NULL,
  `password` VARCHAR(255) NOT NULL,
  `nom` VARCHAR(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

3. Exemples de code clés

Connexion à la base de données (PDO)

<?php
// config/database.php

declare(strict_types=1);

$host = 'localhost';
$dbname = 'blog_php';
$username = 'root';
$password = '';

$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
];

try {
    $pdo = new PDO(
        "mysql:host=$host;dbname=$dbname;charset=utf8mb4",
        $username,
        $password,
        $options
    );
} catch (PDOException $e) {
    error_log('Erreur de connexion : ' . $e->getMessage());
    header('HTTP/1.1 500 Database Error');
    exit('Une erreur de base de données est survenue');
}

Modèle Article

<?php
// src/Model/Article.php

declare(strict_types=1);

class Article
{
    private ?int $id;
    private string $titre;
    private string $slug;
    private string $contenu;
    private string $auteur;
    private DateTime $dateCreation;
    private ?DateTime $dateModification;
    private ?string $image;

    public function __construct(
        string $titre,
        string $slug,
        string $contenu,
        string $auteur,
        ?string $image = null,
        ?int $id = null,
        ?DateTime $dateCreation = null,
        ?DateTime $dateModification = null
    ) {
        $this->id = $id;
        $this->titre = $titre;
        $this->slug = $slug;
        $this->contenu = $contenu;
        $this->auteur = $auteur;
        $this->image = $image;
        $this->dateCreation = $dateCreation ?? new DateTime();
        $this->dateModification = $dateModification;
    }

    // Getters et méthodes de persistence...
}

4. Espace d'administration

<?php
// admin/index.php

declare(strict_types=1);

require_once __DIR__.'/../vendor/autoload.php';
require_once __DIR__.'/../config/bootstrap.php';

use App\Auth;
use App\Controller\AdminController;

// Sécurité
if (!Auth::check()) {
    header('Location: login.php');
    exit;
}

// Routing basique
$action = $_GET['action'] ?? 'dashboard';

try {
    $controller = new AdminController();
    
    switch ($action) {
        case 'articles':
            $controller->listArticles();
            break;
        case 'edit-article':
            $id = $_GET['id'] ?? null;
            $controller->editArticle($id);
            break;
        case 'delete-article':
            $controller->deleteArticle((int)$_GET['id']);
            break;
        case 'comments':
            $controller->manageComments();
            break;
        default:
            $controller->dashboard();
    }
} catch (Exception $e) {
    error_log($e->getMessage());
    header('HTTP/1.1 500 Internal Server Error');
    exit('Une erreur est survenue');
}

5. Résumé du projet

  • ✅ Architecture MVC moderne
  • ✅ Sécurité renforcée (XSS, CSRF, SQLi, injections)
  • ✅ Validation des données côté serveur
  • ✅ Gestion des erreurs et logs
  • ✅ Interface responsive et accessible