<sect1 id="zend.db.table.relationships">

    <title>Связи между таблицами Zend_Db_Table</title>

    <sect2 id="zend.db.table.relationships.introduction">

        <title>Введение</title>

        <para>
            В реляционной БД таблицы имеют связи друг с другом. Запись в таблице
            может быть связана с одной или более записей в другой таблице с
            использованием ограничений ссылочной целостности, определенных в
            конкретной БД.
        </para>

        <para>
            Класс Zend_Db_Table_Row имеет методы для запрашивания связанных
            строк в другой таблице.
        </para>

    </sect2>

    <sect2 id="zend.db.table.relationships.defining">

        <title>Определение связей</title>

        <para>
            Определите классы для каждой из ваших таблиц, расширяя абстрактный
            класс Zend_Db_Table_Abstract, как описано в <xref
            linkend="zend.db.table.defining" />. Также смотрите описание БД, для
            которой написан приведнный ниже код в
            <xref linkend="zend.db.adapter.example-database" />
        </para>

        <para>
            Ниже приведено определение классов для этих таблиц:
        </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>
            Если вы используете Zend_Db_Table для эмулирования каскадных
            операций обновления и удаления, то объявите массив
            <varname>$_dependentTables</varname> в классе для
            родительской таблицы. Перечислите имена классов всех зависимых
            таблиц. Используйте имена классов, а не таблиц в БД.
        </para>

        <note>
            <para>
                Пропустите объявление массива <varname>$_dependentTables</varname>,
                если используете ограничения ссылочной на сервере СУРБД для
                реализации каскадных операций. См.
                <xref linkend="zend.db.table.relationships.cascading" /> для
                получения более подробной информации.
            </para>
        </note>

        <para>
            Объявите массив <varname>$_referenceMap</varname> во всех классах
            зависимых таблиц. Это ассоциативный массив "правил связей". Правило
            связи определяет, какая таблица является родительской в конкретной
            связи, и какие столбцы в зависимой таблице ссылаются на какие
            столбцы в родительской таблице.
        </para>

        <para>
            Ключом правила является строка, используемая как индекс массива
            <varname>$_referenceMap</varname>. Этот ключ правила используется для
            идентификации каждой связи. Выбирайте для него описательное имя.
            Лучше всего использовать строку, которая может быть частью имени
            метода, как вы увидите позднее.
        </para>

        <para>
            В примере выше ключами правил являются:
            <code>'Reporter'</code>,
            <code>'Engineer'</code>,
            <code>'Verifier'</code>, и
            <code>'Product'</code>.
        </para>

        <para>
            Значением каждого правила в массиве <varname>$_referenceMap</varname>
            является также ассоциативный массив. Элементы этого массива описаны
            ниже:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <emphasis>columns</emphasis> =>
                    Строка или массив строк с имен(ем/ами)
                    столбцов внешних ключей в зависимой таблице.
                </para>
                <para>
                    Обычно это один столбец, но некоторые таблицы имеют
                    составные ключи.
                </para>
            </listitem>
            <listitem>
                <para>
                    <emphasis>refTableClass</emphasis> =>
                    Имя класса родительской таблицы. Используйте имя класса, а
                    не таблицы в БД.
                </para>
                <para>
                    Обычно зависимые таблицы имеют одну связь со своей
                    родительской таблицей, но некоторые таблицы имеют
                    множественные связи с одной и той же родительской таблицей.
                    В базе данных, которую мы рассматриваем для примера, таблица
                    <code>bugs</code> ссылается на таблицу
                    <code>products</code>, но имеет также три связи с таблицей
                    <code>accounts</code>.
                    Помещайте каждую ссылку в отдельную запись в массиве
                    <varname>$_referenceMap</varname>.
                </para>
            </listitem>
            <listitem>
                <para>
                    <emphasis>refColumns</emphasis> =>
                    Строка или массив строк, в котором перечислены имена
                    столбцов первичного ключа в родительской таблице.
                </para>
                <para>
                    Обычно это один столбец, но некоторые таблицы имеют
                    составные ключи. Если ссылка использует составной ключ, то
                    порядок столбцов в элементе <code>'columns'</code> должен
                    соответствовать порядку столбцов в элементе
                    <code>'refColumns'</code>.
                </para>
                <para>
                    Этот элемент является опциональным. Если вы не определите
                    <code>refColumns</code>, то по умолчанию используются имена
                    столбцов, объявленных как столбцы первичных ключей
                    родительской таблицы.
                </para>
            </listitem>
            <listitem>
                <para>
                    <emphasis>onDelete</emphasis> =>
                    Правило для действия, выполняемого, когда в родительской
                    таблице удаляется строка. См.
                    <xref linkend="zend.db.table.relationships.cascading" />
                    для получения более подробной информации.
                </para>
            </listitem>
            <listitem>
                <para>
                    <emphasis>onUpdate</emphasis> =>
                    Правило для действия, выполняемого, когда изменяются
                    значения в столбцах первичного ключа родительской таблицы.
                    См.
                    <xref linkend="zend.db.table.relationships.cascading" /> для
                    получения более подробной информации.
                </para>
            </listitem>
        </itemizedlist>

    </sect2>

    <sect2 id="zend.db.table.relationships.fetching.dependent">

        <title>Извлечение зависимых строк</title>

        <para>
            Если вы имеете объект Row (строка) в результате запроса к
            родительской таблице, то можете извлечь строки из зависимых
            таблиц, ссылающихся на текущую строку.
            Используйте следующий метод:
        </para>

        <programlisting language="php">
<![CDATA[
$row->findDependentRowset($table, [$rule]);
]]>
        </programlisting>

        <para>
            Этот метод возвращает объект Zend_Db_Table_Rowset_Abstract,
            содержащий набор строк из зависимой таблицы <varname>$table</varname>,
            ссылающихся на строку, представленную объектом <varname>$row</varname>.
        </para>

        <para>
            Первый аргумент <varname>$table</varname> может быть строкой с именем
            класса зависимой таблицы. Вы можете также определить зависимую
            таблицу, используя объект класса этой таблицы.
        </para>

        <example id="zend.db.table.relationships.fetching.dependent.example">

            <title>Извлечение зависимых строк</title>

            <para>
                Этот пример демонстрирует получение объекта строки из таблицы
                <code>Accounts</code> и поиск ошибок в таблице
                <code>Bugs</code>, о которых сообщил данный пользователь.
            </para>

            <programlisting language="php">
<![CDATA[
$accountsTable = new Accounts();
$accountsRowset = $accountsTable->find(1234);
$user1234 = $accountsRowset->current();

$bugsReportedByUser = $user1234->findDependentRowset('Bugs');
]]>
            </programlisting>

        </example>

        <para>
            Втрой аргумент <varname>$rule</varname> является опциональным. Это строка
            с ключом правила в массиве <varname>$_referenceMap</varname> класса
            зависимой таблицы.
            Если вы не определите правило, то будет использоваться первое
            правило из массива, ссылающееся на родительскую таблицу.
            Для того, чтобы использовать правило, отличное от первого,
            необходимо указать ключ.
        </para>

        <para>
            В примере выше ключ правила не определен, поэтому используется
            первое правило, соответствующее родительской таблице. Это будет правило <code>'Reporter'</code>.
        </para>

        <example id="zend.db.table.relationships.fetching.dependent.example-by">

            <title>Извлечение зависимых строк по определенному правилу</title>

            <para>
                Этот пример демонстрирует получение строки из таблицы
                <code>Accounts</code> и поиск ошибок в таблице
                <code>Bugs</code>, устранение которых назначено данному
                пользователю. Ключ правила, соответствующий этой связи в данном
                примере - <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>

            <example id="zend.db.table.relationships.fetching.dependent.example-by-select">

                <title>Извлечение зависимых строк с использованием Zend_Db_Table_Select</title>

                <para>
                    Этот пример демонстрирует получение объекта строки из
                    таблицы <code>Accounts</code> и поиск ошибок в таблице
                    <code>Bugs</code>, устранение которых назначено данному
                    пользователю. При этом извлекается не более 3-х строк и они
                    должны быть отсортированы по имени.
                </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>

            Вы можете также запрашивать строки из зависимой таблицы, используя
            специальный механизм -так называемые "магические методы".
            Zend_Db_Table_Row_Abstract вызывает метод:
            <code>findDependentRowset('&lt;TableClass&gt;', '&lt;Rule&gt;')</code>,
            если вы вызываете метод объекта строки,
            соответствующий одному из следующих шаблонов:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <varname>$row->find&lt;TableClass&gt;()</varname>
                </para>
            </listitem>
            <listitem>
                <para>
                    <varname>$row->find&lt;TableClass&gt;By&lt;Rule&gt;()</varname>
                </para>
            </listitem>
        </itemizedlist>

        <para>
            В этих шаблонах <code>&lt;TableClass&gt;</code> и
            <code>&lt;Rule&gt;</code> являются именем класса зависимой таблицы
            и ключом правила зависимой таблицы, ссылающегося на родительскую
            таблицу.
        </para>

        <note>

            <para>
                Некоторые фреймворки приложений, такие, как Ruby on Rails,
                используют механизм, называемый "инфлексией" (inflection), и
                состоящий в изменении написания идентификаторов в зависимости от
                использования. Для простоты Zend_Db_Table_Row не предоставляет
                никакого механизма инфлексии. Имя таблицы и ключ правила в
                вызовах методов должны в точности соответствовать написанию
                имени класса таблицы и ключа правила при объявлении.
            </para>

        </note>

        <example id="zend.db.table.relationships.fetching.dependent.example-magic">

            <title>Извлечение зависимых строк с использованием магического
            метода</title>

            <para>
                Этот пример демонстрирует поиск зависимых строк, эквивалентный
                тому, что был в предыдущих примерах. В данном случае приложение
                использует вызов магического метода вместо передачи имени
                таблицы и ключа правила в качестве аргументов.
            </para>

            <programlisting language="php"><![CDATA[
$accountsTable = new Accounts();
$accountsRowset = $accountsTable->find(1234);
$user1234 = $accountsRowset->current();

// Используется правило связи по умолчанию
$bugsReportedBy = $user1234->findBugs();

// Задается правило связи
$bugsAssignedTo = $user1234->findBugsByEngineer();
]]>
            </programlisting>

        </example>

    </sect2>

    <sect2 id="zend.db.table.relationships.fetching.parent">

        <title>Извлечение родительской строки</title>

        <para>
            Если вы имеете объект Row в результате запроса к зависимой таблице,
            то можете извлечь ту строку из родительской таблицы, на которую
            ссылается зависимая строка.
            Используйте метод:
        </para>

        <programlisting language="php"><![CDATA[
$row->findParentRow($table, [$rule]);
]]>
        </programlisting>

        <para>
            Зависимая строка всегда должна ссылаться только на одну строку в
            родительской таблице, поэтому этот метод возвращает объект Row, а не
            Rowset.
        </para>

        <para>
            Первый аргумент <varname>$table</varname> может быть строкой с именем
            класса родительской таблицы. Вы можете также задавать родительскую
            таблицу, используя объект класса этой таблицы.
        </para>

        <example id="zend.db.table.relationships.fetching.parent.example">

            <title>Извлечение родительской строки</title>

            <para>
                Этот пример демонстрирует получение объекта Row из таблицы
                <code>Bugs</code> (для примера, одна из этих ошибок имеет статус
                'NEW') и поиск строки в таблице <code>Accounts</code>,
                соответствующей пользователю, сообщившем об этой ошибке.
            </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>
            Второй аргумент <varname>$rule</varname> является опциональным. Это строка
            с ключом правила в массиве <varname>$_referenceMap</varname> класса
            зависимой таблицы.
            Если вы не определите правило, то будет использоваться первое
            правило в массиве, ссылающееся на родительскую таблицу.
            Для того, чтобы использовать правило, отличное от первого,
            необходимо указать ключ.
        </para>

        <para>
            В примере кода выше ключ правила не определен, поэтому используется
            первое правило, соответствующее родительской таблице. Это будет
            правило <code>'Reporter'</code>.
        </para>

        <example id="zend.db.table.relationships.fetching.parent.example-by">

            <title>Извлечение родительской строки по определенному правилу</title>

            <para>
                Этот пример демонстрирует получение объекта Row из таблицы
                <code>Bugs</code> и поиск аккаунта пользователя, которому
                назначено исправление этой ошибки. Ключ правила,
                соответствующего этой связи в данном примере -
                <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>
            Вы можете также запрашивать строки из родительской таблицы,
            используя "магический метод".
            Zend_Db_Table_Row_Abstract вызывает метод:
            <code>findParentRow('&lt;TableClass&gt;', '&lt;Rule&gt;')</code>,
            если вы вызываете метод объекта Row, соответствующий одному из
            следующих шаблонов:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <varname>$row->findParent&lt;TableClass&gt;([Zend_Db_Table_Select $select])</varname>
                </para>
            </listitem>

            <listitem>
                <para>
                    <varname>$row->findParent&lt;TableClass&gt;By&lt;Rule&gt;([Zend_Db_Table_Select
                    $select])</varname>
                </para>
            </listitem>
        </itemizedlist>

        <para>
            В этих шаблонах <code>&lt;TableClass&gt;</code> и
            <code>&lt;Rule&gt;</code> - соответственно имя класса родительской
            таблицы и ключ правила зависимой таблицы, ссылающегося на
            родительскую таблицу.
        </para>

        <note>

            <para>
                Имя таблицы и ключ правила в вызовах методов должны в точности
                соответствовать написанию имени класса таблицы и ключа правила
                при объявлении.
            </para>

        </note>

        <example id="zend.db.table.relationships.fetching.parent.example-magic">

            <title>Извлечение родительской строки с использованием магического метода</title>

            <para>
                Этот пример демонстрирует поиск родительской строки,
                эквивалентный тому, что был в предыдущих примерах. В данном
                случае используется вызов магического метода вместо передачи
                имени таблицы и ключа правила в качестве аргументов.
            </para>

            <programlisting language="php"><![CDATA[
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->fetchAll(array('bug_status = ?', 'NEW'));
$bug1 = $bugsRowset->current();

// Используется правило связи по умолчанию
$reporter = $bug1->findParentAccounts();

// Задается правило связи
$engineer = $bug1->findParentAccountsByEngineer();
]]>
            </programlisting>

        </example>

    </sect2>

    <sect2 id="zend.db.table.relationships.fetching.many-to-many">

        <title>Извлечение строк через связи "многие-ко-многим"</title>

        <para>
            Если вы имеете объект Row в результате выполнения запроса к одной из
            таблиц, находящихся в связи "многие-ко-многим" (в рамках данного
            примера будем называть эту таблицу "исходной"), вы можете извлечь
            соответствующие строки в другой таблице ("целевая" таблица) через
            таблицу пересечений. Используйте метод:
        </para>

        <programlisting language="php">
<![CDATA[
$row->findManyToManyRowset($table,
                           $intersectionTable,
                           [$rule1,
                               [$rule2,
                                   [Zend_Db_Table_Select $select]
                               ]
                           ]);
]]>
        </programlisting>

        <para>
            Этот метод возвращает объект Zend_Db_Table_Rowset_Abstract,
            содержащий строки из таблицы <varname>$table</varname>, соответствующие
            связи "многие-ко-многим". Текущий объект строки <varname>$row</varname>
            исходной таблицы используется в поиске строк в таблице пересечений
            и производится объединение с целевой таблицей.
        </para>

        <para>
            Первый аргумент <varname>$table</varname> может быть именем класса целевой
            таблицы в связи "многие-ко-многим". Вы можете также задавать целевую
            таблицу, используя объект класса этой таблицы.
        </para>

        <para>
            Второй аргумент <varname>$intersectionTable</varname> может быть именем
            класса таблицы пересечений между двумя таблицами в связи
            "многие-ко-многим". Вы можете также задавать таблицу пересечений,
            используя объект класса этой таблицы.
        </para>

        <example id="zend.db.table.relationships.fetching.many-to-many.example">

            <title>Извлечение строк через метод для связей "многие-ко-многим"</title>

            <para>
                Этот пример демонстрирует получение объекта Row из исходной
                таблицы <code>Accounts</code> и поиск строк в целевой таблице
                <code>Products</code>, соответствующих продуктам, об ошибках в
                которых сообщил этот пользователь.
            </para>

            <programlisting language="php"><![CDATA[
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->find(1234);
$bug1234 = $bugsRowset->current();

$productsRowset = $bug1234->findManyToManyRowset('Products',
                                                 'BugsProducts');
]]>
            </programlisting>

        </example>

        <para>
            Третий и четвертый аргументы - <varname>$rule1</varname> и
            <varname>$rule2</varname> - являются опциональными. Это строки с ключами
            правил в массиве <varname>$_referenceMap</varname> класса таблицы
            пересечений.
        </para>

        <para>
            <varname>$rule1</varname> должен содержать ключ правила для ссылок таблицы
            пересечений на исходную таблицу. В данном примере это связь между
            таблицами <code>BugsProducts</code> и <code>Bugs</code>.
        </para>

        <para>
            <varname>$rule2</varname> должен содержать ключ правила для ссылок таблицы
            пересечений на целевую таблицу. В данном примере это связь между
            таблицами <code>Bugs</code> и <code>Products</code>
        </para>

        <para>
            Как и в случае использования методов для извлечения родительских и
            зависимых строк, если вы не зададите правило, то метод использует
            первое правило в массиве <varname>$_referenceMap</varname>,
            соответствующее таблицам в связи. Если нужно использовать другое
            правило, то необходимо указать ключ.
        </para>

        <para>
            В примере кода выше ключ правила не указан, поэтому по умолчанию
            используются первые подходящие правила из массива. В данном случае
            для правила <varname>$rule1</varname> будет использоваться
            <code>'Reporter'</code>, для правила <varname>$rule2</varname> -
            <code>'Product'</code>.
        </para>

        <example id="zend.db.table.relationships.fetching.many-to-many.example-by">

            <title>Извлечение строк через метод для связей "многие-ко-многим" по определенному правилу</title>

            <para>
                Этот пример демонстрирует получение объекта Row из исходной
                таблицы <code>Bugs</code> и поиск строк в целевой таблице,
                <code>Products</code>, соответствующих продуктам, к
                которым относится данная ошибка.
            </para>

            <programlisting language="php">
<![CDATA[
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->find(1234);
$bug1234 = $bugsRowset->current();

$productsRowset = $bug1234->findManyToManyRowset('Products',
                                                 'BugsProducts',
                                                 'Bug');
]]>
            </programlisting>

        </example>

        <para>
            Вы можете также запрашивать строки из целевой таблицы в связи
            "многие-ко-многим", используя "магический метод".
            Zend_Db_Table_Row_Abstract вызывает метод
            <code>findManyToManyRowset('&lt;TableClass&gt;', '&lt;IntersectionTableClass&gt;', '&lt;Rule1&gt;', '&lt;Rule2&gt;')</code>, если вы вызываете метод, соотвествующий
            одному из следующих шаблонов:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <varname>$row->find&lt;TableClass&gt;Via&lt;IntersectionTableClass&gt;
                    ([Zend_Db_Table_Select $select])</varname>
                </para>
            </listitem>

            <listitem>
                <para>
                    <varname>$row->find&lt;TableClass&gt;Via&lt;IntersectionTableClass&gt;By&lt;Rule1&gt;
                    ([Zend_Db_Table_Select $select])</varname>
                </para>
            </listitem>

            <listitem>
                <para>
                    <varname>$row->find&lt;TableClass&gt;Via&lt;IntersectionTableClass&gt;By&lt;Rule1&gt;And&lt;Rule2&gt;
                    ([Zend_Db_Table_Select $select])</varname>
                </para>
            </listitem>
        </itemizedlist>

        <para>
            В этих шаблонах <code>&lt;TableClass&gt;</code> и
            <code>&lt;IntersectionTableClass&gt;</code> являются именами классов
            целевой таблицы и таблицы пересечений
            соответственно. <code>&lt;Rule1&gt;</code> и
            <code>&lt;Rule2&gt;</code> являются ключами правил в таблице
            пересечений, соответствующими исходной таблице и целевой таблице,
            соответственно.
        </para>

        <note>
            <para>
                Имя таблицы и ключ правила в вызовах методов должны в точности
                соответствовать написанию имени класса таблицы и ключа правила
                при объявлении.
            </para>
        </note>

        <example id="zend.db.table.relationships.fetching.many-to-many.example-magic">

            <title>Извлечение строк с использованием магического метода для связей "многие-ко-многим"</title>

            <para>
                Этот пример демонстрирует поиск в целевой таблице в связи
                "многие-ко многим" строк, соответствующих продуктам, к которым
                относится данная ошибка.
            </para>

            <programlisting language="php">
<![CDATA[
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->find(1234);
$bug1234 = $bugsRowset->current();

// Используется правило связи по умолчанию
$products = $bug1234->findProductsViaBugsProducts();

// Задается правило связи
$products = $bug1234->findProductsViaBugsProductsByBug();
]]>
            </programlisting>

        </example>

    </sect2>

    <sect2 id="zend.db.table.relationships.cascading">

        <title>Каскадные операции записи</title>

        <note>
            <title>Объявление DRI в БД</title>
            <para>
                Объявление каскадных операций в Zend_Db_Table предназначено
                <emphasis>только</emphasis> для тех СУРБД, которые
                не поддерживают декларативной ссылочной целостности (declarative
                referential integrity - сокр. DRI).
            </para>
            <para>
                Например, если вы используете механизм хранения MyISAM в MySQL
                или SQLite, не поддерживающие DRI, то для вас может быть
                полезным объявить каскадные операции через Zend_Db_Table.
            </para>
            <para>
                Если ваша СУРБД реализует DRI и поддерживает предложения
                <code>ON DELETE</code> и <code>ON UPDATE</code>, то вам следует
                объявить эти предложения в вашей БД вместо использования
                каскадных возможностей Zend_Db_Table.
                Объявление каскадных правил DRI в СУРБД лучше в плане
                производительности, стабильности работы с БД и целостности
                данных.
            </para>
            <para>
                Тем более, не объявляйте каскадные операции
                одновременно в СУРБД и в классе Zend_Db_Table.
            </para>
        </note>

        <para>
            Вы можете объявить каскадные операции для их выполнения в зависимой
            таблице при применении операций <code>UPDATE</code> и
            <code>DELETE</code> к строкам в родительской таблице.
        </para>

        <example id="zend.db.table.relationships.cascading.example-delete">
            <title>Пример каскадного удаления</title>
            <para>
                Этот пример демонстрирует удаление строки в таблице
                <code>Products</code>, которая была сконфигурирована для
                автоматического удаления зависимых строк в таблице
                <code>Bugs</code>.
            </para>
            <programlisting language="php">
<![CDATA[
$productsTable  = new Products();
$productsRowset = $productsTable->find(1234);
$product1234    = $productsRowset->current();

$product1234->delete();
// Автоматически выполняется каскадное
// удаление зависимых строк в таблице Bugs
]]>
            </programlisting>
        </example>

        <para>
            Аналогично, если вы используете <code>UPDATE</code> для изменения
            значения первичного ключа в родительской таблице, то при этом часто
            требуется, чтобы значение внешнего ключа в зависимой таблице также
            изменялось на новое, и таким образом поддерживалась ссылочная
            целостность.
        </para>

        <para>
            Обычно нет необходимости в том, чтобы изменять значение первичного
            ключа, которое генерируется последовательностью (sequence) или
            другим механизмом. Но если вы используете <emphasis>естетственные
            ключи</emphasis>, которые иногда могут изменять свое значение, то,
            скорее всего, нужно будет использовать каскадное обновление
            зависимых таблиц.
        </para>

        <para>
            Для объявления каскадных связей в Zend_Db_Table отредактируйте
            правила в массиве <varname>$_referenceMap</varname>. Установите в
            ассоциативного массиве под ключами  <code>'onDelete'</code> и
            <code>'onUpdate'</code> значение 'cascade' (или константу
            <code>self::CASCADE</code>). До того, как строка будет удалена из
            родительской таблицы или изменится значение ее первичного ключа,
            будут удалены или обновлены любые строки в зависимой таблице,
            ссылающиеся на эту строку родительской таблицы.
        </para>

        <example id="zend.db.table.relationships.cascading.example-declaration">
            <title>Пример объявления каскадных операций</title>
            <para>
                В примере ниже строки в таблице <code>Bugs</code> автоматически
                удаляются, если строка в таблице <code>Products</code>, на
                которую они ссылаются, удаляется. Элемент
                <code>'onDelete'</code> записи в массиве связей установлен в
                <code>self::CASCADE</code>.
            </para>
            <para>
                В примере ниже не выполняется каскадное обновление, если
                изменяется значение первичного ключа. Элемент
                <code>'onUpdate'</code> записи в массиве связей установлен в
                <code>self::RESTRICT</code>. Вы можете получить тот же самый
                результат, используя значение <code>self::NO_ACTION</code> или
                пропустив элемент <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>Примечания относительно каскадных операций</title>

            <para>
                <emphasis>Каскадные операции, вызываемые
                Zend_Db_Table, не являются атомарными.</emphasis>
            </para>

            <para>
                Это означает, что если ваша БД реализует ограничения ссылочной
                целостности и принуждает к их использованию, то каскадное
                обновление, выполняемое классом Zend_Db_Table, конфликтует с
                этими ограничениями и результатом будет нарушение ссылочной
                целостности.
                Вы можете использовать каскадное обновление в Zend_Db_Table
                <emphasis>только</emphasis> если когда ваша БД не принуждает к
                использованию ограничений ссылочной целостности.
            </para>

            <para>
                Каскадное удаление меньше страдает от проблем нарушения
                ссылочной целостности. Вы можете удалить зависимые строки в
                неатомарном действии до удаления родительской строки, на
                которую они ссылаются.
            </para>

            <para>
                Тем не менее, неатомарность операций изменения и удаления в
                БД приводит к тому, что есть риск того, что другой пользователь
                БД будет видеть противоречивые данные. Например, если вы удалите
                строку и все зависимые строки, то есть небольшая вероятность
                того, что другой клиент может сделать запрос к БД после того,
                как вы удалили зависимые строки, но до того, как вы удалите
                родительскую строку. Эта клиентская программа может увидеть
                родительскую строку без зависимых строк и решить, что это
                задуманное состояние данных.
            </para>

            <para>
                Проблема неатомарных измнений может быть частично решена
                использованием транзакций для изолирования ваших изменений. Но
                некоторые СУРБД не поддерживают транзакции или позволяют
                клиентам читать "грязные" изменения в БД, которые не были еще
                зафиксированы.
            </para>

            <para>
                <emphasis>Каскадные операции в Zend_Db_Table вызываются только классом Zend_Db_Table</emphasis>
            </para>

            <para>
                Каскадные операции удаления и добавления, объявленные в ваших
                классах Zend_Db_Table выполняются, если вы выполняете методы
                <code>save()</code> и <code>delete()</code> класса Row.
                Но если вы обновляете или удаляете данные, используя другой
                интерфейс, например, утилиту запросов или другое приложение, то
                каскадные операции не выполняются.
                Даже когда используются методы <code>update()</code> и
                <code>delete()</code> класса Zend_Db_Adapter, каскадные
                операции, определенные в ваших классах Zend_Db_Table, не
                выполняются.
            </para>

            <para>
                <emphasis>Не существует каскадного добавления
                <code>INSERT</code>.</emphasis>
            </para>

            <para>
                Не поддерживается каскадное добавление <code>INSERT</code>. Вы
                должны добавить строку в родительской таблице в одной операции и
                добавить строки в зависимой таблице в другой операции.
            </para>

        </sect3>

    </sect2>

</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
