TigerZF
🌐Español

Capítulo 27. Zend_Db

Tabla de contenidos

27.1. Zend_Db_Adapter
27.1.1. Conectando a una base de datos usando un adaptador
27.1.1.1. Usando un constructor de adaptador de Zend_Db
27.1.1.2. Usando la fábrica de Zend_Db
27.1.1.3. Usando Zend_Config con la fábrica de Zend_Db
27.1.1.4. Parámetros del adaptador
27.1.1.5. Gestión de conexiones perezosas (lazy)
27.1.2. Base de datos de ejemplo
27.1.3. Lectura de resultados de consultas
27.1.3.1. Obtención de un conjunto de resultados completo
27.1.3.2. Cambio del modo de obtención (fetch)
27.1.3.3. Obtención de un conjunto de resultados como array asociativo
27.1.3.4. Obtención de una sola columna de un conjunto de resultados
27.1.3.5. Obtención de pares clave-valor de un conjunto de resultados
27.1.3.6. Obtención de una sola fila de un conjunto de resultados
27.1.3.7. Obtención de un solo valor escalar de un conjunto de resultados
27.1.4. Escritura de cambios en la base de datos
27.1.4.1. Inserción de datos
27.1.4.2. Recuperación de un valor generado
27.1.4.3. Actualización de datos
27.1.4.4. Eliminación de datos
27.1.5. Entrecomillado de valores e identificadores
27.1.5.1. Uso de quote()
27.1.5.2. Uso de quoteInto()
27.1.5.3. Uso de quoteIdentifier()
27.1.6. Control de transacciones de la base de datos
27.1.7. Listado y descripción de tablas
27.1.8. Cierre de una conexión
27.1.9. Ejecución de otras sentencias de base de datos
27.1.10. Recuperación de la versión del servidor
27.1.11. Notas sobre adaptadores específicos
27.1.11.1. IBM DB2
27.1.11.2. MySQLi
27.1.11.3. Oracle
27.1.11.4. Microsoft SQL Server
27.1.11.5. PDO para IBM DB2 e Informix Dynamic Server (IDS)
27.1.11.6. PDO Microsoft SQL Server
27.1.11.7. PDO MySQL
27.1.11.8. PDO Oracle
27.1.11.9. PDO PostgreSQL
27.1.11.10. PDO SQLite
27.1.11.11. Firebird (Interbase)
27.2. Zend_Db_Statement
27.2.1. Creación de una sentencia (Statement)
27.2.2. Ejecución de una sentencia
27.2.3. Obtención de resultados de una sentencia SELECT
27.2.3.1. Obtención de una sola fila de un conjunto de resultados
27.2.3.2. Obtención de un conjunto de resultados completo
27.2.3.3. Cambio del modo de obtención (fetch)
27.2.3.4. Obtención de una sola columna de un conjunto de resultados
27.2.3.5. Obtención de una fila como objeto
27.3. Zend_Db_Profiler
27.3.1. Introducción
27.3.2. Uso del Profiler
27.3.3. Uso avanzado del Profiler
27.3.3.1. Filtrar por tiempo transcurrido de la consulta
27.3.3.2. Filtrar por tipo de consulta
27.3.3.3. Recuperar perfiles por tipo de consulta
27.3.4. Profilers especializados
27.3.4.1. Perfilado con Firebug
27.4. Zend_Db_Select
27.4.1. Introducción
27.4.2. Creación de un objeto Select
27.4.3. Construcción de consultas Select
27.4.3.1. Agregar una cláusula FROM
27.4.3.2. Agregar columnas
27.4.3.3. Agregar columnas de expresión
27.4.3.4. Agregar columnas a una tabla FROM o JOIN existente
27.4.3.5. Agregar otra tabla a la consulta con JOIN
27.4.3.6. Agregar una cláusula WHERE
27.4.3.7. Agregar una cláusula GROUP BY
27.4.3.8. Agregar una cláusula HAVING
27.4.3.9. Agregar una cláusula ORDER BY
27.4.3.10. Agregar una cláusula LIMIT
27.4.3.11. Agregar el modificador de consulta DISTINCT
27.4.3.12. Agregar el modificador de consulta FOR UPDATE
27.4.3.13. Construcción de una consulta UNION
27.4.4. Ejecución de consultas Select
27.4.4.1. Ejecución de consultas Select desde el adaptador Db
27.4.4.2. Ejecución de consultas Select desde el objeto
27.4.4.3. Conversión de un objeto Select en una cadena SQL
27.4.5. Otros métodos
27.4.5.1. Recuperación de partes del objeto Select
27.4.5.2. Reinicio de partes del objeto Select
27.5. Zend_Db_Table
27.5.1. Introducción
27.5.2. Uso de Zend_Db_Table como clase concreta
27.5.3. Definición de una clase Table
27.5.3.1. Definición del nombre y esquema de la tabla
27.5.3.2. Definición de la clave primaria de la tabla
27.5.3.3. Sobrescribir los métodos de configuración de la tabla
27.5.3.4. Inicialización de la tabla
27.5.4. Creación de una instancia de una tabla
27.5.4.1. Especificación de un adaptador de base de datos
27.5.4.2. Establecimiento de un adaptador de base de datos por defecto
27.5.4.3. Almacenamiento de un adaptador de base de datos en el Registro
27.5.5. Inserción de filas en una tabla
27.5.5.1. Uso de una tabla con clave autoincremental
27.5.5.2. Uso de una tabla con una secuencia
27.5.5.3. Uso de una tabla con una clave natural
27.5.6. Actualización de filas en una tabla
27.5.7. Eliminación de filas de una tabla
27.5.8. Búsqueda de filas por clave primaria
27.5.9. Consulta de un conjunto de filas
27.5.9.1. API de Select
27.5.9.2. Obtención de un conjunto de filas (rowset)
27.5.9.3. Uso avanzado
27.5.10. Consulta de una sola fila
27.5.11. Recuperación de información de metadatos de la tabla
27.5.12. Caché de metadatos de la tabla
27.5.12.1. Codificación fija de los metadatos de la tabla
27.5.13. Personalización y extensión de una clase Table
27.5.13.1. Uso de clases Row o Rowset personalizadas
27.5.13.2. Definición de lógica personalizada para Insert, Update y Delete
27.5.13.3. Definir métodos de búsqueda personalizados en Zend_Db_Table
27.5.13.4. Definir la inflexión en Zend_Db_Table
27.6. Zend_Db_Table_Row
27.6.1. Introducción
27.6.2. Obtención de una fila
27.6.2.1. Lectura de valores de columna de una fila
27.6.2.2. Recuperación de los datos de una fila como array
27.6.2.3. Obtención de datos de tablas relacionadas
27.6.3. Escritura de filas en la base de datos
27.6.3.1. Cambio de valores de columna en una fila
27.6.3.2. Inserción de una nueva fila
27.6.3.3. Cambio de valores en varias columnas
27.6.3.4. Eliminación de una fila
27.6.4. Serialización y deserialización de filas
27.6.4.1. Serialización de una fila
27.6.4.2. Deserialización de los datos de una fila
27.6.4.3. Reactivación de una fila como datos vivos
27.6.5. Extensión de la clase Row
27.6.5.1. Inicialización de la fila
27.6.5.2. Definición de lógica personalizada para Insert, Update y Delete en Zend_Db_Table_Row
27.6.5.3. Definir la inflexión en Zend_Db_Table_Row
27.7. Zend_Db_Table_Rowset
27.7.1. Introducción
27.7.2. Obtención de un Rowset
27.7.3. Recuperación de filas de un Rowset
27.7.4. Recuperación de un Rowset como array
27.7.5. Serialización y deserialización de un Rowset
27.7.6. Extensión de la clase Rowset
27.8. Relaciones de Zend_Db_Table
27.8.1. Introducción
27.8.2. Definición de relaciones
27.8.3. Obtención de un Rowset dependiente
27.8.4. Obtención de una fila padre
27.8.5. Obtención de un Rowset mediante una relación muchos a muchos
27.8.6. Cascada de operaciones de escritura
27.8.6.1. Notas sobre las operaciones en cascada
27.9. Zend_Db_Table_Definition
27.9.1. Introducción
27.9.2. Uso básico
27.9.3. Uso avanzado

27.1. Zend_Db_Adapter

Zend_Db y sus clases relacionadas proporcionan una interfaz de base de datos SQL sencilla para Zend Framework. El Zend_Db_Adapter es la clase básica que se utiliza para conectar su aplicación PHP a un RDBMS. Hay una clase Adapter distinta para cada marca de RDBMS.

Los adaptadores de Zend_Db crean un puente entre las extensiones PHP específicas de cada proveedor y una interfaz común para ayudarle a escribir aplicaciones PHP una sola vez y desplegarlas con múltiples marcas de RDBMS con muy poco esfuerzo.

La interfaz de la clase adaptadora es similar a la interfaz de la extensión PHP Data Objects. Zend_Db proporciona clases Adapter para los controladores PDO de las siguientes marcas de RDBMS:

  • IBM DB2 e Informix Dynamic Server (IDS), usando la extensión PHP pdo_ibm

  • MariaDB, usando la extensión PHP pdo_mysql

  • MySQL, usando la extensión PHP pdo_mysql

  • Microsoft SQL Server, usando la extensión PHP pdo_dblib

  • Oracle, usando la extensión PHP pdo_oci

  • PostgreSQL, usando la extensión PHP pdo_pgsql

  • SQLite, usando la extensión PHP pdo_sqlite

Además, Zend_Db proporciona clases Adapter que utilizan extensiones de base de datos PHP para las siguientes marcas de RDBMS:

  • MariaDB, usando la extensión PHP mysqli

  • MySQL, usando la extensión PHP mysqli

  • Oracle, usando la extensión PHP oci8

  • IBM DB2 y DB2 I5, usando la extensión PHP ibm_db2

  • Firebird (Interbase), usando la extensión PHP php_interbase

[Note] Nota

Cada adaptador de Zend_Db utiliza una extensión PHP. Debe tener habilitada la respectiva extensión PHP en su entorno PHP para usar un adaptador de Zend_Db. Por ejemplo, si usa cualquiera de los adaptadores PDO de Zend_Db, necesita habilitar tanto la extensión PDO como el controlador PDO para la marca de RDBMS que utilice.

27.1.1. Conectando a una base de datos usando un adaptador

Esta sección describe cómo crear una instancia de un adaptador de base de datos. Esto corresponde a establecer una conexión a su servidor RDBMS desde su aplicación PHP.

27.1.1.1. Usando un constructor de adaptador de Zend_Db

Puede crear una instancia de un adaptador usando su constructor. El constructor de un adaptador toma un argumento, que es un array de parámetros usados para declarar la conexión.

Ejemplo 27.1. Uso de un constructor de adaptador

$db = new Zend_Db_Adapter_Pdo_Mysql(array(
    'host'     => '127.0.0.1',
    'username' => 'webuser',
    'password' => 'xxxxxxxx',
    'dbname'   => 'test'
));

27.1.1.2. Usando la fábrica de Zend_Db

Como alternativa al uso directo de un constructor de adaptador, puede crear una instancia de un adaptador usando el método estático Zend_Db::factory(). Este método carga dinámicamente el archivo de la clase adaptadora bajo demanda usando Zend_Loader::loadClass().

El primer argumento es una cadena que nombra la parte base del nombre de la clase adaptadora. Por ejemplo, la cadena 'Pdo_Mysql' corresponde a la clase Zend_Db_Adapter_Pdo_Mysql. El segundo argumento es el mismo array de parámetros que habría entregado al constructor del adaptador.

Ejemplo 27.2. Uso del método de fábrica del adaptador

// No necesitamos la siguiente sentencia porque el
// archivo Zend_Db_Adapter_Pdo_Mysql será cargado por nosotros mediante el
// método de fábrica de Zend_Db.

// require_once 'Zend/Db/Adapter/Pdo/Mysql.php';

// Cargar automáticamente la clase Zend_Db_Adapter_Pdo_Mysql
// y crear una instancia de ella.
$db = Zend_Db::factory('Pdo_Mysql', array(
    'host'     => '127.0.0.1',
    'username' => 'webuser',
    'password' => 'xxxxxxxx',
    'dbname'   => 'test'
));

Si crea su propia clase que extienda Zend_Db_Adapter_Abstract, pero no nombra su clase con el prefijo de paquete "Zend_Db_Adapter", puede usar el método factory() para cargar su adaptador si especifica la parte inicial de la clase adaptadora con la clave 'adapterNamespace' en el array de parámetros.

Ejemplo 27.3. Uso del método de fábrica del adaptador para una clase de adaptador personalizada

// No necesitamos cargar el archivo de la clase adaptadora
// porque será cargado por nosotros mediante el método de fábrica de Zend_Db.

// Cargar automáticamente la clase MyProject_Db_Adapter_Pdo_Mysql y crear
// una instancia de ella.
$db = Zend_Db::factory('Pdo_Mysql', array(
    'host'             => '127.0.0.1',
    'username'         => 'webuser',
    'password'         => 'xxxxxxxx',
    'dbname'           => 'test',
    'adapterNamespace' => 'MyProject_Db_Adapter'
));

27.1.1.3. Usando Zend_Config con la fábrica de Zend_Db

Opcionalmente, puede especificar cualquiera de los argumentos del método factory() como un objeto de tipo Zend_Config.

Si el primer argumento es un objeto de configuración, se espera que contenga una propiedad llamada adapter, que contenga la cadena que nombra la parte base del nombre de la clase adaptadora. Opcionalmente, el objeto puede contener una propiedad llamada params, con subpropiedades correspondientes a los nombres de parámetros del adaptador. Esto solo se utiliza si el segundo argumento del método factory() está ausente.

Ejemplo 27.4. Uso del método de fábrica del adaptador con un objeto Zend_Config

En el ejemplo siguiente, se crea un objeto Zend_Config a partir de un array. También puede cargar datos desde un archivo externo usando clases como Zend_Config_Ini y Zend_Config_Xml.

$config = new Zend_Config(
    array(
        'database' => array(
            'adapter' => 'Mysqli',
            'params'  => array(
                'host'     => '127.0.0.1',
                'dbname'   => 'test',
                'username' => 'webuser',
                'password' => 'secret',
            )
        )
    )
);

$db = Zend_Db::factory($config->database);

El segundo argumento del método factory() puede ser un array asociativo que contenga entradas correspondientes a los parámetros del adaptador. Este argumento es opcional. Si el primer argumento es de tipo Zend_Config, se asume que contiene todos los parámetros, y el segundo argumento se ignora.

27.1.1.4. Parámetros del adaptador

La siguiente lista explica los parámetros comunes reconocidos por las clases adaptadoras de Zend_Db.

  • host: una cadena que contiene un nombre de host o dirección IP del servidor de base de datos. Si la base de datos se ejecuta en el mismo host que la aplicación PHP, puede usar 'localhost' o '127.0.0.1'.

  • username: identificador de cuenta para autenticar una conexión al servidor RDBMS.

  • password: credencial de contraseña de la cuenta para autenticar una conexión al servidor RDBMS.

  • dbname: nombre de la instancia de base de datos en el servidor RDBMS.

  • port: algunos servidores RDBMS pueden aceptar conexiones de red en un número de puerto especificado por el administrador. El parámetro port le permite especificar el puerto al que se conecta su aplicación PHP, para coincidir con el puerto configurado en el servidor RDBMS.

  • charset: especifica el juego de caracteres usado para la conexión.

  • options: este parámetro es un array asociativo de opciones que son genéricas para todas las clases Zend_Db_Adapter.

  • driver_options: este parámetro es un array asociativo de opciones adicionales específicas de una extensión de base de datos determinada. Un uso típico de este parámetro es establecer atributos de un controlador PDO.

  • adapterNamespace: nombra la parte inicial del nombre de la clase del adaptador, en lugar de 'Zend_Db_Adapter'. Use esto si necesita usar el método factory() para cargar una clase de adaptador de base de datos que no sea de Zend.

  • socket: le permite especificar el socket o pipe con nombre a usar. Actualmente solo compatible con el adaptador mysqli.

Ejemplo 27.5. Pasar la opción de conversión de mayúsculas/minúsculas a la fábrica

Puede especificar esta opción mediante la constante Zend_Db::CASE_FOLDING. Esto corresponde al atributo ATTR_CASE en los controladores de base de datos PDO e IBM DB2, ajustando la mayúscula/minúscula de las claves de cadena en los conjuntos de resultados de consulta. La opción toma los valores Zend_Db::CASE_NATURAL (el valor por defecto), Zend_Db::CASE_UPPER, y Zend_Db::CASE_LOWER.

$options = array(
    Zend_Db::CASE_FOLDING => Zend_Db::CASE_UPPER
);

$params = array(
    'host'           => '127.0.0.1',
    'username'       => 'webuser',
    'password'       => 'xxxxxxxx',
    'dbname'         => 'test',
    'options'        => $options
);

$db = Zend_Db::factory('Db2', $params);

Ejemplo 27.6. Pasar la opción de entrecomillado automático a la fábrica

Puede especificar esta opción mediante la constante Zend_Db::AUTO_QUOTE_IDENTIFIERS. Si el valor es TRUE (el valor por defecto), los identificadores como nombres de tabla, nombres de columna, e incluso alias, se delimitan en toda la sintaxis SQL generada por el objeto Adapter. Esto facilita el uso de identificadores que contienen palabras clave SQL, o caracteres especiales. Si el valor es FALSE, los identificadores no se delimitan automáticamente. Si necesita delimitar identificadores, debe hacerlo usted mismo usando el método quoteIdentifier().

$options = array(
    Zend_Db::AUTO_QUOTE_IDENTIFIERS => false
);

$params = array(
    'host'           => '127.0.0.1',
    'username'       => 'webuser',
    'password'       => 'xxxxxxxx',
    'dbname'         => 'test',
    'options'        => $options
);

$db = Zend_Db::factory('Pdo_Mysql', $params);

Ejemplo 27.7. Pasar opciones del controlador PDO a la fábrica

$pdoParams = array(
    PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
);

$params = array(
    'host'           => '127.0.0.1',
    'username'       => 'webuser',
    'password'       => 'xxxxxxxx',
    'dbname'         => 'test',
    'driver_options' => $pdoParams
);

$db = Zend_Db::factory('Pdo_Mysql', $params);

echo $db->getConnection()
        ->getAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY);

Ejemplo 27.8. Pasar opciones de serialización a la fábrica

$options = array(
    Zend_Db::ALLOW_SERIALIZATION => false
);

$params = array(
    'host'           => '127.0.0.1',
    'username'       => 'webuser',
    'password'       => 'xxxxxxxx',
    'dbname'         => 'test',
    'options'        => $options
);

$db = Zend_Db::factory('Pdo_Mysql', $params);

27.1.1.5. Gestión de conexiones perezosas (lazy)

Crear una instancia de una clase Adapter no conecta inmediatamente al servidor RDBMS. El Adapter guarda los parámetros de conexión, y establece la conexión real bajo demanda, la primera vez que necesita ejecutar una consulta. Esto garantiza que crear un objeto Adapter sea rápido y económico. Puede crear una instancia de un Adapter incluso si no está seguro de que necesite ejecutar alguna consulta de base de datos durante la solicitud actual que su aplicación está atendiendo.

Si necesita forzar al Adapter a conectarse al RDBMS, use el método getConnection(). Este método devuelve un objeto para la conexión tal como lo representa la respectiva extensión de base de datos PHP. Por ejemplo, si usa cualquiera de las clases Adapter para controladores PDO, entonces getConnection() devuelve el objeto PDO, después de iniciarlo como una conexión activa a la base de datos específica.

Puede ser útil forzar la conexión si desea capturar cualquier excepción que lance como resultado de credenciales de cuenta no válidas, u otro fallo al conectarse al servidor RDBMS. Estas excepciones no se lanzan hasta que se establece la conexión, por lo que puede ayudar a simplificar el código de su aplicación si maneja las excepciones en un solo lugar, en lugar de en el momento de la primera consulta contra la base de datos.

Adicionalmente, un adaptador puede serializarse para almacenarlo, por ejemplo, en una variable de sesión. Esto puede ser muy útil no solo para el adaptador en sí, sino para otros objetos que lo agregan, como un objeto Zend_Db_Select. Por defecto, se permite serializar los adaptadores; si no lo desea, debería considerar pasar la opción Zend_Db::ALLOW_SERIALIZATION con FALSE, vea el ejemplo anterior. Para respetar el principio de conexiones perezosas, el adaptador no se reconectará por sí mismo después de ser deserializado. Debe entonces llamar usted mismo a getConnection(). Puede hacer que el adaptador se reconecte automáticamente pasando Zend_Db::AUTO_RECONNECT_ON_UNSERIALIZE con TRUE como opción del adaptador.

Ejemplo 27.9. Manejo de excepciones de conexión

try {
    $db = Zend_Db::factory('Pdo_Mysql', $parameters);
    $db->getConnection();
} catch (Zend_Db_Adapter_Exception $e) {
    // quizás una credencial de acceso fallida, o quizás el RDBMS no está en ejecución
} catch (Zend_Exception $e) {
    // quizás factory() no pudo cargar la clase Adapter especificada
}

27.1.2. Base de datos de ejemplo

En la documentación de las clases Zend_Db, usamos un conjunto de tablas simples para ilustrar el uso de las clases y métodos. Estas tablas de ejemplo podrían almacenar información para el seguimiento de errores (bugs) en un proyecto de desarrollo de software. La base de datos contiene cuatro tablas:

  • accounts almacena información sobre cada usuario de la base de datos de seguimiento de errores.

  • products almacena información sobre cada producto para el que puede registrarse un error.

  • bugs almacena información sobre errores, incluyendo el estado actual del error, la persona que reportó el error, la persona asignada para corregir el error, y la persona asignada para verificar la corrección.

  • bugs_products almacena una relación entre errores y productos. Esto implementa una relación muchos a muchos, porque un error dado puede ser relevante para múltiples productos, y por supuesto un producto dado puede tener múltiples errores.

El siguiente pseudocódigo de lenguaje de definición de datos SQL describe las tablas de esta base de datos de ejemplo. Estas tablas de ejemplo se usan extensamente en las pruebas automatizadas de unidad para Zend_Db.

CREATE TABLE accounts (
  account_name      VARCHAR(100) NOT NULL PRIMARY KEY
);

CREATE TABLE products (
  product_id        INTEGER NOT NULL PRIMARY KEY,
  product_name      VARCHAR(100)
);

CREATE TABLE bugs (
  bug_id            INTEGER NOT NULL PRIMARY KEY,
  bug_description   VARCHAR(100),
  bug_status        VARCHAR(20),
  reported_by       VARCHAR(100) REFERENCES accounts(account_name),
  assigned_to       VARCHAR(100) REFERENCES accounts(account_name),
  verified_by       VARCHAR(100) REFERENCES accounts(account_name)
);

CREATE TABLE bugs_products (
  bug_id            INTEGER NOT NULL REFERENCES bugs,
  product_id        INTEGER NOT NULL REFERENCES products,
  PRIMARY KEY       (bug_id, product_id)
);

Note también que la tabla 'bugs' contiene múltiples referencias de clave foránea a la tabla 'accounts'. Cada una de estas claves foráneas puede referenciar una fila diferente en la tabla 'accounts' para un error dado.

El diagrama a continuación ilustra el modelo de datos físico de la base de datos de ejemplo.

27.1.3. Lectura de resultados de consultas

Esta sección describe métodos de la clase Adapter con los que puede ejecutar consultas SELECT y recuperar los resultados de las consultas.

27.1.3.1. Obtención de un conjunto de resultados completo

Puede ejecutar una consulta SQL SELECT y recuperar sus resultados en un solo paso usando el método fetchAll().

El primer argumento de este método es una cadena que contiene una sentencia SELECT. Alternativamente, el primer argumento puede ser un objeto de la clase Zend_Db_Select. El Adapter convierte automáticamente este objeto a una representación en cadena de la sentencia SELECT.

El segundo argumento de fetchAll() es un array de valores para sustituir los marcadores de posición de parámetros en la sentencia SQL.

Ejemplo 27.10. Uso de fetchAll()

$sql = 'SELECT * FROM bugs WHERE bug_id = ?';

$result = $db->fetchAll($sql, 2);

27.1.3.2. Cambio del modo de obtención (fetch)

Por defecto, fetchAll() devuelve un array de filas, cada una de las cuales es un array asociativo. Las claves del array asociativo son las columnas o alias de columna nombrados en la consulta select.

Puede especificar un estilo diferente de obtención de resultados usando el método setFetchMode(). Los modos soportados se identifican mediante constantes:

  • Zend_Db::FETCH_ASSOC: devuelve los datos en un array de arrays asociativos. Las claves del array son los nombres de columna, como cadenas. Este es el modo de obtención por defecto para las clases Zend_Db_Adapter.

    Tenga en cuenta que si su lista de selección contiene más de una columna con el mismo nombre, por ejemplo si provienen de dos tablas diferentes en un JOIN, solo puede haber una entrada en el array asociativo para un nombre dado. Si usa el modo FETCH_ASSOC, debería especificar alias de columna en su consulta SELECT para asegurar que los nombres resulten en claves de array únicas.

    Por defecto, estas cadenas se devuelven tal como las devuelve el controlador de la base de datos. Esto generalmente es la forma en que se escribe la columna en el servidor RDBMS. Puede especificar la mayúscula/minúscula para estas cadenas, usando la opción Zend_Db::CASE_FOLDING. Especifique esto al instanciar el Adapter. Vea este ejemplo

  • Zend_Db::FETCH_NUM: devuelve los datos en un array de arrays. Los arrays se indexan mediante enteros, correspondientes a la posición del respectivo campo en la lista de selección de la consulta.

  • Zend_Db::FETCH_BOTH: devuelve los datos en un array de arrays. Las claves del array son tanto cadenas como se usan en el modo FETCH_ASSOC, como enteros como se usan en el modo FETCH_NUM. Tenga en cuenta que el número de elementos en el array es el doble del que habría en el array si usara FETCH_ASSOC o FETCH_NUM.

  • Zend_Db::FETCH_COLUMN: devuelve los datos en un array de valores. El valor en cada array es el valor devuelto por una columna del conjunto de resultados. Por defecto, esta es la primera columna, indexada como 0.

  • Zend_Db::FETCH_OBJ: devuelve los datos en un array de objetos. La clase por defecto es la clase incorporada de PHP stdClass. Las columnas del conjunto de resultados están disponibles como propiedades públicas del objeto.

Ejemplo 27.11. Uso de setFetchMode()

$db->setFetchMode(Zend_Db::FETCH_OBJ);

$result = $db->fetchAll('SELECT * FROM bugs WHERE bug_id = ?', 2);

// $result es un array de objetos
echo $result[0]->bug_description;

27.1.3.3. Obtención de un conjunto de resultados como array asociativo

El método fetchAssoc() devuelve los datos en un array de arrays asociativos, sin importar qué valor haya establecido para el modo de obtención, usando la primera columna como índice del array.

Ejemplo 27.12. Uso de fetchAssoc()

$db->setFetchMode(Zend_Db::FETCH_OBJ);

$result = $db->fetchAssoc(
    'SELECT bug_id, bug_description, bug_status FROM bugs'
);

// $result es un array de arrays asociativos, a pesar del modo de obtención
echo $result[2]['bug_description']; // Descripción del Bug #2
echo $result[1]['bug_description']; // Descripción del Bug #1

27.1.3.4. Obtención de una sola columna de un conjunto de resultados

El método fetchCol() devuelve los datos en un array de valores, sin importar el valor que haya establecido para el modo de obtención. Esto solo devuelve la primera columna devuelta por la consulta. Cualquier otra columna devuelta por la consulta se descarta. Si necesita devolver una columna distinta de la primera, vea esta sección.

Ejemplo 27.13. Uso de fetchCol()

$db->setFetchMode(Zend_Db::FETCH_OBJ);

$result = $db->fetchCol(
    'SELECT bug_description, bug_id FROM bugs WHERE bug_id = ?', 2);

// contiene bug_description; bug_id no se devuelve
echo $result[0];

27.1.3.5. Obtención de pares clave-valor de un conjunto de resultados

El método fetchPairs() devuelve los datos en un array de pares clave-valor, como un array asociativo con una sola entrada por fila. La clave de este array asociativo se toma de la primera columna devuelta por la consulta SELECT. El valor se toma de la segunda columna devuelta por la consulta SELECT. Cualquier otra columna devuelta por la consulta se descarta.

Debería diseñar la consulta SELECT de modo que la primera columna devuelta tenga valores únicos. Si hay valores duplicados en la primera columna, las entradas en el array asociativo se sobrescribirán.

Ejemplo 27.14. Uso de fetchPairs()

$db->setFetchMode(Zend_Db::FETCH_OBJ);

$result = $db->fetchPairs('SELECT bug_id, bug_status FROM bugs');

echo $result[2];

27.1.3.6. Obtención de una sola fila de un conjunto de resultados

El método fetchRow() devuelve los datos usando el modo de obtención actual, pero devuelve solo la primera fila obtenida del conjunto de resultados.

Ejemplo 27.15. Uso de fetchRow()

$db->setFetchMode(Zend_Db::FETCH_OBJ);

$result = $db->fetchRow('SELECT * FROM bugs WHERE bug_id = 2');

// note que $result es un solo objeto, no un array de objetos
echo $result->bug_description;

27.1.3.7. Obtención de un solo valor escalar de un conjunto de resultados

El método fetchOne() es como una combinación de fetchRow() con fetchCol(), en el sentido de que devuelve datos solo de la primera fila obtenida del conjunto de resultados, y devuelve solo el valor de la primera columna de esa fila. Por lo tanto, devuelve solo un único valor escalar, no un array ni un objeto.

Ejemplo 27.16. Uso de fetchOne()

$result = $db->fetchOne('SELECT bug_status FROM bugs WHERE bug_id = 2');

// este es un único valor de cadena
echo $result;

27.1.4. Escritura de cambios en la base de datos

Puede usar la clase Adapter para escribir nuevos datos o cambiar datos existentes en su base de datos. Esta sección describe los métodos para hacer estas operaciones.

27.1.4.1. Inserción de datos

Puede añadir nuevas filas a una tabla de su base de datos usando el método insert(). El primer argumento es una cadena que nombra la tabla, y el segundo argumento es un array asociativo, que asigna nombres de columna a valores de datos.

Ejemplo 27.17. Inserción en una tabla

$data = array(
    'created_on'      => '2007-03-22',
    'bug_description' => 'Something wrong',
    'bug_status'      => 'NEW'
);

$db->insert('bugs', $data);

Las columnas que excluya del array de datos no se especifican a la base de datos. Por lo tanto, siguen las mismas reglas que sigue una sentencia SQL INSERT: si la columna tiene una cláusula DEFAULT, la columna toma ese valor en la fila creada; en caso contrario, la columna queda en estado NULL.

Por defecto, los valores en su array de datos se insertan usando parámetros. Esto reduce el riesgo de algunos tipos de problemas de seguridad. No necesita aplicar escape o entrecomillado a los valores en el array de datos.

Podría necesitar que los valores en el array de datos se traten como expresiones SQL, en cuyo caso no deben entrecomillarse. Por defecto, todos los valores de datos pasados como cadenas se tratan como literales de cadena. Para especificar que el valor es una expresión SQL y por lo tanto no debe entrecomillarse, pase el valor en el array de datos como un objeto de tipo Zend_Db_Expr en lugar de una cadena simple.

Ejemplo 27.18. Inserción de expresiones en una tabla

$data = array(
    'created_on'      => new Zend_Db_Expr('CURDATE()'),
    'bug_description' => 'Something wrong',
    'bug_status'      => 'NEW'
);

$db->insert('bugs', $data);

27.1.4.2. Recuperación de un valor generado

Algunas marcas de RDBMS admiten claves primarias autoincrementales. Una tabla definida de esta manera genera un valor de clave primaria automáticamente durante un INSERT de una nueva fila. El valor de retorno del método insert() no es el último ID insertado, porque la tabla podría no tener una columna autoincremental. En su lugar, el valor de retorno es el número de filas afectadas (normalmente 1).

Si su tabla está definida con una clave primaria autoincremental, puede llamar al método lastInsertId() después de la inserción. Este método devuelve el último valor generado en el ámbito de la conexión actual a la base de datos.

Ejemplo 27.19. Uso de lastInsertId() para una clave autoincremental

$db->insert('bugs', $data);

// devuelve el último valor generado por una columna autoincremental
$id = $db->lastInsertId();

Algunas marcas de RDBMS admiten un objeto de secuencia, que genera valores únicos para servir como valores de clave primaria. Para admitir secuencias, el método lastInsertId() acepta dos argumentos de cadena opcionales. Estos argumentos nombran la tabla y la columna, asumiendo que ha seguido la convención de que una secuencia se nombra usando los nombres de la tabla y la columna para los que la secuencia genera valores, y un sufijo "_seq". Esto se basa en la convención usada por PostgreSQL al nombrar secuencias para columnas SERIAL. Por ejemplo, una tabla "bugs" con columna de clave primaria "bug_id" usaría una secuencia llamada "bugs_bug_id_seq".

Ejemplo 27.20. Uso de lastInsertId() para una secuencia

$db->insert('bugs', $data);

// devuelve el último valor generado por la secuencia 'bugs_bug_id_seq'.
$id = $db->lastInsertId('bugs', 'bug_id');

// alternativamente, devuelve el último valor generado por la secuencia 'bugs_seq'.
$id = $db->lastInsertId('bugs');

Si el nombre de su objeto de secuencia no sigue esta convención de nomenclatura, use el método lastSequenceId() en su lugar. Este método toma un único argumento de cadena, que nombra la secuencia literalmente.

Ejemplo 27.21. Uso de lastSequenceId()

$db->insert('bugs', $data);

// devuelve el último valor generado por la secuencia 'bugs_id_gen'.
$id = $db->lastSequenceId('bugs_id_gen');

Para las marcas de RDBMS que no admiten secuencias, incluyendo MariaDB, MySQL, Microsoft SQL Server, y SQLite, los argumentos del método lastInsertId() se ignoran, y el valor devuelto es el valor más reciente generado para cualquier tabla mediante operaciones INSERT durante la conexión actual. Para estas marcas de RDBMS, el método lastSequenceId() siempre devuelve NULL.

[Note] ¿Por qué no usar "SELECT MAX(id) FROM table"?

A veces esta consulta devuelve el valor de clave primaria más reciente insertado en la tabla. Sin embargo, esta técnica no es segura de usar en un entorno donde múltiples clientes están insertando registros en la base de datos. Es posible, y por lo tanto está destinado a suceder eventualmente, que otro cliente inserte otra fila en el instante entre la inserción realizada por su aplicación cliente y su consulta para el valor MAX(id). Por lo tanto, el valor devuelto no identifica la fila que insertó, identifica la fila insertada por algún otro cliente. No hay forma de saber cuándo ha ocurrido esto.

Usar un modo de aislamiento de transacciones fuerte como "repeatable read" puede mitigar este riesgo, pero algunas marcas de RDBMS no admiten el aislamiento de transacciones requerido para esto, o bien su aplicación puede usar un modo de aislamiento de transacciones más bajo por diseño.

Además, usar una expresión como "MAX(id)+1" para generar un nuevo valor para una clave primaria no es seguro, porque dos clientes podrían realizar esta consulta simultáneamente, y luego ambos usar el mismo valor calculado para su siguiente operación INSERT.

Todas las marcas de RDBMS proporcionan mecanismos para generar valores únicos, y para devolver el último valor generado. Estos mecanismos necesariamente funcionan fuera del ámbito del aislamiento de transacciones, por lo que no hay posibilidad de que dos clientes generen el mismo valor, y no hay posibilidad de que el valor generado por otro cliente se informe a la conexión de su cliente como el último valor generado.

27.1.4.3. Actualización de datos

Puede actualizar filas en una tabla de base de datos usando el método update() de un Adapter. Este método toma tres argumentos: el primero es el nombre de la tabla; el segundo es un array asociativo que asigna columnas por cambiar a nuevos valores a asignar a estas columnas.

Los valores en el array de datos se tratan como literales de cadena. Vea esta sección para información sobre el uso de expresiones SQL en el array de datos.

El tercer argumento es una cadena que contiene una expresión SQL que se usa como criterio para las filas a cambiar. Los valores e identificadores en este argumento no se entrecomillan ni se escapan. Usted es responsable de asegurar que cualquier contenido dinámico se interpole en esta cadena de forma segura. Vea esta sección para conocer métodos que le ayuden a hacer esto.

El valor de retorno es el número de filas afectadas por la operación de actualización.

Ejemplo 27.22. Actualización de filas

$data = array(
    'updated_on'      => '2007-03-23',
    'bug_status'      => 'FIXED'
);

$n = $db->update('bugs', $data, 'bug_id = 2');

Si omite el tercer argumento, entonces todas las filas en la tabla de la base de datos se actualizan con los valores especificados en el array de datos.

Si proporciona un array de cadenas como tercer argumento, estas cadenas se unen como términos en una expresión separados por operadores AND.

Si proporciona un array de arrays como tercer argumento, los valores se entrecomillarán automáticamente en las claves. Estos luego se unirán como términos, separados por operadores AND.

Ejemplo 27.23. Actualización de filas usando un array de expresiones

$data = array(
    'updated_on'      => '2007-03-23',
    'bug_status'      => 'FIXED'
);

$where[] = "reported_by = 'goofy'";
$where[] = "bug_status = 'OPEN'";

$n = $db->update('bugs', $data, $where);

// El SQL resultante es:
//  UPDATE "bugs" SET "update_on" = '2007-03-23', "bug_status" = 'FIXED'
//  WHERE ("reported_by" = 'goofy') AND ("bug_status" = 'OPEN')

Ejemplo 27.24. Actualización de filas usando un array de arrays

$data = array(
    'updated_on'      => '2007-03-23',
    'bug_status'      => 'FIXED'
);

$where['reported_by = ?'] = 'goofy';
$where['bug_status = ?']  = 'OPEN';

$n = $db->update('bugs', $data, $where);

// El SQL resultante es:
//  UPDATE "bugs" SET "update_on" = '2007-03-23', "bug_status" = 'FIXED'
//  WHERE ("reported_by" = 'goofy') AND ("bug_status" = 'OPEN')

27.1.4.4. Eliminación de datos

Puede eliminar filas de una tabla de base de datos usando el método delete(). Este método toma dos argumentos: el primero es una cadena que nombra la tabla.

El segundo argumento es una cadena que contiene una expresión SQL que se usa como criterio para las filas a eliminar. Los valores e identificadores en este argumento no se entrecomillan ni se escapan. Usted es responsable de asegurar que cualquier contenido dinámico se interpole en esta cadena de forma segura. Vea esta sección para conocer métodos que le ayuden a hacer esto.

El valor de retorno es el número de filas afectadas por la operación de eliminación.

Ejemplo 27.25. Eliminación de filas

$n = $db->delete('bugs', 'bug_id = 3');

Si omite el segundo argumento, el resultado es que se eliminan todas las filas de la tabla de la base de datos.

Si proporciona un array de cadenas como segundo argumento, estas cadenas se unen como términos en una expresión separados por operadores AND.

Si proporciona un array de arrays como segundo argumento, los valores se entrecomillarán automáticamente en las claves. Estos luego se unirán como términos, separados por operadores AND.

27.1.5. Entrecomillado de valores e identificadores

Cuando forma consultas SQL, a menudo es el caso que necesita incluir los valores de variables PHP en expresiones SQL. Esto es riesgoso, porque si el valor en una cadena PHP contiene ciertos símbolos, como el símbolo de comilla, podría resultar en un SQL no válido. Por ejemplo, note las comillas desbalanceadas en la siguiente consulta:

$name = "O'Reilly";
$sql = "SELECT * FROM bugs WHERE reported_by = '$name'";

echo $sql;
// SELECT * FROM bugs WHERE reported_by = 'O'Reilly'

Peor aún es el riesgo de que tales errores de código puedan ser explotados deliberadamente por una persona que intenta manipular el funcionamiento de su aplicación web. Si pueden especificar el valor de una variable PHP mediante el uso de un parámetro HTTP u otro mecanismo, podrían lograr que sus consultas SQL hagan cosas que usted no pretendía que hicieran, como devolver datos a los que la persona no debería tener privilegio de leer. Esta es una técnica seria y generalizada para vulnerar la seguridad de aplicaciones, conocida como "Inyección SQL" (vea http://en.wikipedia.org/wiki/SQL_Injection).

La clase Adapter de Zend_Db proporciona funciones convenientes para ayudarle a reducir las vulnerabilidades a ataques de inyección SQL en su código PHP. La solución consiste en escapar caracteres especiales como comillas en los valores PHP antes de que se interpolen en sus cadenas SQL. Esto protege contra la manipulación tanto accidental como deliberada de cadenas SQL por variables PHP que contienen caracteres especiales.

27.1.5.1. Uso de quote()

El método quote() acepta un único argumento, un valor de cadena escalar. Devuelve el valor con caracteres especiales escapados de manera apropiada para el RDBMS que esté usando, y rodeado por delimitadores de valor de cadena. El delimitador de valor de cadena SQL estándar es la comilla simple (').

Ejemplo 27.26. Uso de quote()

$name = $db->quote("O'Reilly");
echo $name;
// 'O\'Reilly'

$sql = "SELECT * FROM bugs WHERE reported_by = $name";

echo $sql;
// SELECT * FROM bugs WHERE reported_by = 'O\'Reilly'

Tenga en cuenta que el valor de retorno de quote() incluye los delimitadores de comilla alrededor de la cadena. Esto es diferente de algunas funciones que escapan caracteres especiales pero no añaden los delimitadores de comilla, por ejemplo mysql_real_escape_string().

Los valores pueden necesitar entrecomillarse o no según el contexto de tipo de dato SQL en el que se usen. Por ejemplo, en algunas marcas de RDBMS, un valor entero no debe entrecomillarse como cadena si se compara con una columna o expresión de tipo entero. En otras palabras, lo siguiente es un error en algunas implementaciones SQL, asumiendo que intColumn tiene un tipo de dato SQL de INTEGER

SELECT * FROM atable WHERE intColumn = '123'

Puede usar el segundo argumento opcional del método quote() para aplicar el entrecomillado de forma selectiva para el tipo de dato SQL que especifique.

Ejemplo 27.27. Uso de quote() con un tipo SQL

$value = '1234';
$sql = 'SELECT * FROM atable WHERE intColumn = '
     . $db->quote($value, 'INTEGER');

Cada clase Zend_Db_Adapter ha codificado los nombres de los tipos de datos SQL numéricos para la respectiva marca de RDBMS. También puede usar las constantes Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, y Zend_Db::FLOAT_TYPE para escribir código de una manera más independiente del RDBMS.

Zend_Db_Table especifica los tipos SQL a quote() automáticamente al generar consultas SQL que hacen referencia a las columnas clave de una tabla.

27.1.5.2. Uso de quoteInto()

El uso más típico del entrecomillado es interpolar una variable PHP en una expresión o sentencia SQL. Puede usar el método quoteInto() para hacer esto en un solo paso. Este método toma dos argumentos: el primer argumento es una cadena que contiene un símbolo de marcador de posición (?), y el segundo argumento es un valor o variable PHP que debe sustituirse por ese marcador de posición.

El símbolo de marcador de posición es el mismo símbolo usado por muchas marcas de RDBMS para parámetros posicionales, pero el método quoteInto() solo emula parámetros de consulta. El método simplemente interpola el valor en la cadena, escapa caracteres especiales, y aplica comillas alrededor de él. Los verdaderos parámetros de consulta mantienen la separación entre la cadena SQL y los parámetros a medida que la sentencia se analiza en el servidor RDBMS.

Ejemplo 27.28. Uso de quoteInto()

$sql = $db->quoteInto("SELECT * FROM bugs WHERE reported_by = ?", "O'Reilly");

echo $sql;
// SELECT * FROM bugs WHERE reported_by = 'O\'Reilly'

Puede usar el tercer parámetro opcional de quoteInto() para especificar el tipo de dato SQL. Los tipos de datos numéricos no se entrecomillan, y otros tipos sí.

Ejemplo 27.29. Uso de quoteInto() con un tipo SQL

$sql = $db
    ->quoteInto("SELECT * FROM bugs WHERE bug_id = ?", '1234', 'INTEGER');

echo $sql;
// SELECT * FROM bugs WHERE reported_by = 1234

27.1.5.3. Uso de quoteIdentifier()

Los valores no son la única parte de la sintaxis SQL que podría necesitar ser variable. Si usa variables PHP para nombrar tablas, columnas, u otros identificadores en sus sentencias SQL, podría necesitar entrecomillar también estas cadenas. Por defecto, los identificadores SQL tienen reglas de sintaxis similares a PHP y a la mayoría de otros lenguajes de programación. Por ejemplo, los identificadores no deben contener espacios, cierta puntuación o caracteres especiales, o caracteres internacionales. Además, ciertas palabras están reservadas para la sintaxis SQL, y no deben usarse como identificadores.

Sin embargo, SQL tiene una característica llamada identificadores delimitados, que permite opciones más amplias para la escritura de identificadores. Si encierra un identificador SQL entre el tipo apropiado de comillas, puede usar identificadores con formas que serían inválidas sin las comillas. Los identificadores delimitados pueden contener espacios, puntuación, o caracteres internacionales. También puede usar palabras reservadas SQL si las encierra en delimitadores de identificador.

El método quoteIdentifier() funciona como quote(), pero aplica los caracteres delimitadores de identificador a la cadena según el tipo de Adapter que use. Por ejemplo, el SQL estándar usa comillas dobles (") para delimitadores de identificador, y la mayoría de las marcas de RDBMS usan ese símbolo. MySQL usa comillas invertidas (`) por defecto. El método quoteIdentifier() también escapa caracteres especiales dentro del argumento de cadena.

Ejemplo 27.30. Uso de quoteIdentifier()

// podríamos tener un nombre de tabla que sea una palabra reservada de SQL
$tableName = $db->quoteIdentifier("order");

$sql = "SELECT * FROM $tableName";

echo $sql
// SELECT * FROM "order"

Los identificadores delimitados SQL son sensibles a mayúsculas/minúsculas, a diferencia de los identificadores sin comillas. Por lo tanto, si usa identificadores delimitados, debe usar la escritura del identificador exactamente como está almacenada en su esquema, incluyendo las mayúsculas y minúsculas de las letras.

En la mayoría de los casos en que se genera SQL dentro de las clases Zend_Db, el valor por defecto es que todos los identificadores se delimitan automáticamente. Puede cambiar este comportamiento con la opción Zend_Db::AUTO_QUOTE_IDENTIFIERS. Especifique esto al instanciar el Adapter. Vea este ejemplo.

27.1.6. Control de transacciones de la base de datos

Las bases de datos definen las transacciones como unidades lógicas de trabajo que se pueden confirmar o revertir como un único cambio, incluso si operan sobre múltiples tablas. Todas las consultas a una base de datos se ejecutan dentro del contexto de una transacción, incluso si el controlador de la base de datos las gestiona implícitamente. Esto se llama modo auto-commit (confirmación automática), en el que el controlador de la base de datos crea una transacción para cada sentencia que ejecuta, y confirma esa transacción después de que se ha ejecutado su sentencia SQL. Por defecto, todas las clases Adapter de Zend_Db operan en modo auto-commit.

Alternativamente, puede especificar el inicio y la resolución de una transacción, y así controlar cuántas consultas SQL se incluyen en un único grupo que se confirma (o revierte) como una única operación. Use el método beginTransaction() para iniciar una transacción. Las sentencias SQL subsiguientes se ejecutan en el contexto de la misma transacción hasta que la resuelva explícitamente.

Para resolver la transacción, use el método commit() o rollBack(). El método commit() marca los cambios realizados durante su transacción como confirmados, lo que significa que los efectos de estos cambios se muestran en consultas ejecutadas en otras transacciones.

El método rollBack() hace lo contrario: descarta los cambios realizados durante su transacción. Los cambios se deshacen efectivamente, y el estado de los datos vuelve a cómo estaba antes de que comenzara su transacción. Sin embargo, revertir su transacción no tiene efecto en los cambios realizados por otras transacciones que se ejecutan concurrentemente.

Después de resolver esta transacción, Zend_Db_Adapter vuelve al modo auto-commit hasta que llame de nuevo a beginTransaction().

Ejemplo 27.31. Gestión de una transacción para garantizar la coherencia

// Iniciar una transacción explícitamente.
$db->beginTransaction();

try {
    // Intentar ejecutar una o más consultas:
    $db->query(...);
    $db->query(...);
    $db->query(...);

    // Si todas tienen éxito, confirmar la transacción y todos los cambios
    // se confirman de una vez.
    $db->commit();

} catch (Exception $e) {
    // Si alguna de las consultas falló y lanzó una excepción,
    // queremos revertir toda la transacción, revirtiendo los
    // cambios realizados en la transacción, incluso los que tuvieron éxito.
    // Así todos los cambios se confirman juntos, o ninguno se confirma.
    $db->rollBack();
    echo $e->getMessage();
}

27.1.7. Listado y descripción de tablas

El método listTables() devuelve un array de cadenas, que nombran todas las tablas de la base de datos actual.

El método describeTable() devuelve un array asociativo de metadatos sobre una tabla. Especifique el nombre de la tabla como cadena en el primer argumento de este método. El segundo argumento es opcional, y nombra el esquema en el que existe la tabla.

Las claves del array asociativo devuelto son los nombres de columna de la tabla. El valor correspondiente a cada columna es también un array asociativo, con las siguientes claves y valores:

Tabla 27.1. Campos de metadatos devueltos por describeTable()

Clave Tipo Descripción
SCHEMA_NAME (cadena) Nombre del esquema de la base de datos en el que existe esta tabla.
TABLE_NAME (cadena) Nombre de la tabla a la que pertenece esta columna.
COLUMN_NAME (cadena) Nombre de la columna.
COLUMN_POSITION (entero) Posición ordinal de la columna en la tabla.
DATA_TYPE (cadena) Nombre RDBMS del tipo de dato de la columna.
DEFAULT (cadena) Valor por defecto de la columna, si lo hay.
NULLABLE (booleano) TRUE si la columna acepta NULL's de SQL, FALSE si la columna tiene una restricción NOT NULL.
LENGTH (entero) Longitud o tamaño de la columna según lo informado por el RDBMS.
SCALE (entero) Escala del tipo SQL NUMERIC o DECIMAL.
PRECISION (entero) Precisión del tipo SQL NUMERIC o DECIMAL.
UNSIGNED (booleano) TRUE si un tipo basado en entero se informa como UNSIGNED.
PRIMARY (booleano) TRUE si la columna forma parte de la clave primaria de esta tabla.
PRIMARY_POSITION (entero) Posición ordinal (basada en 1) de la columna en la clave primaria.
IDENTITY (booleano) TRUE si la columna usa un valor autogenerado.

[Note] Cómo se relaciona el campo de metadatos IDENTITY con RDBMS específicos

El campo de metadatos IDENTITY se eligió como un término 'idiomático' para representar una relación con claves sustitutas. Este campo puede conocerse comúnmente por los siguientes valores:-

  • IDENTITY - DB2, MSSQL

  • AUTO_INCREMENT - MySQL/MariaDB

  • SERIAL - PostgreSQL

  • SEQUENCE - Oracle

Si no existe ninguna tabla que coincida con el nombre de tabla y el nombre de esquema opcional especificados, entonces describeTable() devuelve un array vacío.

27.1.8. Cierre de una conexión

Normalmente no es necesario cerrar una conexión de base de datos. PHP limpia automáticamente todos los recursos al final de una solicitud. Las extensiones de base de datos están diseñadas para cerrar la conexión cuando la referencia al objeto de recurso se limpia.

Sin embargo, si tiene un script PHP de larga duración que inicia muchas conexiones de base de datos, podría necesitar cerrar la conexión, para evitar agotar la capacidad de su servidor RDBMS. Puede usar el método closeConnection() del Adapter para cerrar explícitamente la conexión de base de datos subyacente.

Desde la versión 1.7.2, puede comprobar si está actualmente conectado al servidor RDBMS con el método isConnected(). Esto significa que se ha iniciado un recurso de conexión y no se ha cerrado. Esta función actualmente no puede detectar, por ejemplo, un cierre de la conexión realizado por el lado del servidor. Esto se usa internamente para cerrar la conexión. Le permite cerrar la conexión varias veces sin errores. Ya era el caso antes de 1.7.2 para los adaptadores PDO, pero no para los demás.

Ejemplo 27.32. Cierre de una conexión de base de datos

$db->closeConnection();

[Note] ¿Admite Zend_Db conexiones persistentes?

Sí, la persistencia se admite mediante la adición del indicador persistent establecido en TRUE en la configuración (no driver_configuration) de un adaptador en Zend_Db.

Ejemplo 27.33. Uso del indicador de persistencia con el adaptador Oracle

$db = Zend_Db::factory('Oracle', array(
    'host'       => '127.0.0.1',
    'username'   => 'webuser',
    'password'   => 'xxxxxxxx',
    'dbname'     => 'test',
    'persistent' => true
));

Tenga en cuenta que el uso de conexiones persistentes puede causar un exceso de conexiones inactivas en el servidor RDBMS, lo que causa más problemas que cualquier ganancia de rendimiento que pudiera lograr reduciendo la sobrecarga de establecer conexiones.

Las conexiones de base de datos tienen estado. Es decir, algunos objetos en el servidor RDBMS existen en el ámbito de sesión. Ejemplos son bloqueos, variables de usuario, tablas temporales, e información sobre la consulta ejecutada más recientemente, como filas afectadas, y el último valor de id generado. Si usa conexiones persistentes, su aplicación podría acceder a datos inválidos o privilegiados que fueron creados en una solicitud PHP anterior.

Actualmente, solo Oracle, DB2, y los adaptadores PDO (cuando lo especifica PHP) admiten la persistencia en Zend_Db.

27.1.9. Ejecución de otras sentencias de base de datos

Puede haber casos en los que necesite acceder directamente al objeto de conexión, tal como lo proporciona la extensión de base de datos PHP. Algunas de estas extensiones pueden ofrecer características que no se exponen mediante métodos de Zend_Db_Adapter_Abstract.

Por ejemplo, todas las sentencias SQL ejecutadas por Zend_Db se preparan, y luego se ejecutan. Sin embargo, algunas características de base de datos son incompatibles con las sentencias preparadas. Las sentencias DDL como CREATE y ALTER no se pueden preparar en MySQL. Además, las sentencias SQL no se benefician de la caché de consultas de MySQL, antes de MySQL 5.1.17.

La mayoría de las extensiones de base de datos PHP proporcionan un método para ejecutar sentencias SQL sin prepararlas. Por ejemplo, en PDO, este método es exec(). Puede acceder al objeto de conexión en la extensión PHP directamente usando getConnection().

Ejemplo 27.34. Ejecución de una sentencia no preparada en un adaptador PDO

$result = $db->getConnection()->exec('DROP TABLE bugs');

De manera similar, puede acceder a otros métodos o propiedades que son específicos de las extensiones de base de datos PHP. Sin embargo, tenga en cuenta que al hacer esto podría limitar su aplicación a la interfaz proporcionada por la extensión para una marca específica de RDBMS.

En futuras versiones de Zend_Db, habrá oportunidades para añadir puntos de entrada de métodos para la funcionalidad que es común a las extensiones de base de datos PHP admitidas. Esto no afectará la compatibilidad con versiones anteriores.

27.1.10. Recuperación de la versión del servidor

Desde la versión 1.7.2, puede recuperar la versión del servidor con el estilo de sintaxis PHP para poder usar version_compare(). Si la información no está disponible, recibirá NULL.

Ejemplo 27.35. Verificación de la versión del servidor antes de ejecutar una consulta

$version = $db->getServerVersion();
if (!is_null($version)) {
    if (version_compare($version, '5.0.0', '>=')) {
        // hacer algo
    } else {
        // hacer otra cosa
    }
} else {
    // imposible leer la versión del servidor
}

27.1.11. Notas sobre adaptadores específicos

Esta sección enumera las diferencias entre las clases Adapter de las que debería estar al tanto.

27.1.11.1. IBM DB2

  • Especifique este Adapter al método factory() con el nombre 'Db2'.

  • Este Adapter usa la extensión PHP IBM_DB2.

  • IBM DB2 admite tanto secuencias como claves autoincrementales. Por lo tanto, los argumentos de lastInsertId() son opcionales. Si no proporciona argumentos, el Adapter devuelve el último valor generado para una clave autoincremental. Si proporciona argumentos, el Adapter devuelve el último valor generado por la secuencia nombrada según la convención 'tabla_columna_seq'.

27.1.11.2. MySQLi

  • Especifique este Adapter al método factory() con el nombre 'Mysqli'.

  • Este Adapter utiliza la extensión PHP mysqli.

  • MySQL y MariaDB no admiten secuencias, por lo que lastInsertId() ignora sus argumentos y siempre devuelve el último valor generado para una clave autoincremental. El método lastSequenceId() devuelve NULL.

27.1.11.3. Oracle

  • Especifique este Adapter al método factory() con el nombre 'Oracle'.

  • Este Adapter usa la extensión PHP oci8.

  • Oracle no admite claves autoincrementales, por lo que debería especificar el nombre de una secuencia a lastInsertId() o lastSequenceId().

  • La extensión Oracle no admite parámetros posicionales. Debe usar parámetros con nombre.

  • Actualmente la opción Zend_Db::CASE_FOLDING no es compatible con el adaptador Oracle. Para usar esta opción con Oracle, debe usar el adaptador PDO OCI.

  • Por defecto, los campos LOB se devuelven como objetos OCI-Lob. Puede recuperarlos como cadena para todas las solicitudes usando las opciones de controlador 'lob_as_string' o para una solicitud en particular usando setLobAsString(boolean) en el adaptador o en la sentencia.

27.1.11.4. Microsoft SQL Server

  • Especifique este Adapter al método factory() con el nombre 'Sqlsrv'.

  • Este Adapter usa la extensión PHP sqlsrv

  • Solo se admite Microsoft SQL Server 2005 o superior.

  • Microsoft SQL Server no admite secuencias, por lo que lastInsertId() ignora el argumento de clave primaria y devuelve el último valor generado para una clave autoincremental si se especifica un nombre de tabla o si una consulta de última inserción devolvió un id. El método lastSequenceId() devuelve NULL.

  • Zend_Db_Adapter_Sqlsrv establece QUOTED_IDENTIFIER en ON inmediatamente después de conectarse a una base de datos SQL Server. Esto hace que el controlador use el símbolo delimitador de identificador SQL estándar (") en lugar de la sintaxis propietaria de corchetes que SQL Server usa para delimitar identificadores.

  • Puede especificar driver_options como clave en el array de opciones. El valor puede ser cualquier cosa de aquí http://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx.

  • Puede usar setTransactionIsolationLevel() para establecer el nivel de aislamiento para la conexión actual. El valor puede ser SQLSRV_TXN_READ_UNCOMMITTED, SQLSRV_TXN_READ_COMMITTED, SQLSRV_TXN_REPEATABLE_READ, SQLSRV_TXN_SNAPSHOT o SQLSRV_TXN_SERIALIZABLE.

  • A partir de Zend Framework 1.9, la versión mínima admitida de la extensión PHP SQL Server de Microsoft es 1.0.1924.0. y la versión del cliente nativo de MSSQL Server 9.00.3042.00.

27.1.11.5. PDO para IBM DB2 e Informix Dynamic Server (IDS)

  • Especifique este Adapter al método factory() con el nombre 'Pdo_Ibm'.

  • Este Adapter usa las extensiones PHP PDO y PDO_IBM.

  • Debe usar al menos la versión 1.2.2 de la extensión PDO_IBM. Si tiene una versión anterior de esta extensión, debe actualizar la extensión PDO_IBM desde PECL.

27.1.11.6. PDO Microsoft SQL Server

  • Especifique este Adapter al método factory() con el nombre 'Pdo_Mssql'.

  • Este Adapter usa las extensiones PHP pdo y pdo_dblib.

  • Microsoft SQL Server no admite secuencias, por lo que lastInsertId() ignora sus argumentos y siempre devuelve el último valor generado para una clave autoincremental. El método lastSequenceId() devuelve NULL.

  • Si trabaja con cadenas unicode en una codificación distinta de UCS-2 (como UTF-8), es posible que deba realizar una conversión en el código de su aplicación o almacenar los datos en una columna binaria. Consulte la Base de conocimiento de Microsoft para más información.

  • Zend_Db_Adapter_Pdo_Mssql establece QUOTED_IDENTIFIER en ON inmediatamente después de conectarse a una base de datos SQL Server. Esto hace que el controlador use el símbolo delimitador de identificador SQL estándar (") en lugar de la sintaxis propietaria de corchetes que SQL Server usa para delimitar identificadores.

  • Puede especificar pdoType como clave en el array de opciones. El valor puede ser "mssql" (el valor por defecto), "dblib", "freetds", o "sybase". Esta opción afecta al prefijo DSN que usa el adaptador al construir la cadena DSN. Tanto "freetds" como "sybase" implican un prefijo de "sybase:", que se usa para el conjunto de bibliotecas FreeTDS. Vea también http://www.php.net/manual/en/ref.pdo-dblib.connection.php para más información sobre los prefijos DSN usados en este controlador.

27.1.11.7. PDO MySQL

  • Especifique este Adapter al método factory() con el nombre 'Pdo_Mysql'.

  • Este Adapter usa las extensiones PHP pdo y pdo_mysql.

  • MySQL y MariaDB no admiten secuencias, por lo que lastInsertId() ignora sus argumentos y siempre devuelve el último valor generado para una clave autoincremental. El método lastSequenceId() devuelve NULL.

27.1.11.8. PDO Oracle

  • Especifique este Adapter al método factory() con el nombre 'Pdo_Oci'.

  • Este Adapter usa las extensiones PHP pdo y pdo_oci.

  • Oracle no admite claves autoincrementales, por lo que debería especificar el nombre de una secuencia a lastInsertId() o lastSequenceId().

27.1.11.9. PDO PostgreSQL

  • Especifique este Adapter al método factory() con el nombre 'Pdo_Pgsql'.

  • Este Adapter usa las extensiones PHP pdo y pdo_pgsql.

  • PostgreSQL admite tanto secuencias como claves autoincrementales. Por lo tanto, los argumentos de lastInsertId() son opcionales. Si no proporciona argumentos, el Adapter devuelve el último valor generado para una clave autoincremental. Si proporciona argumentos, el Adapter devuelve el último valor generado por la secuencia nombrada según la convención 'tabla_columna_seq'.

27.1.11.10. PDO SQLite

  • Especifique este Adapter al método factory() con el nombre 'Pdo_Sqlite'.

  • Este Adapter usa las extensiones PHP pdo y pdo_sqlite.

  • SQLite no admite secuencias, por lo que lastInsertId() ignora sus argumentos y siempre devuelve el último valor generado para una clave autoincremental. El método lastSequenceId() devuelve NULL.

  • Para conectarse a una base de datos SQLite2, especifique 'sqlite2' => true en el array de parámetros al crear una instancia del Adapter Pdo_Sqlite.

  • Para conectarse a una base de datos SQLite en memoria, especifique 'dbname' => ':memory:' en el array de parámetros al crear una instancia del Adapter Pdo_Sqlite.

  • Las versiones antiguas del controlador SQLite para PHP no parecen admitir los comandos PRAGMA necesarios para garantizar que se usen nombres de columna cortos en los conjuntos de resultados. Si tiene problemas porque sus conjuntos de resultados se devuelven con claves de la forma "tablename.columnname" cuando hace una consulta de unión (join), entonces debería actualizar a la versión actual de PHP.

27.1.11.11. Firebird (Interbase)

  • Este Adapter usa la extensión PHP php_interbase.

  • Firebird (Interbase) no admite claves autoincrementales, por lo que debería especificar el nombre de una secuencia a lastInsertId() o lastSequenceId().

  • Actualmente la opción Zend_Db::CASE_FOLDING no es compatible con el adaptador Firebird (Interbase). Los identificadores sin comillas se devuelven automáticamente en mayúsculas.

  • El nombre del Adapter es ZendX_Db_Adapter_Firebird.

    Recuerde usar el parámetro adapterNamespace con el valor ZendX_Db_Adapter.

    Recomendamos actualizar el gds32.dll (o su equivalente en linux) incluido con PHP, a la misma versión del servidor. Para Firebird el equivalente de gds32.dll es fbclient.dll.

    Por defecto, todos los identificadores (nombres de tabla, campos) se devuelven en mayúsculas.