| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- EN-Revision: 24249 -->
- <!-- Reviewed: no -->
- <sect1 id="zend.db.table.relationships">
- <title>Relations Zend_Db_Table</title>
- <sect2 id="zend.db.table.relationships.introduction">
- <title>Introduction</title>
- <para>
- Les tables possèdent des relations entre elles, dans une base de données
- relationnelle. Une entité d'une table peut être liée à une autre entité d'une autre
- table, via un procédé appelé contrainte d'intégrité référentielle
- </para>
- <para>
- La classe <classname>Zend_Db_Table_Row</classname> possède des méthodes pour
- récupérer des enregistrement dans d'autres tables, liées à celle en cours.
- </para>
- </sect2>
- <sect2 id="zend.db.table.relationships.defining">
- <title>Définir ses relations</title>
- <para>
- Chaque table doit avoir sa classe étendant
- <classname>Zend_Db_Table_Abstract</classname>, comme décrit dans <xref
- linkend="zend.db.table.defining" />. Voyez aussi <xref
- linkend="zend.db.adapter.example-database" /> pour une description de la base de donnée
- qui servira d'exemple pour la suite de ce chapitre.
- </para>
- <para>Voici les classes correspondantes à ces 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>
- Si vous utilisez <classname>Zend_Db_Table</classname> pour émuler les cascades
- <constant>UPDATE</constant> et <constant>DELETE</constant>, alors déclarez
- <varname>$_dependentTables</varname> en tant que tableau dans la classe des tables parentes.
- Listez ainsi le nom de chaque table dépendante. Utilisez bien le nom des classes, et non
- les noms physiques des tables.
- </para>
- <note>
- <para>
- Si votre SGBD implémente le mécanisme des cascades, alors vous n'avez pas
- besoin de déclarer <varname>$_dependentTables</varname>. Voyez <xref
- linkend="zend.db.table.relationships.cascading" /> pour plus d'informations.
- </para>
- </note>
- <para>
- Déclarez un tableau <varname>$_referenceMap</varname> dans les classes de chaque table
- dépendante (qui "reçoit une clé"). C'est un tableau associatif, dit de "rôles". Un rôle
- définit quelle table est parente dans la relation, et quelle est sa colonne de
- parenté.
- </para>
- <para>
- Le rôle est utilisé comme index du tableau <varname>$_referenceMap</varname>. Il est
- utilisé pour définir la relation, et pourra faire partie du nom de certaines méthodes,
- comme nous le verrons plus tard. Choisissez ainsi un nom de rôle de manière
- intelligente.
- </para>
- <para>
- Dans l'exemple du dessus, les rôles dans la classe Bugs sont :
- <code>"Reporter"</code>, <code>"Engineer"</code>, <code>"Verifier"</code> et
- <code>"Product"</code>.
- </para>
- <para>
- La valeur de chaque rôle dans le tableau <varname>$_referenceMap</varname> est aussi un
- tableau associatif. Les éléments de chaque rôle sont décrits ci-après.
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>columns</emphasis> => une chaîne de caractères ou un tableau
- de chaînes désignant le(s) nom(s) des clés étrangères dans la table dépendante
- (la table actuelle donc).
- </para>
- <para>
- Il est courant qu'il s'agisse d'une seule colonne, mais on peut rencontrer
- le cas de clés composées de multiples colonnes.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>refTableClass</emphasis> => désigne la classe de la table
- parente, liée à cette colonne. Utilisez le nom de la classe et non le nom de la
- table physique.
- </para>
- <para>
- Il est courant qu'une table dépendante n'ait qu'une seule référence d'une
- même table parente. Cependant certaines tables peuvent avoir plusieurs
- références vers une même table parente. Dans notre base de données d'exemple,
- c'est le cas avec la table <code>bugs</code>. Elle possède soit une et une seule
- colonne référençant la table parente <code>products</code>, mais elle possède
- trois références (donc trois colonnes) vers la table parente
- <code>accounts</code>. Chaque référence doit être matérialisée par un rôle
- unique dans le tableau <varname>$_referenceMap</varname>.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>refColumns</emphasis> => c'est une chaîne de caractères ou un
- tableau de chaînes nommant la(es) colonne(s) (clé primaire) de la table
- parente.
- </para>
- <para>
- Si vous devez utiliser de multiples colonnes parentes pour une seule clé,
- alors veillez à bien les entrer dans <code>'columns'</code> dans le même ordre
- que dans <code>'refColumns'</code>.
- </para>
- <para>
- Il est optionnel de spécifier la <code>refColumns</code>. La clé primaire
- est utilisée par défaut comme colonne parente dans une relation.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>onDelete</emphasis> => le nom de l'action à exécuter si un
- enregistrement est supprimé de la table parente. Voyez <xref
- linkend="zend.db.table.relationships.cascading" /> pour plus
- d'informations.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>onUpdate</emphasis> => le nom de l'action à exécuter si un
- enregistrement est mis à jour dans la table parente. Voyez<xref
- linkend="zend.db.table.relationships.cascading" /> pour plus
- d'informations.
- </para>
- </listitem>
- </itemizedlist>
- </sect2>
- <sect2 id="zend.db.table.relationships.fetching.dependent">
- <title>Récupérer des enregistrements dépendants (enfants)</title>
- <para>
- Si vous possédez un enregistrement actif (<code>Row</code>), il est possible de
- récupérer ses enfants dépendants, si les dépendances ont été déclarées suivant la
- procédure ci-dessus. Utilisez la méthode :
- </para>
- <programlisting language="php"><![CDATA[
- $row->findDependentRowset($table, [$rule]);
- ]]></programlisting>
- <para>
- Cette méthode retourne un objet instance de
- <classname>Zend_Db_Table_Rowset_Abstract</classname>, qui contient tous les
- enregistrements (<code>Row</code>) de la table dépendante <varname>$table</varname> faisant
- référence à l'enregistrement actif actuel <varname>$row</varname>.
- </para>
- <para>
- Le paramètre <varname>$table</varname> désigne la table dépendante à utiliser. Ceci peut
- être une chaîne de caractères aussi bien qu'un objet de la classe de cette table.
- </para>
- <example id="zend.db.table.relationships.fetching.dependent.example">
- <title>Récupérer des enregistrements dépendants</title>
- <para>
- Cet exemple montre comment obtenir un enregistrement actif (objet
- <code>Row</code>) de la table <code>Accounts</code>, et comment en récupérer les
- enfants dépendants de la table <code>Bugs</code>. (les bugs reportés par ce
- compte)
- </para>
- <programlisting language="php"><![CDATA[
- $accountsTable = new Accounts();
- $accountsRowset = $accountsTable->find(1234);
- $user1234 = $accountsRowset->current();
- $bugsReportedByUser = $user1234->findDependentRowset('Bugs');
- ]]></programlisting>
- </example>
- <para>
- Le second paramètre <varname>$rule</varname> est optionnel. Il s'agit du nom du rôle à
- utiliser depuis le tableau <varname>$_referenceMap</varname> de la classe de la table
- dépendante. Si vous ne le spécifiez pas, le premier rôle sera utilisé. Il n'y a dans la
- majorité des cas qu'un seul rôle.
- </para>
- <para>
- Dans l'exemple ci dessus, nous ne fournissons pas de nom de rôle, le premier est
- donc pris en considération, et il s'agit de <code>"Reporter"</code>.
- </para>
- <example id="zend.db.table.relationships.fetching.dependent.example-by">
- <title>Récupérer des enregistrements dépendants avec un rôle spécifique</title>
- <para>
- Dans cet exemple nous montrons comment obtenir un enregistrement
- (<code>Row</code>) depuis la table <code>Accounts</code>, et comment trouver les
- <code>Bugs</code> assignés à ce compte (<code>Account</code>). Nous devrons alors
- nommer le rôle <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>
- Vous pouvez rajouter des critères à vos relations, comme l'ordre ou la limite,
- ceci en utilisant l'objet <code>select</code> de l'enregistrement parent.
- </para>
- <para>
- <example id="zend.db.table.relationships.fetching.dependent.example-by-select">
- <title>
- Récupérer des enregistrements dépendants en utilisant un objet
- Zend_Db_Table_Select
- </title>
- <para>
- Dans cet exemple nous montrons comment obtenir un enregistrement
- (<code>Row</code>) depuis la table <code>Accounts</code>, et comment trouver les
- <code>Bugs</code> assignés à ce compte (<code>Account</code>), mais limités
- seulement à trois enregistrements, et ordonnés par nom. Nous devrons nommer le
- rôle <code>"Engineer"</code>.
- </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>Vous pouvez récupérer les enregistrements dépendants d'une autre manière.
- En utilisant les "méthodes magiques". En effet,
- <classname>Zend_Db_Table_Row_Abstract</classname> va utiliser la méthode
- <methodname>findDependentRowset('<TableClass>', '<Rule>')</methodname> si vous appelez
- sur l'enregistrement une méthode correspondante à un de ces motifs :
- </para>
- <itemizedlist>
- <listitem>
- <para><code>$row->find<TableClass>()</code></para>
- </listitem>
- <listitem>
- <para><code>$row->find<TableClass>By<Rule>()</code></para>
- </listitem>
- </itemizedlist>
- <para>
- Dans les motifs ci-dessus, <code><TableClass></code> et
- <code><Rule></code> désignent respectivement le nom de la table dépendante et le
- rôle à utiliser.
- </para>
- <note>
- <para>
- Certains frameworks tels que Rails pour Ruby, utilise un mécanisme dit
- d'inflexion, qui permet de transformer les noms des identifiants (nom de table, de
- rôle...) d'une certaine manière bien spécifique dans les méthodes appelées. Cela
- n'est pas le cas de Zend Framework : vous devez, dans vos méthodes magiques,
- utiliser l'orthographe exacte des noms des rôles et classes, tels que vous les
- définissez.
- </para>
- </note>
- <example id="zend.db.table.relationships.fetching.dependent.example-magic">
- <title>
- Récupérer des enregistrements dépendants en utilisant les méthodes magiques
- </title>
- <para>
- Cet exemple a le même effet que le précédent. Il utilise simplement les
- méthodes magiques pour récupérer les enregistrements dépendants.
- </para>
- <programlisting language="php"><![CDATA[
- $accountsTable = new Accounts();
- $accountsRowset = $accountsTable->find(1234);
- $user1234 = $accountsRowset->current();
- // Utilise le rôle par défaut (le premier de la liste)
- $bugsReportedBy = $user1234->findBugs();
- // Utilise un rôle spécifique
- $bugsAssignedTo = $user1234->findBugsByEngineer();
- ]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.db.table.relationships.fetching.parent">
- <title>Récupérer l'enregistrement parent</title>
- <para>
- Si vous possédez un enregistrement (<code>Row</code>) dont la table possède une
- table parente, il est possible alors de récupérer l'enregistrement parent. Utilisez pour
- cela la méthode :
- </para>
- <programlisting language="php"><![CDATA[
- $row->findParentRow($table, [$rule]);
- ]]></programlisting>
- <para>
- La logique veut qu'il ne puisse y avoir qu'un et un seul parent par
- enregistrement. Ainsi, cette méthode retourne un objet <code>Row</code> et non un objet
- <code>Rowset</code>
- </para>
- <para>
- Le premier paramètre <varname>$table</varname> désigne la table parente. Ceci peut être
- une chaîne de caractères, ou un objet instance de la classe de la table parente.
- </para>
- <example id="zend.db.table.relationships.fetching.parent.example">
- <title>Récupérer l'enregistrement parent</title>
- <para>
- Cet exemple illustre la récupération d'un enregistrement <code>Bugs</code> (disons
- par exemple ceux avec le statut "NEW"), et l'obtention de l'enregistrement
- parent correspondant à <code>Accounts</code> (la personne ayant reporté le
- 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>
- Le second paramètre <varname>$rule</varname> est optionnel. Il s'agit du nom du rôle à
- utiliser depuis le tableau <varname>$_referenceMap</varname> de la classe de la table
- dépendante. Si vous ne le spécifiez pas, le premier rôle sera utilisé. Il n'y a dans la
- majorité des cas qu'un seul rôle.
- </para>
- <para>
- Dans l'exemple ci dessus, nous ne fournissons pas de nom de rôle, le premier est
- donc pris en considération, et il s'agit de <code>"Reporter"</code>.
- </para>
- <example id="zend.db.table.relationships.fetching.parent.example-by">
- <title>Récupérer un enregistrement parent avec un rôle spécifique</title>
- <para>
- Cet exemple va démontrer comment, à partir d'un enregistrement de
- <code>Bugs</code>, récupérer la personne en étant assignée. Il va falloir utiliser
- le rôle <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>
- Vous pouvez récupérer l'enregistrement parent d'une autre manière. En utilisant
- les "méthodes magiques". En effet, Zend_Db_Table_Row_Abstract va utiliser la
- méthode<methodname>findParentRow('<TableClass>', '<Rule>')</methodname> si vous appelez
- sur l'enregistrement une méthode correspondante à un de ces motifs :
- </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>
- Dans les motifs ci-dessus, <code><TableClass></code> et
- <code><Rule></code> représentent respectivement le nom de la classe de la table
- parente, et le rôle à utiliser éventuellement.
- </para>
- <note>
- <para>
- Les noms de la table et du rôle doivent être orthographiés de la même manière
- qu'ils ne le sont lors de leur définition dans la table.
- </para>
- </note>
- <example id="zend.db.table.relationships.fetching.parent.example-magic">
- <title>Récupérer un enregistrement parent en utilisant les méthodes magiques</title>
- <para>
- Cet exemple a le même effet que le précédent. Il utilise simplement les
- méthodes magiques pour récupérer l'enregistrement parent.
- </para>
- <programlisting language="php"><![CDATA[
- $bugsTable = new Bugs();
- $bugsRowset = $bugsTable->fetchAll(array('bug_status = ?', 'NEW'));
- $bug1 = $bugsRowset->current();
- // Utilise le rôle par défaut ( le premier déclaré)
- $reporter = $bug1->findParentAccounts();
- // Utilise un rôle spécifique
- $engineer = $bug1->findParentAccountsByEngineer();
- ]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.db.table.relationships.fetching.many-to-many">
- <title>
- Récupérer des enregistrements dans une relation N-N (plusieurs-à-plusieurs ou
- "many-to-many")
- </title>
- <para>
- Si vous possédez un enregistrement sur une table (appelons la "table d'origine")
- ayant une relation plusieurs à plusieurs vers une autre table (appelons la "table de
- destination"), vous pouvez alors accéder aux enregistrements de la table de destination,
- via une table dite "d'intersection". Utilisez la méthode :
- </para>
- <programlisting language="php"><![CDATA[
- $row->findManyToManyRowset($table,
- $intersectionTable,
- [$rule1,
- [$rule2,
- [Zend_Db_Table_Select $select]]]);
- ]]></programlisting>
- <para>
- Cette méthode retourne un objet instance de
- <classname>Zend_Db_Table_Rowset_Abstract</classname> qui contient les enregistrements de
- la table <varname>$table</varname> qui correspondent à la relation plusieurs à plusieurs.
- L'enregistrement courant de la table courante, <varname>$row</varname>, est utilisé comme
- point de départ pour effectuer une jointure vers la table de destination, via la table
- d'intersection.
- </para>
- <para>
- Le premier paramètre <varname>$table</varname> peut être soit une chaîne soit un objet
- instance de la classe de la table de destination dans la relation plusieurs à
- plusieurs.
- </para>
- <para>
- Le second paramètre <varname>$intersectionTable</varname> peut être soit une chaîne soit
- un objet instance de la classe de la table d'intersection dans la relation plusieurs à
- plusieurs.
- </para>
- <example id="zend.db.table.relationships.fetching.many-to-many.example">
- <title>Récupérer des enregistrements dans une relation plusieurs-à-plusieurs</title>
- <para>
- Cet exemple montre comment posséder un enregistrement de la table d'origine
- <code>Bugs</code>, et comment en récupérer les enregistrements de
- <code>Products</code>, qui représentent les produits qui font référence à ce
- bug.
- </para>
- <programlisting language="php"><![CDATA[
- $bugsTable = new Bugs();
- $bugsRowset = $bugsTable->find(1234);
- $bug1234 = $bugsRowset->current();
- $productsRowset = $bug1234->findManyToManyRowset('Products',
- 'BugsProducts');
- ]]></programlisting>
- </example>
- <para>
- Les troisième et quatrième paramètres, <varname>$rule1</varname> et <varname>$rule2</varname>,
- sont optionnels. Ce sont des chaînes de caractères qui désignent les rôles à utiliser
- dans le tableau <varname>$_referenceMap</varname> de la table d'intersection.
- </para>
- <para>
- <varname>$rule1</varname> nomme le rôle dans la relation entre la table d'origine et la
- table d'intersection. Dans notre exemple, il s'agit donc de la relation de
- <code>Bugs</code> à <code>BugsProducts</code>.
- </para>
- <para>
- <varname>$rule2</varname>nomme le rôle dans la relation entre la table d'origine et la
- table d'intersection. Dans notre exemple, il s'agit donc de la relation de
- <code>BugsProducts</code> à <code>Products</code>.
- </para>
- <para>
- Si vous ne spécifiez pas de rôles, alors le premier rôle trouvé pour la table,
- dans le tableau <varname>$_referenceMap</varname>, sera utilisé. Dans la grande majorité des
- cas, il n'y a qu'un rôle.
- </para>
- <para>
- Dans l'exemple ci-dessus, les rôles ne sont pas spécifiés. Ainsi
- <varname>$rule1</varname> prend la valeur <code>"Reporter"</code> et <varname>$rule2</varname> prend
- la valeur <code>"Product"</code>.
- </para>
- <example id="zend.db.table.relationships.fetching.many-to-many.example-by">
- <title>Récupérer des enregistrements dans une relation plusieurs-à-plusieurs avec un
- rôle spécifique</title>
- <para>
- Cet exemple montre comment à partir d'un enregistrement de <code>Bugs</code>,
- récupérer les enregistrements de <code>Products</code>, représentant les produits
- comportant ce 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>
- Vous pouvez récupérer l'enregistrement de destination d'une autre manière. En
- utilisant les "méthodes magiques". En effet,
- <classname>Zend_Db_Table_Row_Abstract</classname> va utiliser la méthode
- <code>findManyToManyRowset('<TableClass>', '<IntersectionTableClass>',
- '<Rule1>', '<Rule2>')</code> si vous appelez sur l'enregistrement une
- méthode correspondante à un de ces motifs :
- </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>
- Dans les motifs ci dessus, <code><TableClass></code> et
- <code><IntersectionTableClass></code> sont des chaînes de caractères
- correspondantes aux noms des classes des tables de destination et d'intersection
- (respectivement). <code><Rule1></code> et <code><Rule2></code> sont
- respectivement des chaînes désignant les rôles dans la table d'intersection pour la
- table de référence, et de destination.
- </para>
- <note>
- <para>
- Les noms de la table et des rôles doivent être orthographiés de manière
- exacte, tel qu'ils le sont lors de leurs définitions respectives.
- </para>
- </note>
- <example id="zend.db.table.relationships.fetching.many-to-many.example-magic">
- <title>Récupérer des enregistrements dans une relation plusieurs-à-plusieurs avec
- les méthodes magiques</title>
- <para>
- Cet exemple illustre la récupération d'enregistrements dans une table de
- destination, bugs, depuis un produit, en passant par une table d'intersection, le
- tout, via des méthodes magiques.
- </para>
- <programlisting language="php"><![CDATA[
- $bugsTable = new Bugs();
- $bugsRowset = $bugsTable->find(1234);
- $bug1234 = $bugsRowset->current();
- // Utilisation des rôles par défaut
- $products = $bug1234->findProductsViaBugsProducts();
- // Utilisation d'un rôle spécifique
- $products = $bug1234->findProductsViaBugsProductsByBug();
- ]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.db.table.relationships.cascading">
- <title>Opérations d'écritures en cascade</title>
- <note>
- <title>Déclarer l'intégrité référentielle</title>
- <para>
- Déclarer les opérations de cascades dûes à l'intégrité référentielle dans
- <classname>Zend_Db_Table</classname> directement, ne doit se faire
- <emphasis>seulement</emphasis> si votre SGBD ne supporte pas nativement ce genre
- d'opérations.
- </para>
- <para>
- C'est le cas par exemple de MySQL ou MariaDB utilisant le stockage de tables MyISAM, ou
- encore SQLite. Ces solutions là ne supportent pas l'intégrité référentielle. Il peut
- alors être intéressant d'utiliser <classname>Zend_Db_Table</classname> pour émuler
- un tel comportement
- </para>
- <para>
- Si votre SGBD en revanche supporte les clauses <code>ON DELETE</code> et
- <code>ON UPDATE</code>, alors vous devriez les déclarer directement dans le SGBD
- plutôt que de vous fier à l'émulation proposée par
- <classname>Zend_Db_Table</classname>. Déclarer son intégrité référentielle dans son
- SGBD directement est tout à fait recommandé pour les performances, l'intégrité
- (l'atomicité des opérations), et la logique de base de données.
- </para>
- <para>
- Il est très important de ne pas déclarer ses règles d'intégrité référentielle
- à la fois dans son SGBD et dans les classes
- <classname>Zend_Db_Table</classname>.
- </para>
- </note>
- <para>
- Vous pouvez déclarer des opérations de cascade sur un <constant>UPDATE</constant> ou un
- <constant>DELETE</constant>, à appliquer sur les enregistrements dépendants à la table en
- cours.
- </para>
- <example id="zend.db.table.relationships.cascading.example-delete">
- <title>Exemple de DELETE cascade</title>
- <para>
- Cet exemple montre l'effacement d'un enregistrement de <code>Products</code>,
- qui va propager l'effacement des enregistrements dépendants dans la table
- <code>Bugs</code>.
- </para>
- <programlisting language="php"><![CDATA[
- $productsTable = new Products();
- $productsRowset = $productsTable->find(1234);
- $product1234 = $productsRowset->current();
- $product1234->delete();
- // Cascades automatiques vers le table Bugs
- // et suppression des enregistrements dépendants.
- ]]></programlisting>
- </example>
- <para>
- De la même manière, si vous utilisez un <constant>UPDATE</constant> pour changer la valeur
- de la clé primaire d'une table parente, vous pourriez nécessiter que les clés étrangères
- des tables dépendantes soient mises à jour.
- </para>
- <para>
- En général s'il s'agit d'une séquence, il n'est pas nécessaire de mettre à jour
- les enregistrements dépendants. En revanche concernant les clé dites
- <emphasis>naturelles </emphasis>, il peut s'avérer nécessaire de propager un changement
- de valeur.
- </para>
- <para>
- Afin de déclarer une relation de cascades dans
- <classname>Zend_Db_Table</classname>, éditer les rôles dans <varname>$_referenceMap</varname>.
- Ajoutez les clés <code>'onDelete'</code> et <code>'onUpdate'</code> et donnez leur la
- valeur 'cascade' (ou la constante <constant>self::CASCADE</constant>). Avant qu'un
- enregistrement ne soit modifié(sa clé primaire) / supprimé, tous les enregistrements
- dans les tables dépendantes seront modifiés / supprimés.
- </para>
- <example id="zend.db.table.relationships.cascading.example-declaration">
- <title>Exemple de déclaration des opérations de cascade</title>
- <para>
- Dans l'exemple ci-après, les enregistrements de <code>Bugs</code> sont
- automatiquement supprimés si l'enregistrement dans la table <code>Products</code>
- auquel ils font référence est supprimé. L'élément <code>"onDelete"</code> de la
- <varname>$_referenceMap</varname> est mis à <constant>self::CASCADE</constant>.
- </para>
- <para>
- Pas de mise à jour en cascade en revanche pour cette table, si la clé primaire
- de la table parente est changée. En effet, l'élément <code>"onUpdate"</code> est mis
- à <constant>self::RESTRICT</constant>. Vous auriez aussi pu tout simplement ne pas spécifier
- <code>"onUpdate"</code> .
- </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 concernant les opérations de cascade</title>
- <para>
- <emphasis>Les opérations de cascades déclenchées par Zend_Db_Table ne sont pas
- atomiques.</emphasis>
- </para>
- <para>
- Ceci signifie que si votre SGBD possède un moyen de gérer les cascades, comme
- l'intégrité référentielle (et les clés étrangères), alors vous ne devriez pas
- utiliser les cascades INSERT via <classname>Zend_Db_Table</classname>, car elles
- vont entrer en conflit avec le système d'intégrité référentielle du SGBD qui lui,
- est atomique.
- </para>
- <para>
- Le problème est plus mitigé concernant <constant>DELETE</constant>. Vous pouvez
- détruire de manière non atomique un enregistrement dépendant, avant de détruire son
- parent.
- </para>
- <para>
- Cependant, les deux opérations <constant>UPDATE</constant> et <constant>DELETE</constant>
- utilisées de manière non atomique(que), c'est à dire avec le mécanisme de
- <classname>Zend_Db_Table</classname>, peuvent laisser la base de données dans un
- état non désiré, ou état intermédiaire. Supposez que vous supprimiez tous les
- enregistrements dépendants, pour finir par leur parent unique. A un moment donnée,
- la base de donnée sera dans un état tel que le parent sera sans enfants, mais
- toujours bel et bien présent. Si un autre client se connecte exactement à ce moment
- là, il va pouvoir requêter éventuellement le parent, en croyant que celui-ci n'a
- plus d'enfant, ce qui normalement n'est pas le cas. Il est alors totalement
- impossible pour ce client là de se rendre compte qu'il a effectuer une lecture au
- beau milieu d'une plus vaste opération d'effacement.
- </para>
- <para>
- Les problèmes de changements non-atomique peuvent être anéantis en utilisant
- les transactions isolantes, c'est d'ailleurs un de leur rôle clé. Cependant certains
- SGBDs ne supportent pas encore les transactions, et autorisent leurs clients à lire
- des changements incomplets pas validés en totalité.
- </para>
- <para>
- <emphasis>Les opérations de cascades de Zend_Db_Table ne sont utilisées que
- par Zend_Db_Table.</emphasis>
- </para>
- <para>
- Les cascades pour <constant>DELETE</constant> et <constant>UPDATE</constant> définies dans vos
- classes <classname>Zend_Db_Table</classname> ne sont utilisées que lors du recours
- aux méthodes <methodname>save()</methodname> ou <methodname>delete()</methodname> sur les enregistrements
- <code>Row</code>. Si vous utilisez une autre interface pour vos <constant>UPDATE</constant>
- ou <constant>DELETE</constant>, comme par exemple un outil de requêtes, ou une autre
- application, les opérations de cascades ne sont bien sûr pas appliquées. C'est même
- le cas si vous utilisez les méthodes <methodname>update()</methodname> et <methodname>delete()</methodname>
- dans la classe <classname>Zend_Db_Adapter</classname>.
- </para>
- <para><emphasis>Pas d'<constant>INSERT</constant> en cascade</emphasis></para>
- <para>
- Le support pour les cascades d'<constant>INSERT</constant> n'est pas assuré. Vous
- devez explicitement insérer les enregistrements dépendants à un enregistrement
- parent.
- </para>
- </sect3>
- </sect2>
- </sect1>
|