Bonjour à tous,
j'ai un petit probleme de conception pour une appli que je fais pour ré(apprendre) la POO en PHP.
j'ai 3 classes (seance, serie et exercice)avec chacune leur manager (modèle)
Dans ma classe "serie" j'essaye de récupérer le nom de l'exercice de cette serie (ex: 1 serie de squat à 100kgs de 10 repetition) (Squatetant le nom de l'exercice)
VOici un bout de ma classe serie
<?php
class serie{
private $_id;
private $_idExercice;
private $_idSeance;
private $_rep;
private $_charge;
function __construct(array $donnees){
$this->hydrate($donnees);
}
//getter setter ...
public function getExercice(){
/*$db = new PDO('mysql:host=localhost;dbname=sport', 'root', 'azerty');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); */
$manager = new exerciceManager();
return $manager->get($this->_idExercice);
}
Mon probleme se situe au niveau de l'appel de mon manager qui prend en parametre un objet PDO. Je n'arrive pas à trouver comment procéder, créer un objet pdo dans la classe série serait contraire au bonne pratique ? (J'ai un objet PDO déclaré dans mon index.php)
Je ne souhaite pas utiliser Doctrine pour le moment.
Quelle ets la meilleure solution ?
Merci d'avance.
Bonjour, ton problème sera résolue par un DI (Dependency Injector) tel que : http://php-di.org/doc/understanding-di.html
Le souci est le suivant :
Ta classe A a besoin d'un objet de la classe B pour fonctionner. L'erreur serait de faire :
class ClassA
{
protected $b;
function __construct()
{
$this->b = new ClassB();
}
}
class ClassB
{
}
La façon plus élégante serait de faire :
class ClassA
{
protected $b;
function __construct(ClassBInterface $b)
{
$this->b = $b;
}
}
class ClassB implements ClassBInterface
{
}
interface ClassBInterface{
}
Puis ton DI se chargera de faire quelques choses comme :
$objectA = new ClassA(new ClassB());
quand tu en as besoin.
Bonjour,
merci pour ton retour,
je ne comprend pas trop ce que je dois mettre dans l'interface.
La connexion PDO ?
Reprenons un autres exemples un peu plus parlant :
class Voiture
{
protected $motor;
function __construct(MotorInterface $motor)
{
$this->motor = $motor;
}
}
class Diesel implements MotorInterface
{
// [...]
}
class Essence implements MotorInterface
{
// [...]
}
interface Motor {
public function start();
public function stop();
public function vitesseUp();
public function vitesseDown();
}
Une voiture doit pouvoir fonctionner quelques soits le type de moteur, tant que tu sais démarrer/arrêter le moteur, monter/descendre de vitesse. Il est donc naturel d'avoir une classe d'interface forçant ces methodes, ainsi tu es sure que ta voiture sera faire ça. Maintenant coté Dependency Injector.
// implementation
$container = new Container();
/**
* A chaque fois que tu feras un new Voiture(); ce code sera appelé.
**/
$container->set(Voiture::class,function($container) {
return new Voiture($container->get(MotorInterface::class));
});
/**
* A chaque fois que tu auras besoin d'un object implementant ta classe d'interface MotorInterface, ce code sera appelé.
* Tu veux changer tes types de moteur en Essence ? rien de plus simple, change le new en dessous.
**/
$container->set(MotorInterface::class,function($container) {
return new Diesel();
});
$voiture = $container->get('voiture'); // créé une voiture avec le moteur diesel
// [...]
$container->set(MotorInterface::class,function($container) {
return new Essence();
});
$voiture = $container->get('voiture'); // créé une voiture avec le moteur Essence
Bonjour,
d'accord merci. Donc dans mon cas c'est bien mes manager qui ont besoin d'implémenter une inerface connexion.
[[url=https://www.hostingpics.net/viewer.php?id=730866Capturedu20170906074313.png][img]https://img11.hostingpics.net/pics/730866Capturedu20170906074313.png[/img][/url]]()
Non ce n'est pas obligatoire en soit, c'est juste une bonne pratique que je te conseil d'utiliser. Un projet parfait sur ce point et un projet dans lequel toute les classes on comme parent une interface :
interface SuperClass{}
class Parent implements SuperClass {}
class Child extends Parent {}
class BadChild {} // dans cet exemple seul celle-ci n'est pas bonne.
Class GoodChild implements SuperClass {}
Attention, je précise encore un "projet parfait" hors dans a réalité, ça reste pas toujours évident voir faisable d'y parvenir, le tout est d'essayer de tendre vers ça. C'est quand même ça la base de la POO.
Voici un exemple de à quoi ça pourrait ressembler.
<?php
class Serie extends Model {
protected $_idExercice;
public function getExercise()
{
$this->manager->get($this->_idExercice);
}
}
abstract class Model
{
protected $manager;
public function __construct(ManagerInterface $manager) {
$this->manager = $manager;
}
}
class ExerciseManager extends ManagerAbstract
{
//[...]
}
abstract class ManagerAbstract implements ManagerInterface
{
protected $db;
public function __construct(\PDO $db)
{
$this->db = $db;
}
public function get($params) {
//[...]
}
}
interface ManagerInterface {
public function get($params);
}
/**
* Section dependency injection
**/
$container->set(ExerciseManager::class,function($container) {
return new ExerciseManager($container->get(\PDO::class));
});
$container->set(\PDO::class,function($container) {
return new PDO('bla','bla','bla');
});
$container->set(Serie::class,function($container) {
return $container->get(\PDO::class);
});
OK, merci.C'est un petit projet perso donc j'aimerais bien qu'il soit bien fait avant d'en faire de plus gros :D.
SI j'ai bien compris
interface IConnexion{}{
setConnexion(PDO $db);
getConnexion();
}
class Connexion implements IConnexion {
function __construct(PDO $db){
setConnexion($db);
}
}
class ManagerSerie extends Connexion {}
class ManagerExercice extends Connexion {}
class ManagerSeance extends Connexion {}
Par contre ca veux dire que si je fais new Serie() et aprés new Seance() je vais instancier 2 connexions ?
Quoi que non
$db = new Connexion();
$serie = new ManagerSerie(parent::getConnexion());
$seance = new ManagerSeance(parent::getConnexion())
j'ai l'impression de dire n'imp :D
Le rôle du DI est d'assurer la création / injection de tes objets pour toi, si tu regardes du coté de DI-PHP tu as la section Scope http://php-di.org/doc/scopes.html#available-scopes, si tu appliques le scope 'singleton' alors un seul objet sera partagé partout... Très utile pour la connexion justement :) Je te commande chaudement de te faire un mini projet avec quelques classes, juste pour tester le DI-PHP. Tu verras ça sera plus simple ensuite.
c'est ce que j'essaye de faire, mon projet actuel n'a que 3 classe (sans compter les 3 modeles)
Alors met le sur github ou autre et partage le nous, je ferai en sorte de te mettre l'implémentation.
Une interface sert à passer un contrat entre tes classes. J'accepterai n'importe quel objet respectant se contrat. Cela te garantie que quelques soits les évolutions futurs, aussi complexes soient elle, ça fonctionnera toujours étant donné que tu as des contrats pour le garantir.
Bon j'ai avancé un peu avec le singleton pour la connexion. Je mettrais le code a jour ce soir sur git
du coup j'ai des "connexion::getInstance() " avant chaque requete, niveau bonne pratique c'est ok ? (vous allez peutetre me dire que c est le but d'un singleton remarque :D )
//Retourne une seule série
public function getSerie($id){
$req = connexion::getInstance()->query('SELECT * FROM serie WHERE id = '.$id);
$donnee = $req->fetch(PDO::FETCH_ASSOC);
return new serie($donnee);
}
public function getListSerie(){
$req = connexion::getInstance()->query('SELECT s.*, e.* FROM serie s INNER JOIN exercice e ON s.idExercice = e.id ');
while($donnee = $req->fetch(PDO::FETCH_ASSOC)){
$series[]= new serie($donnee);
}
return $series;
}