Salut !

Tout d'abord, je suis débutant en POO =)

J'ai suivi le tuto : Grafikart : Refactorisation d'un espace membre
Dans ce tuto, on reçois les infos du membre dans la variable de session.
J'ai voulu créer une classe Member qui concernerai l'affichage des infos, la modification des infos et tout ce qui touche aux membres.

Est-ce qu'elle est bien écrite et sécurisée ?
Je n'ai pas fini la classe, mais je veux pas partir sur des mauvaises bases ;)

Mon constructeur se retrouve avec beaucoup d'attributs, si je ne les mets pas dans le constructeur, je n'accède pas aux getters.

Et est-ce obligatoire de mettre les attributs en protected ? pourquoi pas en public ?

Pour l'affichage des infos, je pensais à une fonction qui ferai ça plutôt que les getters.

Mercii

<?php

class Member{

    protected $id;
    protected $username;
    protected $email;
    protected $surname;
    protected $name;
    protected $sex;
    protected $birthdate;
    protected $userstart;
    protected $userdate;

    public function __construct($db, $username){
        $user = $db->query('SELECT * FROM users WHERE username = :username',['username' => $username])->fetch();
        $this->id = $user->id;
        $this->username = $user->username;
        $this->email = $user->email;
        $this->surname = $user->surname;
        $this->name = $user->name;
        $this->sex = $user->sex;
        $this->birthdate = $user->birthdate;
        $this->userstart = $user->userstart;
        $this->userdate = $user->userdate;
    }

    public function getId(){
        return $this->id;
    }

    public function setId($id){
        $this->id = $id;
    }

    public function getUsername(){
        return $this->username;
    }

    public function setUsername($username){
        $this->username = $username;
    }

    public function getEmail(){
        return $this->email;
    }

    public function setEmail($email){
        if(!filter_var($email,FILTER_VALIDATE_EMAIL)){
            throw new \InvalidArgumentException('Not a valid value');
        }
        $this->email = $email;
    }

    public function getSurname(){
        return $this->surname;
    }

    public function setSurname($surname){
        $this->surname = $surname;
    }

    public function getName(){
        return $this->name;
    }

    public function setName($name){
        $this->name = $name;
    }

    public function getSex(){
        return $this->sex;
    }

    public function setSex($sex){
        $this->sex = $sex;
    }
    public function getBirthdate(){
        return $this->birthdate;
    }

    public function setBirthdate($birthdate){
        $this->birthdate = $birthdate;
    }

    public function getUserstart(){
        return $this->userstart;
    }

    public function setUserstart($userstart){
        $this->userstart = $userstart;
    }

    public function getUserdate(){
        return $this->userdate;
    }

    public function setUserdate($userdate){
        $this->userdate = $userdate;
    }

}

6 réponses


Salut,

Mon constructeur se retrouve avec beaucoup d'attributs, si je ne les mets pas dans le constructeur, je n'accède pas aux getters.

Je ne sais pas si c'est une question,
si oui, je n'ai pas trop compris.

Et est-ce obligatoire de mettre les attributs en protected ? pourquoi pas en public ?

Rien n'est vraiment 'obligatoire' dans l'absolu,
mais GRANDEMENT recommandé,
mettre les attributs en public violerai le "principe d'encapsulation", qui est LE GRAND principe de la programmation objet.
De ce point de vue,
on va dire que c'est "obligatoire" oui.

Je vais te donner une 'explication' (pourquoi on ne met PAS les attributs en public) dans quelques lignes,
mais avant je vais me servir de ce que tu as fais pour préparer le terrain.

Tu vois, tes getters et setters sont tous ultra basique (c'est normal), SAUF
le setter setEmail, qui fait une chose en plus.

Pour ceux qui sont basique,
on pourrait bien se demander qu'elle est la foutu importance d'écrire

// si l'attribut est 'private' ou 'protected'
public function getEmail()
{
    return $this->email;
}

$ma_variable = $user->getEmail()

plutôt que

// si l'attribut est 'public'
$ma_variable = $user->email;
// c'est plus simple, et pas besoin d'écrire une fonction

Mais, pour ceux qui ne sont PAS basique, cela prends tout son sens d'écrire ces getters/setters.

Si tu n'avais pas écris ton setter setEmail,
à chaque fois que tu voudrai "set" un email tu devrai écrire

// mode public, pénible
if (!filter_var($email,FILTER_VALIDATE_EMAIL))
{
    throw new \InvalidArgumentException('Not a valid value');
}
$user->email = $email;

alors que grâce à ton setter, qui n'est écrit qu'une fois,
tu peux juste utiliser

// mode protected, efficace
$user->setEmail($email);

Il me semble que dans la formation que tu cite,
Grafikart utilise une astuce pour ne pas avoir à écrire péniblement tous les getters/setters qui sont "basique".

Il utilise une class Entity (abstraite, qu'on instanciera jamais), qui contient 2 méthodes "de base",
dont vont hériter toutes les "vraies" entity (MemberEntity par exemple, est une "vraie" entité, qu'on va instancier)

Ça se présente plus ou moins comme ça :

<?php
// La classe de base, abstraite, qu'on instanciera jamais
class Entity
{

    public function get($attribute)
    {
        if (!isset($this->$attribute))
        {
            return null;
        }

        return $this->$attribute;
    }

    public function set($attribute, $value)
    {
        if (!isset($this->$attribute))
        {
            return false;
        }

        $this->$attribute = $value;
        return true;
    }
}

// Ta classe, qui hérite de la classe 'Entity'
class MemberEntity extends Entity
{

    protected $id;
    protected $username;
    protected $email;
    protected $surname;
    protected $name;
    protected $sex;
    protected $birthdate;
    protected $userstart;
    protected $userdate;

    public function __construct($db, $username){
        $user = $db->query('SELECT * FROM users WHERE username = :username',['username' => $username])->fetch();
        $this->id = $user->id;
        $this->username = $user->username;
        $this->email = $user->email;
        $this->surname = $user->surname;
        $this->name = $user->name;
        $this->sex = $user->sex;
        $this->birthdate = $user->birthdate;
        $this->userstart = $user->userstart;
        $this->userdate = $user->userdate;
    }

    public function setEmail($email){
        if(!filter_var($email,FILTER_VALIDATE_EMAIL)){
            throw new \InvalidArgumentException('Not a valid value');
        }
        $this->email = $email;
    }

}
?>

Du coup à l'utilisation :

<?php
// fonctionne grâce au `setter` de la class `Entity`
$user->set('username', $username);

// fonctionne grâce au `setter` de la class `MemberEntity`
$user->setEmail($email);
?>

Pour l'affichage des infos, je pensais à une fonction qui ferai ça plutôt que les getters.

Comment ça ?
Un getter n'affiche rien,
il retourne une valeur.

PS : Désolé si ça parait un peu trop simple ce que je raconte,
j'ai essayé d'être le plus clair possible parce-que tu nous as prévenu que tu débute.

Nikola
Auteur

Salut,

Merci, j'ai bien compris l'encapsulation, c'est une sorte de convention de la POO ;)

Alors je reformule mes questions :

  • Est ce que c'est normal qu'on contruit l'objet Member avec tous les attributs dans le constructeur (tous les champs de ma table users sauf le mot de passe) ?
  • Quand je fais un echo $user->getUsername(); ça affiche le pseudo, si je le met pas dans le constructeur, il ne s'affiche pas.
    Merci pour la classe Entity ;)
  • Donc globalement, ma classe est bien écrite ? ça ne pose pas de problème avec la requête dans le constructeur ?

Je pensais faire une autre classe Profile, quand je veux visiter le profil d'un membre avec la variable GET, mais je ne sais pas comment faire...

Merci d'avance

d'accord,

Est ce que c'est normal qu'on contruit l'objet Member avec tous les attributs dans le constructeur (tous les champs de ma table users sauf le mot de passe) ?

Oui c'est normal d'avoir tous les champs de la table dans le constructeur.
Mais il faudrait aussi le mot de passe. Pour quelle raison tu as voulu l'enlever ?

Quand je fais un echo $user->getUsername(); ça affiche le pseudo, si je le met pas dans le constructeur, il ne s'affiche pas.

Oui c'est normal,
au début de la classe, il y a ça : protected $username; donc l'attribut username existe bien pour chaque objet member, par contre sa valeur est à null, c'est pour ça qu'il ne t'afiche rien.
Par contre, si dans le constructeur tu mets $this->username = $user->username; (ce qu'il faut faire), alors quand tu fera un echo tu verra bien une valeur.

Donc globalement, ma classe est bien écrite ? ça ne pose pas de problème avec la requête dans le constructeur ?

Oui ça va,
il faut juste réfléchir un peut à la suite,
ça dépend complètement de ce que tu fais sur ton site.
Je veux dire... Là avec cette façon de faire, tu es obligé d'avoir le membre déjà présent en base de données pour instancier ta classe.

Donc, quand un nouveau membre voudra s'inscrire, passe par le formulaire d'inscription, et le soumet,
il faudra (dans cet ordre)

  • vérifier les valeurs de $_POST
  • enregistrer le membre en base de données
  • instancier ta classe Member, pour trimballer un objet 'member' pour tous les trucs éventuels que tu as à faire

Alors que SI tu as besoin d'avoir un objet 'member' pour faire tous les trucs éventuels que tu as à faire AVANT d'enregistrer en base de données, tu ne pourras pas.
Mais si tu n'as pas besoin de faire ça sur ton site, alors c'est bon, pas de problèmes.

Il n'y a pas de règle qui interdise d'avoir une requête dans le constructeur,
il faut juste penser à la suite, à comment tu vas procéder pour tout le reste,
et ça, c'est à toi de décider.

L'encapsulation n'est pas une convention puisque je dirais qu'une convention est par défaut arbitraire (je peux me tromper, mais pour moi des conventions c'est par exemple des coding styles...). Or, l'encapsulation repose sur des principes logiques

Je me permettrais juste d'ajouter un petit quelque chose à ce qu'a dit @SLK quant à l'encapsulation

Imagine tu utilises tout le temps

$user->pseudo = "...";

Et un jour tu te dis, oui donc j'aimerais ajouter mon pote, mais pour être sûr qu'il fasse pas de bêtise, je vais valider les données
=> tu devras remplacer $user->pseudo = "..." partout dans ton code
C'est la raison pour laquelle personnellement je n'aime pas vraiment le principe d'Entity fait comme ça, si c'était fait avec des méthodes magiques, pourquoi pas

Pour info, tu peux mettre une class en "abstract" pour qu'elle ne puisse pas être instanciable.
Pour info 2, tu peux utiliser public function __call pour garder le format getMachin, le soucis par contre, c'est qu'un dev peut accéder/modifier toutes les variables de le class dans ce cas là, pour autant, certaines ne devront peut-etre pas être modifiable par un setter.

Ensuite, personnelement, je met rarement la logique dans le contructeur.
C'est à dire que la logique je la met dans une autre méthode, qui éventuellement est appelé par le constructeur. Mais peut de nouveau être appelé par la suite.

Donc la requete + les setters qui vont avec, tu les mets dans une méthode "init($username)" par exemple.
De plus, il faudrait plutôt utiliser les setters plutôt que d'utiliser la variable directement. Au moins les traitements qui tu pourrais prévoir par la suite seront utilisés.

Petite chose également, tu devrais définir le type d'instance que tu attends avec "$db" tel que :
public function __construct(PDO $db)
par exemple.

Nikola
Auteur

Merci pour toutes ces infos ;)

Pour infos j'ai une classe authentification qui gère les connexions, inscriptions, et tout ce qui touche à l'authentification en général.
Donc la classe membre, servirai juste à intéragir avec le membre, donc les setters et les getters seraient bien utiles ;)
J'ai appris l'existence des méthodes magiques get et set ;)
L'encapsulation c'est plutôt facile à assimiler.

Pour ce qui est d'hydrater mes propriétés, est-ce obligatoire quand on veut juste récupérer les infos du membre et si on affecte les propriétés dans le constructeur ?

J'aimerai créer une méthode qui récupère toutes les infos du membre avec la requête, et les affectes dans leurs propriétés accessibles depuis les getters et les setters (en cas de modification).
Auriez-vous un exemple (méthode) pour assigner des valeurs à mes propriétés en passant par la requête ?
Et l'utiliser depuis le constructeur ;)

$user = $db->query('SELECT * FROM users WHERE username = :username',['username' => $username])->fetch();

Je précise aussi, que j'ai une page profil qui cherchera en paramètre GET et donc l'username du membre à visiter.
Dois-je créer une classe Profil qui hérite de Membre pour retrouver le membre avec le paramètre transmis ?

Je vois que pour faire un espace membre, il y a pleins de possibilités en POO, mais il faut bien faire les choses.
Je me forme sur tuto.com en POO, mais manque de pratique avec un espace membre.

Je ne sais pas trop si j'ai été clair ?
Merci d'avance