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

Тэги:

Тэг в списке: