Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
project
drupal
Commits
e5b3f93a
Commit
e5b3f93a
authored
Jun 20, 2014
by
webchick
Browse files
Issue
#2191445
by jhodgdon: Fixed Database abstraction layer topic / landing page needs more info.
parent
233f9caa
Changes
9
Hide whitespace changes
Inline
Side-by-side
core/includes/database.inc
View file @
e5b3f93a
...
...
@@ -18,123 +18,150 @@
* @{
* Allow the use of different database servers using the same code base.
*
* Drupal provides a database abstraction layer to provide developers with
* the ability to support multiple database servers easily. The intent of
* this layer is to preserve the syntax and power of SQL as much as possible,
* but also allow developers a way to leverage more complex functionality in
* a unified way. It also provides a structured interface for dynamically
* constructing queries when appropriate, and enforcing security checks and
* similar good practices.
*
* The system is built atop PHP's PDO (PHP Data Objects) database API and
* inherits much of its syntax and semantics.
*
* Most Drupal database SELECT queries are performed by a call to db_query() or
* db_query_range(). Module authors should also consider using the
* Drupal\Core\Database\Query\PagerSelectExtender for queries that return
* results that need to be presented on multiple pages
* (see https://drupal.org/node/508796), and the
* Drupal\Core\Database\Query\TableSortExtender for generating appropriate
* queries for sortable tables (see https://drupal.org/node/1848372).
*
* For example, one might wish to return a list of the most recent 10 rows
* authored by a given user. Instead of directly issuing the SQL query
* @sec sec_intro Overview
* Drupal's database abstraction layer provides a unified database query API
* that can query different underlying databases. It is built upon PHP's
* PDO (PHP Data Objects) database API, and inherits much of its syntax and
* semantics. Besides providing a unified API for database queries, the
* database abstraction layer also provides a structured way to construct
* complex queries, and it protects the database by using good security
* practices.
*
* For more detailed information on the database abstraction layer, see
* https://drupal.org/developing/api/database
*
* @sec sec_entity Querying entities
* Any query on Drupal entities or fields should use the Entity Query API. See
* the @link entity_api entity API topic @endlink for more information.
*
* @sec sec_simple Simple SELECT database queries
* For simple SELECT queries that do not involve entities, the Drupal database
* abstraction layer provides the functions db_query() and db_query_range(),
* which execute SELECT queries (optionally with range limits) and return result
* sets that you can iterate over using foreach loops. (The result sets are
* objects implementing the \Drupal\Core\Database\StatementInterface interface.)
* You can use the simple query functions for query strings that are not
* dynamic (except for placeholders, see below), and that you are certain will
* work in any database engine. See @ref sec_dynamic below if you have a more
* complex query, or a query whose syntax would be different in some databases.
*
* As a note, db_query() and similar functions are wrappers on connection object
* methods. In most classes, you should use dependency injection and the
* database connection object instead of these wrappers; See @ref sec_connection
* below for details.
*
* To use the simple database query functions, you will need to make a couple of
* modifications to your bare SQL query:
* - Enclose your table name in {}. Drupal allows site builders to use
* database table name prefixes, so you cannot be sure what the actual
* name of the table will be. So, use the name that is in the hook_schema(),
* enclosed in {}, and Drupal will calculate the right name.
* - Instead of putting values for conditions into the query, use placeholders.
* The placeholders are named and start with :, and they take the place of
* putting variables directly into the query, to protect against SQL
* injection attacks.
* - LIMIT syntax differs between databases, so if you have a ranged query,
* use db_query_range() instead of db_query().
*
* For example, if the query you want to run is:
* @code
* SELECT e.id, e.title, e.created FROM example e WHERE e.uid = $uid
* ORDER BY e.created DESC LIMIT 0, 10;
* @endcode
*
one
would
instead call the Drupal function
s:
*
you
would
do it like thi
s:
* @code
* $result = db_query_range('SELECT e.id, e.title, e.created
* FROM {example} e WHERE e.uid = :uid
* ORDER BY e.created DESC', 0, 10, array(':uid' => $uid));
* FROM {example} e
* WHERE e.uid = :uid
* ORDER BY e.created DESC',
* 0, 10, array(':uid' => $uid));
* foreach ($result as $record) {
* // Perform operations on $record->title, etc. here.
* }
* @endcode
* Curly braces are used around "example" to provide table prefixing via
* DatabaseConnection::prefixTables(). The explicit use of a user ID is pulled
* out into an argument passed to db_query() so that SQL injection attacks
* from user input can be caught and nullified. The LIMIT syntax varies between
* database servers, so that is abstracted into db_query_range() arguments.
* Finally, note the PDO-based ability to iterate over the result set using
* foreach ().
*
* All queries are passed as a prepared statement string. A
* prepared statement is a "template" of a query that omits literal or variable
* values in favor of placeholders. The values to place into those
* placeholders are passed separately, and the database driver handles
* inserting the values into the query in a secure fashion. That means you
* should never quote or string-escape a value to be inserted into the query.
*
* There are two formats for placeholders: named and unnamed. Named placeholders
* are strongly preferred in all cases as they are more flexible and
* self-documenting. Named placeholders should start with a colon ":" and can be
* followed by one or more letters, numbers or underscores.
*
* Named placeholders begin with a colon followed by a unique string. Example:
* @code
* SELECT id, title FROM {example} WHERE uid=:uid;
* @endcode
*
* ":uid" is a placeholder that will be replaced with a literal value when
* the query is executed. A given placeholder label cannot be repeated in a
* given query, even if the value should be the same. When using named
* placeholders, the array of arguments to the query must be an associative
* array where keys are a placeholder label (e.g., :uid) and the value is the
* corresponding value to use. The array may be in any order.
*
* Unnamed placeholders are simply a question mark. Example:
* Note that if your query has a string condition, like:
* @code
*
SELECT id, title FROM {example} WHERE uid=?;
*
WHERE e.my_field = 'foo'
* @endcode
*
* In this case, the array of arguments must be an indexed array of values to
* use in the exact same order as the placeholders in the query.
*
* Note that placeholders should be a "complete" value. For example, when
* running a LIKE query the SQL wildcard character, %, should be part of the
* value, not the query itself. Thus, the following is incorrect:
* when you convert it to placeholders, omit the quotes:
* @code
* SELECT id, title FROM {example} WHERE title LIKE :title%;
* WHERE e.my_field = :my_field
* ... array(':my_field' => 'foo') ...
* @endcode
* It should instead read:
*
* @sec sec_dynamic Dynamic SELECT queries
* For SELECT queries where the simple query API described in @ref sec_simple
* will not work well, you need to use the dynamic query API. However, you
* should still use the Entity Query API if your query involves entities or
* fields (see the @link entity_api Entity API topic @endlink for more on
* entity queries).
*
* As a note, db_select() and similar functions are wrappers on connection
* object methods. In most classes, you should use dependency injection and the
* database connection object instead of these wrappers; See @ref sec_connection
* below for details.
*
* The dynamic query API lets you build up a query dynamically using method
* calls. As an illustration, the query example from @ref sec_simple above
* would be:
* @code
* SELECT id, title FROM {example} WHERE title LIKE :title;
* $result = db_select('example', 'e')
* ->fields('e', array('id', 'title', 'created'))
* ->condition('e.uid', $uid)
* ->orderBy('e.created', 'DESC')
* ->range(0, 10)
* ->execute();
* @endcode
* and the value for :title should include a % as appropriate. Again, note the
* lack of quotation marks around :title. Because the value is not inserted
* into the query as one big string but as an explicitly separate value, the
* database server knows where the query ends and a value begins. That is
* considerably more secure against SQL injection than trying to remember
* which values need quotation marks and string escaping and which don't.
*
* There are also methods to join to other tables, add fields with aliases,
* isNull() to have a @code WHERE e.foo IS NULL @code condition, etc. See
* https://drupal.org/developing/api/database for many more details.
*
* One note on chaining: It is common in the dynamic database API to chain
* method calls (as illustrated here), because most of the query methods modify
* the query object and then return the modified query as their return
* value. However, there are some important exceptions; these methods (and some
* others) do not support chaining:
* - join(), innerJoin(), etc.: These methods return the joined table alias.
* - addField(): This method returns the field alias.
* Check the documentation for the query method you are using to see if it
* returns the query or something else, and only chain methods that return the
* query.
*
* @sec_insert INSERT, UPDATE, and DELETE queries
* INSERT, UPDATE, and DELETE queries need special care in order to behave
* consistently across all different databases. Therefore, they use a special
* object-oriented API for defining a query structurally. For example, rather
* than:
* consistently across databases; you should never use db_query() to run
* an INSERT, UPDATE, or DELETE query. Instead, use functions db_insert(),
* db_update(), and db_delete() to obtain a base query on your table, and then
* add dynamic conditions (as illustrated in @ref sec_dynamic above).
*
* As a note, db_insert() and similar functions are wrappers on connection
* object methods. In most classes, you should use dependency injection and the
* database connection object instead of these wrappers; See @ref sec_connection
* below for details.
*
* For example, if your query is:
* @code
* INSERT INTO
{
example
}
(id, uid, path, name) VALUES (1, 2, 'path', 'Name');
* INSERT INTO example (id, uid, path, name) VALUES (1, 2, 'path', 'Name');
* @endcode
*
one would instead write
:
*
You can execute it via
:
* @code
* $fields = array('id' => 1, 'uid' => 2, 'path' => 'path', 'name' => 'Name');
* db_insert('example')
* ->fields($fields)
* ->execute();
* @endcode
* This method allows databases that need special data type handling to do so,
* while also allowing optimizations such as multi-insert queries. UPDATE and
* DELETE queries have a similar pattern.
*
* Drupal also supports transactions, including a transparent fallback for
* @sec sec_transaction Tranactions
* Drupal 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
. W
hen $txn is
* destroyed, the transaction will be committed.
If your transaction is nested
*
call @code
$txn = db_transaction();
@end
code The transaction will
* remain open for as long as the variable $txn remains in scope
; w
hen $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.
* scope
(when
all relevant queries
have
completed successfully
)
.
*
* Example:
* @code
...
...
@@ -177,7 +204,26 @@
* }
* @endcode
*
* @sec sec_connection Database connection objects
* The examples here all use functions like db_select() and db_query(), which
* can be called from any Drupal method or function code. In some classes, you
* may already have a database connection object in a member variable, or it may
* be passed into a class constructor via dependency injection. If that is the
* case, you can look at the code for db_select() and the other functions to see
* how to get a query object from your connection variable. For example:
* @code
* $query = $connection->select('example', 'e');
* @endcode
* would be the equivalent of
* @code
* $query = db_select('example', 'e');
* @endcode
* if you had a connection object variable $connection available to use. See
* also the @link container Services and Dependency Injection topic. @endlink
*
* @see http://drupal.org/developing/api/database
* @see entity_api
* @see schemaapi
*/
...
...
core/lib/Drupal/Core/Database/Query/Delete.php
View file @
e5b3f93a
...
...
@@ -12,6 +12,8 @@
/**
* General class for an abstracted DELETE operation.
*
* @ingroup database
*/
class
Delete
extends
Query
implements
ConditionInterface
{
...
...
core/lib/Drupal/Core/Database/Query/Insert.php
View file @
e5b3f93a
...
...
@@ -11,6 +11,8 @@
/**
* General class for an abstracted INSERT query.
*
* @ingroup database
*/
class
Insert
extends
Query
{
...
...
core/lib/Drupal/Core/Database/Query/Select.php
View file @
e5b3f93a
...
...
@@ -13,6 +13,8 @@
/**
* Query builder for SELECT statements.
*
* @ingroup database
*/
class
Select
extends
Query
implements
SelectInterface
{
...
...
core/lib/Drupal/Core/Database/Query/SelectInterface.php
View file @
e5b3f93a
...
...
@@ -9,6 +9,8 @@
/**
* Interface definition for a Select Query object.
*
* @ingroup database
*/
interface
SelectInterface
extends
ConditionInterface
,
AlterableInterface
,
ExtendableInterface
,
PlaceholderInterface
{
...
...
core/lib/Drupal/Core/Database/Query/Update.php
View file @
e5b3f93a
...
...
@@ -12,6 +12,8 @@
/**
* General class for an abstracted UPDATE operation.
*
* @ingroup database
*/
class
Update
extends
Query
implements
ConditionInterface
{
...
...
core/lib/Drupal/Core/Database/StatementInterface.php
View file @
e5b3f93a
...
...
@@ -25,6 +25,8 @@
* @code
* class Drupal\Core\Database\Driver\oracle\Statement implements Iterator, Drupal\Core\Database\StatementInterface {}
* @endcode
*
* @ingroup database
*/
interface
StatementInterface
extends
\
Traversable
{
...
...
core/lib/Drupal/Core/Entity/Query/QueryInterface.php
View file @
e5b3f93a
...
...
@@ -15,6 +15,8 @@
*
* Never instantiate classes implementing this interface directly. Always use
* the QueryFactory class.
*
* @ingroup database
*/
interface
QueryInterface
extends
AlterableInterface
{
...
...
core/modules/system/core.api.php
View file @
e5b3f93a
...
...
@@ -364,20 +364,53 @@
* \Drupal\Core\Entity\EntityType.
*
* @section load_query Loading and querying entities
* To load entities, use the entity storage manager, which is a
class
* To load entities, use the entity storage manager, which is a
n object
* implementing \Drupal\Core\Entity\EntityStorageInterface that you can
* retrieve with:
* @code
* $storage = \Drupal::entityManager()->getStorage('your_entity_type');
* // Or if you have a $container variable:
* $storage = $container->get('entity.manager')->getStorage('your_entity_type');
* @endcode
* Here, 'your_entity_type' is the machine name of your entity type ('id'
* annotation on the entity class).
* annotation on the entity class), and note that you should use dependency
* injection to retrieve this object if possible. See the
* @link container Services and Dependency Injection topic @endlink for more
* about how to properly retrieve services.
*
* To query to find entities to load, use an entity query, which is a
class
* To query to find entities to load, use an entity query, which is a
object
* implementing \Drupal\Core\Entity\Query\QueryInterface that you can retrieve
* with:
* @code
* $storage = \Drupal::entityQuery('your_entity_type');
* // Simple query:
* $query = \Drupal::entityQuery('your_entity_type');
* // Or, if you have a $container variable:
* $query_service = $container->get('entity.query');
* $query = $query_service->get('your_entity_type');
* @endcode
* If you need aggregation, there is an aggregate query avaialable, which
* implements \Drupal\Core\Entity\Query\QueryAggregateInterface:
* @code
* $query \Drupal::entityQueryAggregate('your_entity_type');
* // Or:
* $query = $query_service->getAggregate('your_entity_type');
* Also, you should use dependency injection to get this object if
* possible; the service you need is entity.query, and its methods getQuery()
* or getAggregateQuery() will get the query object.
*
* In either case, you can then add conditions to your query, using methods
* like condition(), exists(), etc. on $query; add sorting, pager, and range
* if needed, and execute the query to return a list of entity IDs that match
* the query.
*
* Here is an example, using the core File entity:
* @code
* $fids = Drupal::entityQuery('file')
* ->condition('status', FILE_STATUS_PERMANENT, '<>')
* ->condition('changed', REQUEST_TIME - $age, '<')
* ->range(0, 100)
* ->execute();
* $files = $storage->loadMultiple($fids);
* @endcode
* @}
*/
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment