| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- EN-Revision: 17598 -->
- <!-- Reviewed: no -->
- <sect1 id="zend.db.table.relationships">
- <title>Zend_Db_Table Relationships</title>
- <sect2 id="zend.db.table.relationships.introduction">
- <title>Introduction</title>
- <para>
- Tables have relationships to each other in a relational database. An entity in one
- table can be linked to one or more entities in another table by using referential
- integrity constraints defined in the database schema.
- </para>
- <para>
- The
- <classname>Zend_Db_Table_Row</classname>
- class has methods for querying related rows
- in other tables.
- </para>
- </sect2>
- <sect2 id="zend.db.table.relationships.defining">
- <title>Defining Relationships</title>
- <para>
- Define classes for each of your tables, extending the abstract class
- <classname>Zend_Db_Table_Abstract</classname>
- , as described in
- <xref linkend="zend.db.table.defining"/>
- . Also
- see
- <xref linkend="zend.db.adapter.example-database"/>
- for a description of the
- example database for which the following example code is
- designed.
- </para>
- <para>
- Below are the
- <acronym>PHP</acronym>
- class definitions for these tables:
- </para>
- <programlisting language="php"><![CDATA[
- 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')
- )
- );
- }
- ]]></programlisting>
- <para>
- If you use
- <classname>Zend_Db_Table</classname>
- to emulate cascading UPDATE and DELETE
- operations, declare the
- <varname>$_dependentTables</varname>
- array in the class for the
- parent table. List the class name for each dependent table.
- Use the class name, not the
- physical name of the
- <acronym>SQL</acronym>
- table.
- </para>
- <note>
- <para>
- Skip declaration of
- <varname>$_dependentTables</varname>
- if you use referential
- integrity constraints in the
- <acronym>RDBMS</acronym>
- server to implement cascading
- operations. See
- <xref linkend="zend.db.table.relationships.cascading"/>
- for more
- information.
- </para>
- </note>
- <para>
- Declare the
- <varname>$_referenceMap</varname>
- array in the class for each dependent
- table. This is an associative array of reference
- "rules". A reference rule identifies
- which table is the parent table in the relationship,
- and also lists which columns in the
- dependent table reference which columns in the parent
- table.
- </para>
- <para>
- The rule key is a string used as an index to the
- <varname>$_referenceMap</varname>
- array. This rule key is used to identify each reference relationship. Choose a
- descriptive name for this rule key. It's best to use a string that can be part of a
- <acronym>PHP</acronym>
- method name, as you will see later.
- </para>
- <para>
- In the example
- <acronym>PHP</acronym>
- code above, the rule keys in the Bugs table class
- are:
- <code>'Reporter'</code>
- ,
- <code>'Engineer'</code>
- ,
- <code>'Verifier'</code>
- , and
- <code>'Product'</code>
- .
- </para>
- <para>
- The value of each rule entry in the
- <varname>$_referenceMap</varname>
- array is also an
- associative array. The elements of this rule entry are described below:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>columns</emphasis>
- => A string or an array of strings
- naming the foreign key column name(s) in the
- dependent table.
- </para>
- <para>
- It's common for this to be a single column, but some tables have multi-column
- keys.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>refTableClass</emphasis>
- => The class name of the parent table. Use
- the class name, not the physical name
- of the
- <acronym>SQL</acronym>
- table.
- </para>
- <para>
- It's common for a dependent table to have only one reference to its parent
- table, but some tables have multiple references to the same parent table. In
- the
- example database, there is one reference from the
- <code>bugs</code>
- table
- to the
- <code>products</code>
- table, but three references from the
- <code>bugs</code>
- table to the
- <code>accounts</code>
- table. Put each reference
- in a separate entry in the
- <varname>$_referenceMap</varname>
- array.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>refColumns</emphasis>
- => A string or an array of
- strings naming the primary key column name(s) in the
- parent table.
- </para>
- <para>
- It's common for this to be a single column, but some tables have multi-column
- keys. If the reference uses a multi-column key, the order of columns in the
- <code>'columns'</code>
- entry must match the order of columns in the
- <code>'refColumns'</code>
- entry.
- </para>
- <para>
- It is optional to specify this element. If you don't specify the
- <code>refColumns</code>
- , the column(s) reported as the primary key columns of
- the parent table are used
- by default.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>onDelete</emphasis>
- => The rule for an action to
- execute if a row is deleted in the parent table. See
- <xref linkend="zend.db.table.relationships.cascading"/>
- for more information.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>onUpdate</emphasis>
- => The rule for an action to
- execute if values in primary key columns are updated
- in the parent table. See
- <xref linkend="zend.db.table.relationships.cascading"/>
- for more information.
- </para>
- </listitem>
- </itemizedlist>
- </sect2>
- <sect2 id="zend.db.table.relationships.fetching.dependent">
- <title>Fetching a Dependent Rowset</title>
- <para>
- If you have a Row object as the result of a query on a parent table, you can fetch
- rows
- from dependent tables that reference the current row. Use the method:
- </para>
- <programlisting language="php"><![CDATA[
- $row->findDependentRowset($table, [$rule]);
- ]]></programlisting>
- <para>
- This method returns a
- <classname>Zend_Db_Table_Rowset_Abstract</classname>
- object,
- containing a set of rows from the dependent table
- <varname>$table</varname>
- that refer
- to the row identified by the
- <varname>$row</varname>
- object.
- </para>
- <para>
- The first argument
- <varname>$table</varname>
- can be a string that specifies the
- dependent table by its class name. You can also
- specify the dependent table by using an
- object of that table class.
- </para>
- <example id="zend.db.table.relationships.fetching.dependent.example">
- <title>Fetching a Dependent Rowset</title>
- <para>
- This example shows getting a Row object from the table
- <code>Accounts</code>
- , and
- finding the
- <code>Bugs</code>
- reported by that account.
- </para>
- <programlisting language="php"><![CDATA[
- $accountsTable = new Accounts();
- $accountsRowset = $accountsTable->find(1234);
- $user1234 = $accountsRowset->current();
- $bugsReportedByUser = $user1234->findDependentRowset('Bugs');
- ]]></programlisting>
- </example>
- <para>
- The second argument
- <varname>$rule</varname>
- is optional. It is a string that names the
- rule key in the
- <varname>$_referenceMap</varname>
- array of the dependent table class. If
- you don't specify a rule, the first rule in the
- array that references the parent table
- is used. If you need to use a rule other than the
- first, you need to specify the key.
- </para>
- <para>
- In the example code above, the rule key is not specified, so the rule used by default
- is
- the first one that matches the parent table. This is the rule
- <code>'Reporter'</code>
- .
- </para>
- <example id="zend.db.table.relationships.fetching.dependent.example-by">
- <title>Fetching a Dependent Rowset By a Specific Rule</title>
- <para>
- This example shows getting a Row object from the table
- <code>Accounts</code>
- , and
- finding the
- <code>Bugs</code>
- assigned to be fixed by the user of that account. The
- rule key string that
- corresponds to this reference relationship in this example is
- <code>'Engineer'</code>
- .
- </para>
- <programlisting language="php"><![CDATA[
- $accountsTable = new Accounts();
- $accountsRowset = $accountsTable->find(1234);
- $user1234 = $accountsRowset->current();
- $bugsAssignedToUser = $user1234->findDependentRowset('Bugs', 'Engineer');
- ]]></programlisting>
- </example>
- <para>
- You can also add criteria, ordering and limits to your relationships using the parent
- row's select object.
- </para>
- <para>
- <example id="zend.db.table.relationships.fetching.dependent.example-by-select">
- <title>Fetching a Dependent Rowset using a Zend_Db_Table_Select</title>
- <para>
- This example shows getting a Row object from the table
- <code>Accounts</code>
- ,
- and finding the
- <code>Bugs</code>
- assigned to be fixed by the user of that
- account, limited only to 3 rows and
- ordered by name.
- </para>
- <programlisting language="php"><![CDATA[
- $accountsTable = new Accounts();
- $accountsRowset = $accountsTable->find(1234);
- $user1234 = $accountsRowset->current();
- $select = $accountsTable->select()->order('name ASC')
- ->limit(3);
- $bugsAssignedToUser = $user1234->findDependentRowset('Bugs',
- 'Engineer',
- $select);
- ]]></programlisting>
- </example>
- Alternatively, you can query rows from a dependent table using a special mechanism
- called a "magic method".
- <classname>Zend_Db_Table_Row_Abstract</classname>
- invokes the
- method:
- <methodname>findDependentRowset('<TableClass>',
- '<Rule>')</methodname>
- if you invoke a method on the Row object matching
- either of the following patterns:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <code>$row->find<TableClass>()</code>
- </para>
- </listitem>
- <listitem>
- <para>
- <code>$row->find<TableClass>By<Rule>()</code>
- </para>
- </listitem>
- </itemizedlist>
- <para>
- In the patterns above,
- <code><TableClass></code>
- and
- <code><Rule></code>
- are
- strings that correspond to the class name of the dependent table, and the dependent
- table's rule key that references the parent table.
- </para>
- <note>
- <para>
- Some application frameworks, such as Ruby on Rails, use a mechanism called
- "inflection" to allow the spelling of identifiers to change depending on usage. For
- simplicity,
- <classname>Zend_Db_Table_Row</classname>
- does not provide any inflection
- mechanism. The table identity and the rule key named
- in the method call must match
- the spelling of the class and rule key exactly.
- </para>
- </note>
- <example id="zend.db.table.relationships.fetching.dependent.example-magic">
- <title>Fetching Dependent Rowsets using the Magic Method</title>
- <para>
- This example shows finding dependent Rowsets equivalent to those in the previous
- examples. In this case, the application uses the magic method invocation instead of
- specifying the table and rule as strings.
- </para>
- <programlisting language="php"><![CDATA[
- $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();
- ]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.db.table.relationships.fetching.parent">
- <title>Fetching a Parent Row</title>
- <para>
- If you have a Row object as the result of a query on a dependent table, you can fetch
- the row in the parent to which the dependent row refers. Use the method:
- </para>
- <programlisting language="php"><![CDATA[
- $row->findParentRow($table, [$rule]);
- ]]></programlisting>
- <para>
- There always should be exactly one row in the parent table referenced by a dependent
- row, therefore this method returns a Row object, not a Rowset object.
- </para>
- <para>
- The first argument
- <varname>$table</varname>
- can be a string that specifies the parent
- table by its class name. You can also specify
- the parent table by using an object of
- that table class.
- </para>
- <example id="zend.db.table.relationships.fetching.parent.example">
- <title>Fetching the Parent Row</title>
- <para>
- This example shows getting a Row object from the table
- <code>Bugs</code>
- (for
- example one of those bugs with status 'NEW'), and finding the row in the
- <code>Accounts</code>
- table for the user who reported the bug.
- </para>
- <programlisting language="php"><![CDATA[
- $bugsTable = new Bugs();
- $bugsRowset = $bugsTable->fetchAll(array('bug_status = ?' => 'NEW'));
- $bug1 = $bugsRowset->current();
- $reporter = $bug1->findParentRow('Accounts');
- ]]></programlisting>
- </example>
- <para>
- The second argument
- <varname>$rule</varname>
- is optional. It is a string that names the
- rule key in the
- <varname>$_referenceMap</varname>
- array of the dependent table class. If
- you don't specify a rule, the first rule in the
- array that references the parent table
- is used. If you need to use a rule other than the
- first, you need to specify the key.
- </para>
- <para>
- In the example above, the rule key is not specified, so the rule used by default is the
- first one that matches the parent table. This is the rule
- <code>'Reporter'</code>
- .
- </para>
- <example id="zend.db.table.relationships.fetching.parent.example-by">
- <title>Fetching a Parent Row By a Specific Rule</title>
- <para>
- This example shows getting a Row object from the table
- <code>Bugs</code>
- , and
- finding the account for the engineer assigned to fix that bug. The rule key
- string
- that corresponds to this reference relationship in this example is
- <code>'Engineer'</code>
- .
- </para>
- <programlisting language="php"><![CDATA[
- $bugsTable = new Bugs();
- $bugsRowset = $bugsTable->fetchAll(array('bug_status = ?', 'NEW'));
- $bug1 = $bugsRowset->current();
- $engineer = $bug1->findParentRow('Accounts', 'Engineer');
- ]]></programlisting>
- </example>
- <para>
- Alternatively, you can query rows from a parent table using a "magic method".
- <classname>Zend_Db_Table_Row_Abstract</classname>
- invokes the method:
- <methodname>findParentRow('<TableClass>', '<Rule>')</methodname>
- if you
- invoke a method on the Row object matching either of the following patterns:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <code>$row->findParent<TableClass>([Zend_Db_Table_Select $select])</code>
- </para>
- </listitem>
- <listitem>
- <para>
- <code>$row->findParent<TableClass>By<Rule>([Zend_Db_Table_Select
- $select])</code>
- </para>
- </listitem>
- </itemizedlist>
- <para>
- In the patterns above,
- <code><TableClass></code>
- and
- <code><Rule></code>
- are strings that correspond to the class name of the parent table, and the dependent
- table's rule key that references the parent table.
- </para>
- <note>
- <para>
- The table identity and the rule key named in the method call must match the
- spelling of the class and rule key exactly.
- </para>
- </note>
- <example id="zend.db.table.relationships.fetching.parent.example-magic">
- <title>Fetching the Parent Row using the Magic Method</title>
- <para>
- This example shows finding parent Rows equivalent to those in the previous
- examples. In this case, the application uses the magic method invocation instead of
- specifying the table and rule as strings.
- </para>
- <programlisting language="php"><![CDATA[
- $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();
- ]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.db.table.relationships.fetching.many-to-many">
- <title>Fetching a Rowset via a Many-to-many Relationship</title>
- <para>
- If you have a Row object as the result of a query on one table in a many-to-many
- relationship (for purposes of the example, call this the "origin" table), you can
- fetch
- corresponding rows in the other table (call this the "destination" table) via an
- intersection table. Use the method:
- </para>
- <programlisting language="php"><![CDATA[
- $row->findManyToManyRowset($table,
- $intersectionTable,
- [$rule1,
- [$rule2,
- [Zend_Db_Table_Select $select]
- ]
- ]);
- ]]></programlisting>
- <para>
- This method returns a
- <classname>Zend_Db_Table_Rowset_Abstract</classname>
- containing
- rows from the table
- <varname>$table</varname>
- , satisfying the many-to-many relationship.
- The current Row object
- <varname>$row</varname>
- from the origin table is used to find
- rows in the intersection table, and that is joined
- to the destination table.
- </para>
- <para>
- The first argument
- <varname>$table</varname>
- can be a string that specifies the
- destination table in the many-to-many relationship by
- its class name. You can also
- specify the destination table by using an object of that
- table class.
- </para>
- <para>
- The second argument
- <varname>$intersectionTable</varname>
- can be a string that specifies
- the intersection table between the two tables in the
- many-to-many relationship by
- its class name. You can also specify the intersection table
- by using an object of that
- table class.
- </para>
- <example id="zend.db.table.relationships.fetching.many-to-many.example">
- <title>Fetching a Rowset with the Many-to-many Method</title>
- <para>
- This example shows getting a Row object from the origin table
- <code>Bugs</code>
- , and finding rows from the destination table
- <code>Products</code>
- , representing products related to that bug.
- </para>
- <programlisting language="php"><![CDATA[
- $bugsTable = new Bugs();
- $bugsRowset = $bugsTable->find(1234);
- $bug1234 = $bugsRowset->current();
- $productsRowset = $bug1234->findManyToManyRowset('Products',
- 'BugsProducts');
- ]]></programlisting>
- </example>
- <para>
- The third and fourth arguments
- <varname>$rule1</varname>
- and
- <varname>$rule2</varname>
- are optional. These are strings that name the rule keys in the
- <varname>$_referenceMap</varname>
- array of the intersection table.
- </para>
- <para>
- The
- <varname>$rule1</varname>
- key names the rule for the relationship from the
- intersection table to the origin table.
- In this example, this is the relationship from
- <code>BugsProducts</code>
- to
- <code>Bugs</code>
- .
- </para>
- <para>
- The
- <varname>$rule2</varname>
- key names the rule for the relationship from the
- intersection table to the destination
- table. In this example, this is the relationship
- from
- <code>Bugs</code>
- to
- <code>Products</code>
- .
- </para>
- <para>
- Similarly to the methods for finding parent and dependent rows, if you don't specify a
- rule, the method uses the first rule in the
- <varname>$_referenceMap</varname>
- array that
- matches the tables in the relationship. If you need to use a rule other than
- the first,
- you need to specify the key.
- </para>
- <para>
- In the example code above, the rule key is not specified, so the rules used by default
- are the first ones that match. In this case,
- <varname>$rule1</varname>
- is
- <code>'Reporter'</code>
- and
- <varname>$rule2</varname>
- is
- <code>'Product'</code>
- .
- </para>
- <example id="zend.db.table.relationships.fetching.many-to-many.example-by">
- <title>Fetching a Rowset with the Many-to-many Method By a Specific Rule</title>
- <para>
- This example shows geting a Row object from the origin table
- <code>Bugs</code>
- , and finding rows from the destination table
- <code>Products</code>
- , representing products related to that bug.
- </para>
- <programlisting language="php"><![CDATA[
- $bugsTable = new Bugs();
- $bugsRowset = $bugsTable->find(1234);
- $bug1234 = $bugsRowset->current();
- $productsRowset = $bug1234->findManyToManyRowset('Products',
- 'BugsProducts',
- 'Bug');
- ]]></programlisting>
- </example>
- <para>
- Alternatively, you can query rows from the destination table in a many-to-many
- relationship using a "magic method."
- <classname>Zend_Db_Table_Row_Abstract</classname>
- invokes the method:
- <code>findManyToManyRowset('<TableClass>',
- '<IntersectionTableClass>', '<Rule1>', '<Rule2>')</code>
- if you invoke
- a method matching any of the following patterns:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <code>$row->find<TableClass>Via<IntersectionTableClass>
- ([Zend_Db_Table_Select $select])</code>
- </para>
- </listitem>
- <listitem>
- <para>
- <code>$row->find<TableClass>Via<IntersectionTableClass>By<Rule1>
- ([Zend_Db_Table_Select $select])</code>
- </para>
- </listitem>
- <listitem>
- <para>
- <code>$row->find<TableClass>Via<IntersectionTableClass>By<Rule1>And<Rule2>
- ([Zend_Db_Table_Select $select])</code>
- </para>
- </listitem>
- </itemizedlist>
- <para>
- In the patterns above,
- <code><TableClass></code>
- and
- <code><IntersectionTableClass></code>
- are strings that correspond to the class
- names of the destination table and the
- intersection table, respectively.
- <code><Rule1></code>
- and
- <code><Rule2></code>
- are strings that correspond
- to the rule keys in the intersection table that reference the
- origin table and the
- destination table, respectively.
- </para>
- <note>
- <para>
- The table identities and the rule keys named in the method call must match the
- spelling of the class and rule key exactly.
- </para>
- </note>
- <example id="zend.db.table.relationships.fetching.many-to-many.example-magic">
- <title>Fetching Rowsets using the Magic Many-to-many Method</title>
- <para>
- This example shows finding rows in the destination table of a many-to-many
- relationship representing products related to a given bug.
- </para>
- <programlisting language="php"><![CDATA[
- $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();
- ]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.db.table.relationships.cascading">
- <title>Cascading Write Operations</title>
- <note>
- <title>Declare DRI in the database:</title>
- <para>
- Declaring cascading operations in
- <classname>Zend_Db_Table</classname>
- is intended
- <emphasis>only</emphasis>
- for
- <acronym>RDBMS</acronym>
- brands that do not support
- declarative referential integrity (DRI).
- </para>
- <para>
- For example, if you use MySQL's MyISAM storage engine, or SQLite, these solutions
- do
- not support DRI. You may find it helpful to declare the cascading operations
- with
- <classname>Zend_Db_Table</classname>
- .
- </para>
- <para>
- If your
- <acronym>RDBMS</acronym>
- implements DRI and the
- <code>ON DELETE</code>
- and
- <code>ON UPDATE</code>
- clauses, you should declare these clauses in your database
- schema, instead of using
- the cascading feature in
- <classname>Zend_Db_Table</classname>
- . Declaring cascading DRI rules in the
- <acronym>RDBMS</acronym>
- is better for database performance, consistency, and
- integrity.
- </para>
- <para>
- Most importantly, do not declare cascading operations both in the
- <acronym>RDBMS</acronym>
- and in your
- <classname>Zend_Db_Table</classname>
- class.
- </para>
- </note>
- <para>
- You can declare cascading operations to execute against a dependent table when you
- apply
- an
- <constant>UPDATE</constant>
- or a
- <constant>DELETE</constant>
- to a row in a
- parent table.
- </para>
- <example id="zend.db.table.relationships.cascading.example-delete">
- <title>Example of a Cascading Delete</title>
- <para>
- This example shows deleting a row in the
- <code>Products</code>
- table, which is
- configured to automatically delete dependent rows in the
- <code>Bugs</code>
- table.
- </para>
- <programlisting language="php"><![CDATA[
- $productsTable = new Products();
- $productsRowset = $productsTable->find(1234);
- $product1234 = $productsRowset->current();
- $product1234->delete();
- // Automatically cascades to Bugs table
- // and deletes dependent rows.
- ]]></programlisting>
- </example>
- <para>
- Similarly, if you use
- <constant>UPDATE</constant>
- to change the value of a primary key
- in a parent table, you may want the value in foreign
- keys of dependent tables to be
- updated automatically to match the new value, so that such
- references are kept up to
- date.
- </para>
- <para>
- It's usually not necessary to update the value of a primary key that was generated by a
- sequence or other mechanism. But if you use a
- <emphasis>natural key</emphasis>
- that may
- change value occasionally, it is more likely that you need to apply cascading
- updates
- to dependent tables.
- </para>
- <para>
- To declare a cascading relationship in the
- <classname>Zend_Db_Table</classname>
- , edit
- the rules in the
- <varname>$_referenceMap</varname>
- . Set the associative array keys
- <code>'onDelete'</code>
- and
- <code>'onUpdate'</code>
- to the string 'cascade' (or the
- constant
- <constant>self::CASCADE</constant>
- ). Before a row is deleted from the parent
- table, or its primary key values updated, any
- rows in the dependent table that refer to
- the parent's row are deleted or updated first.
- </para>
- <example id="zend.db.table.relationships.cascading.example-declaration">
- <title>Example Declaration of Cascading Operations</title>
- <para>
- In the example below, rows in the
- <code>Bugs</code>
- table are automatically deleted
- if the row in the
- <code>Products</code>
- table to which they refer is deleted. The
- <code>'onDelete'</code>
- element of the reference map entry is set to
- <constant>self::CASCADE</constant>
- .
- </para>
- <para>
- No cascading update is done in the example below if the primary key value in the
- parent class is changed. The
- <code>'onUpdate'</code>
- element of the reference map
- entry is
- <constant>self::RESTRICT</constant>
- . You can get the same result by
- omitting the
- <code>'onUpdate'</code>
- entry.
- </para>
- <programlisting language="php"><![CDATA[
- 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
- ),
- ...
- );
- }
- ]]></programlisting>
- </example>
- <sect3 id="zend.db.table.relationships.cascading.notes">
- <title>Notes Regarding Cascading Operations</title>
- <para>
- <emphasis>
- Cascading operations invoked by
- <classname>Zend_Db_Table</classname>
- are
- not atomic.
- </emphasis>
- </para>
- <para>
- This means that if your database implements and enforces referential integrity
- constraints, a cascading
- <constant>UPDATE</constant>
- executed by a
- <classname>Zend_Db_Table</classname>
- class conflicts with the constraint, and
- results in a referential integrity
- violation. You can use cascading
- <constant>UPDATE</constant>
- in
- <classname>Zend_Db_Table</classname>
- <emphasis>only</emphasis>
- if your database does not enforce that referential
- integrity constraint.
- </para>
- <para>
- Cascading
- <constant>DELETE</constant>
- suffers less from the problem of referential
- integrity violations. You can delete
- dependent rows as a non-atomic action before
- deleting the parent row that they
- reference.
- </para>
- <para>
- However, for both
- <constant>UPDATE</constant>
- and
- <constant>DELETE</constant>
- ,
- changing the database in a non-atomic way also creates the risk that another
- database user can see the data in an inconsistent state. For example, if you delete
- a row and all its dependent rows, there is a small chance that another database
- client program can query the database after you have deleted the dependent rows, but
- before you delete the parent row. That client program may see the parent row with no
- dependent rows, and assume this is the intended state of the data. There is no way
- for that client to know that its query read the database in the middle of a change.
- </para>
- <para>
- The issue of non-atomic change can be mitigated by using transactions to isolate
- your change. But some
- <acronym>RDBMS</acronym>
- brands don't support transactions, or
- allow clients to read "dirty" changes that have
- not been committed yet.
- </para>
- <para>
- <emphasis>
- Cascading operations in
- <classname>Zend_Db_Table</classname>
- are invoked
- only by
- <classname>Zend_Db_Table</classname>
- .
- </emphasis>
- </para>
- <para>
- Cascading deletes and updates defined in your
- <classname>Zend_Db_Table</classname>
- classes are applied if you execute the
- <methodname>save()</methodname>
- or
- <methodname>delete()</methodname>
- methods on the Row class. However, if you update
- or delete data using another
- interface, such as a query tool or another application,
- the cascading operations are
- not applied. Even when using
- <methodname>update()</methodname>
- and
- <methodname>delete()</methodname>
- methods
- in the
- <classname>Zend_Db_Adapter</classname>
- class, cascading operations defined in
- your
- <classname>Zend_Db_Table</classname>
- classes are not executed.
- </para>
- <para>
- <emphasis>
- No Cascading
- <constant>INSERT</constant>
- .
- </emphasis>
- </para>
- <para>
- There is no support for a cascading
- <constant>INSERT</constant>
- . You must insert a
- row to a parent table in one operation, and insert row(s) to a
- dependent table in a
- separate operation.
- </para>
- </sect3>
- </sect2>
- </sect1>
- <!--
- vim:se ts=4 sw=4 et:
- -->
|