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