Pregunta

Estoy pensando en usar PDO en todas mis futuras aplicaciones web. Actualmente (usando lo que he aprendido de SO hasta ahora), lo que tengo en mi sitio para manejar la conexión de la base de datos es una clase Singleton como esta:

class DB {

    private static $instance = NULL;
    private static $dsn      = "mysql:host=localhost;dbname=mydatabase;";
    private static $db_user  = 'root';
    private static $db_pass  = '0O0ooIl1';

    private function __construct() 
    {

    }
    private function __clone()
    {

    }   
    public static function getInstance() {

        if (!self::$instance)
        {           
            self::$instance = new PDO(self::$dsn, self::$db_user, self::$db_pass);
            self::$instance-> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }
        return self::$instance;
    }
}

y otro archivo (functions.php) con funciones específicas de contenido que se ve exactamente como este:

function get_recent_activities ()
{    
    try
    {    
        $db = DB::getInstance();
        // --prepare and execute query here, fetch the result--
        return $my_list_of_recent_activities;
    }
    catch (PDOException $e)
    {
        return "some fail-messages";
    }
}
...

lo que significa que tengo que repetir la parte try .. catch en todas las funciones.

Mis preguntas son:

  1. ¿Cómo debería hacerlo más eficiente? (p. ej., no tener que repetir try..catch en todas las funciones, y aún así poder devolver diferentes "mensajes de error" en cada una)
  2. ¿Ya es una buena práctica? Todavía soy nuevo en PDO y OOP (todavía tengo mucho más que aprender), así que (a partir de ahora), realmente no puedo ver ninguna desventaja o cosas que puedan mejorarse allí.

Lo siento si eso parece poco claro o demasiado largo. Gracias de antemano.

¿Fue útil?

Solución

Su implementación está bien y funcionará perfectamente para la mayoría de los propósitos.

No es necesario poner cada consulta dentro de un bloque try / catch, y de hecho, en la mayoría de los casos, en realidad no desea hacerlo. La razón de esto es que si una consulta genera una excepción, es el resultado de un problema fatal como un error de sintaxis o un problema de la base de datos, y esos no son problemas que debe tener en cuenta con cada consulta que realice.

Por ejemplo:

try {
    $rs = $db->prepare('SELECT * FROM foo');
    $rs->execute();
    $foo = $rs->fetchAll();
} catch (Exception $e) {
    die("Oh noes! There's an error in the query!");
}

La consulta aquí funcionará correctamente o no funcionará en absoluto. Las circunstancias en las que no funcionaría nunca deberían ocurrir con regularidad en un sistema de producción, por lo que no son condiciones que debe verificar aquí. Hacerlo es en realidad contraproducente, porque sus usuarios reciben un error que nunca cambiará, y no reciben un mensaje de excepción que lo alertará sobre el problema.

En cambio, solo escribe esto:

$rs = $db->prepare('SELECT * FROM foo');
$rs->execute();
$foo = $rs->fetchAll();

En general, el único momento en que querrá detectar y manejar una excepción de consulta es cuando desea hacer otra cosa si la consulta falla. Por ejemplo:

// We're handling a file upload here.
try {
    $rs = $db->prepare('INSERT INTO files (fileID, filename) VALUES (?, ?)');
    $rs->execute(array(1234, '/var/tmp/file1234.txt'));
} catch (Exception $e) {
    unlink('/var/tmp/file1234.txt');
    throw $e;
}

Deberá escribir un controlador de excepciones simple que registre o le notifique los errores de la base de datos que ocurren en su entorno de producción y muestre un mensaje de error amigable a sus usuarios en lugar del seguimiento de la excepción. Consulte http://www.php.net/set-exception-handler para obtener información sobre cómo hacer eso.

Otros consejos

Algunas advertencias aquí son:

  • Este código está escrito para tener en cuenta varios problemas heredados, como el registro de la base de datos y la administración de la configuración de la base de datos.
  • Recomiendo encarecidamente que busque una solución existente antes de crear la suya. Muchas personas piensan para sí mismas cuando comienzan que no quieren usar un marco o biblioteca existente porque son demasiado grandes, requieren demasiado tiempo para aprender, etc., pero después de haber sido una de estas personas, No puedo decir con suficiente énfasis que estoy dejando mi marco personalizado y mis clases de contenedor para pasar a un marco. Estoy buscando mudarme a Zend, pero hay varias opciones excelentes disponibles.

Oh, debo señalar que este punto ilustra cómo se puede ajustar una sola función para manejar todo el manejo de excepciones para sus consultas. No escribo try catch blocks en casi ningún otro lugar ahora porque el seguimiento de la pila de la consulta me da toda la información que necesito para depurar el problema y solucionarlo.

Aquí está mi implementación actual de clase de contenedor PDO:

class DB extends PDO 
{
    // Allows implementation of the singleton pattern -- ndg 5/24/2008
    private static $instance;

    // Public static variables for configuring the DB class for a particular database -- ndg 6/16/2008
    public static $error_table;
    public static $host_name;
    public static $db_name;
    public static $username;
    public static $password;
    public static $driver_options;
    public static $db_config_path;



    function __construct($dsn="", $username="", $password="", $driver_options=array()) 
    {
        if(isset(self::$db_config_path))
        {
            try 
            {
                if(!require_once self::$db_config_path)
                {
                    throw new error('Failed to require file: ' . self::$db_config_path); 
                }
            } 
            catch(error $e) 
            {
                $e->emailAdmin();
            }
        }
        elseif(isset(

Algunas advertencias aquí son:

  • Este código está escrito para tener en cuenta varios problemas heredados, como el registro de la base de datos y la administración de la configuración de la base de datos.
  • Recomiendo encarecidamente que busque una solución existente antes de crear la suya. Muchas personas piensan para sí mismas cuando comienzan que no quieren usar un marco o biblioteca existente porque son demasiado grandes, requieren demasiado tiempo para aprender, etc., pero después de haber sido una de estas personas, No puedo decir con suficiente énfasis que estoy dejando mi marco personalizado y mis clases de contenedor para pasar a un marco. Estoy buscando mudarme a Zend, pero hay varias opciones excelentes disponibles.

Oh, debo señalar que este punto ilustra cómo se puede ajustar una sola función para manejar todo el manejo de excepciones para sus consultas. No escribo try catch blocks en casi ningún otro lugar ahora porque el seguimiento de la pila de la consulta me da toda la información que necesito para depurar el problema y solucionarlo.

Aquí está mi implementación actual de clase de contenedor PDO:

<*>ENV['DB'])) { self::$db_config_path = 'config.db.php'; try { if(!require_once self::$db_config_path) { throw new error('Failed to require file: ' . self::$db_config_path); } } catch(error $e) { $e->emailAdmin(); } } parent::__construct("mysql:host=" . self::$host_name . ";dbname=" .self::$db_name, self::$username, self::$password, self::$driver_options); $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('QueryStatement', array($this))); if(!isset(self::$error_table)) { self::$error_table = 'errorlog_rtab'; } } /** * Return a DB Connection Object * * @return DB */ public static function connect() { // New PDO Connection to be used in NEW development and MAINTENANCE development try { if(!isset(self::$instance)) { if(!self::$instance = new DB()) { throw new error('PDO DB Connection failed with error: ' . self::errorInfo()); } } return self::$instance; } catch(error $e) { $e->printErrMsg(); } } /** * Returns a QueryBuilder object which can be used to build dynamic queries * * @return QueryBuilder * */ public function createQuery() { return new QueryBuilder(); } public function executeStatement($statement, $params = null, $FETCH_MODE = null) { if($FETCH_MODE == 'scalar') { return $this->executeScalar($statement, $params); } try { try { if(!empty($params)) { $stmt = $this->prepare($statement); $stmt->execute($params); } else { $stmt = $this->query($statement); } } catch(PDOException $pdo_error) { throw new error("Failed to execute query:\n" . $statement . "\nUsing Parameters:\n" . print_r($params, true) . "\nWith Error:\n" . $pdo_error->getMessage()); } } catch(error $e) { $this->logDBError($e); $e->emailAdmin(); return false; } try { if($FETCH_MODE == 'all') { $tmp = $stmt->fetchAll(); } elseif($FETCH_MODE == 'column') { $arr = $stmt->fetchAll(); foreach($arr as $key => $val) { foreach($val as $var => $value) { $tmp[] = $value; } } } elseif($FETCH_MODE == 'row') { $tmp = $stmt->fetch(); } elseif(empty($FETCH_MODE)) { return true; } } catch(PDOException $pdoError) { return true; } $stmt->closeCursor(); return $tmp; } public function executeScalar($statement, $params = null) { $stmt = $this->prepare($statement); if(!empty($this->bound_params) && empty($params)) { $params = $this->bound_params; } try { try { if(!empty($params)) { $stmt->execute($params); } else { $stmt = $this->query($statement); } } catch(PDOException $pdo_error) { throw new error("Failed to execute query:\n" . $statement . "\nUsing Parameters:\n" . print_r($params, true) . "\nWith Error:\n" . $pdo_error->getMessage()); } } catch(error $e) { $this->logDBError($e); $e->emailAdmin(); } $count = $stmt->fetchColumn(); $stmt->closeCursor(); //echo $count; return $count; } protected function logDBError($e) { $error = $e->getErrorReport(); $sql = " INSERT INTO " . self::$error_table . " (message, time_date) VALUES (:error, NOW())"; $this->executeStatement($sql, array(':error' => $error)); } } class QueryStatement extends PDOStatement { public $conn; protected function __construct() { $this->conn = DB::connect(); $this->setFetchMode(PDO::FETCH_ASSOC); } public function execute($bound_params = null) { return parent::execute($bound_params); } }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top