POO, Besoin de quelque eclairssisement
Bonjour,
Voila quelques semaines que je m'essaie à la POO. Après lecture des cours de la section "tutoriels" et des différents sujets du forum, je me suis lancé dans la création d'une classe News.
Je souhaiterais avoir votre avis sur l'implementation de ma classe et de sa structure.
Pour l'utilisation j'ai créé un petit fichier de test :
J'aurais aimé vos avis. J'ai aussi relevé une erreur que je ne comprend pas. En effet dans la classe j'utilise le typage de certaine variable (notamment id_news en int). cependant lors de mon test php me retourne une erreur. pourtant je passe le nombre 17 à la fonction et est donc bien un entier.
Voci l'erreur retournée : Fatal error: Default value for parameters with a class type hint can only be NULL in C:\wamp\www\test\News.class.php on line 124
Merci de votre aide, Cordialement Mimos@
Réponses apportées à cette discussion
Un petit conseil pour ton site, tu peux utiliser le modèle MVC , c'est bien expliquer ici :
http://julien-pauli.developpez.com/tutoriels/php/mvc-controleur/
Le début est accessible après sa se complexifie.
Bonne chance!
Salut Mimosa,
Ta classe est plutôt pas mal faite mais il y'a des incohérences et des choses que l'on peut largement améliorer.
- Tout d'abord tes getters. C'est bien d'avoir penser à la sécurité en faisant l'escaping dans les getters mais c'est problématique si ton format de sortie est différent du HTML ou du XML. Admettons que je veuille proposer en plus mes news dans un format PDF ou plain text, alors dans ce cas je n'ai pas besoin d'échapper les données. Il faut donc que tu spécifies un moyen de pouvoir activer / désactiver l'escaping des données.
- Les objets PDO et PDOStatement n'ont pas besoin de figurer dans la classe. Il vaudrait mieux passer l'objet PDO en paramètre des méthodes save() et delete(). Ou alors, utiliser un PDO Singleton (voire tutoriel de Palleas) pour pouvoir récupérer l'instance PDO depuis n'importe où sans pour autant la stocker quelque part.
- Toujours au sujet des getters, pourquoi ne renvoies-tu pas les valeurs des attributs ? Ce qu'il faut fair, c'est une méthode qui hydrate ton objet avec les valeurs récupérées de la BDD et faire des "finders"statiques pour les dissocier de ton instance.
- Pour gérer proprement les insert / update, il faut que tu aies dans ton objet un tableau contenant les noms des champs que tu as setter / getter. C'est d'après lui que tu vas devoir construire tes requêtes INSERT et UPDATE pour n'insérer et ne mettre à jour que les attributs que tu as setter.
Au final, voilà comment je vois ta classe :
<?php
class News
{
const TABLE_NAME = 'news';
private $id = null;
private $title = null;
private $body = null;
private $author = null;
private $modified = array();
public static function findById($id) {
if (!filter_var($id, FILTER_VALIDATE_INT)) {
throw new Exception('You must provide an integer value');
}
$infos = PDOSingleton::getInstance()->query('SELECT '. self::TABLE_NAME .'.* FROM '. self::TABLE_NAME .' WHERE id = ?', array($id))->fetchOne(PDO::FETCH_ASSOC);
if (empty($infos)) {
return null;
}
$news = new News();
$news->hydrateFromArray($infos);
return $news;
}
public function hydrateFromArray($values) {
foreach($values as $fieldName => $value) {
$this->set($fieldName, $value);
}
}
protected function set($property, $value) {
if (isset($this->$property)) {
$this->$property = $value;
}
}
protected function addModifiedField($name) {
if (!in_array($name, $this->modified)) {
$this->modified[] = $name;
}
}
public function setTitle($title) {
$this->title = $title;
$this->addModifiedField('title');
}
public function setAuthor($author) {
$this->author = $author;
$this->addModifiedField('author');
}
public function setBody($body) {
$this->body = $body;
$this->addModifiedField('body');
}
public function save() {
if (is_null($this->id)) {
// Ici tu construis ta requête INSERT grâce au tableau $modified
$query = '...';
PDOSingleton::getInstance()->query($query, $params);
$this->id = (int) PDOSingleton::getInstance()->getLastInsertId();
}
else
{
// Ici tu construis ta requête UPDATE grâce au tableau $modified
$query = '...';
PDOSingleton::getInstance()->query($query, $params);
}
$this->modified[] = array();
}
public function delete() {
return PDOSingleton::getInstance()->query('DELETE FROM '. self::TABLE_NAME .' WHERE id = '. $this->id);
}
}
Là ce sont les grandes lignes pour te donner une idée de comment faire mais il manque à cela beaucoup de choses comme :
- Un tableau qui mappe les champs de la table et leur type afin de pouvoir appliquer les bons types automatiquement sur chaque attribut (int, string) au moment de l'hydratation et de construction de la requête SQL.
- La gestion des relations entre les tables.
Tout ça c'est le travail d'un ORM en fin de compte comme le sont Propel et Doctrine. Au final tu utilises ta classe comme ça :
<?php
$news = News::findById(24);
echo $news->getTitle();
$news = new News();
$news->setTitle('Ma première news');
$news->setAuthor('Moi');
$news->setBody('Corps de ma news');
$news->save();
++
Bonsoir,
Tout dabord merci de répondre aussi vite. Il mets assez difficile de pense objet et j'ai du mal à comprendre quelques points.
- Je ne vois pas ou ne comprend pas l'utilité de la fonction addModifiedField 2. Dans ta première reuete sql, vous passez un array($id). Quel est l'intéret ?
En tout cas merci, je découvre beaucoup sur se site et vos réaction sont toujours pertinante.
Mimos@
Salut Mimosa,
La méthode addModifiedField() a pour objectif de mettre dans le tableau privé $modified, la liste des champs de la table qui ont été modifié seulement afin de ne pas construire une requête UPDATE risquant de remettre à "null" des champs de la table. En si admettons que dans ma table news, je ne modifie que le titre. Au final, le but est d'obtenir la requête suivante : UPDATE news SET titre = 'Nouveau titre' WHERE id = 32.
Si l'on ne vérifie pas quels champs ont été modifiés, on risque d'obtenir cette requête :
UPDATE news SET titre = 'Nouveau titre', body = null, author = null WHERE id = 32.
A son exécution, cette requête réinitialisera à la valeur Null les deux autres champs, ce que nous ne souhaitons pas évidemment.
Concernant le array($id) c'est juste un exemple pour montrer comment on peut passer les paramètres d'une requête préparée qui serait générée dans la méthode query() de notre classe Singleton PDOSingleton.
++
Bonsoir,
Si je comprend bien, le tableau $modified doit me permettre de créer des réquetes sql UPDATE différentes selon se que contient le tableau.
Si le tableau contient uniquement titre alors la requete contruite est UPDATE news SET titre = la_valeur WHERE id = 32 Si il contient d'autre parmètre par exemple titre et body : UPDATE news SET titre =la_valeur_titre, body=la_valeur_body WHERE id = 32
Donc au final, si j'ai bien compris..., il faut compter le nombre de valeur de mon tableau et que je le parcours dans un foreach afin de contruire ma requete.
Je n'avais pas encore vraiment testé les méthodes pour la lecture d'un news. Au risque de passer pour un ignar de la programmation, j'avoue être perdu. Après avoir testé votre exemple avec une requete select valide par rapport à la table news, j'ai testéla méthode findById et est appelé la méthode getTitle.
J'ai evidament eu une erreur... Donc ma question revient a : Dois-je ajouter les fonction getTitle, getBody,... ou ces fonctions sont créé automatiquement à l'aide de la fonction set appellé danshydrateFromArray.
J'ai testé de définir la méthode mais n'ai pas su quoi renvoyé. Après un print_r sur l'objet news, je vois bien qu'il est remplie mais je n'rrive pas à afficher les donnée.
Merci de l'attention que tu porte à ma discution,
Mimos@
Oui il faut que tu crées tes getters à la main dans ta classe ou bien en utilisant les méthodes magiques __get() et __call(). Je ne les ai pas fait car ça fait partie des bases d'une classe :)
Très bien je te remercie de ton aide.
Je pense que tu peux mettre le sujet résolue.
A bientôt
Comment faire la boucle pour écrire la requete ?