Open Source Software Technical Articles

Want the Best of the Wazi Blogs Delivered Directly to your Inbox?

Subscribe to Wazi by Email

Your email:

Connect with Us!

Current Articles | RSS Feed RSS Feed

Build your own custom modules for Drupal 7, part 2

  
  
  

In our previous tutorial, we got started setting up a custom module for Drupal 7. Modules make Drupal immensely powerful and flexible, and it's well worth learning how to make your own if only to get a better understanding of how the various contributed modules available to everyone work. In this second part, we'll learn to use the Drupal Database API to get data out of a site, and to write a hook to show a block on the site.

So far, the module we wrote last time doesn't really do anything; it just tells you what it will do. To provide actual site content in the form of a new block, we need to use the Database API to talk to Drupal's database. This API is just for Drupal 7; in previous versions you had to write your own SQL queries manually, but now there's a spiffy interface to help you out. The Drupal 7 Database API is object-oriented (although a procedural front end is also available) and built with the native PHP PDO engine, and it's easy to use. You can still write your own SQL code by hand if you prefer, but use the Database API unless you're certain it won't do what you want. If you're interested in learning more about the change and its advantages, this excellent article by Jeff Eaton examines the code changes the Database API allowed him to make in one module.

For our module, we're going to use the Database API in a helper function, just to perform the database query. This function isn't a hook itself, in that it won't be called directly from Drupal core. Instead, we'll write a separate function, hook_block_view, to display the data, which will be called from Drupal core. As with all coding, it's good to keep your functionality modular wherever possible; you might, for example, want to do something different with this query later.

Because this function isn't being called as a hook from Drupal core, it doesn't have to have a specific name. (As discussed in the previous tutorial, hooks called from Drupal core must have a specific name and API for Drupal to be able to find them.) However, Drupal does have module and function naming standards that we have to follow. We'll make this function a private one, meaning no other modules should call it, which means we should start the function name with an underscore, and call it _same_author_dbquery(). If it were a public function, we would use same_author_dbquery().

All this function does is perform the database query; in a moment we'll write the hook that lets Drupal display the information it retrieves.

 
function _same_author_dbquery() {
  $node = menu_get_object();
  $query = db_select('node', 'n')
    ->fields('n', array('nid', 'title', 'created', 'uid'))
    ->condition('status', 1)
    ->condition('uid', $node->uid)
    ->orderBy('created', 'DESC')
    ->execute();
  return $query;
}

$node is the current node object (page), which we need so that we can find out its author. The meat of this hook is, obviously, in the db_select function. The first line identifies the table we wish to query (node), and gives it a short-form name (n) for easy reference. We then choose a set of fields to return from the given table, and set two conditions: that to be returned, a node must have status 1, meaning published, and that its author ID (uid) must be the same as the author ID of the current node. We specify that we want the results returned by their time of creation, most recent first, and send the query to be executed. We can do all of this neatly in a single statement, with linked method applications. The function returns the $query result.

Displaying a block

Next, we need the same_author_block_view function, which tells Drupal how to display the block:

 
function same_author_block_view($delta = '') {
  switch($delta) {
    case 'same_author':
      $block['subject'] = t('Same Author');
      if(user_access('access content')) {
        $result = same_author_dbquery();
        $items = array();
        foreach ($result as $node) {
          $items[] = array(
            'data' => l($node->title, 'node/' . $node->nid),
          );
        }
        if (empty($items)) {
          $block['content'] = t('No other posts by this author');
        } else {
          $block['content'] = theme('item_list', array(
            'items' => $items),
          );
        }
      }
    return $block;
  }
}

Here's a rundown of what you need to know about this:

  • hook_block_view() returns a two-value array with subject and content elements, so we need to make sure that our function matches this.
  • $delta holds the name of the specific block that is requested. As you'll remember, our module could have multiple blocks, and they'd all be dealt with by this hook. We use a switch statement to handle the block, despite the fact that currently there's only a single case, to make it easy to add more blocks in future.
  • Once we're handling our specific block, we first set its Subject line.
  • user_access('access content') deals with user access controls. access content is the most basic permission check; on most sites, all users can access content (unless your site is private). You can get a list of machine-readable (i.e. usable in code) permission names from the Permissions dropdown at http://site/admin/people.
  • $result stores the result of the database query block we already wrote; we iterate over it, grabbing the title and ID of each node in the query result in turn, and generating a link to add to the $items array. The link is generated by the l() function (that's the letter l), which takes two parameters and turns them into a site-specific link. The first parameter is the link text (here the node's title), and the second is the path, which is always node/IDNUMBER.
  • If this list is empty, we tell the user what's wrong. (The t() function translates its argument.)
  • Otherwise, we pipe the list of nodes and locations through the current theme, and add them to the block content.
  • Finally, we return the block, whereupon Drupal (once this hook is called) shows it.

To demonstrate this, enable your module, then go to the admin/structure/block URL and choose a location to display your new block. (Make sure you then click Save Blocks.) Load any page, and you should see your block displayed:

Drupal Custom Module Block

The Same Author block is showing in the left sidebar

Improving your block

You might want to make a couple of quick improvements to your block. You'll notice in the above screenshot that the list of same author posts is quite long, and it includes (shown with no link, so in black in this theme) the current post. To fix these issues, you just need a couple of extra lines in _same_author_dbquery:

$query = db_select('node', 'n')
  ->fields('n', array('nid', 'title', 'created', 'uid'))
  ->condition('status', 1)
  ->condition('uid', $node->uid)
  ->condition('nid', $node->nid,'!=')
  ->orderBy('created', 'DESC')
  ->range(0,3)
  ->execute();

The DBSelect API, as shown in this part of the function, makes this incredibly easy to do. We just add one more condition, that the NID should not be the same as this node's NID, and a range statement, limiting the result set to the first three rows.

The nid condition demonstrates another aspect of the Database API syntax: the default comparator for condition is '='. This means that if you give just two arguments, they must be equal to satisfy the condition. If that's not what you want, you can pass in a third argument to specify the comparator. Here, it's !=; it could also be <, BETWEEN (in which case the second argument needs to be a two-value array), or any other valid SQL comparator that works for your data.

We now have a basic block module working on our site. Next time we'll look at some more options, such as creating extra configuration options for the module, creating a page instead of a block, and using the module to display both.

 




This work is licensed under a Creative Commons Attribution 3.0 Unported License
Creative Commons License.

Comments

Awesome article... Thanks a lot..
Posted @ Thursday, January 23, 2014 3:36 AM by sumithra
1) When using private function _same_author_dbquery() getting Fatal error: Call to undefined function same_author_dbquery()in same_author.module but running if remove the front _from the function declaration.  
2) Also when using ->condition('uid', $node->uid) 
->condition('nid', $node->nid,'!=') this two condition it is showing Notice: Trying to get property of non-object in same_author_dbquery() for the above two lines of condition and also not getting the result where as getting result removing the above two line of conditions.
Posted @ Tuesday, February 18, 2014 5:17 AM by Atanu
Post Comment
Name
 *
Email
 *
Website (optional)
Comment
 *

Allowed tags: <a> link, <b> bold, <i> italics