123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- <?php
- /**
- * @file
- * Hooks related to the Database system and the Schema API.
- */
- use Drupal\Core\Database\Query\Condition;
- /**
- * @defgroup database Database abstraction layer
- * @{
- * Allow the use of different database servers using the same code base.
- *
- * @section 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://www.drupal.org/developing/api/database.
- *
- * @section 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.
- *
- * @section 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
- * you would do it like this:
- * @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));
- * foreach ($result as $record) {
- * // Perform operations on $record->title, etc. here.
- * }
- * @endcode
- *
- * Note that if your query has a string condition, like:
- * @code
- * WHERE e.my_field = 'foo'
- * @endcode
- * when you convert it to placeholders, omit the quotes:
- * @code
- * WHERE e.my_field = :my_field
- * ... array(':my_field' => 'foo') ...
- * @endcode
- *
- * @section 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
- * $result = db_select('example', 'e')
- * ->fields('e', array('id', 'title', 'created'))
- * ->condition('e.uid', $uid)
- * ->orderBy('e.created', 'DESC')
- * ->range(0, 10)
- * ->execute();
- * @endcode
- *
- * There are also methods to join to other tables, add fields with aliases,
- * isNull() to have a @code WHERE e.foo IS NULL @endcode condition, etc. See
- * https://www.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.
- *
- * @section_insert INSERT, UPDATE, and DELETE queries
- * INSERT, UPDATE, and DELETE queries need special care in order to behave
- * 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');
- * @endcode
- * You can execute it via:
- * @code
- * $fields = array('id' => 1, 'uid' => 2, 'path' => 'path', 'name' => 'Name');
- * db_insert('example')
- * ->fields($fields)
- * ->execute();
- * @endcode
- *
- * @section sec_transaction Transactions
- * Drupal supports transactions, including a transparent fallback for
- * databases that do not support transactions. To start a new transaction,
- * call @code $txn = db_transaction(); @endcode 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 (when all relevant queries have completed successfully).
- *
- * Example:
- * @code
- * function my_transaction_function() {
- * // The transaction opens here.
- * $txn = db_transaction();
- *
- * try {
- * $id = db_insert('example')
- * ->fields(array(
- * 'field1' => 'mystring',
- * 'field2' => 5,
- * ))
- * ->execute();
- *
- * my_other_function($id);
- *
- * return $id;
- * }
- * catch (Exception $e) {
- * // Something went wrong somewhere, so roll back now.
- * $txn->rollBack();
- * // Log the exception to watchdog.
- * watchdog_exception('type', $e);
- * }
- *
- * // $txn goes out of scope here. Unless the transaction was rolled back, it
- * // gets automatically committed here.
- * }
- *
- * function my_other_function($id) {
- * // The transaction is still open here.
- *
- * if ($id % 2 == 0) {
- * db_update('example')
- * ->condition('id', $id)
- * ->fields(array('field2' => 10))
- * ->execute();
- * }
- * }
- * @endcode
- *
- * @section 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 https://www.drupal.org/developing/api/database
- * @see entity_api
- * @see schemaapi
- *
- * @}
- */
- /**
- * @defgroup schemaapi Schema API
- * @{
- * API to handle database schemas.
- *
- * A Drupal schema definition is an array structure representing one or
- * more tables and their related keys and indexes. A schema is defined by
- * hook_schema(), which usually lives in a modulename.install file.
- *
- * By implementing hook_schema() and specifying the tables your module
- * declares, you can easily create and drop these tables on all
- * supported database engines. You don't have to deal with the
- * different SQL dialects for table creation and alteration of the
- * supported database engines.
- *
- * hook_schema() should return an array with a key for each table that
- * the module defines.
- *
- * The following keys are defined:
- * - 'description': A string in non-markup plain text describing this table
- * and its purpose. References to other tables should be enclosed in
- * curly-brackets. For example, the node_field_revision table
- * description field might contain "Stores per-revision title and
- * body data for each {node}."
- * - 'fields': An associative array ('fieldname' => specification)
- * that describes the table's database columns. The specification
- * is also an array. The following specification parameters are defined:
- * - 'description': A string in non-markup plain text describing this field
- * and its purpose. References to other tables should be enclosed in
- * curly-brackets. For example, the node table vid field
- * description might contain "Always holds the largest (most
- * recent) {node_field_revision}.vid value for this nid."
- * - 'type': The generic datatype: 'char', 'varchar', 'text', 'blob', 'int',
- * 'float', 'numeric', or 'serial'. Most types just map to the according
- * database engine specific datatypes. Use 'serial' for auto incrementing
- * fields. This will expand to 'INT auto_increment' on MySQL.
- * A special 'varchar_ascii' type is also available for limiting machine
- * name field to US ASCII characters.
- * - 'mysql_type', 'pgsql_type', 'sqlite_type', etc.: If you need to
- * use a record type not included in the officially supported list
- * of types above, you can specify a type for each database
- * backend. In this case, you can leave out the type parameter,
- * but be advised that your schema will fail to load on backends that
- * do not have a type specified. A possible solution can be to
- * use the "text" type as a fallback.
- * - 'serialize': A boolean indicating whether the field will be stored as
- * a serialized string.
- * - 'size': The data size: 'tiny', 'small', 'medium', 'normal',
- * 'big'. This is a hint about the largest value the field will
- * store and determines which of the database engine specific
- * datatypes will be used (e.g. on MySQL, TINYINT vs. INT vs. BIGINT).
- * 'normal', the default, selects the base type (e.g. on MySQL,
- * INT, VARCHAR, BLOB, etc.).
- * Not all sizes are available for all data types. See
- * DatabaseSchema::getFieldTypeMap() for possible combinations.
- * - 'not null': If true, no NULL values will be allowed in this
- * database column. Defaults to false.
- * - 'default': The field's default value. The PHP type of the
- * value matters: '', '0', and 0 are all different. If you
- * specify '0' as the default value for a type 'int' field it
- * will not work because '0' is a string containing the
- * character "zero", not an integer.
- * - 'length': The maximal length of a type 'char', 'varchar' or 'text'
- * field. Ignored for other field types.
- * - 'unsigned': A boolean indicating whether a type 'int', 'float'
- * and 'numeric' only is signed or unsigned. Defaults to
- * FALSE. Ignored for other field types.
- * - 'precision', 'scale': For type 'numeric' fields, indicates
- * the precision (total number of significant digits) and scale
- * (decimal digits right of the decimal point). Both values are
- * mandatory. Ignored for other field types.
- * - 'binary': A boolean indicating that MySQL should force 'char',
- * 'varchar' or 'text' fields to use case-sensitive binary collation.
- * This has no effect on other database types for which case sensitivity
- * is already the default behavior.
- * All parameters apart from 'type' are optional except that type
- * 'numeric' columns must specify 'precision' and 'scale', and type
- * 'varchar' must specify the 'length' parameter.
- * - 'primary key': An array of one or more key column specifiers (see below)
- * that form the primary key.
- * - 'unique keys': An associative array of unique keys ('keyname' =>
- * specification). Each specification is an array of one or more
- * key column specifiers (see below) that form a unique key on the table.
- * - 'foreign keys': An associative array of relations ('my_relation' =>
- * specification). Each specification is an array containing the name of
- * the referenced table ('table'), and an array of column mappings
- * ('columns'). Column mappings are defined by key pairs ('source_column' =>
- * 'referenced_column'). This key is for documentation purposes only; foreign
- * keys are not created in the database, nor are they enforced by Drupal.
- * - 'indexes': An associative array of indexes ('indexname' =>
- * specification). Each specification is an array of one or more
- * key column specifiers (see below) that form an index on the
- * table.
- *
- * A key column specifier is either a string naming a column or an
- * array of two elements, column name and length, specifying a prefix
- * of the named column.
- *
- * As an example, here is a SUBSET of the schema definition for
- * Drupal's 'node' table. It show four fields (nid, vid, type, and
- * title), the primary key on field 'nid', a unique key named 'vid' on
- * field 'vid', and two indexes, one named 'nid' on field 'nid' and
- * one named 'node_title_type' on the field 'title' and the first four
- * bytes of the field 'type':
- *
- * @code
- * $schema['node'] = array(
- * 'description' => 'The base table for nodes.',
- * 'fields' => array(
- * 'nid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
- * 'vid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE,'default' => 0),
- * 'type' => array('type' => 'varchar','length' => 32,'not null' => TRUE, 'default' => ''),
- * 'language' => array('type' => 'varchar','length' => 12,'not null' => TRUE,'default' => ''),
- * 'title' => array('type' => 'varchar','length' => 255,'not null' => TRUE, 'default' => ''),
- * 'uid' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
- * 'status' => array('type' => 'int', 'not null' => TRUE, 'default' => 1),
- * 'created' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
- * 'changed' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
- * 'comment' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
- * 'promote' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
- * 'moderate' => array('type' => 'int', 'not null' => TRUE,'default' => 0),
- * 'sticky' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
- * 'translate' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
- * ),
- * 'indexes' => array(
- * 'node_changed' => array('changed'),
- * 'node_created' => array('created'),
- * 'node_moderate' => array('moderate'),
- * 'node_frontpage' => array('promote', 'status', 'sticky', 'created'),
- * 'node_status_type' => array('status', 'type', 'nid'),
- * 'node_title_type' => array('title', array('type', 4)),
- * 'node_type' => array(array('type', 4)),
- * 'uid' => array('uid'),
- * 'translate' => array('translate'),
- * ),
- * 'unique keys' => array(
- * 'vid' => array('vid'),
- * ),
- * // For documentation purposes only; foreign keys are not created in the
- * // database.
- * 'foreign keys' => array(
- * 'node_revision' => array(
- * 'table' => 'node_field_revision',
- * 'columns' => array('vid' => 'vid'),
- * ),
- * 'node_author' => array(
- * 'table' => 'users',
- * 'columns' => array('uid' => 'uid'),
- * ),
- * ),
- * 'primary key' => array('nid'),
- * );
- * @endcode
- *
- * @see drupal_install_schema()
- *
- * @}
- */
- /**
- * @addtogroup hooks
- * @{
- */
- /**
- * Perform alterations to a structured query.
- *
- * Structured (aka dynamic) queries that have tags associated may be altered by any module
- * before the query is executed.
- *
- * @param $query
- * A Query object describing the composite parts of a SQL query.
- *
- * @see hook_query_TAG_alter()
- * @see node_query_node_access_alter()
- * @see AlterableInterface
- * @see SelectInterface
- *
- * @ingroup database
- */
- function hook_query_alter(Drupal\Core\Database\Query\AlterableInterface $query) {
- if ($query->hasTag('micro_limit')) {
- $query->range(0, 2);
- }
- }
- /**
- * Perform alterations to a structured query for a given tag.
- *
- * @param $query
- * An Query object describing the composite parts of a SQL query.
- *
- * @see hook_query_alter()
- * @see node_query_node_access_alter()
- * @see AlterableInterface
- * @see SelectInterface
- *
- * @ingroup database
- */
- function hook_query_TAG_alter(Drupal\Core\Database\Query\AlterableInterface $query) {
- // Skip the extra expensive alterations if site has no node access control modules.
- if (!node_access_view_all_nodes()) {
- // Prevent duplicates records.
- $query->distinct();
- // The recognized operations are 'view', 'update', 'delete'.
- if (!$op = $query->getMetaData('op')) {
- $op = 'view';
- }
- // Skip the extra joins and conditions for node admins.
- if (!\Drupal::currentUser()->hasPermission('bypass node access')) {
- // The node_access table has the access grants for any given node.
- $access_alias = $query->join('node_access', 'na', '%alias.nid = n.nid');
- $or = new Condition('OR');
- // If any grant exists for the specified user, then user has access to the node for the specified operation.
- foreach (node_access_grants($op, $query->getMetaData('account')) as $realm => $gids) {
- foreach ($gids as $gid) {
- $or->condition((new Condition('AND'))
- ->condition($access_alias . '.gid', $gid)
- ->condition($access_alias . '.realm', $realm)
- );
- }
- }
- if (count($or->conditions())) {
- $query->condition($or);
- }
- $query->condition($access_alias . 'grant_' . $op, 1, '>=');
- }
- }
- }
- /**
- * Define the current version of the database schema.
- *
- * A Drupal schema definition is an array structure representing one or more
- * tables and their related keys and indexes. A schema is defined by
- * hook_schema() which must live in your module's .install file.
- *
- * The tables declared by this hook will be automatically created when the
- * module is installed, and removed when the module is uninstalled. This happens
- * before hook_install() is invoked, and after hook_uninstall() is invoked,
- * respectively.
- *
- * By declaring the tables used by your module via an implementation of
- * hook_schema(), these tables will be available on all supported database
- * engines. You don't have to deal with the different SQL dialects for table
- * creation and alteration of the supported database engines.
- *
- * See the Schema API Handbook at https://www.drupal.org/node/146843 for details
- * on schema definition structures. Note that foreign key definitions are for
- * documentation purposes only; foreign keys are not created in the database,
- * nor are they enforced by Drupal.
- *
- * @return array
- * A schema definition structure array. For each element of the
- * array, the key is a table name and the value is a table structure
- * definition.
- *
- * @ingroup schemaapi
- */
- function hook_schema() {
- $schema['node'] = [
- // Example (partial) specification for table "node".
- 'description' => 'The base table for nodes.',
- 'fields' => [
- 'nid' => [
- 'description' => 'The primary identifier for a node.',
- 'type' => 'serial',
- 'unsigned' => TRUE,
- 'not null' => TRUE,
- ],
- 'vid' => [
- 'description' => 'The current {node_field_revision}.vid version identifier.',
- 'type' => 'int',
- 'unsigned' => TRUE,
- 'not null' => TRUE,
- 'default' => 0,
- ],
- 'type' => [
- 'description' => 'The type of this node.',
- 'type' => 'varchar',
- 'length' => 32,
- 'not null' => TRUE,
- 'default' => '',
- ],
- 'title' => [
- 'description' => 'The node title.',
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- ],
- ],
- 'indexes' => [
- 'node_changed' => ['changed'],
- 'node_created' => ['created'],
- ],
- 'unique keys' => [
- 'nid_vid' => ['nid', 'vid'],
- 'vid' => ['vid'],
- ],
- // For documentation purposes only; foreign keys are not created in the
- // database.
- 'foreign keys' => [
- 'node_revision' => [
- 'table' => 'node_field_revision',
- 'columns' => ['vid' => 'vid'],
- ],
- 'node_author' => [
- 'table' => 'users',
- 'columns' => ['uid' => 'uid'],
- ],
- ],
- 'primary key' => ['nid'],
- ];
- return $schema;
- }
- /**
- * @} End of "addtogroup hooks".
- */
|