Working with a Database in Drupal 7 - Lesson 5 - Extenders
Select queries in Drupal support extenders. An extender allows you to add functionality to a SELECT query at runtime. This functionality can either be a new method or can override the behavior of an existing one.
This is achieved using object-oriented programming design patterns. Extenders implement the Decorator Pattern, attaching additional responsibilities to a dynamic object by providing a flexible subclass-like extension of query methods.
Using Extenders
To use an extender, you must start with a query object. Calling the extend()
method on a query returns a new object with added functionality. For example:
extend('PagerDefault'); ?>
In this example, a new PagerDefault
object wraps the original query object, adding pagination functionality. The $query
variable is replaced with the returned extended object—important for using the extended methods. If you forget to assign the result of extend()
, you'll lose access to the extended features:
fields('n', array('nid', 'title')) ->extend('PagerDefault') // returns a new object ->limit(5); // works on the PagerDefault object // But this refers to the original SelectQuery object again! $query->orderBy('title'); // So the execute() below won't use the PagerDefault features. $result = $query->execute(); ?>
To avoid such issues, always assign the result of extend()
back to the query variable:
extend('PagerDefault')->extend('TableSort'); $query->fields(...); // ... ?>
This ensures that $query
is fully extended before calling any methods. You can chain multiple extenders, but note that the order can matter. For instance, TableSort should come after PagerDefault.
Creating Custom Extenders
An extender is simply a class that implements SelectQueryInterface
and accepts two parameters in its constructor: the base select query and the database connection. Typically, you’ll subclass SelectQueryExtender
, which handles most of the interface for you.
You can define any extender by subclassing SelectQueryExtender
. The name of the class is what you pass to extend()
. You may override existing methods or define your own, as needed.
The extender must return what the interface expects—usually the query object or another extender object. You can chain methods or decorate the query however you like.
Example Extender
query->orderBy($field, $direction); return $this; } } ?>
In this example, orderBy()
is overridden with a no-op, and a new method orderByForReal()
is introduced to perform the actual sorting. Both methods return $this
to preserve the query object for chaining.
Any module can define its own extenders. Drupal core provides two useful built-in extenders: PagerDefault and TableSort.
Supporting Multiple Databases
Just like db_select()
, extenders support database-specific versions. Drupal will automatically look for a database-specific suffix for your extender class. For example:
ExampleExtender_pgsql
ExampleExtender_mysql
If available, Drupal will use the DBMS-specific class automatically. This helps ensure compatibility with different database backends while maintaining consistent logic in your query handling.