Bonjour,
Ce message fait suite à mon précédent message. Je suis en train de m'autoformer sur PHP. Tout semble clair sur les spécificités du langage, je m'engage donc dans la POO et le Design Pattern....Et là c'est le drame.
Donc je vous explique où j'en suis:
Pour la création d'un blog, je construit un CRUD avec le PDO et les méthodes de base. Un des camarades de la communauté Grafikart m'indique que pour la gestion du CRUD il est plus simple de passer ces éléments en entity (objet). Je comprends le pourquoi. Mais là où je patauge c'est comment je mélange mon objet à mon CRUD.
Voici ce que j'ai compris:
1 - Le user (ou articles ....) j'en fais des objets avec les getter et les setter
2 - Dans le crud j'initie les entités (donc objets)
3 - J'appelle ces objets soit dans les méthodes style updateElement ou searchElement ou dans des classes style Auth
4 - Je get ou set ces objets à partir de ses méthodes ou classes

Ce que je ne comprends pas, c'est comment dans mon CRUD je peux "remplir" mon entity, et comment dans mes différents script php je peux faire appel à la fois à mon CRUD et à mon entity.

Voici le code que j'ai écrit pour chercher les utilisateurs:

<?php

namespace App;

use App\user\User;
use \PDO;
use \PDOException;

class Crud {

    protected $pdo;
    private $entity;

    public function __construct()
    {
        try{
            $this->pdo = new PDO('mysql:host=blog;dbname=blog;port=3307;charset=utf8', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ]);
        }catch (PDOException $error){
            throw new PDOException($error->getMessage(), (int)$error->getCode());
        }
    }

    protected function getPDO()
    {
        if ($this->pdo instanceof PDO) {
            return $this->pdo;
       }
    }

    public function createElement(array $datas)
    {

    }

    public function searchElement(string $entity, string $table = "")
    {  
        $this->entity = $entity::class;
        $req = $this->getPDO()->prepare("SELECT * FROM $table");
        $req->execute();
        $find = $req->fetchAll(PDO::FETCH_CLASS, $this->entity);

        return $find;
    }

Je vous remercie par avance de votre aide.

Cordialement

3 réponses


Pardon de te répondre tardivement mais j'avais un peu de boulot :)
Tu pourras bien entendu améliorer les choses par la suite quand tu comprendras mieux mais l'idée c'est:
(J'ai enlevé les namespaces et les use pour limiter le code)

Ta classe Auth

class Auth {

    protected $userCrud;

    public function __construct(UserCrud $userCrud)
    {
        $this->userCrud = $userCrud;
    }

    public function authUser(array $datas) :bool
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        $user = $this->userCrud->findUser($datas);

        if (!empty($user)) {
            $_SESSION['user_connected'] = true;

            $this->userCrud->updateUser($user->getEmail());

            return true;
        }else{
            return false;
        }
    }
}

Ton CRUD


class Crud {

    protected $pdo;
    protected $table;
    protected $entity;

    public function __construct()
    {
        try {
            $this->pdo = new PDO('mysql:host=blog;dbname=blog;port=3307;charset=utf8', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ]);
        } catch (PDOException $error){
            throw new PDOException($error->getMessage(), (int)$error->getCode());
        }
    }

    protected function getPDO()
    {
        if ($this->pdo instanceof PDO) {
            return $this->pdo;
       }
    }

    protected function searchElement(int $id)
    {
        $req = $this->getPDO()->prepare("SELECT * FROM $this->table WHERE id = :id");

        $req->execute([":id" => $id]);

        if ($this->entity != null) {
            $req->setFetchMode(PDO::FETCH_CLASS, $this->entity);
        }

        return $req->fetch();
    }

     protected function findAll()
    {
        $req = $this->getPDO()->prepare("SELECT * FROM $this->table");

        $req->execute();

        if ($this->entity != null) {
            $req->setFetchMode(PDO::FETCH_CLASS, $this->entity);
        }

        return $req->fetchAll();
    }
}

Ta classe UserCrud

class UserCrud extends Crud {

    protected $table = "users";
    protected $entity = User::class;
    protected $selector = ['email', 'pass', 'last_login'];

    public function findUser(array $datas)
    {
        $email = $datas[0];
        $pass = $datas[1];

        $req = $this->getPDO()->prepare(
            "SELECT * FROM $this->table WHERE email = :email AND pass = :pass");

        $req->execute(array(
            'email' => $email,
            'pass' => $pass
        ));

        $req->setFetchMode(PDO::FETCH_CLASS, $this->entity);

        return $req->fetch();
    }

    public function updateUser(string $email)
    {
        date_default_timezone_set('Europe/Paris');
        $date = date("Y-m-d H:i:s");

        $req_update = $this->getPDO()->prepare(
            "UPDATE $this->table SET $this->selector[2] = :last_login
            WHERE email = $email");

        $req_update->bindParam('last_login', $date, PDO::PARAM_STR);
        $req_update->execute();
    }
}

Quand tu initialises auth

$userCrud = new userCrud()
$loginuser = new Auth($userCrud);

Par la suite, imaginons un PostCrud

class PostCrud extends Crud {

    protected $table = "posts";
    protected $entity = Post::class;
}

Avec ta méthode SearchElement, nous pourrions imaginer récupérer un article, un utilisateur, une catégorie,... avec l'id

$postCrud = new PostCrud();
$post = $this->postCrud->searchElement(1);

Comme la propriété (variable) "entity" de la classe "postCrud" c'est Post::class ton fetch de la méthode "searchElement" te renverra un objet pour modèle ta classe Post de ta table posts.

La méthode parent prendra en compte la propriété entity du parent sauf si dans la classe enfant elle est redéfinie.

Pardon s'il y a des fautes, il est tard et je n'ai pas testé le code mais l'idée est là.

N'hésites pas :)

Bonjour,

T'inquiète pas je n'en tiens pas rigueur. Ma période GrammarNazi est passé ;-)
Je comprends un peu mieu l'imbrication. Maintenant avec le code que tu m'as fourni j'ai un peu de mal à comprendre l'intérêt, ou plutôt à quel moment tu fais appel aux méthodes get et set de la classe User. Et je ne comprends pas pourquoi tu fais $this->userCrud->updateUser($user->getEmail()); dans la classe Auth.
Autre chose que je ne comprends pas, c'est comment avec un $user->getEmail() ou un $user->getLastname() dans un espace user du blog je vais pouvoir afficher son nom ou son email, puisque le fetch ressort les champs de la table users et que dans la classe il n'y a dans les méthodes qu'un $this->email = $email.
Ce que j'avais commencé à faire hier c'est:

La classe USER

<?php

namespace App\user;

use App\crud;

class User{

    private $forname;
    private $lastname;
    private $email;
    private $password;
    private $lastlogin;

    public function getForname()
    {
        return $this->forname;
    }

    public function getLastname()
    {
        return $this->lastname;
    } 

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

    public function getPassword()
    {
        return $this->password;
    }

    public function getLastLogin()
    {
        return $this->lastlogin;
    }

    public function setForname(string $forname)
    {
        $this->forname = $forname;
    }

    public function setLastname(string $lastname)
    {
        $this->lastname = $lastname;
    }

    public function setEmail(string $email)
    {
        $this->email = $email;
    }

    public function setPassword(string $password)
    {
        $this->password = $password;
    }

    public function setLastLogin(string $lastlogin)
    {
        $this->lastlogin = $lastlogin;
    }

}

Le CRUD

<?php

namespace App;

use App\user\User;
use \PDO;
use \PDOException;

class Crud {

    protected $pdo;
    private $entity;

    public function __construct()
    {
        try{
            $this->pdo = new PDO('mysql:host=blog;dbname=blog;port=3307;charset=utf8', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ]);
        }catch (PDOException $error){
            throw new PDOException($error->getMessage(), (int)$error->getCode());
        }
    }

    protected function getPDO()
    {
        if ($this->pdo instanceof PDO) {
            return $this->pdo;
       }
    }
    public function searchElement(string $entity, string $table = "")
    {  
        $this->entity = $entity::class;
        $req = $this->getPDO()->prepare("SELECT * FROM $table");
        $req->execute();
        $find = $req->fetchAll(PDO::FETCH_CLASS, $this->entity);

        return $find;
    }

J'aimerais que dans le CRUD le searchElement soit le plus neutre possible pour le redéfinir dans les classes enfants selon les besoins. Donc en mettant dans les paramètres que le nom de l'entité et le nom de la table alors la méthode est plus polyvalente.
J'imagine que j'étais pas loin et qu'en corrigeant ça devrait fonctionner. Ca commence tout doucement à venir. Enfin j'espère.

Tu n'as pas besoin de redéfinir "searchElement" dans les enfants
car ta méthode est générique, tu dois redéfinir simplement la propriété
table et entity dans ta classe enfant. Tu ne passeras pas directement par
ton CRUD pour utiliser tes méthodes mais par les enfants.
Si tu as besoin d'une méthode plus spécifique, tu l'ajoutes à ton enfant.

public function searchElement()
{  
    $req = $this->getPDO()->prepare("SELECT * FROM $this->table");
    $req->execute();

    // Si entity est différent de null on récupère un objet pour modèle entity
    // Sinon on récupère un tableau associatif
    if ($this->entity != null) {
        $req->setFetchMode(PDO::FETCH_CLASS, $this->entity);
    }

    return $req->fetchAll();
}
class UserCrud extends Crud
{
    protected $table = 'users';
    protected $entity = User::class;
}

PDO va remplir les propriétés de ta classe user et donc tu auras un objet pour modèle user

Si tu fais:

$users = $this->userCrud->searchElement();

Tu récupères un array d'objet user

Pour la partie update:

On récupère l'utilisateur

$user = $this->userCrud->findUser($datas);

Il faut envoyer l'adresse e-mail de l'utilisateur pour mettre à jour le last_login

$this->userCrud->updateUser($user->getEmail());

Dans updateUser tu fais ta requète pour mettre à jour le last_login WHERE l'email recu mais nous pourrions également utiliser directement la méthode updateUser dans la méthode findUser.

La récupération d'un utilisateur est plus spécifique donc tu ajoutes une méthode dans ta classe userCrud
mais par la suite tu pourras rendre dynamique la prise en compte des champs afin de faire des méthodes les plus génériques possible

Par exemple:

public function update(int $id, array $params): void
{
    $fieldQuery = implode(', ', array_map(function ($field) {
        return "$field = :$field";
    }, array_keys($params)));

    $sql = "UPDATE $this->table SET $fieldQuery WHERE id = $id";
    $s = $this->pdo->prepare($sql);
    $params['id'] = $id;
    $s->execute($params);
}

Le setter te renvoie simplement la valeur de la propriété privée, tu pourrais très bien avoir

public $email; 

et faire

$user->email;

au lieu de

$user->getEmail();

Pour les setters, tu pourrais par exemple à la création d'un utilisateur hydrater ton objet avec les champs recu
de ton formulaire

$user = new User();
$user->setEmail($_POST['email']);
$user->setName($_POST['name']);
etc,...

et envoyer ton utilisateur dans une méthode create

$this->userCrud->create($user);

Dans cette méthode, tu récupères les données de l'utilisateur afin de les encoders dans ta base de donnée avec ta requète INSERT

$user->getEmail();
$user->getName();

etc,...