Community Documentation

Extenders

Last updated March 4, 2013. Created by Crell on July 2, 2009.
Edited by markpavlitski, drupalshrek, joachim, Sylvain Lecoy. Log in to edit this page.

Select queries support the concept of "extenders". An extender is a way to add functionality to a Select query at runtime. That functionality could be additional methods or altering the behavior of existing methods.

For those familiar with object-oriented design patterns, extenders are an implementation of the Decorator Pattern. They attach additional responsibilities to an object dynamically by providing a flexible alternative to subclassing for extending functionality.

Using an Extender

To use an extender, you must first have a query object. From the query object, the extend() method will return a new object that should be used in place of the query object. For example:

<?php
$query
= $query->extend('PagerDefault');
?>

The above line takes a select query, creates a new PagerDefault query object that contains the original select query, and returns the new object. $query may now be used as if it were the original query object but with additional methods now available.

Note that $query is not altered in place. The new object is returned from extend(), and if it is not saved to a variable it will be lost. For example, the following will not do what you expect:

<?php
$query
= db_select('node', 'n');
$query
 
->fields('n', array('nid', 'title'))
  ->
extend('PagerDefault')   // This line returns a new PagerDefault object.
 
->limit(5);               // This line works, because the PagerDefault object is what is called.

// The return from extend() was never saved to a variable, so $query is still just the Select object.
$query->orderBy('title');

// This line executes the Select object, not the extender.  The extender no longer exists.
$result = $query->execute();
?>

To avoid this problem, the recommended convention for extending a Select query is to do so when the query is first declared.

<?php
$query
= db_select('node', 'n')->extend('PagerDefault')->extend('TableSort');
$query->fields(...);
// ...
?>

That ensures that $query is the fully extended object right from the beginning.

Also note that while extenders may be stacked (as in the example above), not all extenders are compatible with other extenders and the order may matter. For example, a query that is extended with both a pager and table-sort behavior must be extended with PagerDefault first.

Creating new extenders

An extender is simply a class that implements the SelectQueryInterface, and takes two parameters in its constructor: A select query (or rather, another object that implements SelectQueryInterface) and a DatabaseConnection object. It must then re-implement the methods of SelectQueryInterface and pass them through to query object specified in the constructor, returning itself where appropriate.

In the vast majority of cases, all of that can be done by extending the SelectQueryExtender class which handles all of that internally. In practice, therefore, an extender is any class that extends SelectQueryExtender. The name of the class is what should be specified in the extend() call from a query object.

It is up to the extender class, then, to add or override methods as appropriate. Any method that it does not override will be transparently passed through to the wrapped query object. When overriding a method, the extender may or may not call the underlying query object but it must return the same value that is expected from the SelectQuery interface. In most cases, that is the query object itself, or for the extender the extender object itself.

The following example should make that clearer.

<?php
class ExampleExtender extends SelectQueryExtender {

 
/**
   * Override the normal orderBy behavior.
   */
 
public function orderBy($field, $direction = 'ASC') {
    return
$this;
  }

 
/**
   * Add a new method of our own.
   */
 
public function orderByForReal($field, $direction = 'ASC') {
   
$this->query->orderBy($field, $direction);
    return
$this;
  }
}
?>

The example above overrides the orderBy() method of a query to do nothing, but adds another method, orderByForReal(), that implements actual ordering behavior. (Naturally this is a rather pointless example, but it does serve to illustrate how extenders work.) Note that in both methods, the $this being returned is the extender object itself. That ensures that the extender doesn't "get lost" by returning the query object.

Any module may declare an Extender. Core ships with two that are generally useful: PagerDefault and TableSort. See the API documentation for those classes for how to leverage them in your own code.

Supporting multiple database types

The extend method works the same way as db_select, in that it searches for a class name that is suffixed with the database driver.

Therefore, you can define a class ExampleExtender_pgsql as well as ExampleExtender and the former will be used if applicable.

Comments

Pager query

This is a dead simple example of a pager with query and output:

<?php
function mymodule_page() {
 
// this will be filled with an array of our content
 
$build = array();
 
 
// basic select query
 
$select = db_select('node', 'n')
    ->
fields('n', array('nid'))
    ->
extend('PagerDefault') // this adds the pager
   
->limit(10)
 
 
// execute the query
 
$nids = $select->execute()->fetchCol();
 
 
// do something to output your results here.
 
foreach ($nids as $nid) {
   
$content .= $nid;
  }
 
// add the content
 
$build['content'] = array('#markup' => $content)
 
 
// Add the pager
 
$build['pager'] = array(
   
'#theme' => 'pager',
   
'#weight' => 5,
  );
 
 
// return the renderable array
 
return $build;
}
?>

About this page

Drupal version
Drupal 7.x
Audience
Programmers
Drupal’s online documentation is © 2000-2013 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License. Comments on documentation pages are used to improve content and then deleted.
nobody click here