PDO es el acrónimo en inglés para PHP Data Objects. Y funcionalmente hablando es el conjunto de funcionalidades nativas de PHP que nos permiten manejar de forma fluida conexiones y manipular datos de algún motor de bases de datos. A diferencia de otros conectores específicos los PDOs se centran en una solución genérica que recibe el driver que vamos a usar y maneja las conexiones y las consultas de manera fluida y obedeciendo ciertos patrones de diseño bien definidos.

En ocasiones el PDO no viene activado por defecto en la instalación de PHP por lo que se hace necesario instalarlo. Pará ello es necesario activar el PDO para la base de datos que vamos a usar. Para ello debemos actualizar el archivo php.ini y quitar el comentario o agrear la extensión:

extension=pdo.so

O en caso de que usen windows

extension=php_pdo.dll

En resumen todas las consultas a bases de datos realizadas utilizando PDO deben tener los siguientes 4 pasos, según su complejidad:

  • exec
  • query fetch
  • prepare execute fetch
  • prepare bind execute fetch

Establecer conexión de base de datos

Antes de entrar en cada categoría, primero deberá familiarizarse con el establecimiento de una conexión de base de datos mediante PDO. Este es el absoluto fundamental de PDO, ya que se utiliza en cada parte del código a continuación:

try {
    $dbh = new PDO('mysql:host=localhost;dbname=clientes', $usuario, $password);
} catch (PDOException $error) {
    die($error->getMessage());
}

Para establecer una conexión de base de datos, instanciamos un objeto PDO con tres parámetros. El primer parámetro especifica una fuente de base de datos (conocida como DSN), que consiste en el nombre del controlador PDO, seguido de dos puntos, seguido de la sintaxis de conexión específica del controlador PDO. El segundo y tercer parámetro son nombre de usuario y contraseña de la base de datos.

Se lanzará una excepción si falla la conexión. Esta se puede atrapar y manejarla correctamente.

En los siguientes ejemplos de código, asumiremos que este fragmento de código ya fue referenciado. Tenga en cuenta que siempre tendrá que hacer la conexión antes de realizar cualquier operación, a menos que usemos un pool de conexiones el cual explicaré en algún otro post.

exec

Esta es la forma más simple de ejecutar una consulta. Podemos usarlo para ejecutar una consulta rápida y normalmente no esperamos que devuelva ningún resultado.

$dbh->exec('INSERT INTO clientes VALUES (1, "Andres")');

Aunque PDO::exec no devuelve el resultado correspondiente a su consulta, sí devuelve algo. Independientemente de la consulta que ejecute con PDO::exec, devuelve el número de filas afectadas en caso de éxito. También devuelve Boolean FALSE en caso de error (lo cual es horrible).

Una advertencia al verificar el tipo de retorno: dado que PDO::exec devuelve 0 cuando no hay ninguna fila afectada, siempre debemos usar el operador de comparación === para verificar el éxito de la ejecución del método.

$resultado = $dbh->exec('INSERT INTO clientes VALUES (1, "Andres")')
if (FALSE === $resultado) {
    throw new Exception('Hubo un error');
}

Exec en general es una mala opción, porque el tipo de datos que regresa es mutable (puede ser un entero o un boolean). Este tipo de cosas son las que hacen que el mundo odie php. Ademas exec nos deja desprotegidos ante un mundo que busca inyectar SQL en donde pueda ya que los queries no son propiamente escapados antes de ser ejecutados.

Query Fetch

Al ejecutar consultas como la instrucción SELECT, esperamos un retorno de los resultados correspondientes. La forma más fácil de lograr esto es usar:

$declaracion = $dbh->query('SELECT * FROM clientes');
while ($row = $declaracion->fetch(PDO::FETCH_ASSOC)) {
    echo $row['id'] . ' ' . $row['nombre'] . PHP_EOL;
}

Hay que tener en cuenta que los métodos $dbh->query() y $declaracion->fetch(), son cómo nombramos nuestras categorías, por las secuencias de llamadas a las API de PDO.

Debido a que PDO::query devuelve un conjunto de resultados como un objeto PDOStatement en caso de éxito (devolverá Boolean FALSE en caso de error, haga una comprobación similar a PDO::exec si desea verificar). La clase PDOStatement implementa la interfaz Traversable, que es la interfaz base para Iterator, lo que significa que puede usarse en una declaración de iteración como un bucle. Naturalmente, hay una versión corta del código anterior:

foreach ($dbh->query('SELECT * FROM clientes', PDO::FETCH_ASSOC) as $row) {
    echo $row['id'] . ' ' . $row['nombre'] . PHP_EOL;
}

Al llamar a PDO::query o PDOStatement::fetch, hemos proporcionado un parámetro de marca. Este parámetro especifica qué tipo de estructura de datos queremos del destinatario.

Algunas de las opciones incluyen:

  • PDO::FETCH_ASSOC: devuelve una matriz asociativa indexada por nombre de columna.
  • PDO::FETCH_NUM: devuelve una matriz indexada numéricamente
  • PDO::FETCH_BOTH (predeterminado): devuelve una matriz indexada tanto por el nombre de la columna como por el número de la columna indexada por 0 como se devuelve en su conjunto de resultados. (Combinación de PDO :: FETCH_ASSOC y PDO :: FETCH_NUM).

Hay muchas más opciones. Recomiendo que les echen un vistazo rápido en la documentación oficial de PHP. Aunque este parámetro es opcional, siempre deberíamos especificarlo a menos que realmente queramos una matriz indexada por nombre y número de columna. PDO::FETCH_BOTH toma el doble de memoria.

Mientras que esta opción es mucho mejor que el simple y plano exec, aun es insegura y por los mismos motivos que el anterior. La unica diferencia real es que este nos provee un poco mas de opciones sobre como vamos a obtener los resultados de una consulta.

prepare execute fetch

Con frecuencia necesitamos aceptar datos de entrada del usuario para ejecutar una consulta en la base de datos. Hay dos preocupaciones principales si tuviéramos que usar un enfoque de búsqueda de consultas.

Primero, tendremos que asegurarnos de que la consulta sql pasada a PDO::query sea segura. Asegurarse de escapar y citar los valores de entrada. En segundo lugar, PDO::query ejecuta una instrucción SQL en una sola llamada de función, lo que significa que si necesitamos ejecutar la misma consulta varias veces, usará múltiples veces los mismos recursos. Y bueno, hay una mejor manera de hacer esto.

PDO introduce la preparación (prepare) de consultas por primera vez.

La declaración de preparación resuelve las dos preocupaciones planteadas anteriormente. No solo mejora la eficiencia de ejecutar múltiples consultas similares, sino que también se encarga de escapar y citar los valores de entrada del usuario.

A continuación se muestra cómo implementamos la declaración de preparación usando PDO:

$usuarios = ['Andres', 'Tomas'];
$declaracion = $dbh->prepare('SELECT * FROM clientes where name = :nombre');
foreach ($usuarios as $usuario) {
    $declaracion->execute([':nombre' => $usuario]);
    while ($row = $declaracion->fetch(PDO::FETCH_ASSOC)) {
        echo $row['id'];
    }
}

Tengan en cuenta los pasos que hemos tomado aquí:

  • PDO::prepare se usa para crear una consulta sql que contiene un parámetro variable. La convención de nomenclatura para los parámetros son variables nombradas precedidas por dos puntos (:) o un signo de interrogación (?).
  • PDOStatement::execute se llama para ejecutar una consulta con el valor de los parámetros. Cuando se usa un signo de interrogación en la declaración de preparación, son parámetros numerados. Podemos vincular los valores usando una matriz indexada numérica. Noten el foreach, utiliza la misma declaración para llevar a cabo la consulta después de vincular el valor. Devuelve Boolean FALSE en caso de falla. Podemos usar PDOStatement::errorInfo() para obtener la información de error asociada con la operación.
  • PDOStatement::fetch se usa para obtener resultados con la estructura de datos deseada.
ya vamos mejorando 🙂

prepare bind execute fetch

Un problema menor que pueden haber observado en el código anterior es lo que sucede cuando hay muchos parámetros en la instrucción de preparación. Podemos crear fácilmente una pieza de código como esta:

$declaracion->execute([':name' => $user, ':mobile' => $mobile, ':address' => $address ]);

La lista puede seguir y seguir. Y cognitivamente esto hace que el código sea muy difícil de leer. Sin embargo, una cosa más importante a tener en cuenta aquí es que, PHP hara un cast del valor de entrada del usuario para que coincida con su tipo de campo de base de datos y si estos no coinciden exactamente, obtendremos errores en tiempo de ejecución.

Aquí es donde entra PDOStatement::bindValue; la forma recomendada de ejecutar anterior es:

$usuarios = ['Andres', 'Tomas'];
$declaracion = $dbh->prepare('SELECT * FROM clientes where name = :nombre');
foreach ($usuarios as $usuario) {
    while ($row = $declaracion->fetch(PDO::FETCH_ASSOC)) {
        $declaracion->bindValue(':nombre', $usuario, PDO::PARAM_STR);
        $declaracion->execute();
        echo $row['id'];
    }
}

En lugar de usar PDOStatement::execute para vincular el valor al parámetro, usamos PDOStatement::binValue. El cual agrega algunas mejoras significativas a nuestro código:

  • Legibilidad: hace que el código sea fácil de leer para otros desarrolladores, ya que indica el tipo de datos exactos que un parámetro debe aceptar.
  • Mantenibilidad: El tercer parámetro, que especifica el tipo de datos de la variable de paso, evita que PHP arroje tipos de datos incompatibles, que son propensos a errores. A la larga, también facilita el mantenimiento del código, ya que el futuro desarrollador podrá detectar el tipo de datos de un vistazo.

Estas cuatro técnicas definitivamente no son oficiales: solo son convenciones de nombres hechas para memorizar las API de PDO. No hay necesidad de seguirlos estrictamente, pero eso es lo que hace que el código se vuelva desastrozo y lleno de bugs.

0 bugs, y mucha seguridad.

Recuerden que pueden preparar, todo tipo de declaraciones para completar sus CRUDS e incluso hacer clases con métodos que abstraigan su base de datos, en dado caso no quieran usar un framework completo para algo sencillo.

Categorized in:

Tagged in:

, ,