Zend_Db_Table-Relationships.xml 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976
  1. <sect1 id="zend.db.table.relationships">
  2. <title>Связи между таблицами Zend_Db_Table</title>
  3. <sect2 id="zend.db.table.relationships.introduction">
  4. <title>Введение</title>
  5. <para>
  6. В реляционной БД таблицы имеют связи друг с другом. Запись в таблице
  7. может быть связана с одной или более записей в другой таблице с
  8. использованием ограничений ссылочной целостности, определенных в
  9. конкретной БД.
  10. </para>
  11. <para>
  12. Класс Zend_Db_Table_Row имеет методы для запрашивания связанных
  13. строк в другой таблице.
  14. </para>
  15. </sect2>
  16. <sect2 id="zend.db.table.relationships.defining">
  17. <title>Определение связей</title>
  18. <para>
  19. Определите классы для каждой из ваших таблиц, расширяя абстрактный
  20. класс Zend_Db_Table_Abstract, как описано в <xref
  21. linkend="zend.db.table.defining" />. Также смотрите описание БД, для
  22. которой написан приведнный ниже код в
  23. <xref linkend="zend.db.adapter.example-database" />
  24. </para>
  25. <para>
  26. Ниже приведено определение классов для этих таблиц:
  27. </para>
  28. <programlisting language="php"><![CDATA[
  29. class Accounts extends Zend_Db_Table_Abstract
  30. {
  31. protected $_name = 'accounts';
  32. protected $_dependentTables = array('Bugs');
  33. }
  34. class Products extends Zend_Db_Table_Abstract
  35. {
  36. protected $_name = 'products';
  37. protected $_dependentTables = array('BugsProducts');
  38. }
  39. class Bugs extends Zend_Db_Table_Abstract
  40. {
  41. protected $_name = 'bugs';
  42. protected $_dependentTables = array('BugsProducts');
  43. protected $_referenceMap = array(
  44. 'Reporter' => array(
  45. 'columns' => 'reported_by',
  46. 'refTableClass' => 'Accounts',
  47. 'refColumns' => 'account_name'
  48. ),
  49. 'Engineer' => array(
  50. 'columns' => 'assigned_to',
  51. 'refTableClass' => 'Accounts',
  52. 'refColumns' => 'account_name'
  53. ),
  54. 'Verifier' => array(
  55. 'columns' => array('verified_by'),
  56. 'refTableClass' => 'Accounts',
  57. 'refColumns' => array('account_name')
  58. )
  59. );
  60. }
  61. class BugsProducts extends Zend_Db_Table_Abstract
  62. {
  63. protected $_name = 'bugs_products';
  64. protected $_referenceMap = array(
  65. 'Bug' => array(
  66. 'columns' => array('bug_id'),
  67. 'refTableClass' => 'Bugs',
  68. 'refColumns' => array('bug_id')
  69. ),
  70. 'Product' => array(
  71. 'columns' => array('product_id'),
  72. 'refTableClass' => 'Products',
  73. 'refColumns' => array('product_id')
  74. )
  75. );
  76. }
  77. ]]>
  78. </programlisting>
  79. <para>
  80. Если вы используете Zend_Db_Table для эмулирования каскадных
  81. операций обновления и удаления, то объявите массив
  82. <varname>$_dependentTables</varname> в классе для
  83. родительской таблицы. Перечислите имена классов всех зависимых
  84. таблиц. Используйте имена классов, а не таблиц в БД.
  85. </para>
  86. <note>
  87. <para>
  88. Пропустите объявление массива <varname>$_dependentTables</varname>,
  89. если используете ограничения ссылочной на сервере СУРБД для
  90. реализации каскадных операций. См.
  91. <xref linkend="zend.db.table.relationships.cascading" /> для
  92. получения более подробной информации.
  93. </para>
  94. </note>
  95. <para>
  96. Объявите массив <varname>$_referenceMap</varname> во всех классах
  97. зависимых таблиц. Это ассоциативный массив "правил связей". Правило
  98. связи определяет, какая таблица является родительской в конкретной
  99. связи, и какие столбцы в зависимой таблице ссылаются на какие
  100. столбцы в родительской таблице.
  101. </para>
  102. <para>
  103. Ключом правила является строка, используемая как индекс массива
  104. <varname>$_referenceMap</varname>. Этот ключ правила используется для
  105. идентификации каждой связи. Выбирайте для него описательное имя.
  106. Лучше всего использовать строку, которая может быть частью имени
  107. метода, как вы увидите позднее.
  108. </para>
  109. <para>
  110. В примере выше ключами правил являются:
  111. <code>'Reporter'</code>,
  112. <code>'Engineer'</code>,
  113. <code>'Verifier'</code>, и
  114. <code>'Product'</code>.
  115. </para>
  116. <para>
  117. Значением каждого правила в массиве <varname>$_referenceMap</varname>
  118. является также ассоциативный массив. Элементы этого массива описаны
  119. ниже:
  120. </para>
  121. <itemizedlist>
  122. <listitem>
  123. <para>
  124. <emphasis>columns</emphasis> =>
  125. Строка или массив строк с имен(ем/ами)
  126. столбцов внешних ключей в зависимой таблице.
  127. </para>
  128. <para>
  129. Обычно это один столбец, но некоторые таблицы имеют
  130. составные ключи.
  131. </para>
  132. </listitem>
  133. <listitem>
  134. <para>
  135. <emphasis>refTableClass</emphasis> =>
  136. Имя класса родительской таблицы. Используйте имя класса, а
  137. не таблицы в БД.
  138. </para>
  139. <para>
  140. Обычно зависимые таблицы имеют одну связь со своей
  141. родительской таблицей, но некоторые таблицы имеют
  142. множественные связи с одной и той же родительской таблицей.
  143. В базе данных, которую мы рассматриваем для примера, таблица
  144. <code>bugs</code> ссылается на таблицу
  145. <code>products</code>, но имеет также три связи с таблицей
  146. <code>accounts</code>.
  147. Помещайте каждую ссылку в отдельную запись в массиве
  148. <varname>$_referenceMap</varname>.
  149. </para>
  150. </listitem>
  151. <listitem>
  152. <para>
  153. <emphasis>refColumns</emphasis> =>
  154. Строка или массив строк, в котором перечислены имена
  155. столбцов первичного ключа в родительской таблице.
  156. </para>
  157. <para>
  158. Обычно это один столбец, но некоторые таблицы имеют
  159. составные ключи. Если ссылка использует составной ключ, то
  160. порядок столбцов в элементе <code>'columns'</code> должен
  161. соответствовать порядку столбцов в элементе
  162. <code>'refColumns'</code>.
  163. </para>
  164. <para>
  165. Этот элемент является опциональным. Если вы не определите
  166. <code>refColumns</code>, то по умолчанию используются имена
  167. столбцов, объявленных как столбцы первичных ключей
  168. родительской таблицы.
  169. </para>
  170. </listitem>
  171. <listitem>
  172. <para>
  173. <emphasis>onDelete</emphasis> =>
  174. Правило для действия, выполняемого, когда в родительской
  175. таблице удаляется строка. См.
  176. <xref linkend="zend.db.table.relationships.cascading" />
  177. для получения более подробной информации.
  178. </para>
  179. </listitem>
  180. <listitem>
  181. <para>
  182. <emphasis>onUpdate</emphasis> =>
  183. Правило для действия, выполняемого, когда изменяются
  184. значения в столбцах первичного ключа родительской таблицы.
  185. См.
  186. <xref linkend="zend.db.table.relationships.cascading" /> для
  187. получения более подробной информации.
  188. </para>
  189. </listitem>
  190. </itemizedlist>
  191. </sect2>
  192. <sect2 id="zend.db.table.relationships.fetching.dependent">
  193. <title>Извлечение зависимых строк</title>
  194. <para>
  195. Если вы имеете объект Row (строка) в результате запроса к
  196. родительской таблице, то можете извлечь строки из зависимых
  197. таблиц, ссылающихся на текущую строку.
  198. Используйте следующий метод:
  199. </para>
  200. <programlisting language="php">
  201. <![CDATA[
  202. $row->findDependentRowset($table, [$rule]);
  203. ]]>
  204. </programlisting>
  205. <para>
  206. Этот метод возвращает объект Zend_Db_Table_Rowset_Abstract,
  207. содержащий набор строк из зависимой таблицы <varname>$table</varname>,
  208. ссылающихся на строку, представленную объектом <varname>$row</varname>.
  209. </para>
  210. <para>
  211. Первый аргумент <varname>$table</varname> может быть строкой с именем
  212. класса зависимой таблицы. Вы можете также определить зависимую
  213. таблицу, используя объект класса этой таблицы.
  214. </para>
  215. <example id="zend.db.table.relationships.fetching.dependent.example">
  216. <title>Извлечение зависимых строк</title>
  217. <para>
  218. Этот пример демонстрирует получение объекта строки из таблицы
  219. <code>Accounts</code> и поиск ошибок в таблице
  220. <code>Bugs</code>, о которых сообщил данный пользователь.
  221. </para>
  222. <programlisting language="php">
  223. <![CDATA[
  224. $accountsTable = new Accounts();
  225. $accountsRowset = $accountsTable->find(1234);
  226. $user1234 = $accountsRowset->current();
  227. $bugsReportedByUser = $user1234->findDependentRowset('Bugs');
  228. ]]>
  229. </programlisting>
  230. </example>
  231. <para>
  232. Втрой аргумент <varname>$rule</varname> является опциональным. Это строка
  233. с ключом правила в массиве <varname>$_referenceMap</varname> класса
  234. зависимой таблицы.
  235. Если вы не определите правило, то будет использоваться первое
  236. правило из массива, ссылающееся на родительскую таблицу.
  237. Для того, чтобы использовать правило, отличное от первого,
  238. необходимо указать ключ.
  239. </para>
  240. <para>
  241. В примере выше ключ правила не определен, поэтому используется
  242. первое правило, соответствующее родительской таблице. Это будет правило <code>'Reporter'</code>.
  243. </para>
  244. <example id="zend.db.table.relationships.fetching.dependent.example-by">
  245. <title>Извлечение зависимых строк по определенному правилу</title>
  246. <para>
  247. Этот пример демонстрирует получение строки из таблицы
  248. <code>Accounts</code> и поиск ошибок в таблице
  249. <code>Bugs</code>, устранение которых назначено данному
  250. пользователю. Ключ правила, соответствующий этой связи в данном
  251. примере - <code>'Engineer'</code>.
  252. </para>
  253. <programlisting language="php"><![CDATA[
  254. $accountsTable = new Accounts();
  255. $accountsRowset = $accountsTable->find(1234);
  256. $user1234 = $accountsRowset->current();
  257. $bugsAssignedToUser = $user1234->findDependentRowset('Bugs', 'Engineer');
  258. ]]>
  259. </programlisting>
  260. </example>
  261. <para>
  262. <example id="zend.db.table.relationships.fetching.dependent.example-by-select">
  263. <title>Извлечение зависимых строк с использованием Zend_Db_Table_Select</title>
  264. <para>
  265. Этот пример демонстрирует получение объекта строки из
  266. таблицы <code>Accounts</code> и поиск ошибок в таблице
  267. <code>Bugs</code>, устранение которых назначено данному
  268. пользователю. При этом извлекается не более 3-х строк и они
  269. должны быть отсортированы по имени.
  270. </para>
  271. <programlisting language="php"><![CDATA[
  272. $accountsTable = new Accounts();
  273. $accountsRowset = $accountsTable->find(1234);
  274. $user1234 = $accountsRowset->current();
  275. $select = $accountsTable->select()->order('name ASC')
  276. ->limit(3);
  277. $bugsAssignedToUser = $user1234->findDependentRowset('Bugs',
  278. 'Engineer',
  279. $select);
  280. ]]>
  281. </programlisting>
  282. </example>
  283. Вы можете также запрашивать строки из зависимой таблицы, используя
  284. специальный механизм -так называемые "магические методы".
  285. Zend_Db_Table_Row_Abstract вызывает метод:
  286. <code>findDependentRowset('&lt;TableClass&gt;', '&lt;Rule&gt;')</code>,
  287. если вы вызываете метод объекта строки,
  288. соответствующий одному из следующих шаблонов:
  289. </para>
  290. <itemizedlist>
  291. <listitem>
  292. <para>
  293. <varname>$row->find&lt;TableClass&gt;()</varname>
  294. </para>
  295. </listitem>
  296. <listitem>
  297. <para>
  298. <varname>$row->find&lt;TableClass&gt;By&lt;Rule&gt;()</varname>
  299. </para>
  300. </listitem>
  301. </itemizedlist>
  302. <para>
  303. В этих шаблонах <code>&lt;TableClass&gt;</code> и
  304. <code>&lt;Rule&gt;</code> являются именем класса зависимой таблицы
  305. и ключом правила зависимой таблицы, ссылающегося на родительскую
  306. таблицу.
  307. </para>
  308. <note>
  309. <para>
  310. Некоторые фреймворки приложений, такие, как Ruby on Rails,
  311. используют механизм, называемый "инфлексией" (inflection), и
  312. состоящий в изменении написания идентификаторов в зависимости от
  313. использования. Для простоты Zend_Db_Table_Row не предоставляет
  314. никакого механизма инфлексии. Имя таблицы и ключ правила в
  315. вызовах методов должны в точности соответствовать написанию
  316. имени класса таблицы и ключа правила при объявлении.
  317. </para>
  318. </note>
  319. <example id="zend.db.table.relationships.fetching.dependent.example-magic">
  320. <title>Извлечение зависимых строк с использованием магического
  321. метода</title>
  322. <para>
  323. Этот пример демонстрирует поиск зависимых строк, эквивалентный
  324. тому, что был в предыдущих примерах. В данном случае приложение
  325. использует вызов магического метода вместо передачи имени
  326. таблицы и ключа правила в качестве аргументов.
  327. </para>
  328. <programlisting language="php"><![CDATA[
  329. $accountsTable = new Accounts();
  330. $accountsRowset = $accountsTable->find(1234);
  331. $user1234 = $accountsRowset->current();
  332. // Используется правило связи по умолчанию
  333. $bugsReportedBy = $user1234->findBugs();
  334. // Задается правило связи
  335. $bugsAssignedTo = $user1234->findBugsByEngineer();
  336. ]]>
  337. </programlisting>
  338. </example>
  339. </sect2>
  340. <sect2 id="zend.db.table.relationships.fetching.parent">
  341. <title>Извлечение родительской строки</title>
  342. <para>
  343. Если вы имеете объект Row в результате запроса к зависимой таблице,
  344. то можете извлечь ту строку из родительской таблицы, на которую
  345. ссылается зависимая строка.
  346. Используйте метод:
  347. </para>
  348. <programlisting language="php"><![CDATA[
  349. $row->findParentRow($table, [$rule]);
  350. ]]>
  351. </programlisting>
  352. <para>
  353. Зависимая строка всегда должна ссылаться только на одну строку в
  354. родительской таблице, поэтому этот метод возвращает объект Row, а не
  355. Rowset.
  356. </para>
  357. <para>
  358. Первый аргумент <varname>$table</varname> может быть строкой с именем
  359. класса родительской таблицы. Вы можете также задавать родительскую
  360. таблицу, используя объект класса этой таблицы.
  361. </para>
  362. <example id="zend.db.table.relationships.fetching.parent.example">
  363. <title>Извлечение родительской строки</title>
  364. <para>
  365. Этот пример демонстрирует получение объекта Row из таблицы
  366. <code>Bugs</code> (для примера, одна из этих ошибок имеет статус
  367. 'NEW') и поиск строки в таблице <code>Accounts</code>,
  368. соответствующей пользователю, сообщившем об этой ошибке.
  369. </para>
  370. <programlisting language="php"><![CDATA[
  371. $bugsTable = new Bugs();
  372. $bugsRowset = $bugsTable->fetchAll(array('bug_status = ?' => 'NEW'));
  373. $bug1 = $bugsRowset->current();
  374. $reporter = $bug1->findParentRow('Accounts');
  375. ]]>
  376. </programlisting>
  377. </example>
  378. <para>
  379. Второй аргумент <varname>$rule</varname> является опциональным. Это строка
  380. с ключом правила в массиве <varname>$_referenceMap</varname> класса
  381. зависимой таблицы.
  382. Если вы не определите правило, то будет использоваться первое
  383. правило в массиве, ссылающееся на родительскую таблицу.
  384. Для того, чтобы использовать правило, отличное от первого,
  385. необходимо указать ключ.
  386. </para>
  387. <para>
  388. В примере кода выше ключ правила не определен, поэтому используется
  389. первое правило, соответствующее родительской таблице. Это будет
  390. правило <code>'Reporter'</code>.
  391. </para>
  392. <example id="zend.db.table.relationships.fetching.parent.example-by">
  393. <title>Извлечение родительской строки по определенному правилу</title>
  394. <para>
  395. Этот пример демонстрирует получение объекта Row из таблицы
  396. <code>Bugs</code> и поиск аккаунта пользователя, которому
  397. назначено исправление этой ошибки. Ключ правила,
  398. соответствующего этой связи в данном примере -
  399. <code>'Engineer'</code>.
  400. </para>
  401. <programlisting language="php"><![CDATA[
  402. $bugsTable = new Bugs();
  403. $bugsRowset = $bugsTable->fetchAll(array('bug_status = ?', 'NEW'));
  404. $bug1 = $bugsRowset->current();
  405. $engineer = $bug1->findParentRow('Accounts', 'Engineer');
  406. ]]>
  407. </programlisting>
  408. </example>
  409. <para>
  410. Вы можете также запрашивать строки из родительской таблицы,
  411. используя "магический метод".
  412. Zend_Db_Table_Row_Abstract вызывает метод:
  413. <code>findParentRow('&lt;TableClass&gt;', '&lt;Rule&gt;')</code>,
  414. если вы вызываете метод объекта Row, соответствующий одному из
  415. следующих шаблонов:
  416. </para>
  417. <itemizedlist>
  418. <listitem>
  419. <para>
  420. <varname>$row->findParent&lt;TableClass&gt;([Zend_Db_Table_Select $select])</varname>
  421. </para>
  422. </listitem>
  423. <listitem>
  424. <para>
  425. <varname>$row->findParent&lt;TableClass&gt;By&lt;Rule&gt;([Zend_Db_Table_Select
  426. $select])</varname>
  427. </para>
  428. </listitem>
  429. </itemizedlist>
  430. <para>
  431. В этих шаблонах <code>&lt;TableClass&gt;</code> и
  432. <code>&lt;Rule&gt;</code> - соответственно имя класса родительской
  433. таблицы и ключ правила зависимой таблицы, ссылающегося на
  434. родительскую таблицу.
  435. </para>
  436. <note>
  437. <para>
  438. Имя таблицы и ключ правила в вызовах методов должны в точности
  439. соответствовать написанию имени класса таблицы и ключа правила
  440. при объявлении.
  441. </para>
  442. </note>
  443. <example id="zend.db.table.relationships.fetching.parent.example-magic">
  444. <title>Извлечение родительской строки с использованием магического метода</title>
  445. <para>
  446. Этот пример демонстрирует поиск родительской строки,
  447. эквивалентный тому, что был в предыдущих примерах. В данном
  448. случае используется вызов магического метода вместо передачи
  449. имени таблицы и ключа правила в качестве аргументов.
  450. </para>
  451. <programlisting language="php"><![CDATA[
  452. $bugsTable = new Bugs();
  453. $bugsRowset = $bugsTable->fetchAll(array('bug_status = ?', 'NEW'));
  454. $bug1 = $bugsRowset->current();
  455. // Используется правило связи по умолчанию
  456. $reporter = $bug1->findParentAccounts();
  457. // Задается правило связи
  458. $engineer = $bug1->findParentAccountsByEngineer();
  459. ]]>
  460. </programlisting>
  461. </example>
  462. </sect2>
  463. <sect2 id="zend.db.table.relationships.fetching.many-to-many">
  464. <title>Извлечение строк через связи "многие-ко-многим"</title>
  465. <para>
  466. Если вы имеете объект Row в результате выполнения запроса к одной из
  467. таблиц, находящихся в связи "многие-ко-многим" (в рамках данного
  468. примера будем называть эту таблицу "исходной"), вы можете извлечь
  469. соответствующие строки в другой таблице ("целевая" таблица) через
  470. таблицу пересечений. Используйте метод:
  471. </para>
  472. <programlisting language="php">
  473. <![CDATA[
  474. $row->findManyToManyRowset($table,
  475. $intersectionTable,
  476. [$rule1,
  477. [$rule2,
  478. [Zend_Db_Table_Select $select]
  479. ]
  480. ]);
  481. ]]>
  482. </programlisting>
  483. <para>
  484. Этот метод возвращает объект Zend_Db_Table_Rowset_Abstract,
  485. содержащий строки из таблицы <varname>$table</varname>, соответствующие
  486. связи "многие-ко-многим". Текущий объект строки <varname>$row</varname>
  487. исходной таблицы используется в поиске строк в таблице пересечений
  488. и производится объединение с целевой таблицей.
  489. </para>
  490. <para>
  491. Первый аргумент <varname>$table</varname> может быть именем класса целевой
  492. таблицы в связи "многие-ко-многим". Вы можете также задавать целевую
  493. таблицу, используя объект класса этой таблицы.
  494. </para>
  495. <para>
  496. Второй аргумент <varname>$intersectionTable</varname> может быть именем
  497. класса таблицы пересечений между двумя таблицами в связи
  498. "многие-ко-многим". Вы можете также задавать таблицу пересечений,
  499. используя объект класса этой таблицы.
  500. </para>
  501. <example id="zend.db.table.relationships.fetching.many-to-many.example">
  502. <title>Извлечение строк через метод для связей "многие-ко-многим"</title>
  503. <para>
  504. Этот пример демонстрирует получение объекта Row из исходной
  505. таблицы <code>Accounts</code> и поиск строк в целевой таблице
  506. <code>Products</code>, соответствующих продуктам, об ошибках в
  507. которых сообщил этот пользователь.
  508. </para>
  509. <programlisting language="php"><![CDATA[
  510. $bugsTable = new Bugs();
  511. $bugsRowset = $bugsTable->find(1234);
  512. $bug1234 = $bugsRowset->current();
  513. $productsRowset = $bug1234->findManyToManyRowset('Products',
  514. 'BugsProducts');
  515. ]]>
  516. </programlisting>
  517. </example>
  518. <para>
  519. Третий и четвертый аргументы - <varname>$rule1</varname> и
  520. <varname>$rule2</varname> - являются опциональными. Это строки с ключами
  521. правил в массиве <varname>$_referenceMap</varname> класса таблицы
  522. пересечений.
  523. </para>
  524. <para>
  525. <varname>$rule1</varname> должен содержать ключ правила для ссылок таблицы
  526. пересечений на исходную таблицу. В данном примере это связь между
  527. таблицами <code>BugsProducts</code> и <code>Bugs</code>.
  528. </para>
  529. <para>
  530. <varname>$rule2</varname> должен содержать ключ правила для ссылок таблицы
  531. пересечений на целевую таблицу. В данном примере это связь между
  532. таблицами <code>Bugs</code> и <code>Products</code>
  533. </para>
  534. <para>
  535. Как и в случае использования методов для извлечения родительских и
  536. зависимых строк, если вы не зададите правило, то метод использует
  537. первое правило в массиве <varname>$_referenceMap</varname>,
  538. соответствующее таблицам в связи. Если нужно использовать другое
  539. правило, то необходимо указать ключ.
  540. </para>
  541. <para>
  542. В примере кода выше ключ правила не указан, поэтому по умолчанию
  543. используются первые подходящие правила из массива. В данном случае
  544. для правила <varname>$rule1</varname> будет использоваться
  545. <code>'Reporter'</code>, для правила <varname>$rule2</varname> -
  546. <code>'Product'</code>.
  547. </para>
  548. <example id="zend.db.table.relationships.fetching.many-to-many.example-by">
  549. <title>Извлечение строк через метод для связей "многие-ко-многим" по определенному правилу</title>
  550. <para>
  551. Этот пример демонстрирует получение объекта Row из исходной
  552. таблицы <code>Bugs</code> и поиск строк в целевой таблице,
  553. <code>Products</code>, соответствующих продуктам, к
  554. которым относится данная ошибка.
  555. </para>
  556. <programlisting language="php">
  557. <![CDATA[
  558. $bugsTable = new Bugs();
  559. $bugsRowset = $bugsTable->find(1234);
  560. $bug1234 = $bugsRowset->current();
  561. $productsRowset = $bug1234->findManyToManyRowset('Products',
  562. 'BugsProducts',
  563. 'Bug');
  564. ]]>
  565. </programlisting>
  566. </example>
  567. <para>
  568. Вы можете также запрашивать строки из целевой таблицы в связи
  569. "многие-ко-многим", используя "магический метод".
  570. Zend_Db_Table_Row_Abstract вызывает метод
  571. <code>findManyToManyRowset('&lt;TableClass&gt;', '&lt;IntersectionTableClass&gt;', '&lt;Rule1&gt;', '&lt;Rule2&gt;')</code>, если вы вызываете метод, соотвествующий
  572. одному из следующих шаблонов:
  573. </para>
  574. <itemizedlist>
  575. <listitem>
  576. <para>
  577. <varname>$row->find&lt;TableClass&gt;Via&lt;IntersectionTableClass&gt;
  578. ([Zend_Db_Table_Select $select])</varname>
  579. </para>
  580. </listitem>
  581. <listitem>
  582. <para>
  583. <varname>$row->find&lt;TableClass&gt;Via&lt;IntersectionTableClass&gt;By&lt;Rule1&gt;
  584. ([Zend_Db_Table_Select $select])</varname>
  585. </para>
  586. </listitem>
  587. <listitem>
  588. <para>
  589. <varname>$row->find&lt;TableClass&gt;Via&lt;IntersectionTableClass&gt;By&lt;Rule1&gt;And&lt;Rule2&gt;
  590. ([Zend_Db_Table_Select $select])</varname>
  591. </para>
  592. </listitem>
  593. </itemizedlist>
  594. <para>
  595. В этих шаблонах <code>&lt;TableClass&gt;</code> и
  596. <code>&lt;IntersectionTableClass&gt;</code> являются именами классов
  597. целевой таблицы и таблицы пересечений
  598. соответственно. <code>&lt;Rule1&gt;</code> и
  599. <code>&lt;Rule2&gt;</code> являются ключами правил в таблице
  600. пересечений, соответствующими исходной таблице и целевой таблице,
  601. соответственно.
  602. </para>
  603. <note>
  604. <para>
  605. Имя таблицы и ключ правила в вызовах методов должны в точности
  606. соответствовать написанию имени класса таблицы и ключа правила
  607. при объявлении.
  608. </para>
  609. </note>
  610. <example id="zend.db.table.relationships.fetching.many-to-many.example-magic">
  611. <title>Извлечение строк с использованием магического метода для связей "многие-ко-многим"</title>
  612. <para>
  613. Этот пример демонстрирует поиск в целевой таблице в связи
  614. "многие-ко многим" строк, соответствующих продуктам, к которым
  615. относится данная ошибка.
  616. </para>
  617. <programlisting language="php">
  618. <![CDATA[
  619. $bugsTable = new Bugs();
  620. $bugsRowset = $bugsTable->find(1234);
  621. $bug1234 = $bugsRowset->current();
  622. // Используется правило связи по умолчанию
  623. $products = $bug1234->findProductsViaBugsProducts();
  624. // Задается правило связи
  625. $products = $bug1234->findProductsViaBugsProductsByBug();
  626. ]]>
  627. </programlisting>
  628. </example>
  629. </sect2>
  630. <sect2 id="zend.db.table.relationships.cascading">
  631. <title>Каскадные операции записи</title>
  632. <note>
  633. <title>Объявление DRI в БД</title>
  634. <para>
  635. Объявление каскадных операций в Zend_Db_Table предназначено
  636. <emphasis>только</emphasis> для тех СУРБД, которые
  637. не поддерживают декларативной ссылочной целостности (declarative
  638. referential integrity - сокр. DRI).
  639. </para>
  640. <para>
  641. Например, если вы используете механизм хранения MyISAM в MySQL
  642. или SQLite, не поддерживающие DRI, то для вас может быть
  643. полезным объявить каскадные операции через Zend_Db_Table.
  644. </para>
  645. <para>
  646. Если ваша СУРБД реализует DRI и поддерживает предложения
  647. <code>ON DELETE</code> и <code>ON UPDATE</code>, то вам следует
  648. объявить эти предложения в вашей БД вместо использования
  649. каскадных возможностей Zend_Db_Table.
  650. Объявление каскадных правил DRI в СУРБД лучше в плане
  651. производительности, стабильности работы с БД и целостности
  652. данных.
  653. </para>
  654. <para>
  655. Тем более, не объявляйте каскадные операции
  656. одновременно в СУРБД и в классе Zend_Db_Table.
  657. </para>
  658. </note>
  659. <para>
  660. Вы можете объявить каскадные операции для их выполнения в зависимой
  661. таблице при применении операций <code>UPDATE</code> и
  662. <code>DELETE</code> к строкам в родительской таблице.
  663. </para>
  664. <example id="zend.db.table.relationships.cascading.example-delete">
  665. <title>Пример каскадного удаления</title>
  666. <para>
  667. Этот пример демонстрирует удаление строки в таблице
  668. <code>Products</code>, которая была сконфигурирована для
  669. автоматического удаления зависимых строк в таблице
  670. <code>Bugs</code>.
  671. </para>
  672. <programlisting language="php">
  673. <![CDATA[
  674. $productsTable = new Products();
  675. $productsRowset = $productsTable->find(1234);
  676. $product1234 = $productsRowset->current();
  677. $product1234->delete();
  678. // Автоматически выполняется каскадное
  679. // удаление зависимых строк в таблице Bugs
  680. ]]>
  681. </programlisting>
  682. </example>
  683. <para>
  684. Аналогично, если вы используете <code>UPDATE</code> для изменения
  685. значения первичного ключа в родительской таблице, то при этом часто
  686. требуется, чтобы значение внешнего ключа в зависимой таблице также
  687. изменялось на новое, и таким образом поддерживалась ссылочная
  688. целостность.
  689. </para>
  690. <para>
  691. Обычно нет необходимости в том, чтобы изменять значение первичного
  692. ключа, которое генерируется последовательностью (sequence) или
  693. другим механизмом. Но если вы используете <emphasis>естетственные
  694. ключи</emphasis>, которые иногда могут изменять свое значение, то,
  695. скорее всего, нужно будет использовать каскадное обновление
  696. зависимых таблиц.
  697. </para>
  698. <para>
  699. Для объявления каскадных связей в Zend_Db_Table отредактируйте
  700. правила в массиве <varname>$_referenceMap</varname>. Установите в
  701. ассоциативного массиве под ключами <code>'onDelete'</code> и
  702. <code>'onUpdate'</code> значение 'cascade' (или константу
  703. <code>self::CASCADE</code>). До того, как строка будет удалена из
  704. родительской таблицы или изменится значение ее первичного ключа,
  705. будут удалены или обновлены любые строки в зависимой таблице,
  706. ссылающиеся на эту строку родительской таблицы.
  707. </para>
  708. <example id="zend.db.table.relationships.cascading.example-declaration">
  709. <title>Пример объявления каскадных операций</title>
  710. <para>
  711. В примере ниже строки в таблице <code>Bugs</code> автоматически
  712. удаляются, если строка в таблице <code>Products</code>, на
  713. которую они ссылаются, удаляется. Элемент
  714. <code>'onDelete'</code> записи в массиве связей установлен в
  715. <code>self::CASCADE</code>.
  716. </para>
  717. <para>
  718. В примере ниже не выполняется каскадное обновление, если
  719. изменяется значение первичного ключа. Элемент
  720. <code>'onUpdate'</code> записи в массиве связей установлен в
  721. <code>self::RESTRICT</code>. Вы можете получить тот же самый
  722. результат, используя значение <code>self::NO_ACTION</code> или
  723. пропустив элемент <code>'onUpdate'</code>.
  724. </para>
  725. <programlisting language="php"><![CDATA[
  726. class BugsProducts extends Zend_Db_Table_Abstract
  727. {
  728. ...
  729. protected $_referenceMap = array(
  730. 'Product' => array(
  731. 'columns' => array('product_id'),
  732. 'refTableClass' => 'Products',
  733. 'refColumns' => array('product_id'),
  734. 'onDelete' => self::CASCADE,
  735. 'onUpdate' => self::RESTRICT
  736. ),
  737. ...
  738. );
  739. }
  740. ]]>
  741. </programlisting>
  742. </example>
  743. <sect3 id="zend.db.table.relationships.cascading.notes">
  744. <title>Примечания относительно каскадных операций</title>
  745. <para>
  746. <emphasis>Каскадные операции, вызываемые
  747. Zend_Db_Table, не являются атомарными.</emphasis>
  748. </para>
  749. <para>
  750. Это означает, что если ваша БД реализует ограничения ссылочной
  751. целостности и принуждает к их использованию, то каскадное
  752. обновление, выполняемое классом Zend_Db_Table, конфликтует с
  753. этими ограничениями и результатом будет нарушение ссылочной
  754. целостности.
  755. Вы можете использовать каскадное обновление в Zend_Db_Table
  756. <emphasis>только</emphasis> если когда ваша БД не принуждает к
  757. использованию ограничений ссылочной целостности.
  758. </para>
  759. <para>
  760. Каскадное удаление меньше страдает от проблем нарушения
  761. ссылочной целостности. Вы можете удалить зависимые строки в
  762. неатомарном действии до удаления родительской строки, на
  763. которую они ссылаются.
  764. </para>
  765. <para>
  766. Тем не менее, неатомарность операций изменения и удаления в
  767. БД приводит к тому, что есть риск того, что другой пользователь
  768. БД будет видеть противоречивые данные. Например, если вы удалите
  769. строку и все зависимые строки, то есть небольшая вероятность
  770. того, что другой клиент может сделать запрос к БД после того,
  771. как вы удалили зависимые строки, но до того, как вы удалите
  772. родительскую строку. Эта клиентская программа может увидеть
  773. родительскую строку без зависимых строк и решить, что это
  774. задуманное состояние данных.
  775. </para>
  776. <para>
  777. Проблема неатомарных измнений может быть частично решена
  778. использованием транзакций для изолирования ваших изменений. Но
  779. некоторые СУРБД не поддерживают транзакции или позволяют
  780. клиентам читать "грязные" изменения в БД, которые не были еще
  781. зафиксированы.
  782. </para>
  783. <para>
  784. <emphasis>Каскадные операции в Zend_Db_Table вызываются только классом Zend_Db_Table</emphasis>
  785. </para>
  786. <para>
  787. Каскадные операции удаления и добавления, объявленные в ваших
  788. классах Zend_Db_Table выполняются, если вы выполняете методы
  789. <code>save()</code> и <code>delete()</code> класса Row.
  790. Но если вы обновляете или удаляете данные, используя другой
  791. интерфейс, например, утилиту запросов или другое приложение, то
  792. каскадные операции не выполняются.
  793. Даже когда используются методы <code>update()</code> и
  794. <code>delete()</code> класса Zend_Db_Adapter, каскадные
  795. операции, определенные в ваших классах Zend_Db_Table, не
  796. выполняются.
  797. </para>
  798. <para>
  799. <emphasis>Не существует каскадного добавления
  800. <code>INSERT</code>.</emphasis>
  801. </para>
  802. <para>
  803. Не поддерживается каскадное добавление <code>INSERT</code>. Вы
  804. должны добавить строку в родительской таблице в одной операции и
  805. добавить строки в зависимой таблице в другой операции.
  806. </para>
  807. </sect3>
  808. </sect2>
  809. </sect1>
  810. <!--
  811. vim:se ts=4 sw=4 et:
  812. -->