Backend PHP (BPH) Help

Mettre en place les Models et Managers

Les Models

Lorsque vous travaillez avec le MVC, vos Models sont les classes qui représentent les tables de votre base de données. En prgrammation lorsqu'une clase sert à représenter en code des données présentes en base, on appelle ça un DAO (Data Access Object), vous en trouverez dans pratiquement tous les frameworks et design patterns.

Dans le MVC, on les appelle donc les Models (c'est eux le M de MVC).

Représenter une table

Imaginons que vous avez une table users en base de données qui représente les utilisateurs d'une bibliothèque.

La table pourrait ressembler à ceci :

nom de la colonne

type de la colonne

id

int

firstName

varchar(255)

lastName

varchar(255)

email

varchar(255)

password

varchar(255)

Le Model qui correspond à la table users devra être une classe qui correspond à la structure de la table.

Ici, nous aurions donc :

<?php class User { public function __construct(private string $firstName, private string $lastName, private string $email, private string $password, private ? int $id = null) { } public function getFirstName(): string { return $this->firstName; } public function setFirstName(string $firstName): void { $this->firstName = $firstName; } public function getLastName(): string { return $this->lastName; } public function setLastName(string $lastName): void { $this->lastName = $lastName; } public function getEmail(): string { return $this->email; } public function setEmail(string $email): void { $this->email = $email; } public function getPassword(): string { return $this->password; } public function setPassword(string $password): void { $this->password = $password; } public function getId(): ?int { return $this->id; } public function setId(?int $id): void { $this->id = $id; } }

J'ai bien tous les attributs présents dans la table, initialisés dans le constructeur, et j'ai ajouté mes getters et setters.

Représenter une relation simple

Imaginons que les usages de notre bibliothèque aient des cartes pour emprunter des livres, stockées dans une table cards.

nom de la colonne

type de la colonne

id

int

user_id

int Foreign Key

expiration

datetime

Pour représenter la relation entre la Card et le User, nous allons utiliser la composition (une classe est l'attribut d'une autre) :

class Card { public function __construct(private User $user, private DateTime $expiration, private ? int $id = null) { } public function getUser(): User { return $this->user; } public function setUser(User $user): void { $this->user = $user; } public function getExpiration(): DateTime { return $this->expiration; } public function setExpiration(DateTime $expiration): void { $this->expiration = $expiration; } public function getId(): ?int { return $this->id; } public function setId(?int $id): void { $this->id = $id; } }

Représenter une relation complexe

Imaginons maintenant qu'avec leurs cartes, nos utilisateurs emprunte des livres, nous allons avoir besoin de pouvoir suivre où sont quels livres. Dans la base de données nous aurons donc eux nouvelles tables : books et cards_books.

la table books

nom de la colonne

type de la colonne

id

int

title

varchar(255)

author

varchar(255)

la table card_books

nom de la colonne

type de la colonne

card_id

int FK

book_id

int FK

Pour représenter cette relation complexe dans notre classe, nous allons encore utiliser la composition. Mais au lieu d'avoir en attribut une unique instance de classe, nous aurons en attribut, un tableau d'instances de classes.

la classe Book

<?php class Book { public function __construct(private string $title, private string $author, private ? int $id) { } public function getTitle(): string { return $this->title; } public function setTitle(string $title): void { $this->title = $title; } public function getAuthor(): string { return $this->author; } public function setAuthor(string $author): void { $this->author = $author; } public function getId(): ?int { return $this->id; } public function setId(?int $id): void { $this->id = $id; } }

Modifions la classe Card

<?php class Card { public function __construct(private User $user, private DateTime $expiration, private array $books = [], private ? int $id = null) { } public function getUser(): User { return $this->user; } public function setUser(User $user): void { $this->user = $user; } public function getExpiration(): DateTime { return $this->expiration; } public function setExpiration(DateTime $expiration): void { $this->expiration = $expiration; } public function getBooks(): array { return $this->books; } public function setBooks(array $books): void { $this->books = $books; } public function getId(): ?int { return $this->id; } public function setId(?int $id): void { $this->id = $id; } public function addBook(Book $book): void { $this->books[] = $book; } public function removeBook(Book $book): void { foreach($this->books as $i => $boo) { if($book->getId() === $boo->getId()) { unset($this->books[$i]); } } } }

Vous avez donc ici des exemples pour les trois principaux cas de figure que l'on rencontre dans les Models. Maintenant il ne nous reste plus qu'à les faire remplir / sauvegarder par la base de données.

Mettre en place les Managers

Les Managers (parfois appellés Repositories) sont des classes dont les méthodes correspondent aux requêtes que vous voulez effectuer sur la base de données. Vous devez avoir un Manager par Model.

AbstractManager

Tous vos Managers auront besoin d'une connexion à la base de données, vous allez donc placer celle-ci dans une classe abstraite AbstractManager dont hériteront tous vos Managers.

<?php abstract class AbstractManager { protected PDO $db; public function __construct() { $host = "host de la base, généralement localhost"; $port = "3306"; $dbname = "nomdelabase"; $connexionString = "mysql:host=$host;port=$port;dbname=$dbname;charset=utf8"; $user = "votre_username"; $password = "votre_password"; $this->db = new PDO( $connexionString, $user, $password ); } }

Le CardManager

Je vais montrer ici l'exemple du CardManager parce que c'est lui qui aura les requêtes les plus complexes, mais le même principe s'applique pour tous les managers.

Un Manager doit au minimum avoir 5 méthodes publiques :

  • create qui permet d'insérer en base de données

  • update qui permet de mettre à jour en base de données

  • delete qui permet de supprimer de la base de données

  • findOne qui permet de trouver une entrée dans la base de données

  • findAll qui permet de trouver toutes les entrées de la base de données

<?php class CardManager extends AbstractManager { public function __construct() { parent::__construct(); } public function findOne(int $id) : Card { $query = $this->db->prepare('SELECT cards.*, users.* FROM cards JOIN users ON users.id = cards.user_id WHERE cards.id = :id'); $parameters = [ 'id' => $id ]; $query->execute($parameters); $result = $query->fetch(PDO::FETCH_ASSOC); $user = new User($result["firstName"], $result["lastName"], $result["email"], $result["password"], $result["users.id"]); $card = new Card($user, DateTime::createFromFormat('Y-m-d H:i:s', $result["expiration"]), [], $result["cards.id"]); $query = $this->db->prepare('SELECT books.* FROM books JOIN cards_books ON cards_books.book_id = books.id JOIN cards ON cards_books.card_id = cards.id WHERE cards.id = :id'); $parameters = [ 'id' => $id ]; $query->execute($parameters); $resultBooks = $query->fetch(PDO::FETCH_ASSOC); $books = []; foreach($resultBooks as $resultBook) { $books[] = new Book($resultBook["title"], $resultBook["author"], $resultBook["id"]); } $card->setBooks($books); return $card; } public function findAll() : array { $query = $this->db->prepare('SELECT cards.*, users.* FROM cards JOIN users ON users.id = cards.user_id'); $parameters = [ ]; $query->execute($parameters); $results = $query->fetchAll(PDO::FETCH_ASSOC); $cards = []; foreach($results as $result) { $user = new User($result["firstName"], $result["lastName"], $result["email"], $result["password"], $result["users.id"]); $card = new Card($user, DateTime::createFromFormat('Y-m-d H:i:s', $result["expiration"]), [], $result["cards.id"]); $query = $this->db->prepare('SELECT books.* FROM books JOIN cards_books ON cards_books.book_id = books.id JOIN cards ON cards_books.card_id = cards.id WHERE cards.id = :id'); $parameters = [ 'id' => $result["cards.id"] ]; $query->execute($parameters); $resultBooks = $query->fetch(PDO::FETCH_ASSOC); $books = []; foreach($resultBooks as $resultBook) { $books[] = new Book($resultBook["title"], $resultBook["author"], $resultBook["id"]); } $card->setBooks($books); $cards[] = $card; } return $cards; } }

Les méthodes create, update et delete en revanche ne changent pas de celles que vous connaissez déjà avec les cours et exercices du j4 etles cours et exercices du J5.

Exemples sur une classe plus simple

Imaginons que dans mon BookManager je veux pouvoir effectuer ces 5 opérations pour les Books :

<?php class BookManager extends AbstractManager { public function __construct() { parent::__construct(); } public function findOne(int $id) : Book { $query = $this->db->prepare('SELECT * FROM books WHERE id = :id'); $parameters = [ 'id' => $id ]; $query->execute($parameters); $result = $query->fetch(PDO::FETCH_ASSOC); return new Book($result["title"], $result["author"], $result["id"]); } public function findAll() : array { $query = $this->db->prepare('SELECT * FROM books'); $parameters = [ ]; $query->execute($parameters); $results = $query->fetchAll(PDO::FETCH_ASSOC); $books = []; foreach($results as $result) { $books[] = new Book($result["title"], $result["author"], $result["id"]); } return $books; } }
04 December 2025