[php] besoin d'aide pour optimiser connexion sql en objet [résolu]

besoin d'aide pour optimiser connexion sql en objet [résolu] [php] - PHP - Programmation

Marsh Posté le 03-01-2011 à 11:20:32    

bonjour
 
je galère depuis plusieurs jours sur un sujet : comment faire proprement sa connexion sql en objet, dans le cadre d'un modéle MVC ?
 
J'ai une classe "connectionBDD.class" qui fait la connection à la bdd avec un $bdd = new PDO ('etc.....').
 
J'ai ensuite d'autres classes qui pointent toutes vers cette classe pour faire des requetes sql.
 
Tout fonctionne.  
 
Mais j'aimerai optimiser tout cela car actuellement en fait je fais une création de connection à chaque requete. Je pense que c'est pas terrible. Car ça fait des dizaines de connection créées inutilement.
 
J'ai donc créé un fichier "controleur" qui appelle la classe de création de la connection. Et j'aimerai que cette connexion puisse me servir pour toutes les requetes à réaliser. Et j'ai supprimé la création de la connexion dans la classe qui fait la requete (pour l'instant je fais le test sur une seule classe, avant de généraliser aux autres).  
 
Et là ça ne fonctionne plus , j'ai le message Undefined variable: bdd in C:\wamp\www\mvc\class\etc....
 
 
Plusieurs questions :
- devant les difficultés à faire ceci je me demande si finalement ma volonté d'optimiser tout cela en vaut il le coup ?
- comment faites vous pour déclarer cette connexion , de manière unique , pour toutes les requetes ?
 
Vos avis l'intéressent.  
 
Dominique


Message édité par domi_bu le 05-01-2011 à 13:35:20
Reply

Marsh Posté le 03-01-2011 à 11:20:32   

Reply

Marsh Posté le 03-01-2011 à 11:28:52    

Regarde comment c'est fait dans les différents framework. Tu utilise un Singleton pour faire le lien avec ta base de donnée, histoire d'avoir une seule connexion (tu as raison de te préoccuper de ça, ça prend beaucoup de temps) et ensuite tu balances toutes tes requêtes dans le tuyau ..


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 03-01-2011 à 12:03:52    

bonjour esox_ch et merci de ta réponse;  
 
donc je le pressentais bien : c'est pas terrible de faire une création de connexion à chaque requete ! je vais rechercher de la doc sur Singleton (nom que je découvre ce matin).  
 
Merci. Dominique

Reply

Marsh Posté le 03-01-2011 à 13:11:35    

Cadeau : http://php.net/manual/en/language.oop5.patterns.php


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 03-01-2011 à 13:52:28    

Un truc du style...
 
Le dbExecute fait un dbConnect, qui ne se reconnecte pas si la connexion existe déjà.
Ce n'est pas un singleton dans le sens ou il existe une connexion par objet de type Database, mais si tu réutilises cet objet sans en instancier d'autres du même type, alors ton problème est résolu.
 
Si tu est un fan des new Database() multiples, alors le singleton est la solution qu'il te faut.
 

Code :
  1. class Database {
  2.  /**
  3.   * This variable is used to store the database handler.
  4.   * @var string
  5.   */
  6.  protected $dbh                  = null;
  7.  /**
  8.   * This variable is used to store the name of the provider.
  9.   * @var string
  10.   */
  11.  protected $provider             = 'mysql';
  12.  /**
  13.   * This variable is used to store the name of the host.
  14.   * @var string
  15.   */
  16.  protected $host                 = '127.0.0.1';
  17.  /**
  18.   * This variable is used to store the name of the database.
  19.   * @var string
  20.   */
  21.  protected $database             = 'mydatabase';
  22.  /**
  23.   * This variable is used to store the name of the user.
  24.   * @var string
  25.   */
  26.  protected $user                 = 'root';
  27.  /**
  28.   * This variable is used to store the password.
  29.   * @var string
  30.   */
  31.  protected $password            = 'mypassword';
  32.  /**
  33.   * This variable is used to store the character set.
  34.   * @var string
  35.   */
  36.  protected $charset              = 'utf8';
  37.  /**
  38.   * This variable is used to store the collation.
  39.   * @var string
  40.   */
  41.  protected $collation            = 'utf8_unicode_ci';
  42.  /**
  43.   * This variable is used to set or unset the debug print.
  44.   * @var integer
  45.   */
  46.  protected $debugMode            = 0;
  47.  /**
  48.   * This variable is used to store the number of rows affected by the lastest executed query.
  49.   * @var integer
  50.   */
  51.  protected $numRows              = 0;
  52.  
  53.  /**
  54.   * Constructs the class.
  55.   */
  56.  public function __construct() {
  57.    //parent::__construct();
  58.  }
  59.  
  60.  /**
  61.   * Destroys the class.
  62.   */
  63.  public function __destruct() {
  64.    //parent::__destruct();
  65.  }
  66.  
  67.  /**
  68.   * Creates a database connection link (called database handler).
  69.   * The database connection link is created in 'persistent mode'.
  70.   * If the database connection link already exists, it is not re-created.
  71.   * @return PDO The database persistent connection link.
  72.   */
  73.  public function dbConnect() {
  74.    try {
  75.      if ($this->dbh == null) {
  76.        $this->dbh = new PDO($this->provider.':host='.$this->host.
  77.                                           ';dbname='.$this->database,
  78.                                                      $this->user,
  79.                                                      $this->password,                
  80.                array(PDO::ATTR_PERSISTENT => true,
  81.                                  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
  82.        $this->dbh->query('SET NAMES \''.$this->charset.'\'');
  83.      }
  84.    }
  85.    catch (PDOException $e) {
  86.      echo 'Error: ',$e->getMessage(),"\n";
  87.      die();
  88.    }
  89.  }
  90.  
  91.  /**
  92.   * Closes the database manager connection link (called database manager handler).
  93.   */
  94.  public function dbManagerClose() {
  95.    $this->dbmh = null;
  96.  }
  97.  
  98.  /**
  99.   * Executes a SQL command on the database and keep only one result.
  100.   * The method 'dbConnect' is called before executing the SQL command.
  101.   * @param string $sqlCommand The SQL command to execute.
  102.   * @param array $sqlCommand (Optional) The parameters to escape.
  103.   */
  104.  public function dbExecute($sqlCommand, $params = null) {
  105.    if ($this->getDebugMode()) {
  106.      $print = $sqlCommand;
  107.      if ($params) {
  108.        foreach ($params as &$param) {
  109.          if (is_numeric($param)) {
  110.            $print = preg_replace('`\?`', $param, $print, 1);
  111.          }
  112.          else {
  113.            $print = preg_replace('`\?`', '\''.$param.'\'', $print, 1);
  114.          }
  115.        }
  116.        unset($param);
  117.      }
  118.      echo 'The following command was executed :',"\n",$print,"\n";
  119.    }
  120.    $result = '';
  121.    try {
  122.      $this->dbConnect();
  123.      $statement = $this->dbh->prepare($sqlCommand);
  124.      if (!$params)
  125.        $statement->execute();
  126.      else
  127.        $statement->execute($params);
  128.      if ($statement->columnCount()) {
  129.          $this->setNumRows($statement->rowCount());
  130.        $result = $statement->fetchAll();
  131.      }
  132.      else {
  133.        $this->setNumRows(0);
  134.             }
  135.      $statement->closeCursor();
  136.    }
  137.    catch (PDOException $e) {
  138.      $print = $sqlCommand;
  139.             if ($params) {
  140.        foreach ($params as &$param) {
  141.          if (is_numeric($param)) {
  142.            $print = preg_replace('`\?`', $param, $print, 1);
  143.          }
  144.          else {
  145.            $print = preg_replace('`\?`', '\''.$param.'\'', $print, 1);
  146.          }
  147.        }
  148.        unset($param);
  149.      }
  150.      echo 'Error: ',$e->getMessage(),"\n";
  151.      echo 'This error occured when the following command was executed :',"\n",$print,"\n";
  152.      die();
  153.    }
  154.    return $result;
  155.  }
  156.  
  157. // ...
  158.  
  159. }


 
Bon là j'ai placé une connexion persistante, à toi de voir si tu en as besoin...  :whistle:


Message édité par CyberDenix le 03-01-2011 à 13:57:51

---------------
Directeur Technique (CTO)
Reply

Marsh Posté le 03-01-2011 à 14:18:26    

T'utilises ça en prod toi :heink: ?


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 03-01-2011 à 17:05:17    

rebonjour
 
Merci de m'avoir lancé sur des pistes. J'ai maintenant une solution qui fonctionne mais j'ai encore un dernier doute.  
 
En fait , avant chaque requete sql je fais maintenant ceci :
 
$bdd = Outils_Bd::getInstance()->getConnexion();
puis
$requete = $bdd->prepare('select id_type, etc....')
 
En tout cas la technique du "singleton" fonctionne. ET j'ai compris que de la sorte je ne crée pas des dizaines de connections
 
Mais maintenant j'essaie de mettre la ligne  
$bdd = Outils_Bd::getInstance()->getConnexion();
dans mon controleur de page, de sorte à ne pas devoir répéter à chaque fois cette ligne avant chaque requete.  
Et bien ça ne fonctionne pas.  
 
On dirait que cette variable $bdd n'est connue que dans la fonction.  
 
Donc est ce normale ?  
 
Dominique
 
 
nb : cuberdenix : j'ai pas tout compris....désolé, mais je débute en php...

Reply

Marsh Posté le 03-01-2011 à 17:09:56    

Montre la structure de ton code, tu dois avoir loupé une étape.


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 03-01-2011 à 17:49:01    

Code :
  1. $this->dbh = new PDO($this->provider.':host='.$this->host.
  2.                                          ';dbname='.$this->database,
  3.                                                     $this->user,
  4.                                                     $this->password,
  5.               array(PDO::ATTR_PERSISTENT => true,
  6.                                 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
 

les PDO::ATTR_ non rien faire la, ce paramètre c'est pour les options de driver

 

et pourquoi les gens s'acharne a mettre le ` (opérateur d'exécution) de php dans les regexp, un jour on va vous sortir ca :

Code :
  1. $str = 'rm    ';
  2. preg_match(`rm *`, $str, $matches);
  3. print_r($matches);
  4. /*
  5. Array
  6. (
  7.    [0] => rm    
  8. )
  9. */

Message cité 1 fois
Message édité par stealth35 le 03-01-2011 à 17:54:38
Reply

Marsh Posté le 03-01-2011 à 21:53:48    

stealth35 a écrit :

Code :
  1. $this->dbh = new PDO($this->provider.':host='.$this->host.
  2.                                          ';dbname='.$this->database,
  3.                                                     $this->user,
  4.                                                     $this->password,
  5.               array(PDO::ATTR_PERSISTENT => true,
  6.                                 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));


 
les PDO::ATTR_ non rien faire la, ce paramètre c'est pour les options de driver
 
et pourquoi les gens s'acharne a mettre le ` (opérateur d'exécution) de php dans les regexp, un jour on va vous sortir ca :

Code :
  1. $str = 'rm    ';
  2. preg_match(`rm *`, $str, $matches);
  3. print_r($matches);
  4. /*
  5. Array
  6. (
  7.    [0] => rm    
  8. )
  9. */



 
Peux-tu commenter davantage ?


---------------
Directeur Technique (CTO)
Reply

Marsh Posté le 03-01-2011 à 21:53:48   

Reply

Marsh Posté le 03-01-2011 à 21:56:29    

esox_ch a écrit :

Montre la structure de ton code, tu dois avoir loupé une étape.


 
Ouais, montre-nous ton code entre deux balises de [ code=php ] ... [ /code ]   (sans les espaces à l'intérieur des [ ])


---------------
Directeur Technique (CTO)
Reply

Marsh Posté le 03-01-2011 à 23:11:57    

CyberDenix a écrit :


 
Peux-tu commenter davantage ?


 
les 2 ?

Reply

Marsh Posté le 04-01-2011 à 13:18:51    

bonjour esox_ch et cuberdenix
 
Voici mes codes :
 
1) d'abord le controleur

Code :
  1. include_once('class/outils_bd.class.php');
  2.  
  3. $bdd = Outils_Bd::getInstance()->getConnexion();
  4.  
  5.  
  6.  
  7. if ($_GET['section'] == 'etablissement')
  8. {
  9.     include_once('controleur/etablissement/index.php');
  10. }


 
2) l'accés à la bdd  

Code :
  1. <?php
  2.  
  3.  
  4.  
  5. class Outils_Bd {
  6.  /* pour être sûr qu'il n'y a qu'une et une seule instance */
  7.  private static $instance;
  8.  
  9.  /* le lien de connexion BD (objet PDO) */
  10.  protected $connexion;
  11.  
  12.  /* constructeur privé qui initialise la connexion*/
  13.  private function __construct() {
  14.    /* création d'un objet PDO avec les constantes définies dans la configuration */
  15.    $this->connexion = new PDO('mysql:host=localhost;dbname=dominiquirecole', 'dominiquirecole', 'azerty123');
  16.     
  17.    /* mettre Exception comme mode d'erreur */
  18.      $this->connexion->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  19.  }
  20.  
  21.  /* clonage impossible */
  22.  private function __clone() {}
  23.  
  24.  /**
  25.   * Accéder à l'UNIQUE instance de la classe
  26.   */
  27.  static public function getInstance() {
  28.    if (! (self::$instance instanceof self)) {
  29.      self::$instance = new self();
  30.    }
  31.    return self::$instance;
  32.  }
  33.  
  34.  /**
  35.   * Accesseur de la connexion
  36.   */
  37.  public function getConnexion() {
  38.    return $this->connexion;
  39.  }
  40. }
  41. ?>


 
 
3) extrait du modele
 

Code :
  1. <?php
  2. if (!isset ($_SESSION)) session_start();
  3.             
  4.             
  5. include_once 'class/etablissement.class.php';
  6. include_once 'class/typeEtablissement.php';
  7. include_once 'class/anneeScolaire.class.php';
  8.  
  9.  
  10. // récupération des types d'établissements
  11. //$liste_type_etablissement = typeEtablissement::listeEtablissement();
  12. $liste_type_etablissement = listeTypeEtablissement();
  13. [/code ]
  14.  
  15.  
  16. 4) le select :
  17.  
  18. [code=php]
  19. <?php
  20.  
  21.  
  22. class typeEtablissement {
  23.  
  24.     
  25.     public static function listeEtablissement()
  26.     {
  27.         
  28.         include_once('class/outils_bd.class.php');
  29.         $bdd = Outils_Bd::getInstance()->getConnexion();
  30.         $requete = $bdd->prepare('select id_type_etablissement,libelle_type_etablissement, date_creation, auteur_creation, date_maj, auteur_maj
  31.                                    from type_etablissement
  32.                                    order by ordre_affichage');
  33.  
  34.         $requete->execute() or die(print_r($requete->errorInfo()));
  35.         
  36.         return $requete->fetchAll();
  37.     }
  38.     
  39. }


 
 
 
Mon pb est que si j'enlève ces 2 lignes duscript de la requete (cf point 4), alors ça n emarche plus :
include_once('class/outils_bd.class.php');
$bdd = Outils_Bd::getInstance()->getConnexion();
 
Et pourtant ces 2 lignes sont déjà dans le point 1)
 
J'espère avoir réussi les insertions de code dans ce message....
 
Merci d'avance pour vos réflexions. En tout cas j'ai bien compris le principe du singleton et ainsi je n'ouvre plus des dizaines de connexions. Rien que pour ça : merci.
 
Dominique

Reply

Marsh Posté le 04-01-2011 à 13:35:13    

Oui mais non :o
$bdd = Outils_Bd::getInstance()->getConnexion(); n'a rien à faire dans ton contrôleur, c'est dans le modèle que ça doit se faire.
Ensuite, tu fais une classe genre "modeleBDD", dans le contructeur tu mets l'appel au singleton qui te prépare ton lien vers la base de donnée. Ensuite, soit tu le mets en protected et tu l'hérites dans tes classes du modèle (et tu fais tes $bdd->prepare comme maintenant), soit tu "surcharges" tes méthodes "prepare & co" (en gros, tu transformes modeleBDD en interface) comme le font certains FrameWorks (mais c'est plus compliqué à faire).
 

Aussi, ton singleton me semble foireux, parce que si tu fais :

Code :
  1. a = new Outils_Bd();
  2. b = new Outils_Bd();


 

Tu auras créé 2 connexions de bdd, là où tu devrais en avoir qu'une seule ... À mon sens faut cacher le fonctionnement du singleton à l’intérieur de la classe => faire les checks de l'unicité de l'instance dans le constructeur,  et après l'utiliser comme un objet normal (sans le getInstance)


Message édité par esox_ch le 04-01-2011 à 20:40:33

---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Marsh Posté le 04-01-2011 à 20:37:14    

Bah non, son constructeur est en privé, de fait sa classe ne peut être instanciée. C'est un des principes de base du singleton.

Reply

Marsh Posté le 04-01-2011 à 20:41:03    

T1 c'est la 2ème connerie de suite que je dis sur ce thread aujourd'hui (loupé le "private" ). Je vais aller me coucher :jap:


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed