Слой абстракции базы данных в 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