Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

ABSTRACTION PDO


Information sur la source

Catégorie :Class et Objet ( POO ) Classé sous : pdo, classe, php5 Niveau : Débutant Date de création : 04/01/2009 Date de mise à jour : 05/01/2009 13:10:11 Vu / téléchargé: 1 684 / 81

Note :
10 / 10 - par 2 personnes
10,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (45)
Ajouter un commentaire et/ou une note

Description

Bonjour,
Cette classe permet d'étendre le module PDO.
La classe PdoManager ouvre la connexion en fonction de l'adapter passé.
Toutes les méthodes de PDO sont accessibles via la classe PdoManager grace a la fonction __call

Ex : appel de la méthode query
     $oPdo = new DaoFactory::factory($aParams);
     $oPdo->query('SELECT tata_yoyo FROM chapeau');

L'avantage est d'avoir des nom de méthode standardisé pour récupérer par exemple la liste des tables, la description des tables. Dans les classes PdoMysql et PdoPgsl on peut facilement ajouter des méthodes qui demandes des syntaxes SQL différentes selon les SGBD
Pour rajouter le support d'autre bdd il suffit par exemple de créer une class PdoMssql et d'y implémenter les méthodes obligatoires.
Voila c'est pas une classe très compliqué mais elle me sert beaucoup.
J'attend vaut critique et idées d'amélioration
 

Source

  • <?php
  • /**
  • * @abstract
  • * @name PdoManager
  • * @author aberthelot
  • * @since 11/10/2008
  • * @package Dao::Pdo
  • * @version 4.0.0 - AXB - 11/10/2008
  • * @version 4.0.1 - AXB - 14/11/2008 - Déportation de la méthode connect dans les classes filles
  • */
  • abstract class PdoManager
  • {
  • /**
  • * Host de connexion
  • *
  • * @access protected
  • * @var string
  • */
  • protected $_sHostname;
  • /**
  • * Nom d'utilisateur
  • *
  • * @access protected
  • * @var string
  • */
  • protected $_sUsername;
  • /**
  • * Password de connexion
  • *
  • * @access protected
  • * @var string
  • */
  • protected $_sPassword;
  • /**
  • * Adapter connexion
  • * Ex : mysql, pgsql
  • *
  • * @access protected
  • * @var string
  • */
  • protected $_sAdapter;
  • /**
  • * Nom de la base de données
  • *
  • * @access protected
  • * @var string
  • */
  • protected $_sBasename;
  • /**
  • * Port de connexion
  • *
  • * @access protected
  • * @var integer
  • */
  • protected $_iPort;
  • /**
  • * Encodage de la base de données
  • *
  • * @access protected
  • * @var string
  • */
  • protected $_sEncoding;
  • /**
  • * Connexion base de données
  • *
  • * @access protected
  • * @var object
  • */
  • protected $_oPdoInstance = null;
  • /**
  • * Constructeur de la classe
  • * Initialise les variables de connexion
  • * Test si l'extension pdo est chargée
  • *
  • * @param array $aParams
  • * @return void
  • */
  • public function __construct($aParams)
  • {
  • $this->_sAdapter = ( isset($aParams['adapter']) ) ? $aParams['adapter'] : null;
  • $this->_sHostname = ( isset($aParams['hostname']) ) ? $aParams['hostname'] : null;
  • $this->_sPassword = ( isset($aParams['password']) ) ? $aParams['password'] : null;
  • $this->_sUsername = ( isset($aParams['username']) ) ? $aParams['username'] : null;
  • $this->_sBasename = ( isset($aParams['basename']) ) ? $aParams['basename'] : null;
  • $this->_iPort = ( isset($aParams['port']) ) ? $aParams['port'] : null;
  • $this->_sEncoding = ( isset($aParams['encoding']) ) ? $aParams['encoding'] : null;
  • if( false === $this->checkExtensionPDo() )
  • {
  • throw new Exception('L\'extension "pdo" ou "pdo_'.$this->_sAdapter.'" ne sont pas activées.', E_ERROR);
  • }
  • }
  • /**
  • * Connexion à la base de données
  • * Passe la gestion des erreurs en exception
  • * Si mysql on active l'émulation des requêtes préparés
  • * Initialisation de l'encodage de la connexion
  • *
  • * @access public
  • * @return void
  • */
  • public function connect()
  • {
  • if( is_null($this->_oPdoInstance) )
  • {
  • $this->_oPdoInstance = new PDO($this->_getDsn(), $this->_sUsername, $this->_sPassword);
  • $this->_oPdoInstance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  • if( $this->_oPdoInstance->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql' )
  • {
  • $this->_oPdoInstance->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
  • }
  • $this->_setConnectionEncoding();
  • }
  • }
  • /**
  • * Mapping des fonction PDO
  • *
  • * @access public
  • * @param string $sMethod
  • * @param array $aArguments
  • * @return mixed|boolean
  • */
  • public function __call($sMethod, $aArguments)
  • {
  • if( method_exists(get_class($this->_oPdoInstance), $sMethod) )
  • {
  • return call_user_func_array(array($this->_oPdoInstance, $sMethod), $aArguments);
  • }
  • return false;
  • }
  • /**
  • * Retourne la liste des drivers disponible
  • *
  • * @static
  • * @access public
  • * @return array
  • */
  • public static function getAvailableDrivers()
  • {
  • return PDO::getAvailableDrivers();
  • }
  • /**
  • * Ferme la connexion à la base de données
  • *
  • * @access public
  • * @return void
  • */
  • public function close()
  • {
  • $this->_oPdoInstance = null;
  • }
  • /**
  • * Test si l'extension pdo est activé
  • *
  • * @access private
  • * @return boolean
  • */
  • private function checkExtensionPDo()
  • {
  • if ( extension_loaded('pdo') )
  • {
  • if( extension_loaded('pdo_'.$this->_sAdapter) )
  • {
  • return true;
  • }
  • }
  • return false;
  • }
  • /**
  • * Retourne le driver de connexion
  • *
  • * @abstract
  • * @access protected
  • * @return void
  • */
  • abstract protected function _getDsn();
  • /**
  • * Modifie l'encodage de la connexion
  • *
  • * @abstract
  • * @access protected
  • * @return void
  • */
  • abstract protected function _setConnectionEncoding();
  • /**
  • * Retourne la description de la table
  • *
  • * @abstract
  • * @access public
  • * @param string $sNameTable
  • * @param string $sSchema
  • * @return array
  • */
  • abstract public function describe($sNameTable, $sSchema = null);
  • /**
  • * Retourne la liste des tables de la base
  • *
  • * @abstract
  • * @access public
  • * @return array
  • */
  • abstract public function listTable();
  • }
  • ?>
  • <?php
  • /**
  • * @name PdoMysql
  • * @author aberthelot
  • * @since 11/10/2008
  • * @package Dao::Pdo
  • * @version 4.0.0 - AXB - 11/10/2008
  • */
  • class PdoMysql extends PdoManager
  • {
  • /**
  • * Constructeur de la classe
  • * Initialise les variables de connexion
  • * Test si l'extension pdo est chargée
  • *
  • * @param array $aParams
  • * @return void
  • */
  • public function __construct($aParams)
  • {
  • parent::__construct($aParams);
  • }
  • /**
  • * Retourne la description de la table
  • *
  • * @access public
  • * @param string $sNameTable
  • * @param string $sSchema
  • * @return array
  • */
  • public function describe($sNameTable, $sSchema = null)
  • {
  • $aData = array();
  • $sSql = 'SHOW COLUMNS FROM '.$sNameTable.';';
  • $aRes = $this->query($sSql);
  • $aRows = $aRes->fetchAll();
  • foreach($aRows as $iKey => $sValue)
  • {
  • $aData[$sValue['Field']]['name'] = $sValue['Field'];
  • $aData[$sValue['Field']]['type'] = $sValue['Type'];
  • $aData[$sValue['Field']]['null'] = ($sValue['Null'] == 'YES') ? true : false;
  • $aData[$sValue['Field']]['key'] = ($sValue['Key'] == 'PRI') ? true : false;
  • $aData[$sValue['Field']]['default'] = $sValue['Default'];
  • $aData[$sValue['Field']]['extra'] = $sValue['Extra'];
  • }
  • return $aData;
  • }
  • /**
  • * Retourne la liste des tables de la base
  • *
  • * @access public
  • * @return array
  • */
  • public function listTable()
  • {
  • $aData = array();
  • $sSql = 'SHOW TABLES;';
  • $aRes = $this->query($sSql);
  • $aRows = $aRes->fetchAll();
  • foreach($aRows as $iKey => $sValue)
  • {
  • $aData[$iKey] = $sValue['Tables_in_'.$this->_sBasename];
  • }
  • return $aData;
  • }
  • /**
  • * Retourne le driver de connexion
  • *
  • * @abstract
  • * @access protected
  • * @return void
  • */
  • protected function _getDsn()
  • {
  • return 'mysql:host='.$this->_sHostname.';dbname='.$this->_sBasename;
  • }
  • /**
  • * Modifie l'encodage de la connexion
  • *
  • * @access protected
  • * @return void
  • */
  • protected function _setConnectionEncoding()
  • {
  • if(! is_null($this->_sEncoding) )
  • {
  • $sStmt = $this->_oPdoInstance->prepare('SET CHARACTER SET ?');
  • $this->execute(array($this->_sEncoding));
  • }
  • }
  • }
  • ?>
  • <?php
  • /**
  • * @name PdoPgsql
  • * @author aberthelot
  • * @since 11/10/2008
  • * @package Dao::Pdo
  • * @version 4.0.0 - AXB - 11/10/2008
  • */
  • class PdoPgsql extends PdoManager
  • {
  • /**
  • * Constructeur de la classe
  • * Initialise les variables de connexion
  • * Test si l'extension pdo est chargée
  • *
  • * @param array $aParams
  • * @return void
  • */
  • public function __construct($aParams)
  • {
  • parent::__construct($aParams);
  • }
  • /**
  • * Retourne la description de la table
  • *
  • * @access public
  • * @param string $sNameTable
  • * @param string $sSchema
  • * @return array
  • */
  • public function describe($sNameTable, $sSchema = null)
  • {
  • $aData = array();
  • $sSql = "SELECT a.attnum, n.nspname, c.relname, a.attname AS colname, t.typname AS type, a.atttypmod, ";
  • $sSql .= "FORMAT_TYPE(a.atttypid, a.atttypmod) AS complete_type, d.adsrc AS default_value, ";
  • $sSql .= "a.attnotnull AS notnull, a.attlen AS length, co.contype, ARRAY_TO_STRING(co.conkey, ',') AS conkey ";
  • $sSql .= "FROM pg_attribute AS a ";
  • $sSql .= "JOIN pg_class AS c ON a.attrelid = c.oid ";
  • $sSql .= "JOIN pg_namespace AS n ON c.relnamespace = n.oid ";
  • $sSql .= "JOIN pg_type AS t ON a.atttypid = t.oid ";
  • $sSql .= "LEFT OUTER JOIN pg_constraint AS co ON (co.conrelid = c.oid ";
  • $sSql .= "AND a.attnum = ANY(co.conkey) AND co.contype = 'p') ";
  • $sSql .= "LEFT OUTER JOIN pg_attrdef AS d ON d.adrelid = c.oid AND d.adnum = a.attnum ";
  • $sSql .= "WHERE a.attnum > 0 AND c.relname = '".$sNameTable."' ";
  • $sSql .= ( is_null($sSchema) ) ? '' : " AND n.nspname = '".$sSchema."' ";
  • $sSql .= "ORDER BY a.attnum;";
  • $aRes = $this->query($sSql);
  • $aRows = $aRes->fetchAll();
  • foreach($aRows as $iKey => $sValue)
  • {
  • $aData[$sValue['colname']]['name'] = $sValue['colname'];
  • $aData[$sValue['colname']]['type'] = $sValue['complete_type'];
  • $aData[$sValue['colname']]['null'] = ($sValue['notnull'] == 1) ? true : false;
  • $aData[$sValue['colname']]['key'] = ($sValue['contype'] == 'P') ? true : false;
  • $aData[$sValue['colname']]['default'] = $sValue['default_value'];
  • }
  • return $aData;
  • }
  • /**
  • * Retourne la liste des tables de la base
  • *
  • * @access public
  • * @return array
  • */
  • public function listTable()
  • {
  • $aData = array();
  • $sSql = "SELECT c.relname AS table_name ";
  • $sSql .= "FROM pg_class c, pg_user u ";
  • $sSql .= "WHERE c.relowner = u.usesysid AND c.relkind = 'r' ";
  • $sSql .= "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) ";
  • $sSql .= "AND c.relname !~ '^(pg_|sql_)' ";
  • $sSql .= "UNION ";
  • $sSql .= "SELECT c.relname AS table_name ";
  • $sSql .= "FROM pg_class c ";
  • $sSql .= "WHERE c.relkind = 'r' ";
  • $sSql .= "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) ";
  • $sSql .= "AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner) ";
  • $sSql .= "AND c.relname !~ '^pg_';";
  • $aRes = $this->query($sSql);
  • $aRows = $aRes->fetchAll();
  • foreach($aRows as $iKey => $sValue)
  • {
  • $aData[$iKey] = $sValue['table_name'];
  • }
  • return $aData;
  • }
  • /**
  • * Retourne le driver de connexion
  • *
  • * @abstract
  • * @access protected
  • * @return void
  • */
  • protected function _getDsn()
  • {
  • return 'pgsql:host='.$this->_sHostname.';dbname='.$this->_sBasename;
  • }
  • /**
  • * Modifie l'encodage de la connexion
  • *
  • * @access protected
  • * @return void
  • */
  • protected function _setConnectionEncoding()
  • {
  • if(! is_null($this->_sEncoding) )
  • {
  • $stmt = $this->_oPdoInstance->prepare('SET client_encoding TO ?');
  • $this->execute(array($this->_sEncoding));
  • }
  • }
  • }
  • ?>
  • <?php
  • namespace Dao;
  • /**
  • * @name DaoFactory
  • * @author aberthelot
  • * @since 11/10/2008
  • * @package Dao
  • * @version 4.0.0 - AXB - 11/10/2008
  • */
  • class DaoFactory
  • {
  • /**
  • * Factory
  • * Retourne une instance de la classe désigné par $sType
  • *
  • * @param array $aParams
  • * @return object
  • */
  • public static function factory(array $aParams)
  • {
  • if( isset($aParams['adapter']) )
  • {
  • $sClass = 'Pdo'.ucfirst(strtolower($aParams['adapter']));
  • $oReflection = new ReflectionClass($sClass);
  • if(! $oReflection->isSubclassOf('PdoManager') )
  • {
  • throw new InvalidArgumentException($sClass.' n\'est pas une classe enfant de "PdoManager".', E_ERROR);
  • }
  • $oInstance = new $sClass($aParams);
  • return $oInstance;
  • }
  • else
  • {
  • throw new Exception('Aucun type de base de données spécifié.', E_ERROR);
  • }
  • }
  • }
  • ?>
  • ##### EXEMPLES #####
  • <?php
  • $oPdo = new DaoFactory::factory($aParams);
  • $sStmt = $oPdo->query('SELECT tata_yoyo FROM chapeau');
  • $aRes = $sStmt->fetchAll();
  • print '<pre>';
  • print_r($aRes);
  • print '</pre>';
  • ?>
<?php
/**
 * @abstract 
 * @name PdoManager
 * @author aberthelot
 * @since 11/10/2008
 * @package Dao::Pdo
 * @version 4.0.0 - AXB - 11/10/2008
 * @version 4.0.1 - AXB - 14/11/2008 - Déportation de la méthode connect dans les classes filles
 */
abstract class PdoManager
{
  /**
   * Host de connexion
   *
   * @access protected
   * @var string
   */
  protected $_sHostname;
  
  /**
   * Nom d'utilisateur
   *
   * @access protected
   * @var string
   */
  protected $_sUsername;
  
  /**
   * Password de connexion
   *
   * @access protected
   * @var string
   */
  protected $_sPassword;
  
  /**
   * Adapter connexion
   * Ex : mysql, pgsql
   * 
   * @access protected
   * @var string
   */
  protected $_sAdapter;
  
  /**
   * Nom de la base de données
   *
   * @access protected
   * @var string
   */
  protected $_sBasename;
  
  /**
   * Port de connexion
   *
   * @access protected
   * @var integer
   */
  protected $_iPort;
  
  /**
   * Encodage de la base de données
   *
   * @access protected
   * @var string
   */
  protected $_sEncoding;
  
  /**
   * Connexion base de données
   *
   * @access protected
   * @var object
   */
  protected $_oPdoInstance = null;
  
  /**
   * Constructeur de la classe
   * Initialise les variables de connexion
   * Test si l'extension pdo est chargée
   *
   * @param array $aParams
   * @return void
   */
  public function __construct($aParams)
  {
    $this->_sAdapter  = ( isset($aParams['adapter']) )   ? $aParams['adapter'] 	: null;
    $this->_sHostname = ( isset($aParams['hostname']) )  ? $aParams['hostname'] : null;
    $this->_sPassword = ( isset($aParams['password']) )  ? $aParams['password'] : null;
    $this->_sUsername = ( isset($aParams['username']) )  ? $aParams['username'] : null;
    $this->_sBasename = ( isset($aParams['basename']) )  ? $aParams['basename'] : null;
    $this->_iPort     = ( isset($aParams['port']) )      ? $aParams['port'] 		: null;
    $this->_sEncoding = ( isset($aParams['encoding']) )  ? $aParams['encoding'] : null;
    
    if( false === $this->checkExtensionPDo() )
    {
      throw new Exception('L\'extension "pdo" ou "pdo_'.$this->_sAdapter.'" ne sont pas activées.', E_ERROR);
    }
  }
  
  /**
   * Connexion à la base de données
   * Passe la gestion des erreurs en exception
   * Si mysql on active l'émulation des requêtes préparés
   * Initialisation de l'encodage de la connexion
   *
   * @access public
   * @return void
   */
  public function connect()
  {
    if( is_null($this->_oPdoInstance) )
    {
      $this->_oPdoInstance = new PDO($this->_getDsn(), $this->_sUsername, $this->_sPassword);
      $this->_oPdoInstance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	    
      if( $this->_oPdoInstance->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql' )
      {
	$this->_oPdoInstance->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
      }
	    
      $this->_setConnectionEncoding();
    }
  }
  
  /**
   * Mapping des fonction PDO
   *
   * @access public
   * @param string $sMethod
   * @param array $aArguments
   * @return mixed|boolean
   */
  public function __call($sMethod, $aArguments)
  {
    if( method_exists(get_class($this->_oPdoInstance), $sMethod) )
    {
      return call_user_func_array(array($this->_oPdoInstance, $sMethod), $aArguments);
    }
    
    return false;
  }
  
  /**
   * Retourne la liste des drivers disponible
   *
   * @static 
   * @access public
   * @return array
   */
  public static function getAvailableDrivers()
  {
    return PDO::getAvailableDrivers();
  }
  
  /**
   * Ferme la connexion à la base de données
   *
   * @access public
   * @return void
   */
  public function close()
  {
    $this->_oPdoInstance = null;
  }
  
  /**
   * Test si l'extension pdo est activé
   *
   * @access private
   * @return boolean
   */
  private function checkExtensionPDo()
  {
    if ( extension_loaded('pdo') )
    { 
      if( extension_loaded('pdo_'.$this->_sAdapter) )
      {
	return true;
      }
    }
		
    return false;
  }
  
  /**
	 * Retourne le driver de connexion
	 *
	 * @abstract 
	 * @access protected
	 * @return void
	 */
	abstract protected function _getDsn();
	
	/**
	 * Modifie l'encodage de la connexion
	 *
	 * @abstract 
	 * @access protected
	 * @return void
	 */
	abstract protected function _setConnectionEncoding();
	
	/**
	 * Retourne la description de la table
	 *
	 * @abstract 
	 * @access public
	 * @param string $sNameTable
	 * @param string $sSchema
	 * @return array
	 */
	abstract public function describe($sNameTable, $sSchema = null);
	
	/**
	 * Retourne la liste des tables de la base
	 *
	 * @abstract 
	 * @access public
	 * @return array
	 */
	abstract public function listTable();
}
?>


<?php
/**
 * @name PdoMysql
 * @author aberthelot
 * @since 11/10/2008
 * @package Dao::Pdo
 * @version 4.0.0 - AXB - 11/10/2008
 */
class PdoMysql extends PdoManager 
{
  /**
   * Constructeur de la classe
   * Initialise les variables de connexion
   * Test si l'extension pdo est chargée
   *
   * @param array $aParams
   * @return void
   */
  public function __construct($aParams)
  {
    parent::__construct($aParams);
  }
  
  /**
	 * Retourne la description de la table
	 *
	 * @access public
	 * @param string $sNameTable
	 * @param string $sSchema
	 * @return array
	 */
	public function describe($sNameTable, $sSchema = null)
	{
	  $aData = array();
	  $sSql  = 'SHOW COLUMNS FROM '.$sNameTable.';';
	  $aRes  = $this->query($sSql);
	  $aRows = $aRes->fetchAll();
	  
	  foreach($aRows as $iKey => $sValue)
	  {
	    $aData[$sValue['Field']]['name']    = $sValue['Field'];
	    $aData[$sValue['Field']]['type']    = $sValue['Type'];
	    $aData[$sValue['Field']]['null']    = ($sValue['Null'] == 'YES') ? true : false;
	    $aData[$sValue['Field']]['key']     = ($sValue['Key'] == 'PRI') ? true : false;
	    $aData[$sValue['Field']]['default'] = $sValue['Default'];
	    $aData[$sValue['Field']]['extra']   = $sValue['Extra'];
	  }
	  
	  return $aData;
	}
	
	/**
	 * Retourne la liste des tables de la base
	 *
	 * @access public
	 * @return array
	 */
	public function listTable()
	{
	  $aData = array();
	  $sSql  = 'SHOW TABLES;';  
	  $aRes  = $this->query($sSql);
	  $aRows = $aRes->fetchAll();
	  
	  foreach($aRows as $iKey => $sValue)
	  {
	    $aData[$iKey] = $sValue['Tables_in_'.$this->_sBasename];
	  }
	  
	  return $aData;
	}
	
	/**
	 * Retourne le driver de connexion
	 *
	 * @abstract 
	 * @access protected
	 * @return void
	 */
	protected function _getDsn()
	{
    return 'mysql:host='.$this->_sHostname.';dbname='.$this->_sBasename;
	}
	
	/**
	 * Modifie l'encodage de la connexion
	 *
	 * @access protected
	 * @return void
	 */
	protected function _setConnectionEncoding()
	{
	  if(! is_null($this->_sEncoding) )
	  {
	    $sStmt = $this->_oPdoInstance->prepare('SET CHARACTER SET ?');
	    $this->execute(array($this->_sEncoding));
	  }
	}
}
?>


<?php
/**
 * @name PdoPgsql
 * @author aberthelot
 * @since 11/10/2008
 * @package Dao::Pdo
 * @version 4.0.0 - AXB - 11/10/2008
 */
class PdoPgsql extends PdoManager 
{
  /**
   * Constructeur de la classe
   * Initialise les variables de connexion
   * Test si l'extension pdo est chargée
   *
   * @param array $aParams
   * @return void
   */
  public function __construct($aParams)
  {
    parent::__construct($aParams);
  }
  
  /**
	 * Retourne la description de la table
	 *
	 * @access public
	 * @param string $sNameTable
	 * @param string $sSchema
	 * @return array
	 */
	public function describe($sNameTable, $sSchema = null)
	{
	  $aData = array();
	  $sSql  = "SELECT a.attnum, n.nspname, c.relname, a.attname AS colname, t.typname AS type, a.atttypmod, ";
    $sSql .= "FORMAT_TYPE(a.atttypid, a.atttypmod) AS complete_type, d.adsrc AS default_value, ";
    $sSql .= "a.attnotnull AS notnull, a.attlen AS length, co.contype, ARRAY_TO_STRING(co.conkey, ',') AS conkey ";
    $sSql .= "FROM pg_attribute AS a ";
    $sSql .= "JOIN pg_class AS c ON a.attrelid = c.oid ";
    $sSql .= "JOIN pg_namespace AS n ON c.relnamespace = n.oid ";
    $sSql .= "JOIN pg_type AS t ON a.atttypid = t.oid ";
    $sSql .= "LEFT OUTER JOIN pg_constraint AS co ON (co.conrelid = c.oid ";
    $sSql .= "AND a.attnum = ANY(co.conkey) AND co.contype = 'p') ";
    $sSql .= "LEFT OUTER JOIN pg_attrdef AS d ON d.adrelid = c.oid AND d.adnum = a.attnum ";
    $sSql .= "WHERE a.attnum > 0 AND c.relname = '".$sNameTable."' ";
    $sSql .= ( is_null($sSchema) ) ? '' : " AND n.nspname = '".$sSchema."' ";
    $sSql .= "ORDER BY a.attnum;";
    $aRes  = $this->query($sSql);
	  $aRows = $aRes->fetchAll();
	  
	  foreach($aRows as $iKey => $sValue)
	  {
	    $aData[$sValue['colname']]['name']    = $sValue['colname'];
	    $aData[$sValue['colname']]['type']    = $sValue['complete_type'];
	    $aData[$sValue['colname']]['null']    = ($sValue['notnull'] == 1) ? true : false;
	    $aData[$sValue['colname']]['key']     = ($sValue['contype'] == 'P') ? true : false;
	    $aData[$sValue['colname']]['default'] = $sValue['default_value'];
	  }
	  
	  return $aData;
	}
	
	/**
	 * Retourne la liste des tables de la base
	 *
	 * @access public
	 * @return array
	 */
	public function listTable()
	{
	  $aData = array();
	  $sSql  = "SELECT c.relname AS table_name ";
    $sSql .= "FROM pg_class c, pg_user u ";
    $sSql .= "WHERE c.relowner = u.usesysid AND c.relkind = 'r' ";
    $sSql .= "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) ";
    $sSql .= "AND c.relname !~ '^(pg_|sql_)' ";
    $sSql .= "UNION ";
    $sSql .= "SELECT c.relname AS table_name ";
    $sSql .= "FROM pg_class c ";
    $sSql .= "WHERE c.relkind = 'r' ";
    $sSql .= "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) ";
    $sSql .= "AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner) ";
    $sSql .= "AND c.relname !~ '^pg_';";
		$aRes  = $this->query($sSql);
	  $aRows = $aRes->fetchAll();
	  
	  foreach($aRows as $iKey => $sValue)
	  {
	    $aData[$iKey] = $sValue['table_name'];
	  }
	  
	  return $aData;
	}
	
	/**
	 * Retourne le driver de connexion
	 *
	 * @abstract 
	 * @access protected
	 * @return void
	 */
	protected function _getDsn()
	{
	  return 'pgsql:host='.$this->_sHostname.';dbname='.$this->_sBasename;
  }
  
  /**
	 * Modifie l'encodage de la connexion
	 *
	 * @access protected
	 * @return void
	 */
	protected function _setConnectionEncoding()
	{
	  if(! is_null($this->_sEncoding) )
	  {
	    $stmt = $this->_oPdoInstance->prepare('SET client_encoding TO ?');
	    $this->execute(array($this->_sEncoding));
	  }
	}
}
?>


<?php
namespace Dao;

/**
 * @name DaoFactory
 * @author aberthelot
 * @since 11/10/2008
 * @package Dao
 * @version 4.0.0 - AXB - 11/10/2008
 */
class DaoFactory
{
  /**
   * Factory 
   * Retourne une instance de la classe désigné par $sType
   *
   * @param array $aParams
   * @return object
   */
  public static function factory(array $aParams)
  {
    if( isset($aParams['adapter']) )
    {
      $sClass       = 'Pdo'.ucfirst(strtolower($aParams['adapter']));
      $oReflection  = new ReflectionClass($sClass);
    
      if(! $oReflection->isSubclassOf('PdoManager') )
      {
        throw new InvalidArgumentException($sClass.' n\'est pas une classe enfant de "PdoManager".', E_ERROR);
      }
    
      $oInstance = new $sClass($aParams);
      return $oInstance;
    }
    else 
    {
      throw new Exception('Aucun type de base de données spécifié.', E_ERROR);
    }
  }
}
?>

##### EXEMPLES #####
<?php
$oPdo  = new DaoFactory::factory($aParams);
$sStmt = $oPdo->query('SELECT tata_yoyo FROM chapeau');
$aRes  = $sStmt->fetchAll();

print '<pre>';
print_r($aRes);
print '</pre>';
?>

Fichier Zip

Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

Historique

04 janvier 2009 04:19:49 :
.
04 janvier 2009 15:43:48 :
Modifier pour php 5.2.x
04 janvier 2009 17:39:09 :
Modification de la méthode DaoFactory::factory, j'ai retiré les namespaces
04 janvier 2009 17:41:21 :
.
05 janvier 2009 12:44:54 :
Remplacement des array_key_exists par isset
05 janvier 2009 13:10:11 :
.

Commentaires et avis

signaler à un administrateur
Commentaire de webdeb le 04/01/2009 12:54:01 10/10

Bravo c'est une jolie source. C'est propre, bien codé et bien documenté ;)

signaler à un administrateur
Commentaire de neigedhiver le 04/01/2009 13:45:58

Salut,

Tu devrais préciser que la version de PHP requise est 5.3, parce que sinon, y'en a pas mal qui vont pas comprendre les messages d'erreurs alors qu'ils sont avec la dernière version stable ;)
C'est la première source que je vois qui nécessite PHP5.3 : ça fait plaisir, mais ça pourrait être intéressant de faire fonctionner ça sur PHP 5.2.x

J'ai pas regardé le code en détail (comme toujours... désolé... :/ ) mais avoir un commentaire comme ça de la part de WebDeb, c'est une belle performance. Donc rien que pour ça, chapeau !

@WebDeb : euh je ne te critique pas, hein, c'est juste que comme tu es exigeant (et c'est une qualité pour moi), il est rare que tu laisses un commentaire qui laisse penser que la source est parfaite... D'où la performance...

Et bonne année tout ça tout ça *=/:-)

signaler à un administrateur
Commentaire de webdeb le 04/01/2009 14:06:06

@NeigeDHiver : lol ! Effectivement en général j'ai plutôt tendance à critiquer assez fortement certaines sources que je vois passer mais quand il y'en a des bonnes qui sortent du lot, je prends beaucoup de plaisir à dire qu'elles sont bien faites et qu'elles méritent d'être saluées. Ici c'est le cas :)

signaler à un administrateur
Commentaire de dorian91 le 04/01/2009 15:57:14

Oups désolé j'ai oublie de retirer les namespaces. La source est mise à jour.
Merci pour vos commentaire ça fait plaisir.
Je suis en train de faire une petite mise a jour pour pouvooir lancer une action sur certaine méthode de pdo
Par exemple logger des requetes.
Je met la source a jour des que c'est fini

signaler à un administrateur
Commentaire de stailer le 04/01/2009 18:16:18

Bonne source, juste ceci :

"Pour rajouter le support d'autre bdd il suffit par exemple de créer une class PdoMssql et d'y implémenter les méthodes obligatoires."

Alors ajoute les interfaces informant sur ces méthodes obligatoires justement

signaler à un administrateur
Commentaire de dorian91 le 04/01/2009 18:39:30

J'ai oublié de préciser que les nouvelles classes doivent etendre PdoManager et donc définir les méthodes abstraites.
Quand tu utilise la fatory il vérifie si la classe passé en paramètre etends la classe PdoManager

signaler à un administrateur
Commentaire de codefalse le 05/01/2009 09:52:22 administrateur CS

Cette source est ... intéressante :p
Quand j'ai vu un "Abstraction [sgbd]", je me suis dit "Encore !!". J'ai donc regardé (un peu en travers, je doit l'admettre) quel en était le but, la qualité, etc.

Je rejoindrais Webdeb et Neige sur le fait que c'est vraiment bien codé, commenté et bien structuré.

Après, pour un avis personnel, je dirai que je ne suis pas pour imposer certaines méthodes dans une classe, tel que "listTable". En effet, je préfère personnellement faire des classes à usage spécifique (une classe Pdo_Show pour effectuer la plupart des requetes "SHOW ..." par exemple). Mais cela n'engage que moi et chacun à son point de vue, c'est ce qui fait notre diversité :)

Cependant, histoire de faire mon pointilleux, j'attirerais ton attention sur un partie de ton code, là ou tu utilise array_key_exists dans tes conditions ternaires : array_key_exists('adapter', $aParams)) ? .... : .... ;
Je te conseil d'utiliser un isset ($aParams['adapter']) qui est beaucoup plus rapide (isset n'est pas réellement une fonction mais une structure du langage, donc beaucoup plus performant).

signaler à un administrateur
Commentaire de webdeb le 05/01/2009 10:04:56

@CodeFalse : concernant ta remarque au sujet de isset() vs array_key_exists(), je plussoie complètement. Une autre différence les sépare également que tu n'as pas mentionnée. isset() vérifie que la variable existe bien et qu'elle n'est pas nulle alors que array_key_exists() vérifie uniquement si la clé existe dans le tableau mais ce moque de savoir si sa valeur est nulle ou non.

signaler à un administrateur
Commentaire de codefalse le 05/01/2009 10:55:45 administrateur CS

@Webdeb : j'ignorais totalement le test effectué par array_key_exists (je l'utilise jamais ;)), mais c'est toujours bon à savoir ! :)

J'avais fait un petit article sur les performances php ya un moment, si ca intéresse certaines personnes :
http://blog.reflectiv.net/2008/07/29/optimisation-php/

signaler à un administrateur
Commentaire de dorian91 le 05/01/2009 12:40:49

Merci pour vos commetaires. Je ne connaissais pas les diférences de comportements et de performance entre isset et array_key_exists. Je vais devoir modifier tous mes codes ^^, j'utilise tout le temps array_key_exists je trouve ça plus jolie ^^. Je met a jour la source

signaler à un administrateur
Commentaire de dorian91 le 05/01/2009 12:51:03

@CODEFALSE j'ai lu ton tuto sur les perfs, bien écrit et complet du coup je l'ai mis en favoris. Merci pour l'info

signaler à un administrateur
Commentaire de codefalse le 05/01/2009 14:51:43 administrateur CS

@Dorian91 : Mais de rien ! :) c'est là pour ca !

signaler à un administrateur
Commentaire de stailer le 05/01/2009 23:14:07

"Quand tu utilise la fatory il vérifie si la classe passé en paramètre etends la classe PdoManager "

Oui, mais encore mieux : l'interface vérifie si toutes les méthodes obligatoires ont bien été implétmentées... Il me semble que pour une source obligeant à PHP5.3, l'orienté objet doit être encore plus "pro", et ajouter une interface à ta classe n'est qu'un travail de quelques secondes. Pourquoi s'en priver ? en plus tu encourages les autres programmeurs à étendre ta classe...

signaler à un administrateur
Commentaire de dorian91 le 06/01/2009 00:18:40

La classe PdoManager est une classe abstraite qui définit 4 méthodes abstraites :

abstract protected function _getDsn();
abstract protected function _setConnectionEncoding();
abstract public function describe($sNameTable, $sSchema = null);
abstract public function listTable();

Si tu étend la classe PdoManager sans implémenter ces méthodes tu aurras une erreur comme avec les interfaces.

signaler à un administrateur
Commentaire de dorian91 le 06/01/2009 00:34:40

Perso j'utilise les interfaces quand je n'ai pas de méthodes a partager entre plusieurs classes et que je dois avoir une norme de nommage des méthodes pour pouvoir adapter au sein d'un projet (pff pas facile a expliquer ^^)
En gros c'est ma façon de faire. Après je me trompe peut être, je ne suis pas un gourou de la poo ^^.

signaler à un administrateur
Commentaire de stailer le 06/01/2009 00:37:19

Mais c'est la que je comprends pas.. pourquoi faire ces verifs etc , alors que PHP via les interfaces les faits automatiquement ? C'est du code en plus avec ton "method_exists" et "get_class".

Enlève ce "if", mets une interface, ça ne sera que plus propre et avantageux pour le maintien et l'évolution de ta classe.

signaler à un administrateur
Commentaire de codefalse le 06/01/2009 09:39:41 administrateur CS

peut-être que les "method_exists" et "get_class" permettent d'ajouter des fonctionnalités non prévue à la base ?

Il peux créer une interface iMachinTruc, contenant 3 fonctions (do, go et po) mais maitenant si toi tu veux créer une nouvelle fonction qo, tu peux, mais elle ne sera pas reconnue par son code ! (si j'ai bien compris).

signaler à un administrateur
Commentaire de dorian91 le 06/01/2009 10:00:05

En gros la méthode :

/**
   * Mapping des fonction PDO
   *
   * @access public
   * @param string $sMethod
   * @param array $aArguments
   * @return mixed|boolean
   */
  public function __call($sMethod, $aArguments)
  {
    if( method_exists(get_class($this->_oPdoInstance), $sMethod) )
    {
      return call_user_func_array(array($this->_oPdoInstance, $sMethod), $aArguments);
    }
    
    return false;
  }

Me sert a mapper les méthodes PDO. Pour pouvoir faire par exemple $oPdoMysql->query()
Je fais un méthode existe pour tester si la méthode est présente dans Pdo si non je retourne false au lieu d'une erreur PHP
Le principale interet est que si je veux lancer certaine action sur des méthodes PDO je peux le faire dans la méthode __call
Un petit exemple :
je fais un $oPdoMysql->query('SELECT tata_yoyo FROM chapeau');
La méthode query va etre attrapé par __call et je souhaite logger toutes les requetes qui passent

public function __call($sMethod, $aArguments)
  {
    if( method_exists(get_class($this->_oPdoInstance), $sMethod) )
    {
      if( $sMethod == 'query' ) Logger::add($aArguments[0]);
      return call_user_func_array(array($this->_oPdoInstance, $sMethod), $aArguments);
    }
    
    return false;
  }

C'est un exemple vite fais hein
La méthode __call ne sert que pour PDO d'ailleur je fais un get_class($this->_oPdoInstance) qui est une instance de PDO
En gros c'est pour avoir a dispo toutes les méthodes de ma classe + celle de pdo dans une seule instance.
Bon je sais pas si c'est clair ^^ moi et les explications ca fais 2

signaler à un administrateur
Commentaire de linkinparkem le 29/01/2009 21:35:35 10/10

Super Code !!

signaler à un administrateur
Commentaire de linkinparkem le 01/02/2009 11:02:57

@Dorian91 : svp, j'ai pris ton script et je l'ai tester sur xampp avec comme version de php la version 5.3bêta2
mais au niveau du script "dao_factory.class.php" les namespaces sont reconnus mais dans les autres scripts il me retourne une errreur ds la 2éme ligne qui concerne l'utilisation des namespace si vous avez une solution à me partager je suis preneurs et si vous avez même une version sans les namespaces sa sera l'idéal et merci d'avance ! ;-)

signaler à un administrateur
Commentaire de neigedhiver le 01/02/2009 12:57:54

Salut,

J'avais pas vraiment fait de commentaire la première fois, je vais en faire un vrai cette fois-ci... En fait il s'agit plus d'une idée de fonctionnalité que d'un vrai commentaire.
PDO_MYSQL ne supportant pas les curseurs (et c'est pas prévu tout de suite : http://bugs.php.net/bug.php?id=44475 ), pourquoi ne pas implémenter cette fonctionnalité ? J'ai du le faire pour mes besoins personnels, c'est quand même plus confortable pour développer...

signaler à un administrateur
Commentaire de dorian91 le 03/02/2009 21:12:43

Salut
Merci pour les commentaires.
linkinparkem >>> Pourrais tu me donner le message d'erreur que tu obtiens
Neigedhiver >>> Ah je ne savais pas pour le support des curseurs avec pdo mysql (pas l'habitude d'utiliser ces choses ^^). Faut que je me renseigne sur le fonctionnement des curseurs en SQL et je vois pour implementer cela.

signaler à un administrateur
Commentaire de dorian91 le 03/02/2009 21:18:15

linkinparkem >>> A tu créé l'arborescence ?

dossier racine
--dossier dao
----fichier "dao_factory.class.php"
----dossier "pdo"
------fichier "pdo_manager.class.php"
------fichier "pdo_mysql.class.php"
------fichier "pdo_pgsql.class.php"

J'ai oublié de modifier le fichier zip (il contient les namespaces)
La source affiché est sans namespace

signaler à un administrateur
Commentaire de neigedhiver le 03/02/2009 23:27:45

En fait, un curseur, c'est un peu (peut-être que je simplifie et que mes connaissances ne sont pas assez poussées) un itérateur, qui implémenterait aussi SeekableIterator.

En fait pour mes besoins à moi, j'ai implémenté cette fonctionnalité. En fait, j'ai plusieurs classes :
- DBStatement implements Countable
- DBStatementIterator extends DBStatement implements Iterator
- DBStatementCursor extends DBStatement implements Iterator, SeekableIterator
- DBStatementCursorNative extends DBStatementCursor
- DBStatementEmulation extends DBStatementCursor

DBStatement est en fait un proxy pour un objet PDOStatement (qui est passé en argument du constructeur). La méthode count() retourne simplement PDOStatement::rowCount()

DBStatementIterator fournit un itérateur très simplifié qui ne parcourt l'objet PDOStatement qu'une seule fois, du premier au dernier enregistrement (sans rewind). C'est la base de ce qu'on attend d'un itérateur : récupérer les résultats du premier au dernier.

DBStatementCursor est une classe abstraite qui définit les méthodes next() et seek()

DBStatementCursorNative utilise les curseurs PDO : c'est la classse la plus performante dans le sens où elle utilise pleinement les curseurs.

DBStatementCursorEmulation est, comme son nom l'indique, une émulation de curseur : pour cela, j'utilise fetchAll() et je parcours le tableau ensuite avec l'itérateur. C'est moins performant puisque tous les résultats sont récupérés avant d'être ensuite parcourus 1 à 1. C'est la contrepartie de l'émulation d'une fonctionnalité. Cette classe est un peu limitée puisqu'elle n'utilise pas toutes les possibilités des curseurs et ne permet que de parcourir dans un sens, dans l'autre, ou d'accéder à un offset donné.

Bien sûr, pour chaque, il reste possible de préciser le mode de récupération (FETCH_OBJ, FETCH_ASSOC, etc).

J'ai pensé publier ma classe, mais comme il y a la tienne, je ne sais pas si c'est utile... La tienne fait des choses que la mienne ne fait pas, notamment, je n'ai écrit aucune fonction utilitaire, contrairement à toi : je me suis longtemps interrogé sur la réelle utilité méthodes utilitaires (sic) dans un DAO... En fait, il est fort probable que ça soit vraiment intéressant dans certains cas (ça simplifie quand même un peu la vie du développeur, non ?). Bref, c'est pour moi un débat qu'il serait intéressant d'avoir (mais peut-être pas ici...)

signaler à un administrateur
Commentaire de codefalse le 04/02/2009 00:16:06 administrateur CS

@Neige : Pour ma part je voterai POUR voir ta source et voir comment tu t'y prends :)

Par contre, j'aimerai juste souligner un point qui peux devenir un très gros piège (j'en ai fait l'expérience :p), la méthode rowCount NE RETOURNE PAS le nombre de ligne sélectionnée ! Elle retourne le nombre de ligne affectée UNIQUEMENT par les requêtes de type INSERT, UPDATE ou DELETE !
La doc le dit clairement, mais beaucoup de gens se sont fait/se font avoir !

http://fr2.php.net/manual/fr/pdostatement.rowcount.php

Note: du coup, aucune méthode "performante" ne permet de connaitre le nombre de lignes retournée par un select. Les deux options sont un SELECT count(col) FROM table WHERE same_conditions;" ou faire un fetchAll () puis un count (pas tip top niveau mémoire !).

signaler à un administrateur
Commentaire de neigedhiver le 04/02/2009 00:50:54

Rhaaaaaaaaaaaaaa !!!!!!!!!!!
Je comprends pas l'exemple 2 qu'on trouve sur la doc de rowCount() : http://fr.php.net/manual/fr/pdostatement.rowcount.php
Pourquoi ils utilisent fetchColumn() en parlant de ligne ? Y'a un truc qui m'a échappé ?
'tain, c'est pas génial quand même d'utiliser count(*)...
En même temps, en y réfléchissant... je me demande si avoir le nombre de lignes retournées est vraiment indispensable...
Quand on utilise les curseurs de PDO, on va pouvoir ne récupérer (facilement) que les résultats que l'on veut. Par exemple les 10 premiers (et moins s'il n'y en a pas autant). Si on veut savoir combien il y en a au total, il y a des chances qu'on soit en backend, et que donc, on puisse se permettre une requête select count(*) supplémentaire.
Avec MySQL, on peut utiliser SQL_CALC_FOUND_ROWS dans la requête SELECT si on utilise LIMIT, avec un petit select FOUND_ROWS() ensuite...
Bon mais j'avais pas fait gaffe à ça... :/

Bon ok, je vais mettre ma source. Ca fera que la 2560890334ème sur un DAO, ça fait rien... ^^
Je vais quand même m'assurer qu'elle est pas bugguée, sachant que je ne peux pas tester les curseurs de PDO, n'ayant pas de serveur Oracle sous la main (peut-être que Postgre les gère, maiis je n'ai pas non plus de serveur Postgre). Pis je dois corriger cette histoire de rowCount()...

Allez, bonne nuit les enfants !

signaler à un administrateur
Commentaire de codefalse le 04/02/2009 01:23:44 administrateur CS

Pour ma part j'utilise souvent rowcount pour savoir si ma requete à retourné des résultats (genre "il y a 0 news pour le moment") et ainsi conditionner (if rowcount > 0) pour faire une boucle pour afficher les résultats ou non.

signaler à un administrateur
Commentaire de neigedhiver le 04/02/2009 01:52:52

@Codefalse : Mmmmm... Ouais, mais avec un itérateur et une boucle foreach, t'as pas besoin de vérifier... Si résultat il y a, la boucle foreach est exécutée au moins une fois. Si pas de résultat, elle ne l'est pas. Le test n'est pas utile, puisque c'est foreach qui le fait en appelant la méthode valid() de l'itérateur...

Et puis je comprends pas : d'abord tu dis que rowCount() ne sert que pour les lignes affectées par DELETE, INSERT et UPDATE, puis là, tu me dis que tu t'en sers pour savoir si tu as des news à afficher... Tu les récupères pas avec une requête SELECT tes news ? Ou alors, je suis fatigué et trop vieux, et je devrais aller me coucher... ?

signaler à un administrateur
Commentaire de codefalse le 04/02/2009 09:49:57 administrateur CS

non, tu as raison, mon post était très perturbant :p Ce que je voulais dire, c'est qu'avant de me rendre compte que rowCount ne fonctionnait pas avec SELECT, je procédais de la manière indiquée. Maintenant je doit trouver des alternatives :p

signaler à un administrateur
Commentaire de dorian91 le 04/02/2009 20:24:23

@Neige : Je suis aussi pour voir ta source. ^^
Pour les méthodes utilitaires je pense qu'elle sont très pratique du moins temps que les logiciels de bdd n'aurons pas une syntaxe et des fonctions sql identique (Je pense surtout a MySQL qui deroge a pas mal de règles).
De plus ce code fait partit d'un plus gros projet qui est un active record (presque fini ^^ en fait c'est un portage de l'active record ruby en php) donc je doit avoir des syntaxes identiques pour récupérer des données sur les tables etc...
Je suis en train de me documenter sur les curseurs et réfléchir a une méthode pour les implémenters.

signaler à un administrateur
Commentaire de codefalse le 04/02/2009 22:37:15 administrateur CS

@Dorian91: Outch ! Un portage de l'Active Record de Ruby en Php ? Sérieux ? Projet trèèèèèèèèès ambitieux !! (C'est une bonne chose hein ! :)). Tu sais que les notions d'ORM, Active Record et autre sont apparues avec l'arrivée de Ruby On Rail ? C'est Ruby, l'initiateur de cette idée ! Depuis, des tonnes de frameworks tentent d'utiliser un style similaire et vu leur très faible implémentation en php (d'une librairie particulière), je pense qu'aucune n'a su se démarquer, non pas car le code des librairies existantes soit mauvais, mais parce que le projet en lui-même est très compliqué !

Je te souhaite d'y arriver ! Mais bon courage !

signaler à un administrateur
Commentaire de neigedhiver le 04/02/2009 23:34:25

J'ai réfléchi un peu, et j'ai trouvé pourquoi je n'ai pas implémenté de méthode à mon DAO pour exécuter des tâches utilitaires.
Mes classes sont destinées à s'intégrer dans un framework. Si elles doivent permettre l'utilisation de n'importe quel SGBD grâce à PDO, elles n'ont pas vocation, dans une même application, à servir sur plusieurs SGBD différents. Contrairement à une application (forum, blog, cms) qui doit pouvoir (autant que possible) être utilisé avec soit MySQL, soit PostgreSQL ou autre (mais un seul à la fois), un framework est utilisé non pas par un utilisateur final, mais par un développeur... Et ça fait toute la différence.
Je sais pas si c'était hors sujet ou pas, bon...
Je retravaille un peu ma source, je la posterai j'espère avant vendredi (ça laisse que demain ça... ah ouais... mince alors...)

signaler à un administrateur
Commentaire de dorian91 le 05/02/2009 00:28:38

@Codefalse : Oui c'est sérieux ^^ C'est clair que je galère. J'ai fais pas mal de ruby avant, je prend chaque méthode et je l'implémente en php. Pour l'instant ça avance pas mal j'ai implémenté les 3/4 des fonctionnalitées et j'en ai ajouté des nouvelles, quand j'ai arrété le ruby il ne gérait pas les clés primaires multiple, il oblige a nommer ca clé primaire "id" moi je récupère dynamiquement dans la structure de la table. D'ou les méthodes utilitaires de ma classe PDO + quelques autres trucs.
@Neige : Justement dans un framework il faut des outils génériques grace aux méthodes utilitaires quelques soit la base tu utilisera toujours le meme nom de méthode. De plus pour certain projet tu peux avoir à utiliser plusieurs type de base.

signaler à un administrateur
Commentaire de neigedhiver le 05/02/2009 00:43:22

Je suis à moitié d'accord avec toi. En fait, on pourrait sûrement en discuter pendant des mois sans parvenir à trancher, campant chacun sur notre position, sans avoir tort ni l'un ni l'autre. Je pense que c'est pas la même manière d'appréhender un projet.

Certains projets peuvent utiliser des SGBD différents... Oui, certes, sur de très gros projets, c'est tout à fait possible. Seulement pour accéder à un SGBD ou à un autre, on ne va pas nécessairement utiliser les mêmes scripts, même si ceux-ci utilisent les mêmes outils (ie le même framework).
Tout dépend où l'on fixe la limite de ce qu'on implémente. C'est un choix de la part du développeur (du framework) de fournir des outils plus ou moins complets (avec plus ou moins d'accessoires).
J'ai vu un framework MVC en PHP qui permet, semble-t-il de générer une interface d'admin sans quasiment écrire une ligne de code (je crois que c'est Jelix, mais je ne sais plus vraiment, et j'ai la flemme de chercher). Ils ont pris le parti de fournir un outil qui permet ceci. CodeIgniter, par exemple, non. C'est un choix. Aucun des deux n'est meilleur que l'autre. Voilà pourquoi nous risquons de ne pas être d'accord sans avoir tort pour autant ;)
Donc ok pour implémenter des utilitaires, je n'y vois aucun inconvénient, à la condition qu'on ne me reproche pas de ne pas en mettre dans mes classes (mais je peux toujours changer d'avis :)
En tout cas, j'apprécie particulièrement ces échanges que nous avons là : c'est vraiment intéressant et enrichissant (en tout cas pour moi, j'espère que c'est le cas pour vous aussi, et même pour les seuls lecteurs).

Bonne fin de soirée et bonne nuit :)

signaler à un administrateur
Commentaire de neigedhiver le 05/02/2009 01:55:36

@Codefalse : au fait, à propos de PDOStatement::rowCount(), il est dit dans la doc (faut vraiment que je fasse des efforts pour ne plus lire en diagonale) :

"Si la dernière requête SQL exécutée par l'objet PDOStatement  associé est une requête de type SELECT, quelques bases de données retourneront le nombre de lignes retournées par cette requête. Néanmoins, ce comportement n'est pas garanti pour toutes les bases de données et ne devrait pas être exécuté pour des applications portables."

Et je pense que ça marche avec MySQL parce que je m'en suis servi dans mes tests, quand je ne faisais pas encore gaffe à cette méthode... Cependant, il est fort probable que ça ne fonctionne pas avec postgresql ou oracle... (je dis ça, mais c'est pure spéculation).
Ca ne règle pas le problème, mais MySQL étant le SGBD le plus répandu dans le monde du webdev, cela explique qu'il soit si facile de ne pas faire attention à cette subtilité...
Comme quoi si PDO tend à unifier l'accès aux BDD, c'est vraiment pas gagné...

signaler à un administrateur
Commentaire de dorian91 le 05/02/2009 09:32:17

@Neige : Je ne te reprocherais pas de ne pas avoir implémenté ces méthodes ^^. En fait j'ai tellement l'habitude de faire des projets pour des clients qui veulent en faire le moins possible, je crois que ca déteint sur mes projets perso et j'oubli parfois que c'est pour des développeurs ^^. En tout cas c'est bien de voir aussi le point de vue et les méthodes des autres.
Pour PDO il tend à unifier l'accès aux BDD mais il y a tellement de différence que c'est pas gagné ^^

signaler à un administrateur
Commentaire de codefalse le 05/02/2009 10:45:30 administrateur CS

Bon, je vais essayer de rattraper mon retard :p

Je serai plutot d'accord avec Neige sur le fait qu'un framework ne doit pas implémenter des tâches utilitaires pour deux raisons : ceci alourdit le framework, et, en général, n'est rarement utilisé par le développeur (qui adaptera ses requêtes à son environnement). Cependant, histoire de mettre tout le monde sur un pied d'égalité, je dirai à Neige que CodeIgniter possède des requêtes "utilitaires" :p (scaffolding par exemple).

@Neige : Ton framework ne doit pas forcément n'utiliser qu'un seul sgbd, mais plusieurs. Tu devrais utiliser une factory qui puisse te retourner plusieures instances de classes sgbd, une MySQL, un PgSQL, etc. On ne sais jamais sur quoi on peux tomber avec un framework :p

@Dorian91 : Alors je te souhaite bien du courage :) J'ai hate de voir la version terminée. Pense surtout à faire des tests de performances, car c'est très important.

@Neige : Le framework MVC qui permette de générer une interface (pas nécésssairement d'admin), c'est Symfony (si j'ai bien compris ce que tu veux, mais en tout cas Symfony ne demande pas de développement à la base).
Pour ce qui est de rowCount et MySQL, je vais t'horrifier : Ca dépends des versions ! :p Mon code marchait au travail et depuis qu'on à fait la mise à jour, tous les rowCount retournent 0 (pour les SELECT), du coup je doit revoir tout mon code ... Moralité : Il ne faut pas partir dans l'idée que MySQL est le principal utilisé et que "quelques bases de données retourneront le nombre de lignes retournées par SELECT" :p

voilà je vous ai rattrapé :p

signaler à un administrateur
Commentaire de dorian91 le 05/02/2009 12:34:04

Oui symphony permet de faire ce genre de chose (d'ailleur c'est le framework qui se rapproche le plus du framework ruby).
J'avoue quand j'utilise un framework j'utilise rarement les méthodes utilitaires ^^.
Je vais tester ce soir le rowCount en php5.3 voir ce qu'il retourne. Peut être qu'ils auront uniformisé le retour.

signaler à un administrateur
Commentaire de dorian91 le 05/02/2009 12:36:03

@CODEFALSE : Oui les tests de performance sont ma priorité. Je le pousse a fond et je dois dire que je suis satisfait des résultats. Pourvu que ca dure ^^

signaler à un administrateur
Commentaire de neigedhiver le 05/02/2009 13:24:05

Entendons-nous bien : je n'ai jamais dit qu'un frammework ne devait pas permettr d'utiliser plusieurs SGBD. Je dis simplement que dans la plupart des cas, on n'utilise dans une application, qu'un seul SGBD. Oui, il peut arriver que de gros projets utilisent plusieurs SGBD SIMULTANEMENT. Mais je reste persuadé que c'est rare (surtout pour des projets en PHP...).
Ce que je voulais dire c'est que quand un développeur utilise un framework, celui-ci (le développeur) va développer pour le SGBD qu'il utilise, pas pour que ce soit compatible avec tous les SGBD de la planète. Contrairement à une application web comme un blog ou un forum qui doit pouvoir être utilisée sur n'importe quel SGBD.
Le public visé n'est pas le même : l'utilisateur final va vouloir installer (et que ça marche) avec le SGBD de son choix, le développeur qui utilise un framework va optimiser pour le SGBD qu'il utilise (et s'il en utilise plusieurs, il optimise pour que ça marche partout, c'est son affaire).

signaler à un administrateur
Commentaire de codefalse le 05/02/2009 15:51:05 administrateur CS

C'est vrai que dans la plupart des cas, une application n'utilise qu'une seule sgbd, sur ce point là je suis tout à fait d'accord avec toi ;)

signaler à un administrateur
Commentaire de kiki2sirom le 06/02/2009 10:48:56

c'est marrant de voir qu'un code que, pour ma part, je qualifierai d'expert, soit mis en 'Débutant', alors que la plupart des scripts qui sont plus que limite, soit parfois (souvent ?) mis en 'Initié' voire 'Expert' ;) ça montre le comportement du programmeur.

En tout cas, je me fie aux autre personnes pour dire que c'est un joli code, même si je ne suis pas capable de le juger : c'est un trop haut niveau pour moi, un jour peut-être...
Par contre, je ne me permettrai pas de le noter

signaler à un administrateur
Commentaire de thibautg le 17/02/2009 21:51:31

Bonsoir,

je viens de tester ta classe parce que j'ai décidé d'enfin passer par pdo.

Par contre en voulant faire ton exemple j'obtiens des erreurs et ça ne marche pas :
"Warning: get_class() expects parameter 1 to be object, null given in C:\wamp\www\pdo\class.pdo_manager.php on line 137

Fatal error: Call to a member function fetchAll() on a non-object in C:\wamp\www\pdo\index.php on line 18"

Je pense que ça viens de la connexion, et donc de la création de l'objet car j'arrive pas à trouvé ou la connexion ce fait dans les class.

signaler à un administrateur
Commentaire de mikaweb88 le 22/03/2009 19:07:23

Bonsoir,

J'ai trouvé ta source très intéressante, j'ai décidé de changer la mienne en gardant certaines options mais en prenant ton système comme base de travail.
Je me posais en revanche une question, est-ce que l'utilisation de __call n'est-elle pas lourde à la longue ?
Une solution serait de re-déclarer toutes les fonctions, un peu redondants et chiant mais sûrement plus efficace, enfin je suis pas à 3 millièmes près :)

Je vais faire des tests et j'essaierai de donner un petit feedback par la suite.

signaler à un administrateur
Commentaire de neigedhiver le 22/03/2009 19:24:09

Salut,

La question sur __call() est intéressante.
En terme de design pattern (motif de conception), la méthode magique __call() permet d'écrire une classe proxy (ou un wrapper). Si les performances peuvent en pâtir, il faut quand même rester réaliste : c'est pas ça qui va ralentir un serveur, même mutualisé. Et puis quand on développe en OO, qu'on respecte des design patterns, etc, les performances sont toujours un peu détériorées. Par contre, on gagne en lecture de code, en maintenabilité, évolutivité, modularité, etc.
Et puis si PDO évolue et que de nouvelles méthodes sont créées, il ne sera pas nécessaire de mettre à jours la source, alors qu'avec des méthodes réécrites, si.

Ajouter un commentaire

Discussions en rapport avec ce code source dans le forum

[PHP5] Etendre une classe prédéfinie [ par ZuGbEn ] Bonjour !Je débute plus ou moins dans la programmation orientée objet, et je souhaite faire une classe qui, à partir d'un DomNode existant, ajoute des Fonctionnement SDO [ par guill76 ] Bonjour,Je viens de découvrir un petit peu l'extension SDO de PHP5 que j'ai récupérée en installant PHP5.2.5 il ya environ un semaine et dans cette ex Pb classe COM [ par iklo18 ] Bonjour,Je suis actuellement en stage, je dois réaliser un module d'inscriptions en ligne.J'ai donc réalisé un formulaire dont je récupère les données Migration PHP4 vers PHP5 [ par ralebole ] Bonjour mon hebergeur à supprimer le Php4 pour le Php5Je me retrouve maintenant avec un message d'erreur sur mon scriptWarning: getenv() has been disa Pdo : Se rendre à une ligne spécifique (seek) [ par codefalse ] Voila je crois que le titre résume plutot bien ! :)J'ai un PDOStatement récupéré grace à un $PDO-&gt;Query et j'aimerai me rendre à la ligne x.Comment Classe d'accès à une base de données [ par MadM@tt ] Bonjour à tous,Voilà j'ai réalisé une classe de connexion à une base de données.Seulement maintenant, pour l'utiliser, je suis face à un choix sur leq Détruire un objet dans son constructeur [ par MadM@tt ] Bonjour à tous,J'ai une classe :class Compte {    ... // Constructeur    function Compte( $ID ) {       // Connexion à la BD et récupération des info passer une instance d'une classe par les sessions [ par hybride11 ] Bonjour ,  dans une application qui comporte plusieurs fichiers *.php qui seront appelés à un moment donné dans l'appli, est il possible de faire pass erreur class redeclare !!! [ par g_fuck ] Salut tout le monde! il se fait que dans une de mes page j'ai besoin d'une class CLASSE_1 donc je fait dans ma page include(url/CLASSE_1.php);Et comme classeforms.php [ par skulls94 ] bonjour,je dois realiser un formulaire qui interagit avec une bdd. Pour cela j'utilise la classe classeforms.php voir: http://www.toutestfacile.com/cl


Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Téléchargements

Logiciels à télécharger sur le même thème :

Comparez les prix Nouvelle version


LG KP501

Entre 9€ et 159€


Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,671 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.