| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 |
- <!-- EN-Revision: 13846 -->
- <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 role="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 <code>UPDATE</code> et
- <code>DELETE</code>, alors déclarez <code>$_dependentTables</code> 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
- <code>$_dependentTables</code>. Voyez <xref linkend="zend.db.table.relationships.cascading" /> pour plus
- d'informations.</para>
- </note>
- <para>Déclarez un tableau <code>$_referenceMap</code> 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 <code>$_referenceMap</code>. 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 <code>$_referenceMap</code> est aussi un tableau associatif. Les
- éléments de chaque rôle sont décrits ci-après.</para>
- <itemizedlist>
- <listitem>
- <para><emphasis role="strong">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 role="strong">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 <code>$_referenceMap</code>.</para>
- </listitem>
- <listitem>
- <para><emphasis role="strong">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 role="strong">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 role="strong">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 role="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 <code>$table</code> faisant référence à
- l'enregistrement actif actuel <code>$row</code>.</para>
- <para>Le paramètre <code>$table</code> 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 role="php"><![CDATA[
- $accountsTable = new Accounts();
- $accountsRowset = $accountsTable->find(1234);
- $user1234 = $accountsRowset->current();
- $bugsReportedByUser = $user1234->findDependentRowset('Bugs');
- ]]></programlisting>
- </example>
- <para>Le second paramètre <code>$rule</code> est optionnel. Il s'agit du nom du rôle à utiliser depuis le
- tableau <code>$_referenceMap</code> 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 role="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
- <classname>Zend_Db_Table_Select</classname></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 role="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
- <code>findDependentRowset('<TableClass>', '<Rule>')</code> 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 role="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 role="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 <code>$table</code> 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 role="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 <code>$rule</code> est optionnel. Il s'agit du nom du rôle à utiliser depuis le
- tableau <code>$_referenceMap</code> 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 role="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<code>findParentRow('<TableClass>',
- '<Rule>')</code> 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 role="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 role="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 <code>$table</code> qui correspondent à la relation plusieurs à plusieurs.
- L'enregistrement courant de la table courante, <code>$row</code>, 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 <code>$table</code> 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 <code>$intersectionTable</code> 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 role="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, <code>$rule1</code> et <code>$rule2</code>, sont optionnels. Ce
- sont des chaînes de caractères qui désignent les rôles à utiliser dans le tableau <code>$_referenceMap</code> de
- la table d'intersection.</para>
- <para><code>$rule1</code> 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><code>$rule2</code>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
- <code>$_referenceMap</code>, 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 <code>$rule1</code> prend la valeur
- <code>"Reporter"</code> et <code>$rule2</code> 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 role="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 role="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 role="strong">seulement</emphasis> si votre SGBD ne supporte pas
- nativement ce genre d'opérations.</para>
- <para>C'est le cas par exemple de MySQL 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 <code>UPDATE</code> ou un <code>DELETE</code>, à
- 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 role="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 <code>UPDATE</code> 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
- <code>$_referenceMap</code>. Ajoutez les clés <code>'onDelete'</code> et <code>'onUpdate'</code> et donnez leur
- la valeur 'cascade' (ou la constante <code>self::CASCADE</code>). 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 <code>$_referenceMap</code> est mis à <code>self::CASCADE</code>.</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 à <code>self::RESTRICT</code>. Vous auriez
- aussi pu tout simplement ne pas spécifier <code>"onUpdate"</code> .</para>
- <programlisting role="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 role="strong">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 <code>DELETE</code>. 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 <code>UPDATE</code> et <code>DELETE</code> 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 role="strong">Les opérations de cascades de Zend_Db_Table ne sont utilisées que par
- Zend_Db_Table.</emphasis></para>
- <para>Les cascades pour <code>DELETE</code> et <code>UPDATE</code> définies dans vos classes
- <classname>Zend_Db_Table</classname> ne sont utilisées que lors du recours aux méthodes <code>save()</code> ou
- <code>delete()</code> sur les enregistrements <code>Row</code>. Si vous utilisez une autre interface pour
- vos <code>UPDATE</code> ou <code>DELETE</code>, 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 <code>update()</code> et <code>delete()</code> dans la classe
- <classname>Zend_Db_Adapter</classname>.</para>
- <para><emphasis role="strong">Pas d'<code>INSERT</code> en cascade</emphasis></para>
- <para>Le support pour les cascades d'<code>INSERT</code> n'est pas assuré. Vous devez explicitement insérer
- les enregistrements dépendants à un enregistrement parent.</para>
- </sect3>
- </sect2>
- </sect1>
|