1. Drupal DataBase
The next Generation
Drupal for the Enterprise.
Austin Drupal User’s Group
David Diers, Developer
Four Kitchens
Austin, TX
D.O - thebruce
@beautyhammer
2. What we’ll cover
! Basics of the Drupal DB API
! Using db_query
! Using and building dynamic queries including
! Criteria clauses, joins, sorting, sub-selects,
extenders, and tagging
! How to work with result sets
3. Drupal and the DB
! Drupal uses the DB to:
! Store content - where
"content" is thought of very
broadly.
! Store user or module
configurations
! Store system values
! Drupal retrieves these values
all of the time.
4. Drupal and DB Abstraction
! What is DB Abstraction?
! Uniformity
! Structure
! DB Abstraction is nothing new for Drupal
5. Drupal 7 DB API uses PDO
! D7 had a facelift for DB Layer
! Built on PDO
! PDO is PHPOOP
! Drupal extends PDO classes
! PDO used in many projects
6. It takes some learning. So
why learn the DB API?
! You may say, with all due
respect:
! Isn’t most of this stuff in
functions already?
! Doesn’t views do this for
me?
! Can’t I just use raw SQL
like they did in ye olde
days™?
7. A few reasons:
! Need a result set that you can’t get or requires custom
views development?
! Writing or supporting custom modules with their own
schema?
! Need results from the contrib module’s schema but there
isn’t a function to do so?
! Patching an existing modules’ database functionality?
! Need additional ways besides exportables (features, views,
panels) to migrate configuration via update hooks?
! Need to implement custom sql statements to improve the
performance of your site?
8. Using the DB API
! 2 Primary ways to interact
with data in D7
! Dbquery
! Dynamic queries
9. db_query – the basics
! IN SQL: ! IN db_query:
<?php
$type = ‘article’;
SELECT nid, title $result = db_query("SELECT nid, title
FROM node FROM {node}
WHERE type = ‘article’; WHERE type = :type", array(':type' =>
$type,));
10. db_query – the breakdown
$result = db_query("SELECT nid, title FROM {node} WHERE type = :type",
array(':type' => $type,));
! db_query($query, $placeholders, $options)
! enclose tables in { }
! Placeholders start with ":"
! EX: WHERE type = :type", array(':type' => 'page',))
! Options array - 2 common ones are:
! Target (default or slave)
! Fetch (pdo fetch type)
http://druptest7.dev:8888/example_dbq
11. db_query d6 => d7
transitions
! In transitioning D6 db_query to D7 be aware:
! The syntax signature has changed –
• D6 parameters were less orderly
• % syntax for placeholders
• Lawless, unregulated SQL could be executed like a
dishonorable Klingon
12. Dynamic sql – the basics
! Dynamic Queries
! Much more transportable, powerful, but complex
! You MUST use for ( INSERT, UPDATE,
DELETE)
! You may use for (SELECT)
• http://www.lullabot.com/articles/simplify-your-code-
with-drupal-7s-database-api ).
13. db_select – the basics
! IN SQL: ! IN db_select:
<?php
$type = ‘article’;
SELECT nid, title $query = db_select(‘node’, ’n’);
FROM node $result = $query->fields(‘n’,
WHERE type = ‘article’; array(‘nid’,’title’)
->condition(‘n.type’,$type)
->execute();
Note: This is an example for comparison, selects are better served by dbquery.
14. db_select – the basics
! IN db_select:
<?php
$type = ‘article’;
(‘node’, ‘n’)
$query = db_select(‘node’, ’n’);
Name of Table / Alias
(fields(‘n’, array(‘nid’,
$result = $query->fields(‘n’,
‘title’))
array(‘nid’,’title’)
->condition(‘n.type’,$type)
Alias, array of fields
->execute();
Single quotes are
important for
transferability.
15. DQ– of special note
$query = db_select(‘node’, ’n’);
$result = $query->fields(‘n’, array(‘nid’,’title’)
->condition(‘n.type’,$type)
->execute();
! Fluid Interface
! ->execute();
! Returns a result set / statement object
16. Working with Result Sets
! Use - foreach loop or
! or - Specificaly get the next record
! $record = $result->fetch(); // Use the default fetch
mode.
! $record = $result->fetchObject(); // Fetch as a
stdClass object.
! $record = $result->fetchAssoc(); // Fetch as an
associative array.
! or - to get a single field
! $record = $result->fetchField($column_index);
18. Questions to help decide
! Is your query static? Use db_query it is faster.
! Does your query need to be constructed at run
time? Use dynamic queries
! Do you need to INSERT, UPDATE, or DELETE?
Use Dynamic queries.
19. More with Dynamic Queries
! How to add fields or select *
! Conditional Statements
! AND / OR
! Sub-selects
! JOINS
! SORT
20. Working with Fields
! Adding fields to a query:
$query->fields('n', array('nid',
'title', 'created', 'uid'));
"select *" is fields with no
field array indicated:
$query->fields('n');
http://druptest7.dev:8888/example_dyn
21. Conditional Statements
! Signature: $query->condition($field, $value = NULL,
$operator = '=')
Default operator is SQL =
! can take ANSI sql comparators <, >, LIKE, = >=
! EX: $query->condition('nid',1)
! EX: $query->condition('nid',1, '<>')
! In or between:
! EX: $query->condition('myfield', array(1, 2, 3), 'IN');
22. Conditional Statements
(more)
! Nested Conditionals:
db_and() / db_or() / db_xor() are used to handle
nested conditionals such as:
->condition(db_or()
->condition('field2', 5)
->condition('field3', 6))
Testing for NULL:
$query->isNull('myfield');
$query->isNotNull('myfield');
23. An example from contrib…
! Original d6 query:
$result = db_query('SELECT * FROM {users}
WHERE ((access <> 0 AND login <> 0 AND access
< (%d - %d)) OR (login = 0 AND created < (%d -
%d))) AND uid <> 1', REQUEST_TIME, $warn_time,
REQUEST_TIME, $warn_time);
…ah, contrib.
25. Subselects
! Subselects - form a query using dbtng then instead
of executing it -
! use that query variable in a condition.
! Best when:
• one value is returned,
• A column return value is used with an IN clause.
! $query->condition('myfield', $querysubselect, 'IN');
28. db_select Tagging
Tagging – lets alter hooks take action
ex: $query->addTag('node_access'); - this should be
implemented on all queries that retrieve nodes.
Node access query alter will then check to see if a
user can see the nodes in the result.
(http://druptest7.dev:8888/example_tag)
29. What about the others?
! db_update, db_insert,
db_delete
! Similar syntax
! Assemble and execute.
30. db_insert
! Syntax: $query = db_insert('node', $options);
$nid = db_insert('node')
->fields(array(
'title' => ’This Example',
'uid' => 1,
'created' => REQUEST_TIME,
))
->execute();
db_insert returns the auto-increment value defined by
hook_schema.
(http://druptest7.dev:8888/example_insert)
32. db_delete
Signature: $query = db_delete('node', $options);
$num_deleted = db_delete('node')
->condition('nid', 5)
->execute();
db_delete returns the number of rows deleted.
http://druptest7.dev:8888/example_delete
33. db_select extenders
! Extenders - implements a decorator pattern, currently only 2
!
TableSort and PagerQuery = adds the methods for these
extension to query
! example:
$query = $query
->extend('TableSort')
->orderByHeader($header);
! Put these near the start.
! Not chainable, if you forget to return it to itself it will have odd
results.
http://druptest7.dev:8888/example_extend
35. What is the ?
Now that you have Drupal DB Chops
You may start to consider…
To VIEWS or
not to
VIEWS
36. DB API an Alternative to
Views
! Who will maintain the functionality?
! Does it require a lot of custom views code?
! Are you doing a lot of aggregated data work?
! Do you need a highly tuned SQL statement for performance?
! Do you need a lot of user facing, non programmer modifications,
will site builders be cloning views or modifying displays?
! Are you integrating with other modules (panels, voting, etc)?
! How complex are the changes you need to views default
queries/output?
To views or not to views (http://drupal.org/node/242311 )
37. Quick case study:
• Client wanted private comments
• Client wanted to see a listing of all comments
made against all nodes
• Views can do this but it is described as “hacky”
• Created a db_select with pager and table
extenders
38. Questions and Thank you!
David Diers, Developer
Four Kitchens
david.diers@fourkitchens.com
D.O - thebruce
@beautyhammer
Notes de l'éditeur
Content – nodes, entities, caches, paths, custom tablesconfig - administrator, moduleconfiguraton, or user based configuration settingssystem – logs (watchdog), sessions, queue to be processed at next cron, class hashes
What is DB Abstraction?-> db implementations vary from product to product, ANSI sql implementations vary widelyA way of uniformly interacting and leveraging SQL commands with multiple types of database products.A structured way of dynamically constructing SQL statementsDB Abstraction is nothing new to Drupal – Just the way it is done.
It was built on PDO – PHP Data ObjectsPDO is written in PHPOOPDrupal implements extensions or interfaces of PDO classesPDO is in wide use across many projects (Symfony, Zend Framework, Magento) so we leverage all of the work, testing and bug fixes from the PHP community at large.
Don’t I already get a lot of stuff through the built in functions of core and contributed modules?Can’t I just use views for my sql stuff.
2 Primary ways to interact with data in D7dbquery – performant limited transferability amongst dbs simpler – more like the sql of OLDEDynamic queries – powerful high transferability among dbs less performant. complex
Let’s use this simple query of the node table
enclose tables in { } for db prefixarg 2 - use placeholders to avoid sql injection, start with ":"EX: WHERE type = :type", array(':type' => 'page',))arg3 – options array - 2 common ones are:target - default or slavefetch - pdo fetch type (the select statement will return into the fetch type specified. Can and should use object(default), assoc array, or a string class name.
IF YOU ARE DOING TRANSITION WORK FROM D6 to D7Be aware:The syntax signature has changed –D6 took a single parameter – the $query, followed by a variable number of arguments or an array of query substitutions.D6 used the % syntax for placeholders instead of the “:” syntax.Functions other than select could be executed via db_query.
Let’s use this simple query of the node table
Let’s use this simple query of the node tableNode table and then ‘n’ as the table alias, we’ll use the table alias throughout the query as we can do in SQL.
$query = db_select(‘node’, ’n’);$result = $query->fields(‘n’, array(‘nid’,’title’) ->condition(‘n.type’,$type) ->execute;Fluid Interface - allows chaining (because the results of each chainable method return an instance of the object itself)Not all methods are chainable so consult your documentation.$result = $query->execute();Query statements are executed by ->execute(); easy to forget, but don't.You’ll get back a result set / statement objectforeach ($result as $record) { //do something
Because you have a results set returned – to handle the results and get them out to your php in a meaningful wayYou might find the following helpful.
How to work with fieldsUse table aliasSyntax is fields(‘table’, array(of fields))Looking for your olde select * inefficient query? - use fields(‘table’)
This complicated query says:Give me all columns from the table users where UID is not =1 andeither the access does not equal 0And log in does not equal 0And access is less than the result of Request_time minus the $warn_time variableOR the login is 0And created less than the result of Request time minus the warn_time variable.
Give me all columns from the table users where UID is not =1 andeither the access does not equal 0And log in does not equal 0And access is less than the result of Request_time minus the $warn_time variableOR the login is 0And created less than the result of Request time minus the warn_time variable.
Tagging - any dynamic select query can be tagged with one or more strings which then allows alter hooks to determine if they need to take action.Via hook_query_alter & hook_query_TAG_alterex: $query->addTag('node_access'); - this should be implemented on all queries that retrieve nodes.Node access query alter will then check to see if a user can see the nodes in the result.
Extenders - implements a decorator pattern, currently only two in coreDecorator pattern – allows the “decoration” of a classes functionality at run time, allowing you to change a single instance of a class with a set of functionalityTableSort and PagerQuery = adds the methods for these extension to queryexample:$query = $query->extend('TableSort')->orderByHeader($header);Put these near the start.Not chainable, if you forget to return it to itself it will have odd results.