Las tablas tienen relaciones entre sí en una base de datos relacional. Una entidad en una tabla puede estar vinculada a una o más entidades en otra tabla mediante el uso de restricciones de integridad referencial definidas en el esquema de la base de datos.
La clase Zend_Db_Table_Row tiene métodos para consultar filas
relacionadas en otras tablas.
Defina clases para cada una de sus tablas, extendiendo la clase abstracta
Zend_Db_Table_Abstract, tal como se describe en
este capítulo. Vea también
este capítulo para una descripción
de la base de datos de ejemplo para la cual está diseñado el siguiente código de ejemplo.
A continuación se muestran las definiciones de clase de PHP para estas tablas:
class Accounts extends Zend_Db_Table_Abstract
{
protected $_name = 'accounts';
protected $_dependentTables = array('Bugs');
}
class Products extends Zend_Db_Table_Abstract
{
protected $_name = 'products';
protected $_dependentTables = array('BugsProducts');
}
class Bugs extends Zend_Db_Table_Abstract
{
protected $_name = 'bugs';
protected $_dependentTables = array('BugsProducts');
protected $_referenceMap = array(
'Reporter' => array(
'columns' => 'reported_by',
'refTableClass' => 'Accounts',
'refColumns' => 'account_name'
),
'Engineer' => array(
'columns' => 'assigned_to',
'refTableClass' => 'Accounts',
'refColumns' => 'account_name'
),
'Verifier' => array(
'columns' => array('verified_by'),
'refTableClass' => 'Accounts',
'refColumns' => array('account_name')
)
);
}
class BugsProducts extends Zend_Db_Table_Abstract
{
protected $_name = 'bugs_products';
protected $_referenceMap = array(
'Bug' => array(
'columns' => array('bug_id'),
'refTableClass' => 'Bugs',
'refColumns' => array('bug_id')
),
'Product' => array(
'columns' => array('product_id'),
'refTableClass' => 'Products',
'refColumns' => array('product_id')
)
);
}
Si utiliza Zend_Db_Table para emular operaciones
en cascada de UPDATE y DELETE,
declare el array $_dependentTables en la clase de la
tabla padre. Enumere el nombre de clase de cada tabla dependiente. Use el nombre de clase, no el
nombre físico de la tabla SQL.
![]() |
Nota |
|---|---|
Omita la declaración de |
Declare el array $_referenceMap en la clase de cada tabla
dependiente. Este es un array asociativo de "reglas" de referencia. Una regla de referencia identifica
cuál es la tabla padre en la relación, y también enumera qué columnas de la
tabla dependiente hacen referencia a qué columnas de la tabla padre.
La clave de la regla es una cadena que se usa como índice del array
$_referenceMap. Esta clave de regla se utiliza para identificar cada relación de
referencia. Elija un nombre descriptivo para esta clave de regla. Es recomendable usar una cadena que
pueda formar parte de un nombre de método PHP, como verá más adelante.
En el código PHP del ejemplo anterior, las claves de regla en la clase de la tabla Bugs son: 'Reporter', 'Engineer', 'Verifier' y 'Product'.
El valor de cada entrada de regla en el array $_referenceMap es también un
array asociativo. Los elementos de esta entrada de regla se describen a continuación:
-
columns => Una cadena o un array de cadenas que nombran las columnas de clave foránea en la tabla dependiente.
Es común que sea una sola columna, pero algunas tablas tienen claves de múltiples columnas.
-
refTableClass => El nombre de clase de la tabla padre. Use el nombre de clase, no el nombre físico de la tabla SQL.
Es común que una tabla dependiente tenga solo una referencia a su tabla padre, pero algunas tablas tienen múltiples referencias a la misma tabla padre. En la base de datos de ejemplo, hay una referencia de la tabla bugs a la tabla products, pero tres referencias de la tabla bugs a la tabla accounts. Coloque cada referencia en una entrada separada del array
$_referenceMap. -
refColumns => Una cadena o un array de cadenas que nombran las columnas de clave primaria en la tabla padre.
Es común que sea una sola columna, pero algunas tablas tienen claves de múltiples columnas. Si la referencia usa una clave de múltiples columnas, el orden de las columnas en la entrada 'columns' debe coincidir con el orden de las columnas en la entrada 'refColumns'.
![[Note]](images/note.png)
Nota Se recomienda que el elemento refColumns se declare siempre, ya que las operaciones en cascada no funcionarán a menos que lo haga.
onDelete => La regla para una acción a ejecutar si se elimina una fila en la tabla padre. Consulte este capítulo para más información.
onUpdate => La regla para una acción a ejecutar si se actualizan los valores de las columnas de clave primaria en la tabla padre. Consulte este capítulo para más información.
Si tiene un objeto Row como resultado de una consulta sobre una tabla padre, puede obtener filas de tablas dependientes que hacen referencia a la fila actual. Use el método:
$row->findDependentRowset($table, [$rule]);
Este método devuelve un objeto Zend_Db_Table_Rowset_Abstract,
que contiene un conjunto de filas de la tabla dependiente $table que hacen referencia
a la fila identificada por el objeto $row.
El primer argumento $table puede ser una cadena que especifica la
tabla dependiente por su nombre de clase. También puede especificar la tabla dependiente usando un
objeto de esa clase de tabla.
Ejemplo 27.145. Obtención de un rowset dependiente
Este ejemplo muestra cómo obtener un objeto Row de la tabla Accounts, y encontrar los Bugs reportados por esa cuenta.
$accountsTable = new Accounts();
$accountsRowset = $accountsTable->find(1234);
$user1234 = $accountsRowset->current();
$bugsReportedByUser = $user1234->findDependentRowset('Bugs');
El segundo argumento $rule es opcional. Es una cadena que nombra la
clave de regla en el array $_referenceMap de la clase de la tabla dependiente. Si
no especifica una regla, se utiliza la primera regla del array que hace referencia a la tabla padre. Si
necesita usar una regla distinta de la primera, debe especificar la clave.
En el código de ejemplo anterior, no se especifica la clave de regla, por lo que la regla utilizada por defecto es la primera que coincide con la tabla padre. Esta es la regla 'Reporter'.
Ejemplo 27.146. Obtención de un rowset dependiente mediante una regla específica
Este ejemplo muestra cómo obtener un objeto Row de la tabla Accounts, y encontrar los Bugs asignados para ser corregidos por el usuario de esa cuenta. La cadena de la clave de regla que corresponde a esta relación de referencia en este ejemplo es 'Engineer'.
$accountsTable = new Accounts();
$accountsRowset = $accountsTable->find(1234);
$user1234 = $accountsRowset->current();
$bugsAssignedToUser = $user1234->findDependentRowset('Bugs', 'Engineer');
También puede agregar criterios, ordenamiento y límites a sus relaciones usando el objeto select de la fila padre.
Ejemplo 27.147. Obtención de un rowset dependiente usando un Zend_Db_Table_Select
Este ejemplo muestra cómo obtener un objeto Row de la tabla Accounts, y encontrar los Bugs asignados para ser corregidos por el usuario de esa cuenta, limitados solo a 3 filas y ordenados por nombre.
$accountsTable = new Accounts();
$accountsRowset = $accountsTable->find(1234);
$user1234 = $accountsRowset->current();
$select = $accountsTable->select()->order('name ASC')
->limit(3);
$bugsAssignedToUser = $user1234->findDependentRowset('Bugs',
'Engineer',
$select);
Alternativamente, puede consultar filas de una tabla dependiente usando un mecanismo especial
llamado "método mágico". Zend_Db_Table_Row_Abstract invoca el
método: findDependentRowset('<TableClass>',
'<Rule>') si invoca un método en el objeto Row que coincida
con alguno de los siguientes patrones:
$row->find<TableClass>()
$row->find<TableClass>By<Rule>()
En los patrones anteriores, <TableClass> y <Rule> son cadenas que corresponden al nombre de clase de la tabla dependiente, y a la clave de regla de la tabla dependiente que hace referencia a la tabla padre.
![]() |
Nota |
|---|---|
Algunos frameworks de aplicaciones, como Ruby on Rails, utilizan un mecanismo llamado
"inflexión" para permitir que la ortografía de los identificadores cambie según el uso. Por
simplicidad, |
Ejemplo 27.148. Obtención de rowsets dependientes usando el método mágico
Este ejemplo muestra cómo encontrar rowsets dependientes equivalentes a los de los ejemplos anteriores. En este caso, la aplicación usa la invocación del método mágico en lugar de especificar la tabla y la regla como cadenas.
$accountsTable = new Accounts(); $accountsRowset = $accountsTable->find(1234); $user1234 = $accountsRowset->current(); // Use the default reference rule $bugsReportedBy = $user1234->findBugs(); // Specify the reference rule $bugsAssignedTo = $user1234->findBugsByEngineer();
Si tiene un objeto Row como resultado de una consulta sobre una tabla dependiente, puede obtener la fila en el padre a la que hace referencia la fila dependiente. Use el método:
$row->findParentRow($table, [$rule]);
Siempre debe existir exactamente una fila en la tabla padre referenciada por una fila dependiente, por lo tanto este método devuelve un objeto Row, no un objeto Rowset.
El primer argumento $table puede ser una cadena que especifica la tabla
padre por su nombre de clase. También puede especificar la tabla padre usando un objeto de
esa clase de tabla.
Ejemplo 27.149. Obtención de la fila padre
Este ejemplo muestra cómo obtener un objeto Row de la tabla Bugs (por ejemplo, uno de esos bugs con estado 'NEW'), y encontrar la fila en la tabla Accounts del usuario que reportó el bug.
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->fetchAll(array('bug_status = ?' => 'NEW'));
$bug1 = $bugsRowset->current();
$reporter = $bug1->findParentRow('Accounts');
El segundo argumento $rule es opcional. Es una cadena que nombra la
clave de regla en el array $_referenceMap de la clase de la tabla dependiente. Si
no especifica una regla, se utiliza la primera regla del array que hace referencia a la tabla padre. Si
necesita usar una regla distinta de la primera, debe especificar la clave.
En el ejemplo anterior, no se especifica la clave de regla, por lo que la regla utilizada por defecto es la primera que coincide con la tabla padre. Esta es la regla 'Reporter'.
Ejemplo 27.150. Obtención de una fila padre mediante una regla específica
Este ejemplo muestra cómo obtener un objeto Row de la tabla Bugs, y encontrar la cuenta del ingeniero asignado para corregir ese bug. La cadena de la clave de regla que corresponde a esta relación de referencia en este ejemplo es 'Engineer'.
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->fetchAll(array('bug_status = ?', 'NEW'));
$bug1 = $bugsRowset->current();
$engineer = $bug1->findParentRow('Accounts', 'Engineer');
Alternativamente, puede consultar filas de una tabla padre usando un "método mágico".
Zend_Db_Table_Row_Abstract invoca el método:
findParentRow('<TableClass>', '<Rule>') si
invoca un método en el objeto Row que coincida con alguno de los siguientes patrones:
$row->findParent<TableClass>([Zend_Db_Table_Select $select])
$row->findParent<TableClass>By<Rule>([Zend_Db_Table_Select $select])
En los patrones anteriores, <TableClass> y <Rule> son cadenas que corresponden al nombre de clase de la tabla padre, y a la clave de regla de la tabla dependiente que hace referencia a la tabla padre.
![]() |
Nota |
|---|---|
La identidad de la tabla y la clave de regla nombradas en la llamada al método deben coincidir con la ortografía de la clase y la clave de regla exactamente. |
Ejemplo 27.151. Obtención de la fila padre usando el método mágico
Este ejemplo muestra cómo encontrar filas padre equivalentes a las de los ejemplos anteriores. En este caso, la aplicación usa la invocación del método mágico en lugar de especificar la tabla y la regla como cadenas.
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->fetchAll(array('bug_status = ?', 'NEW'));
$bug1 = $bugsRowset->current();
// Use the default reference rule
$reporter = $bug1->findParentAccounts();
// Specify the reference rule
$engineer = $bug1->findParentAccountsByEngineer();
Si tiene un objeto Row como resultado de una consulta sobre una tabla en una relación de muchos a muchos (a efectos del ejemplo, llame a esta la tabla "origen"), puede obtener las filas correspondientes en la otra tabla (llame a esta la tabla "destino") a través de una tabla de intersección. Use el método:
$row->findManyToManyRowset($table,
$intersectionTable,
[$rule1,
[$rule2,
[Zend_Db_Table_Select $select]
]
]);
Este método devuelve un Zend_Db_Table_Rowset_Abstract que contiene
filas de la tabla $table, que satisfacen la relación de muchos a muchos.
El objeto Row actual $row de la tabla origen se usa para encontrar
filas en la tabla de intersección, que se une a la tabla destino.
El primer argumento $table puede ser una cadena que especifica la
tabla destino en la relación de muchos a muchos por su nombre de clase. También puede
especificar la tabla destino usando un objeto de esa clase de tabla.
El segundo argumento $intersectionTable puede ser una cadena que especifica
la tabla de intersección entre las dos tablas en la relación de muchos a muchos por
su nombre de clase. También puede especificar la tabla de intersección usando un objeto de esa
clase de tabla.
Ejemplo 27.152. Obtención de un rowset con el método de muchos a muchos
Este ejemplo muestra cómo obtener un objeto Row de la tabla origen Bugs, y encontrar filas de la tabla destino Products, que representan los productos relacionados con ese bug.
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->find(1234);
$bug1234 = $bugsRowset->current();
$productsRowset = $bug1234->findManyToManyRowset('Products',
'BugsProducts');
El tercer y cuarto argumento $rule1 y $rule2
son opcionales. Son cadenas que nombran las claves de regla en el array
$_referenceMap de la tabla de intersección.
La clave $rule1 nombra la regla para la relación desde la
tabla de intersección hacia la tabla origen. En este ejemplo, esta es la relación de
BugsProducts a Bugs.
La clave $rule2 nombra la regla para la relación desde la
tabla de intersección hacia la tabla destino. En este ejemplo, esta es la relación
de Bugs a Products.
De forma similar a los métodos para encontrar filas padre y dependientes, si no especifica una
regla, el método utiliza la primera regla en el array $_referenceMap que
coincide con las tablas en la relación. Si necesita usar una regla distinta de la primera,
debe especificar la clave.
En el código de ejemplo anterior, no se especifica la clave de regla, por lo que las reglas utilizadas por defecto
son las primeras que coinciden. En este caso, $rule1 es
'Reporter' y $rule2 es
'Product'.
Ejemplo 27.153. Obtención de un rowset con el método de muchos a muchos mediante una regla específica
Este ejemplo muestra cómo obtener un objeto Row de la tabla origen Bugs, y encontrar filas de la tabla destino Products, que representan los productos relacionados con ese bug.
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->find(1234);
$bug1234 = $bugsRowset->current();
$productsRowset = $bug1234->findManyToManyRowset('Products',
'BugsProducts',
'Bug');
Alternativamente, puede consultar filas de la tabla destino en una relación de muchos a muchos
usando un "método mágico". Zend_Db_Table_Row_Abstract
invoca el método: findManyToManyRowset('<TableClass>',
'<IntersectionTableClass>', '<Rule1>', '<Rule2>') si
invoca un método que coincida con cualquiera de los siguientes patrones:
$row->find<TableClass>Via<IntersectionTableClass> ([Zend_Db_Table_Select $select])
$row->find<TableClass>Via<IntersectionTableClass>By<Rule1> ([Zend_Db_Table_Select $select])
$row->find<TableClass>Via<IntersectionTableClass>By<Rule1>And<Rule2> ([Zend_Db_Table_Select $select])
En los patrones anteriores, <TableClass> y <IntersectionTableClass> son cadenas que corresponden a los nombres de clase de la tabla destino y de la tabla de intersección, respectivamente. <Rule1> y <Rule2> son cadenas que corresponden a las claves de regla en la tabla de intersección que hacen referencia a la tabla origen y a la tabla destino, respectivamente.
![]() |
Nota |
|---|---|
Las identidades de tabla y las claves de regla nombradas en la llamada al método deben coincidir con la ortografía de la clase y la clave de regla exactamente. |
Ejemplo 27.154. Obtención de rowsets usando el método mágico de muchos a muchos
Este ejemplo muestra cómo encontrar filas en la tabla destino de una relación de muchos a muchos que representan los productos relacionados con un bug determinado.
$bugsTable = new Bugs(); $bugsRowset = $bugsTable->find(1234); $bug1234 = $bugsRowset->current(); // Use the default reference rule $products = $bug1234->findProductsViaBugsProducts(); // Specify the reference rule $products = $bug1234->findProductsViaBugsProductsByBug();
![]() |
Declare la DRI en la base de datos: |
|---|---|
|
La declaración de operaciones en cascada en
Por ejemplo, si utiliza el motor de almacenamiento MyISAM de MySQL o MariaDB, o SQLite, estas soluciones
no admiten DRI. Puede resultarle útil declarar las
operaciones en cascada con
Si su RDBMS implementa DRI y las cláusulas
ON
Lo más importante: no declare operaciones en cascada tanto en el
RDBMS como en su clase |
Puede declarar operaciones en cascada para ejecutar sobre una tabla dependiente cuando
aplica un UPDATE o un DELETE a una fila en una
tabla padre.
Ejemplo 27.155. Ejemplo de una eliminación en cascada
Este ejemplo muestra cómo eliminar una fila en la tabla Products, que está configurada para eliminar automáticamente las filas dependientes en la tabla Bugs.
$productsTable = new Products(); $productsRowset = $productsTable->find(1234); $product1234 = $productsRowset->current(); $product1234->delete(); // Automatically cascades to Bugs table // and deletes dependent rows.
De forma similar, si usa UPDATE para cambiar el valor de una clave primaria
en una tabla padre, es posible que desee que el valor de las claves foráneas de las tablas dependientes se
actualice automáticamente para que coincida con el nuevo valor, de modo que dichas referencias se mantengan
actualizadas.
Por lo general, no es necesario actualizar el valor de una clave primaria que fue generada por una secuencia u otro mecanismo. Pero si utiliza una clave natural que puede cambiar de valor ocasionalmente, es más probable que necesite aplicar actualizaciones en cascada a las tablas dependientes.
Para declarar una relación en cascada en Zend_Db_Table, edite
las reglas en $_referenceMap. Establezca las claves del array asociativo
'onDelete' y 'onUpdate' con una de estas opciones:
Cascade: Esta opción configura una cascada de un solo nivel (la tabla padre más todas las tablas directamente dependientes). Para habilitar esta opción, establezca la clave correspondiente en
$_referenceMapcon la cadena 'cascade' o use la constanteself::CASCADE.Recursive Cascade: Esta opción configura una cascada recursiva completa comenzando por la tabla padre. Para habilitar esta opción, establezca la clave correspondiente en
$_referenceMapcon la cadena 'cascadeRecurse' o use la constanteself::CASCADE_RECURSE.
Antes de que se elimine una fila de la tabla padre, o se actualicen sus valores de clave primaria, se eliminan o actualizan primero las filas en la tabla dependiente que hacen referencia a la fila del padre.
Ejemplo 27.156. Ejemplo de declaración de operaciones en cascada
En el ejemplo siguiente, las filas de la tabla Bugs se eliminan
automáticamente si se elimina la fila de la tabla Products a la que
hacen referencia. El elemento 'onDelete' de la entrada del mapa de referencias
se establece en self::CASCADE.
No se realiza ninguna actualización en cascada en el ejemplo siguiente si se cambia el valor de la
clave primaria en la clase padre. El elemento 'onUpdate' de la entrada del
mapa de referencias es self::RESTRICT. Puede obtener el mismo resultado
omitiendo la entrada 'onUpdate'.
class BugsProducts extends Zend_Db_Table_Abstract
{
...
protected $_referenceMap = array(
'Product' => array(
'columns' => array('product_id'),
'refTableClass' => 'Products',
'refColumns' => array('product_id'),
'onDelete' => self::CASCADE,
'onUpdate' => self::RESTRICT
),
...
);
}
Las operaciones en cascada invocadas por Zend_Db_Table no
son atómicas.
Esto significa que si su base de datos implementa y aplica restricciones de integridad
referencial, un UPDATE en cascada ejecutado por una clase
Zend_Db_Table entra en conflicto con la restricción, y
da lugar a una violación de integridad referencial. Puede usar UPDATE
en cascada en Zend_Db_Table
únicamente si su base de datos no aplica esa restricción de
integridad referencial.
El DELETE en cascada sufre menos el problema de violaciones de integridad
referencial. Puede eliminar las filas dependientes como una acción no atómica antes de
eliminar la fila padre a la que hacen referencia.
Sin embargo, tanto para UPDATE como para DELETE,
cambiar la base de datos de forma no atómica también crea el riesgo de que otro
usuario de la base de datos vea los datos en un estado inconsistente. Por ejemplo, si elimina
una fila y todas sus filas dependientes, existe una pequeña posibilidad de que otro
programa cliente de la base de datos consulte la base de datos después de que usted haya eliminado las filas
dependientes, pero antes de eliminar la fila padre. Ese programa cliente puede ver la fila padre sin
filas dependientes, y suponer que este es el estado deseado de los datos. No hay forma
de que ese cliente sepa que su consulta leyó la base de datos en medio de un cambio.
El problema del cambio no atómico se puede mitigar usando transacciones para aislar su cambio. Pero algunas marcas de RDBMS no admiten transacciones, o permiten a los clientes leer cambios "sucios" que aún no se han confirmado.
Las operaciones en cascada de Zend_Db_Table solo se
invocan mediante Zend_Db_Table.
Las eliminaciones y actualizaciones en cascada definidas en sus clases
Zend_Db_Table se aplican si ejecuta los métodos
save() o delete() en la clase Row. Sin embargo, si
actualiza o elimina datos usando otra interfaz, como una herramienta de consulta u otra
aplicación, las operaciones en cascada no se aplican. Incluso cuando se usan los métodos
update() y delete()
de la clase Zend_Db_Adapter, las operaciones en cascada definidas en
sus clases Zend_Db_Table no se ejecutan.
Sin INSERT en cascada.
No hay soporte para un INSERT en cascada. Debe insertar una
fila en una tabla padre en una operación, e insertar filas en una tabla dependiente en una
operación separada.