Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /**
|
Chris@0
|
4 * @file
|
Chris@0
|
5 * Hooks related to the Database system and the Schema API.
|
Chris@0
|
6 */
|
Chris@0
|
7
|
Chris@0
|
8 use Drupal\Core\Database\Query\Condition;
|
Chris@0
|
9
|
Chris@0
|
10 /**
|
Chris@0
|
11 * @defgroup database Database abstraction layer
|
Chris@0
|
12 * @{
|
Chris@0
|
13 * Allow the use of different database servers using the same code base.
|
Chris@0
|
14 *
|
Chris@0
|
15 * @section sec_intro Overview
|
Chris@0
|
16 * Drupal's database abstraction layer provides a unified database query API
|
Chris@0
|
17 * that can query different underlying databases. It is built upon PHP's
|
Chris@0
|
18 * PDO (PHP Data Objects) database API, and inherits much of its syntax and
|
Chris@0
|
19 * semantics. Besides providing a unified API for database queries, the
|
Chris@0
|
20 * database abstraction layer also provides a structured way to construct
|
Chris@0
|
21 * complex queries, and it protects the database by using good security
|
Chris@0
|
22 * practices.
|
Chris@0
|
23 *
|
Chris@0
|
24 * For more detailed information on the database abstraction layer, see
|
Chris@17
|
25 * https://www.drupal.org/docs/8/api/database-api/database-api-overview.
|
Chris@0
|
26 *
|
Chris@0
|
27 * @section sec_entity Querying entities
|
Chris@0
|
28 * Any query on Drupal entities or fields should use the Entity Query API. See
|
Chris@0
|
29 * the @link entity_api entity API topic @endlink for more information.
|
Chris@0
|
30 *
|
Chris@0
|
31 * @section sec_simple Simple SELECT database queries
|
Chris@0
|
32 * For simple SELECT queries that do not involve entities, the Drupal database
|
Chris@17
|
33 * abstraction layer provides the functions \Drupal::database()->query() and
|
Chris@17
|
34 * \Drupal::database()->queryRange(), which execute SELECT queries (optionally
|
Chris@17
|
35 * with range limits) and return result sets that you can iterate over using
|
Chris@17
|
36 * foreach loops. (The result sets are objects implementing the
|
Chris@17
|
37 * \Drupal\Core\Database\StatementInterface interface.)
|
Chris@0
|
38 * You can use the simple query functions for query strings that are not
|
Chris@0
|
39 * dynamic (except for placeholders, see below), and that you are certain will
|
Chris@0
|
40 * work in any database engine. See @ref sec_dynamic below if you have a more
|
Chris@0
|
41 * complex query, or a query whose syntax would be different in some databases.
|
Chris@0
|
42 *
|
Chris@17
|
43 * Note: \Drupal::database() is used here as a shorthand way to get a reference
|
Chris@17
|
44 * to the database connection object. In most classes, you should use dependency
|
Chris@17
|
45 * injection and inject the 'database' service to perform queries. See
|
Chris@17
|
46 * @ref sec_connection below for details.
|
Chris@0
|
47 *
|
Chris@0
|
48 * To use the simple database query functions, you will need to make a couple of
|
Chris@0
|
49 * modifications to your bare SQL query:
|
Chris@0
|
50 * - Enclose your table name in {}. Drupal allows site builders to use
|
Chris@0
|
51 * database table name prefixes, so you cannot be sure what the actual
|
Chris@0
|
52 * name of the table will be. So, use the name that is in the hook_schema(),
|
Chris@0
|
53 * enclosed in {}, and Drupal will calculate the right name.
|
Chris@0
|
54 * - Instead of putting values for conditions into the query, use placeholders.
|
Chris@0
|
55 * The placeholders are named and start with :, and they take the place of
|
Chris@0
|
56 * putting variables directly into the query, to protect against SQL
|
Chris@0
|
57 * injection attacks.
|
Chris@0
|
58 * - LIMIT syntax differs between databases, so if you have a ranged query,
|
Chris@17
|
59 * use \Drupal::database()->queryRange() instead of
|
Chris@17
|
60 * \Drupal::database()->query().
|
Chris@0
|
61 *
|
Chris@0
|
62 * For example, if the query you want to run is:
|
Chris@0
|
63 * @code
|
Chris@0
|
64 * SELECT e.id, e.title, e.created FROM example e WHERE e.uid = $uid
|
Chris@0
|
65 * ORDER BY e.created DESC LIMIT 0, 10;
|
Chris@0
|
66 * @endcode
|
Chris@0
|
67 * you would do it like this:
|
Chris@0
|
68 * @code
|
Chris@17
|
69 * $result = \Drupal::database()->queryRange('SELECT e.id, e.title, e.created
|
Chris@0
|
70 * FROM {example} e
|
Chris@0
|
71 * WHERE e.uid = :uid
|
Chris@0
|
72 * ORDER BY e.created DESC',
|
Chris@0
|
73 * 0, 10, array(':uid' => $uid));
|
Chris@0
|
74 * foreach ($result as $record) {
|
Chris@0
|
75 * // Perform operations on $record->title, etc. here.
|
Chris@0
|
76 * }
|
Chris@0
|
77 * @endcode
|
Chris@0
|
78 *
|
Chris@0
|
79 * Note that if your query has a string condition, like:
|
Chris@0
|
80 * @code
|
Chris@0
|
81 * WHERE e.my_field = 'foo'
|
Chris@0
|
82 * @endcode
|
Chris@0
|
83 * when you convert it to placeholders, omit the quotes:
|
Chris@0
|
84 * @code
|
Chris@0
|
85 * WHERE e.my_field = :my_field
|
Chris@0
|
86 * ... array(':my_field' => 'foo') ...
|
Chris@0
|
87 * @endcode
|
Chris@0
|
88 *
|
Chris@0
|
89 * @section sec_dynamic Dynamic SELECT queries
|
Chris@0
|
90 * For SELECT queries where the simple query API described in @ref sec_simple
|
Chris@0
|
91 * will not work well, you need to use the dynamic query API. However, you
|
Chris@0
|
92 * should still use the Entity Query API if your query involves entities or
|
Chris@0
|
93 * fields (see the @link entity_api Entity API topic @endlink for more on
|
Chris@0
|
94 * entity queries).
|
Chris@0
|
95 *
|
Chris@0
|
96 * The dynamic query API lets you build up a query dynamically using method
|
Chris@0
|
97 * calls. As an illustration, the query example from @ref sec_simple above
|
Chris@0
|
98 * would be:
|
Chris@0
|
99 * @code
|
Chris@17
|
100 * $result = \Drupal::database()->select('example', 'e')
|
Chris@0
|
101 * ->fields('e', array('id', 'title', 'created'))
|
Chris@0
|
102 * ->condition('e.uid', $uid)
|
Chris@0
|
103 * ->orderBy('e.created', 'DESC')
|
Chris@0
|
104 * ->range(0, 10)
|
Chris@0
|
105 * ->execute();
|
Chris@0
|
106 * @endcode
|
Chris@0
|
107 *
|
Chris@0
|
108 * There are also methods to join to other tables, add fields with aliases,
|
Chris@17
|
109 * isNull() to query for NULL values, etc. See
|
Chris@0
|
110 * https://www.drupal.org/developing/api/database for many more details.
|
Chris@0
|
111 *
|
Chris@0
|
112 * One note on chaining: It is common in the dynamic database API to chain
|
Chris@0
|
113 * method calls (as illustrated here), because most of the query methods modify
|
Chris@0
|
114 * the query object and then return the modified query as their return
|
Chris@0
|
115 * value. However, there are some important exceptions; these methods (and some
|
Chris@0
|
116 * others) do not support chaining:
|
Chris@0
|
117 * - join(), innerJoin(), etc.: These methods return the joined table alias.
|
Chris@0
|
118 * - addField(): This method returns the field alias.
|
Chris@0
|
119 * Check the documentation for the query method you are using to see if it
|
Chris@0
|
120 * returns the query or something else, and only chain methods that return the
|
Chris@0
|
121 * query.
|
Chris@0
|
122 *
|
Chris@17
|
123 * @section sec_insert INSERT, UPDATE, and DELETE queries
|
Chris@0
|
124 * INSERT, UPDATE, and DELETE queries need special care in order to behave
|
Chris@17
|
125 * consistently across databases; you should never use
|
Chris@17
|
126 * \Drupal::database()->query() to run an INSERT, UPDATE, or DELETE query.
|
Chris@17
|
127 * Instead, use functions \Drupal::database()->insert(),
|
Chris@17
|
128 * \Drupal::database()->update(), and \Drupal::database()->delete() to obtain
|
Chris@17
|
129 * a base query on your table, and then add dynamic conditions (as illustrated
|
Chris@17
|
130 * in @ref sec_dynamic above).
|
Chris@0
|
131 *
|
Chris@17
|
132 * Note: \Drupal::database() is used here as a shorthand way to get a reference
|
Chris@17
|
133 * to the database connection object. In most classes, you should use dependency
|
Chris@17
|
134 * injection and inject the 'database' service to perform queries. See
|
Chris@17
|
135 * @ref sec_connection below for details.
|
Chris@0
|
136 *
|
Chris@0
|
137 * For example, if your query is:
|
Chris@0
|
138 * @code
|
Chris@0
|
139 * INSERT INTO example (id, uid, path, name) VALUES (1, 2, 'path', 'Name');
|
Chris@0
|
140 * @endcode
|
Chris@0
|
141 * You can execute it via:
|
Chris@0
|
142 * @code
|
Chris@0
|
143 * $fields = array('id' => 1, 'uid' => 2, 'path' => 'path', 'name' => 'Name');
|
Chris@17
|
144 * \Drupal::database()->insert('example')
|
Chris@0
|
145 * ->fields($fields)
|
Chris@0
|
146 * ->execute();
|
Chris@0
|
147 * @endcode
|
Chris@0
|
148 *
|
Chris@0
|
149 * @section sec_transaction Transactions
|
Chris@0
|
150 * Drupal supports transactions, including a transparent fallback for
|
Chris@0
|
151 * databases that do not support transactions. To start a new transaction,
|
Chris@17
|
152 * call startTransaction(), like this:
|
Chris@17
|
153 * @code
|
Chris@17
|
154 * $transaction = \Drupal::database()->startTransaction();
|
Chris@17
|
155 * @endcode
|
Chris@17
|
156 * The transaction will remain open for as long as the variable $transaction
|
Chris@17
|
157 * remains in scope; when $transaction is destroyed, the transaction will be
|
Chris@17
|
158 * committed. If your transaction is nested inside of another then Drupal will
|
Chris@17
|
159 * track each transaction and only commit the outer-most transaction when the
|
Chris@17
|
160 * last transaction object goes out out of scope (when all relevant queries have
|
Chris@17
|
161 * completed successfully).
|
Chris@0
|
162 *
|
Chris@0
|
163 * Example:
|
Chris@0
|
164 * @code
|
Chris@0
|
165 * function my_transaction_function() {
|
Chris@17
|
166 * $connection = \Drupal::database();
|
Chris@0
|
167 * // The transaction opens here.
|
Chris@17
|
168 * $transaction = $connection->startTransaction();
|
Chris@0
|
169 *
|
Chris@0
|
170 * try {
|
Chris@17
|
171 * $id = $connection->insert('example')
|
Chris@0
|
172 * ->fields(array(
|
Chris@0
|
173 * 'field1' => 'mystring',
|
Chris@0
|
174 * 'field2' => 5,
|
Chris@0
|
175 * ))
|
Chris@0
|
176 * ->execute();
|
Chris@0
|
177 *
|
Chris@0
|
178 * my_other_function($id);
|
Chris@0
|
179 *
|
Chris@0
|
180 * return $id;
|
Chris@0
|
181 * }
|
Chris@0
|
182 * catch (Exception $e) {
|
Chris@0
|
183 * // Something went wrong somewhere, so roll back now.
|
Chris@17
|
184 * $transaction->rollBack();
|
Chris@0
|
185 * // Log the exception to watchdog.
|
Chris@0
|
186 * watchdog_exception('type', $e);
|
Chris@0
|
187 * }
|
Chris@0
|
188 *
|
Chris@17
|
189 * // $transaction goes out of scope here. Unless the transaction was rolled
|
Chris@17
|
190 * // back, it gets automatically committed here.
|
Chris@0
|
191 * }
|
Chris@0
|
192 *
|
Chris@0
|
193 * function my_other_function($id) {
|
Chris@17
|
194 * $connection = \Drupal::database();
|
Chris@0
|
195 * // The transaction is still open here.
|
Chris@0
|
196 *
|
Chris@0
|
197 * if ($id % 2 == 0) {
|
Chris@17
|
198 * $connection->update('example')
|
Chris@0
|
199 * ->condition('id', $id)
|
Chris@0
|
200 * ->fields(array('field2' => 10))
|
Chris@0
|
201 * ->execute();
|
Chris@0
|
202 * }
|
Chris@0
|
203 * }
|
Chris@0
|
204 * @endcode
|
Chris@0
|
205 *
|
Chris@0
|
206 * @section sec_connection Database connection objects
|
Chris@17
|
207 * The examples here all use functions like \Drupal::database()->select() and
|
Chris@17
|
208 * \Drupal::database()->query(), which can be called from any Drupal method or
|
Chris@17
|
209 * function code. In some classes, you may already have a database connection
|
Chris@17
|
210 * object in a member variable, or it may be passed into a class constructor
|
Chris@17
|
211 * via dependency injection. If that is the case, you can look at the code for
|
Chris@17
|
212 * \Drupal::database()->select() and the other functions to see how to get a
|
Chris@17
|
213 * query object from your connection variable. For example:
|
Chris@0
|
214 * @code
|
Chris@0
|
215 * $query = $connection->select('example', 'e');
|
Chris@0
|
216 * @endcode
|
Chris@0
|
217 * would be the equivalent of
|
Chris@0
|
218 * @code
|
Chris@17
|
219 * $query = \Drupal::database()->select('example', 'e');
|
Chris@0
|
220 * @endcode
|
Chris@0
|
221 * if you had a connection object variable $connection available to use. See
|
Chris@0
|
222 * also the @link container Services and Dependency Injection topic. @endlink
|
Chris@0
|
223 *
|
Chris@0
|
224 * @see https://www.drupal.org/developing/api/database
|
Chris@0
|
225 * @see entity_api
|
Chris@0
|
226 * @see schemaapi
|
Chris@0
|
227 *
|
Chris@0
|
228 * @}
|
Chris@0
|
229 */
|
Chris@0
|
230
|
Chris@0
|
231 /**
|
Chris@0
|
232 * @defgroup schemaapi Schema API
|
Chris@0
|
233 * @{
|
Chris@0
|
234 * API to handle database schemas.
|
Chris@0
|
235 *
|
Chris@0
|
236 * A Drupal schema definition is an array structure representing one or
|
Chris@0
|
237 * more tables and their related keys and indexes. A schema is defined by
|
Chris@0
|
238 * hook_schema(), which usually lives in a modulename.install file.
|
Chris@0
|
239 *
|
Chris@0
|
240 * By implementing hook_schema() and specifying the tables your module
|
Chris@0
|
241 * declares, you can easily create and drop these tables on all
|
Chris@0
|
242 * supported database engines. You don't have to deal with the
|
Chris@0
|
243 * different SQL dialects for table creation and alteration of the
|
Chris@0
|
244 * supported database engines.
|
Chris@0
|
245 *
|
Chris@0
|
246 * hook_schema() should return an array with a key for each table that
|
Chris@0
|
247 * the module defines.
|
Chris@0
|
248 *
|
Chris@0
|
249 * The following keys are defined:
|
Chris@0
|
250 * - 'description': A string in non-markup plain text describing this table
|
Chris@17
|
251 * and its purpose. References to other tables should be enclosed in curly
|
Chris@17
|
252 * brackets.
|
Chris@0
|
253 * - 'fields': An associative array ('fieldname' => specification)
|
Chris@0
|
254 * that describes the table's database columns. The specification
|
Chris@0
|
255 * is also an array. The following specification parameters are defined:
|
Chris@0
|
256 * - 'description': A string in non-markup plain text describing this field
|
Chris@17
|
257 * and its purpose. References to other tables should be enclosed in curly
|
Chris@17
|
258 * brackets. For example, the users_data table 'uid' field description
|
Chris@17
|
259 * might contain "The {users}.uid this record affects."
|
Chris@0
|
260 * - 'type': The generic datatype: 'char', 'varchar', 'text', 'blob', 'int',
|
Chris@0
|
261 * 'float', 'numeric', or 'serial'. Most types just map to the according
|
Chris@16
|
262 * database engine specific data types. Use 'serial' for auto incrementing
|
Chris@0
|
263 * fields. This will expand to 'INT auto_increment' on MySQL.
|
Chris@0
|
264 * A special 'varchar_ascii' type is also available for limiting machine
|
Chris@0
|
265 * name field to US ASCII characters.
|
Chris@0
|
266 * - 'mysql_type', 'pgsql_type', 'sqlite_type', etc.: If you need to
|
Chris@0
|
267 * use a record type not included in the officially supported list
|
Chris@0
|
268 * of types above, you can specify a type for each database
|
Chris@0
|
269 * backend. In this case, you can leave out the type parameter,
|
Chris@0
|
270 * but be advised that your schema will fail to load on backends that
|
Chris@0
|
271 * do not have a type specified. A possible solution can be to
|
Chris@0
|
272 * use the "text" type as a fallback.
|
Chris@0
|
273 * - 'serialize': A boolean indicating whether the field will be stored as
|
Chris@0
|
274 * a serialized string.
|
Chris@0
|
275 * - 'size': The data size: 'tiny', 'small', 'medium', 'normal',
|
Chris@0
|
276 * 'big'. This is a hint about the largest value the field will
|
Chris@0
|
277 * store and determines which of the database engine specific
|
Chris@16
|
278 * data types will be used (e.g. on MySQL, TINYINT vs. INT vs. BIGINT).
|
Chris@0
|
279 * 'normal', the default, selects the base type (e.g. on MySQL,
|
Chris@0
|
280 * INT, VARCHAR, BLOB, etc.).
|
Chris@0
|
281 * Not all sizes are available for all data types. See
|
Chris@0
|
282 * DatabaseSchema::getFieldTypeMap() for possible combinations.
|
Chris@0
|
283 * - 'not null': If true, no NULL values will be allowed in this
|
Chris@0
|
284 * database column. Defaults to false.
|
Chris@0
|
285 * - 'default': The field's default value. The PHP type of the
|
Chris@0
|
286 * value matters: '', '0', and 0 are all different. If you
|
Chris@0
|
287 * specify '0' as the default value for a type 'int' field it
|
Chris@0
|
288 * will not work because '0' is a string containing the
|
Chris@0
|
289 * character "zero", not an integer.
|
Chris@0
|
290 * - 'length': The maximal length of a type 'char', 'varchar' or 'text'
|
Chris@0
|
291 * field. Ignored for other field types.
|
Chris@0
|
292 * - 'unsigned': A boolean indicating whether a type 'int', 'float'
|
Chris@0
|
293 * and 'numeric' only is signed or unsigned. Defaults to
|
Chris@0
|
294 * FALSE. Ignored for other field types.
|
Chris@0
|
295 * - 'precision', 'scale': For type 'numeric' fields, indicates
|
Chris@0
|
296 * the precision (total number of significant digits) and scale
|
Chris@0
|
297 * (decimal digits right of the decimal point). Both values are
|
Chris@0
|
298 * mandatory. Ignored for other field types.
|
Chris@0
|
299 * - 'binary': A boolean indicating that MySQL should force 'char',
|
Chris@0
|
300 * 'varchar' or 'text' fields to use case-sensitive binary collation.
|
Chris@0
|
301 * This has no effect on other database types for which case sensitivity
|
Chris@0
|
302 * is already the default behavior.
|
Chris@0
|
303 * All parameters apart from 'type' are optional except that type
|
Chris@0
|
304 * 'numeric' columns must specify 'precision' and 'scale', and type
|
Chris@0
|
305 * 'varchar' must specify the 'length' parameter.
|
Chris@0
|
306 * - 'primary key': An array of one or more key column specifiers (see below)
|
Chris@0
|
307 * that form the primary key.
|
Chris@0
|
308 * - 'unique keys': An associative array of unique keys ('keyname' =>
|
Chris@0
|
309 * specification). Each specification is an array of one or more
|
Chris@0
|
310 * key column specifiers (see below) that form a unique key on the table.
|
Chris@0
|
311 * - 'foreign keys': An associative array of relations ('my_relation' =>
|
Chris@0
|
312 * specification). Each specification is an array containing the name of
|
Chris@0
|
313 * the referenced table ('table'), and an array of column mappings
|
Chris@0
|
314 * ('columns'). Column mappings are defined by key pairs ('source_column' =>
|
Chris@0
|
315 * 'referenced_column'). This key is for documentation purposes only; foreign
|
Chris@0
|
316 * keys are not created in the database, nor are they enforced by Drupal.
|
Chris@0
|
317 * - 'indexes': An associative array of indexes ('indexname' =>
|
Chris@0
|
318 * specification). Each specification is an array of one or more
|
Chris@0
|
319 * key column specifiers (see below) that form an index on the
|
Chris@0
|
320 * table.
|
Chris@0
|
321 *
|
Chris@17
|
322 * A key column specifier is either a string naming a column or an array of two
|
Chris@17
|
323 * elements, column name and length, specifying a prefix of the named column.
|
Chris@0
|
324 *
|
Chris@17
|
325 * As an example, this is the schema definition for the 'users_data' table. It
|
Chris@17
|
326 * shows five fields ('uid', 'module', 'name', 'value', and 'serialized'), the
|
Chris@17
|
327 * primary key (on the 'uid', 'module', and 'name' fields), and two indexes (the
|
Chris@17
|
328 * 'module' index on the 'module' field and the 'name' index on the 'name'
|
Chris@17
|
329 * field).
|
Chris@0
|
330 *
|
Chris@0
|
331 * @code
|
Chris@17
|
332 * $schema['users_data'] = [
|
Chris@17
|
333 * 'description' => 'Stores module data as key/value pairs per user.',
|
Chris@17
|
334 * 'fields' => [
|
Chris@17
|
335 * 'uid' => [
|
Chris@17
|
336 * 'description' => 'The {users}.uid this record affects.',
|
Chris@17
|
337 * 'type' => 'int',
|
Chris@17
|
338 * 'unsigned' => TRUE,
|
Chris@17
|
339 * 'not null' => TRUE,
|
Chris@17
|
340 * 'default' => 0,
|
Chris@17
|
341 * ],
|
Chris@17
|
342 * 'module' => [
|
Chris@17
|
343 * 'description' => 'The name of the module declaring the variable.',
|
Chris@17
|
344 * 'type' => 'varchar_ascii',
|
Chris@17
|
345 * 'length' => DRUPAL_EXTENSION_NAME_MAX_LENGTH,
|
Chris@17
|
346 * 'not null' => TRUE,
|
Chris@17
|
347 * 'default' => '',
|
Chris@17
|
348 * ],
|
Chris@17
|
349 * 'name' => [
|
Chris@17
|
350 * 'description' => 'The identifier of the data.',
|
Chris@17
|
351 * 'type' => 'varchar_ascii',
|
Chris@17
|
352 * 'length' => 128,
|
Chris@17
|
353 * 'not null' => TRUE,
|
Chris@17
|
354 * 'default' => '',
|
Chris@17
|
355 * ],
|
Chris@17
|
356 * 'value' => [
|
Chris@17
|
357 * 'description' => 'The value.',
|
Chris@17
|
358 * 'type' => 'blob',
|
Chris@17
|
359 * 'not null' => FALSE,
|
Chris@17
|
360 * 'size' => 'big',
|
Chris@17
|
361 * ],
|
Chris@17
|
362 * 'serialized' => [
|
Chris@17
|
363 * 'description' => 'Whether value is serialized.',
|
Chris@17
|
364 * 'type' => 'int',
|
Chris@17
|
365 * 'size' => 'tiny',
|
Chris@17
|
366 * 'unsigned' => TRUE,
|
Chris@17
|
367 * 'default' => 0,
|
Chris@17
|
368 * ],
|
Chris@17
|
369 * ],
|
Chris@17
|
370 * 'primary key' => ['uid', 'module', 'name'],
|
Chris@17
|
371 * 'indexes' => [
|
Chris@17
|
372 * 'module' => ['module'],
|
Chris@17
|
373 * 'name' => ['name'],
|
Chris@17
|
374 * ],
|
Chris@0
|
375 * // For documentation purposes only; foreign keys are not created in the
|
Chris@0
|
376 * // database.
|
Chris@17
|
377 * 'foreign keys' => [
|
Chris@17
|
378 * 'data_user' => [
|
Chris@0
|
379 * 'table' => 'users',
|
Chris@17
|
380 * 'columns' => [
|
Chris@17
|
381 * 'uid' => 'uid',
|
Chris@17
|
382 * ],
|
Chris@17
|
383 * ],
|
Chris@17
|
384 * ],
|
Chris@17
|
385 * ];
|
Chris@0
|
386 * @endcode
|
Chris@0
|
387 *
|
Chris@0
|
388 * @see drupal_install_schema()
|
Chris@0
|
389 *
|
Chris@0
|
390 * @}
|
Chris@0
|
391 */
|
Chris@0
|
392
|
Chris@0
|
393 /**
|
Chris@0
|
394 * @addtogroup hooks
|
Chris@0
|
395 * @{
|
Chris@0
|
396 */
|
Chris@0
|
397
|
Chris@0
|
398 /**
|
Chris@0
|
399 * Perform alterations to a structured query.
|
Chris@0
|
400 *
|
Chris@0
|
401 * Structured (aka dynamic) queries that have tags associated may be altered by any module
|
Chris@0
|
402 * before the query is executed.
|
Chris@0
|
403 *
|
Chris@0
|
404 * @param $query
|
Chris@0
|
405 * A Query object describing the composite parts of a SQL query.
|
Chris@0
|
406 *
|
Chris@0
|
407 * @see hook_query_TAG_alter()
|
Chris@0
|
408 * @see node_query_node_access_alter()
|
Chris@0
|
409 * @see AlterableInterface
|
Chris@0
|
410 * @see SelectInterface
|
Chris@0
|
411 *
|
Chris@0
|
412 * @ingroup database
|
Chris@0
|
413 */
|
Chris@0
|
414 function hook_query_alter(Drupal\Core\Database\Query\AlterableInterface $query) {
|
Chris@0
|
415 if ($query->hasTag('micro_limit')) {
|
Chris@0
|
416 $query->range(0, 2);
|
Chris@0
|
417 }
|
Chris@0
|
418 }
|
Chris@0
|
419
|
Chris@0
|
420 /**
|
Chris@0
|
421 * Perform alterations to a structured query for a given tag.
|
Chris@0
|
422 *
|
Chris@0
|
423 * @param $query
|
Chris@0
|
424 * An Query object describing the composite parts of a SQL query.
|
Chris@0
|
425 *
|
Chris@0
|
426 * @see hook_query_alter()
|
Chris@0
|
427 * @see node_query_node_access_alter()
|
Chris@0
|
428 * @see AlterableInterface
|
Chris@0
|
429 * @see SelectInterface
|
Chris@0
|
430 *
|
Chris@0
|
431 * @ingroup database
|
Chris@0
|
432 */
|
Chris@0
|
433 function hook_query_TAG_alter(Drupal\Core\Database\Query\AlterableInterface $query) {
|
Chris@0
|
434 // Skip the extra expensive alterations if site has no node access control modules.
|
Chris@0
|
435 if (!node_access_view_all_nodes()) {
|
Chris@0
|
436 // Prevent duplicates records.
|
Chris@0
|
437 $query->distinct();
|
Chris@0
|
438 // The recognized operations are 'view', 'update', 'delete'.
|
Chris@0
|
439 if (!$op = $query->getMetaData('op')) {
|
Chris@0
|
440 $op = 'view';
|
Chris@0
|
441 }
|
Chris@0
|
442 // Skip the extra joins and conditions for node admins.
|
Chris@0
|
443 if (!\Drupal::currentUser()->hasPermission('bypass node access')) {
|
Chris@0
|
444 // The node_access table has the access grants for any given node.
|
Chris@0
|
445 $access_alias = $query->join('node_access', 'na', '%alias.nid = n.nid');
|
Chris@0
|
446 $or = new Condition('OR');
|
Chris@0
|
447 // If any grant exists for the specified user, then user has access to the node for the specified operation.
|
Chris@0
|
448 foreach (node_access_grants($op, $query->getMetaData('account')) as $realm => $gids) {
|
Chris@0
|
449 foreach ($gids as $gid) {
|
Chris@0
|
450 $or->condition((new Condition('AND'))
|
Chris@0
|
451 ->condition($access_alias . '.gid', $gid)
|
Chris@0
|
452 ->condition($access_alias . '.realm', $realm)
|
Chris@0
|
453 );
|
Chris@0
|
454 }
|
Chris@0
|
455 }
|
Chris@0
|
456
|
Chris@0
|
457 if (count($or->conditions())) {
|
Chris@0
|
458 $query->condition($or);
|
Chris@0
|
459 }
|
Chris@0
|
460
|
Chris@0
|
461 $query->condition($access_alias . 'grant_' . $op, 1, '>=');
|
Chris@0
|
462 }
|
Chris@0
|
463 }
|
Chris@0
|
464 }
|
Chris@0
|
465
|
Chris@0
|
466 /**
|
Chris@0
|
467 * Define the current version of the database schema.
|
Chris@0
|
468 *
|
Chris@0
|
469 * A Drupal schema definition is an array structure representing one or more
|
Chris@0
|
470 * tables and their related keys and indexes. A schema is defined by
|
Chris@0
|
471 * hook_schema() which must live in your module's .install file.
|
Chris@0
|
472 *
|
Chris@0
|
473 * The tables declared by this hook will be automatically created when the
|
Chris@0
|
474 * module is installed, and removed when the module is uninstalled. This happens
|
Chris@0
|
475 * before hook_install() is invoked, and after hook_uninstall() is invoked,
|
Chris@0
|
476 * respectively.
|
Chris@0
|
477 *
|
Chris@0
|
478 * By declaring the tables used by your module via an implementation of
|
Chris@0
|
479 * hook_schema(), these tables will be available on all supported database
|
Chris@0
|
480 * engines. You don't have to deal with the different SQL dialects for table
|
Chris@0
|
481 * creation and alteration of the supported database engines.
|
Chris@0
|
482 *
|
Chris@0
|
483 * See the Schema API Handbook at https://www.drupal.org/node/146843 for details
|
Chris@0
|
484 * on schema definition structures. Note that foreign key definitions are for
|
Chris@0
|
485 * documentation purposes only; foreign keys are not created in the database,
|
Chris@0
|
486 * nor are they enforced by Drupal.
|
Chris@0
|
487 *
|
Chris@0
|
488 * @return array
|
Chris@0
|
489 * A schema definition structure array. For each element of the
|
Chris@0
|
490 * array, the key is a table name and the value is a table structure
|
Chris@0
|
491 * definition.
|
Chris@0
|
492 *
|
Chris@0
|
493 * @ingroup schemaapi
|
Chris@0
|
494 */
|
Chris@0
|
495 function hook_schema() {
|
Chris@17
|
496 $schema['users_data'] = [
|
Chris@17
|
497 'description' => 'Stores module data as key/value pairs per user.',
|
Chris@0
|
498 'fields' => [
|
Chris@17
|
499 'uid' => [
|
Chris@17
|
500 'description' => 'The {users}.uid this record affects.',
|
Chris@0
|
501 'type' => 'int',
|
Chris@0
|
502 'unsigned' => TRUE,
|
Chris@0
|
503 'not null' => TRUE,
|
Chris@0
|
504 'default' => 0,
|
Chris@0
|
505 ],
|
Chris@17
|
506 'module' => [
|
Chris@17
|
507 'description' => 'The name of the module declaring the variable.',
|
Chris@17
|
508 'type' => 'varchar_ascii',
|
Chris@17
|
509 'length' => DRUPAL_EXTENSION_NAME_MAX_LENGTH,
|
Chris@0
|
510 'not null' => TRUE,
|
Chris@0
|
511 'default' => '',
|
Chris@0
|
512 ],
|
Chris@17
|
513 'name' => [
|
Chris@17
|
514 'description' => 'The identifier of the data.',
|
Chris@17
|
515 'type' => 'varchar_ascii',
|
Chris@17
|
516 'length' => 128,
|
Chris@0
|
517 'not null' => TRUE,
|
Chris@0
|
518 'default' => '',
|
Chris@0
|
519 ],
|
Chris@17
|
520 'value' => [
|
Chris@17
|
521 'description' => 'The value.',
|
Chris@17
|
522 'type' => 'blob',
|
Chris@17
|
523 'not null' => FALSE,
|
Chris@17
|
524 'size' => 'big',
|
Chris@17
|
525 ],
|
Chris@17
|
526 'serialized' => [
|
Chris@17
|
527 'description' => 'Whether value is serialized.',
|
Chris@17
|
528 'type' => 'int',
|
Chris@17
|
529 'size' => 'tiny',
|
Chris@17
|
530 'unsigned' => TRUE,
|
Chris@17
|
531 'default' => 0,
|
Chris@17
|
532 ],
|
Chris@0
|
533 ],
|
Chris@17
|
534 'primary key' => ['uid', 'module', 'name'],
|
Chris@0
|
535 'indexes' => [
|
Chris@17
|
536 'module' => ['module'],
|
Chris@17
|
537 'name' => ['name'],
|
Chris@0
|
538 ],
|
Chris@0
|
539 // For documentation purposes only; foreign keys are not created in the
|
Chris@0
|
540 // database.
|
Chris@0
|
541 'foreign keys' => [
|
Chris@17
|
542 'data_user' => [
|
Chris@0
|
543 'table' => 'users',
|
Chris@17
|
544 'columns' => [
|
Chris@17
|
545 'uid' => 'uid',
|
Chris@17
|
546 ],
|
Chris@0
|
547 ],
|
Chris@0
|
548 ],
|
Chris@0
|
549 ];
|
Chris@17
|
550
|
Chris@0
|
551 return $schema;
|
Chris@0
|
552 }
|
Chris@0
|
553
|
Chris@0
|
554 /**
|
Chris@0
|
555 * @} End of "addtogroup hooks".
|
Chris@0
|
556 */
|