Слой абстракции базы данных в Drupal
Разрешает использование различных СУБД с помощью одного и того же программного кода.
Drupal обеспечивает уровень абстракции базы данных, чтобы предоставить разработчикам возможность поддержки нескольких серверов баз данных. Цель этого слоя является сохранение синтаксиса и мощи SQL насколько это возможно , а также дает разработчикам возможность использовать более сложные функции в едином порядке. Он также обеспечивает структурированный интерфейс для динамического построения запросов, когда это необходимо, и обеспечения проверки безопасности и аналогичные передовые практики.
Система работает поверх расширения PDO (PHP Data Objects) API
баз данных и наследует большую часть его синтаксиса и семантики.
Большинство SELECT- запросов базы данных Drupal выполняют с помощью вызова db_query() или db_query_range(). Авторы модулей должны также рассмотреть возможность использования PagerDefault Extender для запросов, который возвращают результаты, которые должны быть представлены на нескольких страницах, и Tablesort Extender для создания соответствующих запросов для сортировки таблицы.
Например, можно получить список последних 10 нод, автором которых является данный пользователь. Вместо непосредственной выдачи SQL-запроса.
SELECT n.nid, n.title, n.created FROM node n WHERE n.uid = $uid LIMIT 0, 10;
можно было бы вместо этого вызвать Drupal функции:
$result = db_query_range('SELECT n.nid, n.title, n.created FROM {node} n WHERE n.uid = :uid', 0, 10, array(':uid' => $uid)); foreach ($result as $record) { // выполняем операции с $record->title, и т.д. }
Фигурные скобки используются вокруг "нод", чтобы обеспечить таблицу префиксов через DatabaseConnection::prefixTables()
. Явное использование идентификатора пользователя выдвигается в качестве аргумента и передается в db_query()
так, чтобы SQL-инъекции от ввода пользователя могли быть пойманы и сведены к нулю. Синтаксис LIMIT
различается в различных серверах баз данных, поэтому он абстрагируется аргументами в db_query_range()
. Наконец, отметим, возможность на основе PDO
перебирать результат, используя foreach()
.
Все запросы передаются в виде подготовленных строка заявления запроса. Подготовленная строка заявления является "шаблоном" из запроса, который замещает буквальные значения или значения переменных заполнителями. Значения, которые помещаются в эти заполнители передаются отдельно, а драйвер базы данных вручную вставляет значения в запросе в безопасном режиме. Это означает, что вы никогда не должны брать в кавычки или экранировать значение, которое будет вставлено в запросе.
Есть два формата для заполнителей: имени и безымянный. Именованные заполнители наиболее предпочтительнее во всех случаях, так как они более гибкие и самодокументируемыми. Именованные метки должны начинаться с двоеточия ":" и далее может следовать одна или несколько букв, цифр или символов подчеркивания.
Именованные метки начинаются с двоеточия с последующей уникальной строкой. Пример:
SELECT nid, title FROM {node} WHERE uid=:uid;
где ":uid"
является заполнителем, который будет заменен буквальным значением при выполнении запроса. Данная метка заполнителя не может быть повторена в данном запросе, даже если это значение должно быть таким же. При использовании именных заполнителей, массив аргументов в запросе должен быть ассоциативным, где ключами являются метка заполнителя (например :uid
) и значение элемента это массива это соответствующее передаваемое значение.Массив может быть в любом порядке.
Безымянные заполнители это просто знак вопроса. Пример:
SELECT nid, title FROM {node} WHERE uid=?;
В этом случае массив аргументов должны быть индексируемым массивом значений для использования в том же порядке, что и заполнители в запросе.
Отметим, что заполнители должны быть «полным» значением. Например, при выполнении LIKE-запроса SQL подставляет символ % как часть значения, а не сам запрос. Таким следующий запрос неправилен:
SELECT nid, title FROM {node} WHERE title LIKE :title%;
Вместо этого она должна быть в следующем виде:
SELECT nid, title FROM {node} WHERE title LIKE :title;
и значение для :title
должно включать % соответственно. Опять же, обратите внимание на отсутствие кавычек вокруг :title
. Поскольку значение вставляется в запрос не как одна большая строка, а как явно отдельное значение, сервер базы данных знает где заканчивается запрос и начинается значение. То есть значительно более безопасен в отношении SQL-инъекций, затем пытается запомнить какие значения должны браться в кавычки и строка строки экранироваться, а какие нет.
INSERT, UPDATE и DELETE
запросы нуждаются в особом внимании для того, чтобы вести себя последовательно во всех различных базах данных. Таким образом, они используют специальное объектно-ориентированное API для определения запросов структурно. Например, вместо:
INSERT INTO node (nid, title, body) VALUES (1, 'my title', 'my body');
Drupal also supports transactions, including a transparent fallback for databases that do not support transactions. To start a new transaction, simply call $txn = db_transaction(); in your own code. The transaction will remain open for as long as the variable $txn remains in scope. When $txn is destroyed, the transaction will be committed. If your transaction is nested inside of another then Drupal will track each transaction and only commit the outer-most transaction when the last transaction object goes out out of scope, that is, all relevant queries completed successfully.
Можно было бы вместо этого написать:
$fields = array('nid' => 1, 'title' => 'my title', 'body' => 'my body'); db_insert('node')->fields($fields)->execute();
Этот метод позволяет базам данных, которые нуждаются в особого типа обработки данных для этого, а также обеспечивает оптимизации, такую как мульти-вставки запросов. UPDATE и DELETE
запросы имеют аналогичный характер.
Drupal поддерживает также транзакции, в том числе прозрачные обходной путь для баз данных, которые не поддерживают транзакции. Чтобы начать новую транзакцию, просто вызовите $txn = db_transaction();
в собственном коде. Транзакция будет оставаться открытой до тех пор, пока есть переменная $txn в области видимости. Когда $txn уничтожена, транзакция будет совершена. If your transaction is nested inside of another then Drupal will track each transaction and only commit the outer-most transaction when the last transaction object goes out out of scope, that is, all relevant queries completed successfully.
Пример:
function my_transaction_function() { // транзакция открывается здесь $txn = db_transaction(); try { $id = db_insert('example') ->fields(array( 'field1' => 'mystring', 'field2' => 5, )) ->execute(); my_other_function($id); return $id; } catch (Exception $e) { // Что-то где-то пошло не так, поэтому сейчас откатываемся назад. $txn->rollback(); // помещаем исключение в watchdog. watchdog_exception('type', $e); } // $txn выходит из области видимости здесь. Если транзакция была отменена, она // становится автоматически совершенной здесь. } function my_other_function($id) { // Транзакция все еще открыта здесь. if ($id % 2 == 0) { db_update('example') ->condition('id', $id) ->fields(array('field2' => 10)) ->execute(); } }
оригинал, перевод от 13 ноября 2012