Разбираемся с FOREIGN KEYS в Друпале
Опубликовано сб, 10/10/2015 - 02:38
Нередко приходилось наблюдать ситуацию, когда в контрибах и в модулях ядра указывали FOREIGN KEYS. Неужели это реально работает? Хотелось попробовать реализовать целостность данных с помощью внешних ключей и я стал разбираться с этим вопросом.
Ответ будем искать, конечно же, в ядре самой CMS. Рассмотрим простую ситуацию на примере инсталляции кастомного модуля.
Структура таблицы, вместе с индексами и ключами (первичными, внешними) описывается в хуке hook_schema(). В процессе инсталляции модуля вызывается функция drupal_create_table(), отвечающая за создание таблицы модуля. Далее в этой функции (после установки соединения с базой данных) будет вызван метод createTableSql() класса DatabaseSchema_mysql, в котором за создание ключей и индексов отвечает другой метод createKeysSql(). Ниже приведено его содержимое.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
protected function createKeysSql($spec) { $keys = array(); if (!empty($spec['primary key'])) { $keys[] = 'PRIMARY KEY (' . $this->createKeysSqlHelper($spec['primary key']) . ')'; } if (!empty($spec['unique keys'])) { foreach ($spec['unique keys'] as $key => $fields) { $keys[] = 'UNIQUE KEY `' . $key . '` (' . $this->createKeysSqlHelper($fields) . ')'; } } if (!empty($spec['indexes'])) { foreach ($spec['indexes'] as $index => $fields) { $keys[] = 'INDEX `' . $index . '` (' . $this->createKeysSqlHelper($fields) . ')'; } } return $keys; } |
Как легко заметить обработки внешних ключей просто нет! Отсюда и соответствующий вывод: FOREIGN KEYS в Drupal не работают из коробки. Но тогда возникает вопрос, раз обработки внешних ключей нет, то зачем вообще их указывать в hook_schema()?
Оказывается все просто. Внешние ключи добавляют исключительно в целях документирования связей таблиц друг с другом. Это подтверждает следующая строка из официальной документации[1]
1 |
Note: Foreign key definitions were added in Drupal 7 for documentation purposes only, and do not modify the database. |
Но что делать, если есть необходимость использовать FOREIGN KEYS? Выход - заимплементить самостоятельно.
Добавим внешний ключ через hook_install() кастомного модуля. Создадим связь таблицы table1 с таблицей table2 через поля table1_id и table2_id.
1 2 3 4 5 6 |
/** * Implements hook_install(). */ function MYMODULE_install() { db_query('ALTER TABLE {table1} ADD CONSTRAINT fk_key FOREIGN KEY (table1_id) REFERENCES {table2} (table2_id) ON DELETE CASCADE'); } |
При создании связей с помощью внешних ключей нужно учитывать следующие особенности:
- таблицы, участвующие в связи должны иметь тип InnoDB, для таблиц иного механизма хранения конструкция с объявлением внешнего ключа будет проигнорирована.
- графы, по которым происходит связь должны быть проиндексированы каждый в своей таблице. Но если вы этого не сделали, InnoDB сгенерирует их автоматически.
- типы данных граф, используемых для связи посредством внешних ключей должны быть одинаковыми.
Удалить таблицу table2 будет невозможно, так как внешний ключ, созданный в table1 не позволит этого сделать. Для того чтобы избавиться от внешнего ключа можно пойти двумя путями:
- удалить таблицу table1
- удалить внешний ключ через ALTER TABLE
Пример удаление внешнего ключа через альтер таблицы table1.
1 |
db_query('ALTER TABLE {table1} DROP FOREIGN KEY fk_key'); |
Дополнительная информация по статье
- https://www.drupal.org/node/146939 - описание структуры Drupal schema.
- http://www.w3schools.com/sql/sql_foreignkey.asp - примеры создания/удаления FOREIGN KEYS на сайте w3schools.com
- https://dev.mysql.com/doc/refman/5.5/en/create-table-foreign-keys.html - полное описание внешних ключей из официальной документации MySQL.