TigerZF
🌐Español

27.5. Zend_Db_Table

27.5.1. Introducción

La clase Zend_Db_Table es una interfaz orientada a objetos para tablas de bases de datos. Proporciona métodos para muchas operaciones comunes sobre tablas. La clase base es extensible, de modo que puede añadir lógica personalizada.

La solución Zend_Db_Table es una implementación del patrón Table Data Gateway. La solución también incluye una clase que implementa el patrón Row Data Gateway.

27.5.2. Usar Zend_Db_Table como una clase concreta

A partir de Zend Framework 1.9, puede instanciar Zend_Db_Table. Este beneficio añadido significa que no tiene que extender una clase base y configurarla para realizar operaciones simples tales como seleccionar, insertar, actualizar y eliminar en una única tabla. A continuación se muestra un ejemplo del caso de uso más simple.

Ejemplo 27.80. Declarar una clase de tabla usando solo el nombre en forma de cadena

Zend_Db_Table::setDefaultAdapter($dbAdapter);
$bugTable = new Zend_Db_Table('bug');

El ejemplo anterior representa el caso de uso más simple. Tenga en cuenta todas las opciones descritas más abajo para configurar tablas Zend_Db_Table. Si quiere poder usar el caso de uso concreto, además de las funciones de relación más complejas, consulte la documentación de Zend_Db_Table_Definition.

27.5.3. Definir una clase de tabla

Para cada tabla de su base de datos a la que quiera acceder, defina una clase que extienda Zend_Db_Table_Abstract.

27.5.3.1. Definir el nombre de la tabla y el esquema

Declare la tabla de la base de datos para la que se define esta clase, usando la variable protegida $_name. Se trata de una cadena, y debe contener el nombre de la tabla tal y como aparece escrito en la base de datos.

Ejemplo 27.81. Declarar una clase de tabla con nombre de tabla explícito

class Bugs extends Zend_Db_Table_Abstract
{
    protected $_name = 'bugs';
}

Si no especifica el nombre de la tabla, se usa por defecto el nombre de la clase. Si confía en este comportamiento por defecto, el nombre de la clase debe coincidir con el nombre de la tabla tal y como aparece en la base de datos.

Ejemplo 27.82. Declarar una clase de tabla con nombre de tabla implícito

class bugs extends Zend_Db_Table_Abstract
{
    // el nombre de la tabla coincide con el nombre de la clase
}

También puede declarar el esquema de la tabla, ya sea con la variable protegida $_schema, o con el esquema anteponiéndolo al nombre de la tabla en la propiedad $_name. Cualquier esquema especificado con la propiedad $_name tiene prioridad sobre un esquema especificado con la propiedad $_schema. En algunas marcas de RDBMS, el término para esquema es "base de datos" o "tablespace", pero se usa de forma similar.

Ejemplo 27.83. Declarar una clase de tabla con esquema

// Primera alternativa:
class Bugs extends Zend_Db_Table_Abstract
{
    protected $_schema = 'bug_db';
    protected $_name   = 'bugs';
}

// Segunda alternativa:
class Bugs extends Zend_Db_Table_Abstract
{
    protected $_name = 'bug_db.bugs';
}

// Si se especifican esquemas tanto en $_name como en $_schema, el que
// se especifica en $_name tiene prioridad:

class Bugs extends Zend_Db_Table_Abstract
{
    protected $_name   = 'bug_db.bugs';
    protected $_schema = 'ignored';
}

El esquema y los nombres de tabla también se pueden especificar mediante directivas de configuración del constructor, que sobrescriben cualquier valor por defecto especificado con las propiedades $_name y $_schema. Una especificación de esquema dada con la directiva name sobrescribe cualquier valor proporcionado con la opción schema.

Ejemplo 27.84. Declarar los nombres de tabla y esquema al instanciar

class Bugs extends Zend_Db_Table_Abstract
{
}

// Primera alternativa:

$tableBugs = new Bugs(array('name' => 'bugs', 'schema' => 'bug_db'));

// Segunda alternativa:

$tableBugs = new Bugs(array('name' => 'bug_db.bugs'));

// Si se especifican esquemas tanto en 'name' como en 'schema', el que
// se especifica en 'name' tiene prioridad:

$tableBugs = new Bugs(array('name' => 'bug_db.bugs',
                            'schema' => 'ignored'));

Si no especifica el nombre del esquema, se usa por defecto el esquema al que está conectada su instancia de adaptador de base de datos.

27.5.3.2. Definir la clave primaria de la tabla

Toda tabla debe tener una clave primaria. Puede declarar la columna de la clave primaria usando la variable protegida $_primary. Puede ser una cadena que nombre la única columna para la clave primaria, o bien un array de nombres de columnas si su clave primaria es compuesta.

Ejemplo 27.85. Ejemplo de especificación de la clave primaria

class Bugs extends Zend_Db_Table_Abstract
{
    protected $_name = 'bugs';
    protected $_primary = 'bug_id';
}

Si no especifica la clave primaria, Zend_Db_Table_Abstract intenta descubrir la clave primaria basándose en la información proporcionada por el método describeTable()´.

[Note] Nota

Toda clase de tabla debe saber qué columnas se pueden usar para identificar filas de forma unívoca. Si no se especifican columnas de clave primaria en la definición de la clase de tabla o en los argumentos del constructor de la tabla, ni se descubren en los metadatos de la tabla proporcionados por describeTable(), entonces la tabla no se puede usar con Zend_Db_Table.

27.5.3.3. Sobrescribir los métodos de configuración de la tabla

Cuando crea una instancia de una clase de tabla, el constructor invoca un conjunto de métodos protegidos que inicializan los metadatos de la tabla. Puede extender cualquiera de estos métodos para definir metadatos de forma explícita. Recuerde invocar el método del mismo nombre en la clase padre al final de su método.

Ejemplo 27.86. Ejemplo de sobrescritura del método _setupTableName()

class Bugs extends Zend_Db_Table_Abstract
{
    protected function _setupTableName()
    {
        $this->_name = 'bugs';
        parent::_setupTableName();
    }
}

Los métodos de configuración que puede sobrescribir son los siguientes:

  • _setupDatabaseAdapter() comprueba que se ha proporcionado un adaptador; obtiene un adaptador por defecto del registro si es necesario. Al sobrescribir este método, puede establecer un adaptador de base de datos desde alguna otra fuente.

  • _setupTableName() establece por defecto el nombre de la tabla como el nombre de la clase. Al sobrescribir este método, puede establecer el nombre de la tabla antes de que se ejecute este comportamiento por defecto.

  • _setupMetadata() establece el esquema si el nombre de la tabla contiene el patrón "schema.table"; invoca describeTable() para obtener información de metadatos; establece por defecto el array $_cols con las columnas reportadas por describeTable(). Al sobrescribir este método, puede especificar las columnas.

  • _setupPrimaryKey() establece por defecto las columnas de la clave primaria como las reportadas por describeTable(); comprueba que las columnas de la clave primaria están incluidas en el array $_cols. Al sobrescribir este método, puede especificar las columnas de la clave primaria.

27.5.3.4. Inicialización de la tabla

Si la lógica específica de la aplicación necesita inicializarse cuando se construye una clase de tabla, puede optar por mover sus tareas al método init(), el cual se invoca después de que se hayan procesado todos los metadatos de la tabla. Esto se recomienda frente al método __construct() si no necesita alterar los metadatos de ninguna forma programática.

Ejemplo 27.87. Ejemplo de uso del método init()

class Bugs extends Zend_Db_Table_Abstract
{
    protected $_observer;

    public function init()
    {
        $this->_observer = new MyObserverClass();
    }
}

27.5.4. Crear una instancia de una tabla

Antes de usar una clase de tabla, cree una instancia usando su constructor. El argumento del constructor es un array de opciones. La opción más importante para un constructor de tabla es la instancia del adaptador de base de datos, que representa una conexión activa a un RDBMS. Hay tres formas de especificar el adaptador de base de datos a una clase de tabla, y estas tres formas se describen a continuación:

27.5.4.1. Especificar un adaptador de base de datos

La primera forma de proporcionar un adaptador de base de datos a una clase de tabla es pasándolo como un objeto de tipo Zend_Db_Adapter_Abstract en el array de opciones, identificado por la clave 'db'.

Ejemplo 27.88. Ejemplo de construcción de una tabla usando un objeto adaptador

$db = Zend_Db::factory('PDO_MYSQL', $options);

$table = new Bugs(array('db' => $db));

27.5.4.2. Establecer un adaptador de base de datos por defecto

La segunda forma de proporcionar un adaptador de base de datos a una clase de tabla es declarando un objeto de tipo Zend_Db_Adapter_Abstract como adaptador de base de datos por defecto para todas las instancias posteriores de tablas en su aplicación. Puede hacer esto con el método estático Zend_Db_Table_Abstract::setDefaultAdapter(). El argumento es un objeto de tipo Zend_Db_Adapter_Abstract.

Ejemplo 27.89. Ejemplo de construcción de una tabla usando el adaptador por defecto

$db = Zend_Db::factory('PDO_MYSQL', $options);
Zend_Db_Table_Abstract::setDefaultAdapter($db);

// Más tarde...

$table = new Bugs();

Puede resultar conveniente crear el objeto adaptador de base de datos en un lugar central de su aplicación, como el bootstrap, y luego almacenarlo como el adaptador por defecto. Esto le proporciona un medio para asegurarse de que la instancia del adaptador es la misma en toda su aplicación. Sin embargo, establecer un adaptador por defecto se limita a una única instancia de adaptador.

27.5.4.3. Almacenar un adaptador de base de datos en el registro

La tercera forma de proporcionar un adaptador de base de datos a una clase de tabla es pasando una cadena en el array de opciones, también identificada por la clave 'db'. La cadena se usa como clave para la instancia estática Zend_Registry, donde la entrada en esa clave es un objeto de tipo Zend_Db_Adapter_Abstract.

Ejemplo 27.90. Ejemplo de construcción de una tabla usando una clave de registro

$db = Zend_Db::factory('PDO_MYSQL', $options);
Zend_Registry::set('my_db', $db);

// Más tarde...

$table = new Bugs(array('db' => 'my_db'));

Al igual que al establecer el adaptador por defecto, esto le proporciona el medio para asegurarse de que se usa la misma instancia de adaptador en toda su aplicación. Usar el registro es más flexible, porque puede almacenar más de una instancia de adaptador. Una instancia de adaptador dada es específica de una determinada marca de RDBMS e instancia de base de datos. Si su aplicación necesita acceder a múltiples bases de datos o incluso a múltiples marcas de base de datos, entonces necesita usar múltiples adaptadores.

27.5.5. Insertar filas en una tabla

Puede usar el objeto tabla para insertar filas en la tabla de base de datos en la que se basa el objeto tabla. Use el método insert() de su objeto tabla. El argumento es un array asociativo, que asigna nombres de columna a valores.

Ejemplo 27.91. Ejemplo de inserción en una tabla

$table = new Bugs();

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

$table->insert($data);

Por defecto, los valores en su array de datos se insertan como valores literales, usando parámetros. Si necesita que se traten como expresiones SQL, debe asegurarse de que se distinguen de las cadenas normales. Use un objeto de tipo Zend_Db_Expr para ello.

Ejemplo 27.92. Ejemplo de inserción de expresiones en una tabla

$table = new Bugs();

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

En los ejemplos de inserción de filas anteriores, se asume que la tabla tiene una clave primaria autoincremental. Este es el comportamiento por defecto de Zend_Db_Table_Abstract, pero también existen otros tipos de claves primarias. Las siguientes secciones describen cómo dar soporte a diferentes tipos de claves primarias.

27.5.5.1. Usar una tabla con clave autoincremental

Una clave primaria autoincremental genera un valor entero único para usted si omite la columna de clave primaria en su sentencia SQL INSERT.

En Zend_Db_Table_Abstract, si define la variable protegida $_sequence con el valor booleano TRUE, entonces la clase asume que la tabla tiene una clave primaria autoincremental.

Ejemplo 27.93. Ejemplo de declaración de una tabla con clave primaria autoincremental

class Bugs extends Zend_Db_Table_Abstract
{
    protected $_name = 'bugs';

    // This is the default in the Zend_Db_Table_Abstract class;
    // you do not need to define this.
    protected $_sequence = true;
}

MySQL, Microsoft SQL Server y SQLite son ejemplos de marcas de RDBMS que admiten claves primarias autoincrementales.

PostgreSQL tiene una notación SERIAL que define implícitamente una secuencia basada en el nombre de la tabla y la columna, y usa la secuencia para generar valores de clave para las nuevas filas. IBM DB2 tiene una notación IDENTITY que funciona de forma similar. Si usa cualquiera de estas notaciones, trate su clase Zend_Db_Table como si tuviera una columna autoincremental respecto a la declaración de la variable miembro $_sequence como TRUE.

27.5.5.2. Usar una tabla con una secuencia

Una secuencia es un objeto de base de datos que genera un valor único, que puede usarse como valor de clave primaria en una o más tablas de la base de datos.

Si define $_sequence como una cadena, entonces Zend_Db_Table_Abstract asume que la cadena nombra un objeto de secuencia en la base de datos. La secuencia se invoca para generar un nuevo valor, y este valor se usa en la operación INSERT.

Ejemplo 27.94. Ejemplo de declaración de una tabla con una secuencia

class Bugs extends Zend_Db_Table_Abstract
{
    protected $_name = 'bugs';

    protected $_sequence = 'bug_sequence';
}

Oracle, PostgreSQL e IBM DB2 son ejemplos de marcas de RDBMS que admiten objetos de secuencia en la base de datos.

PostgreSQL e IBM DB2 también tienen sintaxis que define secuencias de forma implícita y asociada a columnas. Si usa esta notación, trate la tabla como si tuviera una columna de clave autoincremental. Defina el nombre de la secuencia como una cadena solo en los casos en los que invocaría la secuencia de forma explícita para obtener el siguiente valor de clave.

27.5.5.3. Usar una tabla con una clave natural

Algunas tablas tienen una clave natural. Esto significa que la clave no la genera automáticamente la tabla ni una secuencia. Debe especificar el valor para la clave primaria en este caso.

Si define $_sequence con el valor booleano FALSE, entonces Zend_Db_Table_Abstract asume que la tabla tiene una clave primaria natural. Debe proporcionar valores para las columnas de la clave primaria en el array de datos del método insert(), o de lo contrario este método lanza una Zend_Db_Table_Exception.

Ejemplo 27.95. Ejemplo de declaración de una tabla con una clave natural

class BugStatus extends Zend_Db_Table_Abstract
{
    protected $_name = 'bug_status';

    protected $_sequence = false;
}

[Note] Nota

Todas las marcas de RDBMS admiten tablas con claves naturales. Ejemplos de tablas que a menudo se declaran con claves naturales son las tablas de consulta (lookup), las tablas de intersección en relaciones muchos a muchos, o la mayoría de las tablas con claves primarias compuestas.

27.5.6. Actualizar filas en una tabla

Puede actualizar filas en una tabla de base de datos usando el método update() de una clase de tabla. Este método toma dos argumentos: un array asociativo de columnas a cambiar y los nuevos valores que asignar a estas columnas; y una expresión SQL que se usa en una cláusula WHERE, como criterio para las filas a cambiar en la operación UPDATE.

Ejemplo 27.96. Ejemplo de actualización de filas en una tabla

$table = new Bugs();

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

$where = $table->getAdapter()->quoteInto('bug_id = ?', 1234);

$table->update($data, $where);

Dado que el método update() de la tabla delega en el método update() del adaptador de base de datos, el segundo argumento puede ser un array de expresiones SQL. Las expresiones se combinan como términos booleanos usando un operador AND.

[Note] Nota

Los valores e identificadores en la expresión SQL no se entrecomillan automáticamente. Si tiene valores o identificadores que requieran entrecomillado, usted es responsable de hacerlo. Use los métodos quote(), quoteInto(), y quoteIdentifier() del adaptador de base de datos.

27.5.7. Eliminar filas de una tabla

Puede eliminar filas de una tabla de base de datos usando el método delete(). Este método toma un argumento, que es una expresión SQL que se usa en una cláusula WHERE, como criterio para las filas a eliminar.

Ejemplo 27.97. Ejemplo de eliminación de filas de una tabla

$table = new Bugs();

$where = $table->getAdapter()->quoteInto('bug_id = ?', 1235);

$table->delete($where);

Dado que el método delete() de la tabla delega en el método delete() del adaptador de base de datos, el argumento también puede ser un array de expresiones SQL. Las expresiones se combinan como términos booleanos usando un operador AND.

[Note] Nota

Los valores e identificadores en la expresión SQL no se entrecomillan automáticamente. Si tiene valores o identificadores que requieran entrecomillado, usted es responsable de hacerlo. Use los métodos quote(), quoteInto(), y quoteIdentifier() del adaptador de base de datos.

27.5.8. Buscar filas por clave primaria

Puede consultar la tabla de base de datos en busca de filas que coincidan con valores específicos en la clave primaria, usando el método find(). El primer argumento de este método es bien un único valor, bien un array de valores a comparar con la clave primaria de la tabla.

Ejemplo 27.98. Ejemplo de búsqueda de filas por valores de clave primaria

$table = new Bugs();

// Find a single row
// Returns a Rowset
$rows = $table->find(1234);

// Find multiple rows
// Also returns a Rowset
$rows = $table->find(array(1234, 5678));

Si especifica un único valor, el método devuelve como máximo una fila, porque una clave primaria no puede tener valores duplicados y hay como máximo una fila en la tabla de base de datos que coincide con el valor que especifique. Si especifica varios valores en un array, el método devuelve como máximo tantas filas como el número de valores distintos que especifique.

El método find() puede devolver menos filas que el número de valores que especifique para la clave primaria, si algunos de los valores no coinciden con ninguna fila de la tabla de base de datos. El método incluso puede devolver cero filas. Dado que el número de filas devueltas es variable, el método find() devuelve un objeto de tipo Zend_Db_Table_Rowset_Abstract.

Si la clave primaria es compuesta, es decir, consta de varias columnas, puede especificar las columnas adicionales como argumentos adicionales al método find(). Debe proporcionar tantos argumentos como el número de columnas de la clave primaria de la tabla.

Para encontrar múltiples filas en una tabla con clave primaria compuesta, proporcione un array para cada uno de los argumentos. Todos estos arrays deben tener el mismo número de elementos. Los valores de cada array se forman en tuplas en orden; por ejemplo, el primer elemento de todos los argumentos array define el primer valor de clave primaria compuesta, luego los segundos elementos de todos los arrays definen el segundo valor de clave primaria compuesta, y así sucesivamente.

Ejemplo 27.99. Ejemplo de búsqueda de filas por valores de clave primaria compuesta

La llamada a find() siguiente, para encontrar coincidencias de múltiples filas, puede coincidir con dos filas en la base de datos. La primera fila debe tener el valor de clave primaria (1234, 'ABC'), y la segunda fila debe tener el valor de clave primaria (5678, 'DEF').

class BugsProducts extends Zend_Db_Table_Abstract
{
    protected $_name = 'bugs_products';
    protected $_primary = array('bug_id', 'product_id');
}

$table = new BugsProducts();

// Find a single row with a compound primary key
// Returns a Rowset
$rows = $table->find(1234, 'ABC');

// Find multiple rows with compound primary keys
// Also returns a Rowset
$rows = $table->find(array(1234, 5678), array('ABC', 'DEF'));

27.5.9. Consultar un conjunto de filas

27.5.9.1. API de Select

[Warning] Advertencia

La API para operaciones de recuperación se ha sustituido para permitir que un objeto Zend_Db_Table_Select modifique la consulta. Sin embargo, el uso obsoleto de los métodos fetchRow() y fetchAll() seguirá funcionando sin modificación.

Las siguientes sentencias son todas legales y funcionalmente idénticas, sin embargo se recomienda actualizar su código para aprovechar el nuevo uso siempre que sea posible.

/**
 * Fetching a rowset
 */
$rows = $table->fetchAll(
    'bug_status = "NEW"',
    'bug_id ASC',
    10,
    0
    );
$rows = $table->fetchAll(
    $table->select()
        ->where('bug_status = ?', 'NEW')
        ->order('bug_id ASC')
        ->limit(10, 0)
    );
// or with binding
$rows = $table->fetchAll(
    $table->select()
        ->where('bug_status = :status')
        ->bind(array(':status'=>'NEW')
        ->order('bug_id ASC')
        ->limit(10, 0)
    );

/**
 * Fetching a single row
 */
$row = $table->fetchRow(
    'bug_status = "NEW"',
    'bug_id ASC'
    );
$row = $table->fetchRow(
    $table->select()
        ->where('bug_status = ?', 'NEW')
        ->order('bug_id ASC')
    );
// or with binding
$row = $table->fetchRow(
    $table->select()
        ->where('bug_status = :status')
        ->bind(array(':status'=>'NEW')
        ->order('bug_id ASC')
    );

El objeto Zend_Db_Table_Select es una extensión del objeto Zend_Db_Select que aplica restricciones específicas a una consulta. Las mejoras y restricciones son:

  • Puede optar por devolver un subconjunto de columnas dentro de una consulta fetchRow o fetchAll. Esto puede proporcionar beneficios de optimización cuando no es deseable devolver un gran conjunto de resultados para todas las columnas.

  • Puede especificar columnas que evalúen expresiones dentro de la tabla seleccionada. Sin embargo, esto significará que la fila o rowset devuelto será de readOnly y no se podrá usar para operaciones save(). Un Zend_Db_Table_Row con estado readOnly lanzará una excepción si se intenta una operación save().

  • Puede permitir cláusulas JOIN en una consulta select para permitir búsquedas multitabla.

  • No puede especificar columnas de una tabla unida (JOINed) para que se devuelvan en una fila o rowset. Hacerlo desencadenará un error de PHP. Esto se hizo para asegurar que se mantenga la integridad de Zend_Db_Table. Es decir, un Zend_Db_Table_Row solo debería referenciar columnas derivadas de su tabla padre.

Ejemplo 27.100. Uso simple

$table = new Bugs();

$select = $table->select();
$select->where('bug_status = ?', 'NEW');

$rows = $table->fetchAll($select);

Las interfaces fluidas se implementan en todo el componente, por lo que esto se puede reescribir en una forma más abreviada.

Ejemplo 27.101. Ejemplo de interfaz fluida

$table = new Bugs();

$rows =
    $table->fetchAll($table->select()->where('bug_status = ?', 'NEW'));

27.5.9.2. Recuperar un rowset

Puede consultar un conjunto de filas usando cualquier criterio distinto de los valores de la clave primaria, usando el método fetchAll() de la clase de tabla. Este método devuelve un objeto de tipo Zend_Db_Table_Rowset_Abstract.

Ejemplo 27.102. Ejemplo de búsqueda de filas mediante una expresión

$table = new Bugs();

$select = $table->select()->where('bug_status = ?', 'NEW');

$rows = $table->fetchAll($select);

También puede pasar criterios de ordenación en una cláusula ORDER BY, así como valores enteros de cuenta y desplazamiento, usados para que la consulta devuelva un subconjunto específico de filas. Estos valores se usan en una cláusula LIMIT, o en lógica equivalente para marcas de RDBMS que no admiten la sintaxis LIMIT.

Ejemplo 27.103. Ejemplo de búsqueda de filas mediante una expresión

$table = new Bugs();

$order  = 'bug_id';

// Return the 21st through 30th rows
$count  = 10;
$offset = 20;

$select = $table->select()->where('bug_status = ?', 'NEW')
                          ->order($order)
                          ->limit($count, $offset);

$rows = $table->fetchAll($select);

Todos los argumentos anteriores son opcionales. Si omite la cláusula ORDER, el conjunto de resultados incluye filas de la tabla en un orden impredecible. Si no se establece ninguna cláusula LIMIT, recupera todas las filas de la tabla que coincidan con la cláusula WHERE.

27.5.9.3. Uso avanzado

Para solicitudes más específicas y optimizadas, puede que desee limitar el número de columnas devueltas en una fila o rowset. Esto se puede lograr pasando una cláusula FROM al objeto select. El primer argumento de la cláusula FROM es idéntico al de un objeto Zend_Db_Select, con la adición de poder pasar una instancia de Zend_Db_Table_Abstract y hacer que determine automáticamente el nombre de la tabla.

Ejemplo 27.104. Recuperar columnas específicas

$table = new Bugs();

$select = $table->select();
$select->from($table, array('bug_id', 'bug_description'))
       ->where('bug_status = ?', 'NEW');

$rows = $table->fetchAll($select);

[Important] Importante

El rowset contiene filas que siguen siendo 'válidas': simplemente contienen un subconjunto de las columnas de una tabla. Si se invoca un método save() en una fila parcial, solo se modificarán los campos disponibles.

También puede especificar expresiones dentro de una cláusula FROM y hacer que estas se devuelvan como una fila o rowset readOnly. En este ejemplo devolveremos filas de la tabla bugs que muestran un agregado del número de bugs nuevos reportados por individuos. Observe la cláusula GROUP. La columna 'count' se pondrá a disposición de la fila para su evaluación y se puede acceder a ella como si fuera parte del esquema.

Ejemplo 27.105. Recuperar expresiones como columnas

$table = new Bugs();

$select = $table->select();
$select->from($table,
              array('COUNT(reported_by) as `count`', 'reported_by'))
       ->where('bug_status = ?', 'NEW')
       ->group('reported_by');

$rows = $table->fetchAll($select);

También puede usar una tabla de consulta (lookup) como parte de su consulta para refinar aún más sus operaciones de recuperación. En este ejemplo, la tabla accounts se consulta como parte de una búsqueda de todos los bugs nuevos reportados por 'Bob'.

Ejemplo 27.106. Usar una tabla de consulta para refinar los resultados de fetchAll()

$table = new Bugs();

// retrieve with from part set, important when joining
$select = $table->select(Zend_Db_Table::SELECT_WITH_FROM_PART);
$select->setIntegrityCheck(false)
       ->where('bug_status = ?', 'NEW')
       ->join('accounts', 'accounts.account_name = bugs.reported_by')
       ->where('accounts.account_name = ?', 'Bob');

$rows = $table->fetchAll($select);

Zend_Db_Table_Select se usa principalmente para restringir y validar, de modo que pueda aplicar el criterio para una consulta SELECT legal. Sin embargo, puede haber ciertos casos en los que necesite la flexibilidad del componente Zend_Db_Table_Row y no necesite una fila escribible o eliminable. Para este caso de uso específico, es posible recuperar una fila o rowset pasando un valor FALSE a setIntegrityCheck(). La fila o rowset resultante se devolverá como una fila 'bloqueada' (lo que significa que save(), delete() y cualquier método de establecimiento de campo lanzarán una excepción).

Ejemplo 27.107. Eliminar la comprobación de integridad en Zend_Db_Table_Select para permitir filas JOINed

$table = new Bugs();

$select = $table->select(Zend_Db_Table::SELECT_WITH_FROM_PART)
                ->setIntegrityCheck(false);
$select->where('bug_status = ?', 'NEW')
       ->join('accounts',
              'accounts.account_name = bugs.reported_by',
              'account_name')
       ->where('accounts.account_name = ?', 'Bob');

$rows = $table->fetchAll($select);

27.5.10. Consultar una única fila

Puede consultar una única fila usando criterios similares a los del método fetchAll().

Ejemplo 27.108. Ejemplo de búsqueda de una única fila mediante una expresión

$table = new Bugs();

$select  = $table->select()->where('bug_status = ?', 'NEW')
                           ->order('bug_id');

$row = $table->fetchRow($select);

Este método devuelve un objeto de tipo Zend_Db_Table_Row_Abstract. Si el criterio de búsqueda que especificó no coincide con ninguna fila de la tabla de base de datos, entonces fetchRow() devuelve el valor NULL de PHP.

27.5.11. Recuperar información de metadatos de la tabla

La clase Zend_Db_Table_Abstract proporciona cierta información sobre sus metadatos. El método info() devuelve una estructura de array con información sobre la tabla, sus columnas y clave primaria, y otros metadatos.

Ejemplo 27.109. Ejemplo de obtención del nombre de la tabla

$table = new Bugs();

$info = $table->info();

echo "The table name is " . $info['name'] . "\n";

Las claves del array devuelto por el método info() se describen a continuación:

  • name => el nombre de la tabla.

  • cols => un array, con los nombres de las columnas de la tabla.

  • primary => un array, con los nombres de las columnas de la clave primaria.

  • metadata => un array asociativo, que asigna nombres de columna a información sobre las columnas. Esta es la información devuelta por el método describeTable().

  • rowClass => el nombre de la clase concreta usada para los objetos Row devueltos por los métodos de esta instancia de tabla. Por defecto es Zend_Db_Table_Row.

  • rowsetClass => el nombre de la clase concreta usada para los objetos Rowset devueltos por los métodos de esta instancia de tabla. Por defecto es Zend_Db_Table_Rowset.

  • referenceMap => un array asociativo, con información sobre las referencias de esta tabla a cualquier tabla padre. Consulte este capítulo.

  • dependentTables => un array de nombres de clase de las tablas que hacen referencia a esta tabla. Consulte este capítulo.

  • schema => el nombre del esquema (o base de datos o tablespace) de esta tabla.

27.5.12. Almacenar en caché los metadatos de la tabla

Por defecto, Zend_Db_Table_Abstract consulta la base de datos subyacente en busca de metadatos de la tabla cada vez que se necesitan esos datos para realizar operaciones de tabla. El objeto tabla obtiene los metadatos de la tabla desde la base de datos usando el método describeTable() del adaptador. Las operaciones que requieren esta introspección incluyen:

  • insert()

  • find()

  • info()

En algunas circunstancias, particularmente cuando se instancian muchos objetos de tabla contra la misma tabla de base de datos, consultar la base de datos para obtener los metadatos de la tabla en cada instancia puede resultar indeseable desde el punto de vista del rendimiento. En tales casos, los usuarios pueden beneficiarse de almacenar en caché los metadatos de la tabla recuperados de la base de datos.

Hay dos formas principales en las que un usuario puede aprovechar el almacenamiento en caché de metadatos de la tabla:

  • Invocar Zend_Db_Table_Abstract::setDefaultMetadataCache() - Esto permite a un desarrollador establecer una vez el objeto de caché por defecto a usar en todas las clases de tabla.

  • Configurar Zend_Db_Table_Abstract::__construct() - Esto permite a un desarrollador establecer el objeto de caché a usar para una instancia particular de clase de tabla.

En ambos casos, la especificación de caché debe ser bien NULL (es decir, no usar caché), bien una instancia de Zend_Cache_Core. Los métodos se pueden usar conjuntamente cuando sea deseable tener tanto una caché de metadatos por defecto como la capacidad de cambiar la caché para objetos de tabla individuales.

Ejemplo 27.110. Usar una caché de metadatos por defecto para todos los objetos de tabla

El siguiente código muestra cómo establecer una caché de metadatos por defecto para usarla con todos los objetos de tabla:

// First, set up the Cache
$frontendOptions = array(
    'automatic_serialization' => true
    );

$backendOptions  = array(
    'cache_dir'                => 'cacheDir'
    );

$cache = Zend_Cache::factory('Core',
                             'File',
                             $frontendOptions,
                             $backendOptions);

// Next, set the cache to be used with all table objects
Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);

// A table class is also needed
class Bugs extends Zend_Db_Table_Abstract
{
    // ...
}

// Each instance of Bugs now uses the default metadata cache
$bugs = new Bugs();

Ejemplo 27.111. Usar una caché de metadatos para un objeto de tabla específico

El siguiente código muestra cómo establecer una caché de metadatos para una instancia de objeto de tabla específica:

// First, set up the Cache
$frontendOptions = array(
    'automatic_serialization' => true
    );

$backendOptions  = array(
    'cache_dir'                => 'cacheDir'
    );

$cache = Zend_Cache::factory('Core',
                             'File',
                             $frontendOptions,
                             $backendOptions);

// A table class is also needed
class Bugs extends Zend_Db_Table_Abstract
{
    // ...
}

// Configure an instance upon instantiation
$bugs = new Bugs(array('metadataCache' => $cache));

[Note] Serialización automática con el frontend de caché

Dado que la información devuelta por el método describeTable() del adaptador es un array, asegúrese de que la opción automatic_serialization se establece en TRUE para el frontend Zend_Cache_Core.

Aunque los ejemplos anteriores usan Zend_Cache_Backend_File, los desarrolladores pueden usar el backend de caché que resulte apropiado para la situación. Consulte Zend_Cache para más información.

27.5.12.1. Codificar los metadatos de la tabla de forma fija

Para llevar el almacenamiento en caché de metadatos un paso más allá, también puede optar por codificar los metadatos de forma fija (hardcode). En este caso particular, sin embargo, cualquier cambio en el esquema de la tabla requerirá un cambio en su código. Por ello, solo se recomienda para quienes estén optimizando para el uso en producción.

La estructura de metadatos es la siguiente:

protected $_metadata = array(
    '<column_name>' => array(
        'SCHEMA_NAME'      => <string>,
        'TABLE_NAME'       => <string>,
        'COLUMN_NAME'      => <string>,
        'COLUMN_POSITION'  => <int>,
        'DATA_TYPE'        => <string>,
        'DEFAULT'          => NULL|<value>,
        'NULLABLE'         => <bool>,
        'LENGTH'           => <string - length>,
        'SCALE'            => NULL|<value>,
        'PRECISION'        => NULL|<value>,
        'UNSIGNED'         => NULL|<bool>,
        'PRIMARY'          => <bool>,
        'PRIMARY_POSITION' => <int>,
        'IDENTITY'         => <bool>,
    ),
    // additional columns...
);

Una forma fácil de obtener los valores adecuados es usar la caché de metadatos, y luego deserializar los valores almacenados en la caché.

Puede desactivar esta optimización desactivando el indicador metadataCacheInClass:

// At instantiation:
$bugs = new Bugs(array('metadataCacheInClass' => false));

// Or later:
$bugs->setMetadataCacheInClass(false);

El indicador está activado por defecto, lo que asegura que el array $_metadata solo se rellene una vez por instancia.

27.5.13. Personalizar y extender una clase de tabla

27.5.13.1. Usar clases Row o Rowset personalizadas

Por defecto, los métodos de la clase Table devuelven un Rowset en instancias de la clase concreta Zend_Db_Table_Rowset, y los Rowsets contienen una colección de instancias de la clase concreta Zend_Db_Table_Row. Puede especificar una clase alternativa para usar en cualquiera de ellas, pero deben ser clases que extiendan Zend_Db_Table_Rowset_Abstract y Zend_Db_Table_Row_Abstract, respectivamente.

Puede especificar las clases Row y Rowset usando el array de opciones del constructor de la tabla, en las claves 'rowClass' y 'rowsetClass' respectivamente. Especifique los nombres de las clases usando cadenas.

Ejemplo 27.112. Ejemplo de especificación de las clases Row y Rowset

class My_Row extends Zend_Db_Table_Row_Abstract
{
    ...
}

class My_Rowset extends Zend_Db_Table_Rowset_Abstract
{
    ...
}

$table = new Bugs(
    array(
        'rowClass'    => 'My_Row',
        'rowsetClass' => 'My_Rowset'
    )
);

$where = $table->getAdapter()->quoteInto('bug_status = ?', 'NEW')

// Returns an object of type My_Rowset,
// containing an array of objects of type My_Row.
$rows = $table->fetchAll($where);

Puede cambiar las clases especificándolas con los métodos setRowClass() y setRowsetClass(). Esto se aplica a las filas y rowsets creados posteriormente; no cambia la clase de ninguna fila o rowset que haya creado previamente.

Ejemplo 27.113. Ejemplo de cambio de las clases Row y Rowset

$table = new Bugs();

$where = $table->getAdapter()->quoteInto('bug_status = ?', 'NEW')

// Returns an object of type Zend_Db_Table_Rowset
// containing an array of objects of type Zend_Db_Table_Row.
$rowsStandard = $table->fetchAll($where);

$table->setRowClass('My_Row');
$table->setRowsetClass('My_Rowset');

// Returns an object of type My_Rowset,
// containing an array of objects of type My_Row.
$rowsCustom = $table->fetchAll($where);

// The $rowsStandard object still exists, and it is unchanged.

Para más información sobre las clases Row y Rowset, consulte este capítulo y este otro.

27.5.13.2. Definir lógica personalizada para insertar, actualizar y eliminar

Puede sobrescribir los métodos insert() y update() en su clase de tabla. Esto le da la oportunidad de implementar código personalizado que se ejecuta antes de realizar la operación de base de datos. Asegúrese de invocar el método de la clase padre cuando haya terminado.

Ejemplo 27.114. Lógica personalizada para gestionar marcas de tiempo

class Bugs extends Zend_Db_Table_Abstract
{
    protected $_name = 'bugs';

    public function insert(array $data)
    {
        // add a timestamp
        if (empty($data['created_on'])) {
            $data['created_on'] = time();
        }
        return parent::insert($data);
    }

    public function update(array $data, $where)
    {
        // add a timestamp
        if (empty($data['updated_on'])) {
            $data['updated_on'] = time();
        }
        return parent::update($data, $where);
    }
}

También puede sobrescribir el método delete().

27.5.13.3. Definir métodos de búsqueda personalizados en Zend_Db_Table

Puede implementar métodos de consulta personalizados en su clase de tabla, si tiene necesidad frecuente de realizar consultas contra esta tabla con criterios específicos. La mayoría de las consultas se pueden escribir usando fetchAll(), pero esto requiere que duplique código para formar las condiciones de la consulta si necesita ejecutar la consulta en varios lugares de su aplicación. Por lo tanto, puede resultar conveniente implementar un método en la clase de tabla para realizar consultas de uso frecuente contra esta tabla.

Ejemplo 27.115. Método personalizado para buscar bugs por estado

class Bugs extends Zend_Db_Table_Abstract
{
    protected $_name = 'bugs';

    public function findByStatus($status)
    {
        $where = $this->getAdapter()->quoteInto('bug_status = ?', $status);
        return $this->fetchAll($where, 'bug_id');
    }
}

27.5.13.4. Definir inflexión en Zend_Db_Table

Algunas personas prefieren que el nombre de la clase de tabla coincida con un nombre de tabla en el RDBMS mediante una transformación de cadena llamada inflexión.

Por ejemplo, si el nombre de su clase de tabla es "BugsProducts", coincidiría con la tabla física en la base de datos llamada "bugs_products", si omite la declaración explícita de la propiedad de clase $_name. En este mapeo de inflexión, el nombre de la clase escrito en formato "CamelCase" se transformaría a minúsculas, y las palabras se separarían con un guion bajo.

Puede especificar el nombre de la tabla de base de datos independientemente del nombre de la clase declarando el nombre de la tabla con la propiedad de clase $_name en cada una de sus clases de tabla.

Zend_Db_Table_Abstract no realiza ninguna inflexión para mapear el nombre de la clase al nombre de la tabla. Si omite la declaración de $_name en su clase de tabla, la clase se mapea a una tabla de base de datos que coincide exactamente con el nombre de la clase.

Es inapropiado transformar identificadores de la base de datos, porque esto puede producir ambigüedad o hacer que algunos identificadores sean inaccesibles. Usar los identificadores SQL exactamente tal y como aparecen en la base de datos hace que Zend_Db_Table_Abstract sea a la vez más simple y más flexible.

Si prefiere usar inflexión, entonces debe implementar la transformación usted mismo, sobrescribiendo el método _setupTableName() en sus clases de tabla. Una forma de hacerlo es definir una clase abstracta que extienda Zend_Db_Table_Abstract, y luego hacer que el resto de sus tablas extiendan su nueva clase abstracta.

Ejemplo 27.116. Ejemplo de una clase de tabla abstracta que implementa inflexión

abstract class MyAbstractTable extends Zend_Db_Table_Abstract
{
    protected function _setupTableName()
    {
        if (!$this->_name) {
            $this->_name = myCustomInflector(get_class($this));
        }
        parent::_setupTableName();
    }
}

class BugsProducts extends MyAbstractTable
{
}

Usted es responsable de escribir las funciones para realizar la transformación de inflexión. Zend Framework no proporciona tal función.