Tp 4 php classes models blogpoo
Thème :
Junior
1 vues ·2/9 leçons

Branche 4 — Modèles PHP 8.4 : Model, Article, User et Comment

Objectif : Encapsuler les données et la logique d’accès à la base de données dans des classes PHP 8.3+/8.4.
Nous introduisons un seul héritage : une classe de base Model qui fournit la connexion PDO, dont héritent Article, User et Comment.


Pourquoi passer aux Classes ?

Dans la Branche 3, nous avons centralisé les requêtes SQL dans des fonctions dans app/functions/. C’était une grande amélioration.

Mais on peut encore mieux faire :

Branche 3 — Fonctions Branche 4 — Classes
findArticle(5) → retourne un tableau ['id'=>5, ...] Article::find(5) → retourne un objet Article
$article['title'] pour lire un champ $article->title pour lire une propriété typée
Pas de validation au niveau de la donnée Le constructeur garantit les types corrects
Fonctions éparpillées dans plusieurs fichiers Tout le comportement d’un Article est dans Article.php
$pdo = getPdo() répété partout $this->pdo disponible dans toutes les classes via Model

La Classe de Base : Model

Avant de créer Article et User, nous créons une classe parent dont elles vont toutes les deux hériter.

<?php
require_once "database/database.php";

class Model
{
    protected PDO $pdo;

    public function __construct()
    {
        $this->pdo = getPdo();
    }
}

Pourquoi Model est utile ?

  • Elle centralise la connexion getPdo() en un seul endroit.
  • Toute classe qui extends Model hérite automatiquement de $this->pdo.
  • Plus besoin d’écrire $pdo = getPdo(); dans chaque méthode.

Nouvelles fonctionnalités PHP 8.3+/8.4 utilisées

1. Constructor Property Promotion (PHP 8.0+, affiné en 8.4)

Au lieu d’écrire :

class Article extends Model {
    public int $id;
    public string $title;
    public string $slug;

    public function __construct(int $id, string $title, string $slug) {
        parent::__construct();      // appel du constructeur de Model
        $this->id    = $id;
        $this->title = $title;
        $this->slug  = $slug;
    }
}

On écrit maintenant directement avec la promotion :

class Article extends Model {
    public function __construct(
        public readonly int $id,
        public string $title,
        public string $slug,
        // ...
    ) {
        parent::__construct(); // toujours appeler le parent pour avoir $this->pdo
    }
}

2. readonly Properties (PHP 8.1+)

Les propriétés readonly ne peuvent être assignées qu’une seule fois (dans le constructeur). Parfait pour l’id qui ne doit jamais changer après la création de l’objet.

3. Typed Properties (PHP 8.0+)

Chaque propriété a un type strict (int, string, ?string). PHP lance une TypeError si on tente d’assigner un type incorrect.

4. Nullable types (?string)

On utilise ?string pour les propriétés optionnelles comme image (un article peut ne pas avoir d’image).


Architecture cible

app/
├── Models/
│   ├── Model.php          ← Classe de base (connexion PDO partagée)
│   ├── Article.php        ← extends Model — CRUD articles
│   ├── User.php           ← extends Model — auth + profil
│   └── Comment.php        ← extends Model — réactions des lecteurs
├── functions/             ← Conservé pour compatibilité Branche 3
│   ├── article.php
│   ├── user.php
│   └── comment.php
└── helpers.php

Diagramme de l’héritage

             ┌──────────────┐
             │    Model     │
             │  $this->pdo  │
             └──────┬───────┘
                    │ extends
        ┌───────────┼───────────┐
        │           │           │
  ┌─────▼─────┐ ┌───▼────┐ ┌───▼───────┐
  │  Article  │ │  User  │ │  Comment  │
  │ + CRUD    │ │ + auth │ │ + réact.  │
  └───────────┘ └────────┘ └───────────┘

Contenu des Classes

Model.php

Propriétés :
- protected $pdo  ← instance PDO disponible dans toutes les sous-classes

Constructeur :
- __construct()  ← appelle getPdo() et stocke la connexion

Article.php — extends Model

Propriétés (typées, readonly pour id) :
- readonly int $id
- string $title
- string $slug
- string $introduction
- string $content
- ?string $image
- string $created_at
- string $updated_at
- int $comment_count (calculé via sous-requête)

Méthodes statiques (factory methods) :
- Article::count()                              → int
- Article::find(int $id)                       → ?Article
- Article::findAll(?limit, ?offset, $search)   → array<Article>
- Article::slugExists(string $slug)            → bool

Méthodes d'instance (actions) :
- $article->save()    → bool  (INSERT si id=0, UPDATE sinon)
- $article->delete()  → bool
- $article->isNew()   → bool

User.php — extends Model

Propriétés (typées, readonly pour id) :
- readonly int $id
- string $username
- string $email
- string $password   ← toujours stocké haché
- string $role
- string $created_at

Méthodes statiques :
- User::count()                        → int
- User::find(int $id)                  → ?User
- User::findByEmail(string)            → ?User
- User::findByUsername(string)         → ?User
- User::findByEmailOrUsername(string)  → ?User
- User::allWithCommentCount()          → array

Méthodes d'instance :
- $user->save()                  → bool
- $user->verifyPassword(string)  → bool
- $user->hashPassword(string)    → void (met à jour $this->password)
- $user->isAdmin()               → bool
- $user->toSession()             → void (écrit dans $_SESSION['auth'])

Comment.php — extends Model

Propriétés (typées, readonly pour id) :
- readonly int $id
- string $content
- int $article_id    ← clé étrangère vers articles
- int $user_id       ← clé étrangère vers users
- string $created_at
- string $username   ← dénormalisé depuis la jointure users (lecture seule)

Méthodes statiques :
- Comment::count()                         → int
- Comment::find(int $id)                   → ?Comment
- Comment::findByArticle(int $article_id)  → array<Comment>  (avec username)
- Comment::findByUser(int $user_id)        → array<Comment>  (avec titre article)

Méthodes d'instance :
- $comment->save()              → bool (INSERT uniquement, pas d'UPDATE)
- $comment->delete()            → bool
- $comment->belongsTo(int $userId, bool $isAdmin) → bool  (contrôle des droits)

Plan des fichiers de documentation

Fichier Contenu
00_plan.md Ce plan — Vue d’ensemble de la Branche 4
01_introduction_classes.md POO vs Fonctions, héritage simple, vocabulaire
02_class_model.md Code complet de Model.php expliqué pas à pas
03_class_article.md Code complet de Article.php avec promotion PHP 8.4
04_class_user.md Code complet de User.php avec méthodes d’auth
05_class_comment.md Code complet de Comment.php avec contrôle des droits
06_usage_controleurs.md Migration des contrôleurs vers les nouvelles classes

Étapes d’implémentation

  1. Créer app/Models/Model.php — la base
  2. Créer app/Models/Article.php extends Model
  3. Créer app/Models/User.php extends Model
  4. Créer app/Models/Comment.php extends Model
  5. Inclure les modèles dans app/helpers.php
  6. Migrer index.php → Article::findAll()
  7. Migrer les contrôleurs admin → Article::find(), $article->save()
  8. Migrer login.php → User::findByEmailOrUsername(), $user->verifyPassword()
  9. Migrer register.php → User instancié + $user->save()
  10. Migrer user-comment-save.php + user-comment-delete.php → $comment->save(), $comment->delete()

Prêt à passer à la suite ?

Vous avez terminé cette leçon sur Branche 4 — Modèles PHP 8.4 : `Model`, `Article`, `User` et `Comment`. Continuez votre apprentissage avec la prochaine leçon ou explorez d'autres formations.