Generating the block content

Last modified: June 19, 2009 - 18:32

The next step in this tutorial is to generate the content of the block. This will involve accessing the Drupal database. Our goal is to get a list of content (stored as "nodes" in the database) created a week ago. Specifically, we want the content created between midnight and 11:59pm on the day one week ago. When a node is first created, the time of creation is stored in the database. We'll use this database field to find our data.

To tell Drupal what content we want in the block, we use the 'view' operation of hook_block(). So, we'll need to add some code to our previously-defined onthisdate_block() function.

First, we need to calculate the time (in seconds since epoch start, see http://www.php.net/manual/en/function.time.php for more information on time format) for midnight a week ago, and 11:59pm a week ago. This part of the code is Drupal-independent; see the PHP website (http://php.net/) for more details.

<?php
/**
* Generate HTML for the onthisdate block
* @param op the operation from the URL
* @param delta offset
* @returns block HTML
*/
function onthisdate_block($op='list', $delta=0) {

  if (
$op == "list") {
   
// Generate listing of blocks from this module, for the admin/block page
   
$block = array();
   
$block[0]["info"] = t('On This Date');
    return
$block;
  }
  else if (
$op == 'view') {

   
// Generate our block content

    // Get today's date
   
$today = getdate();

   
// calculate midnight one week ago
   
$start_time = mktime(0, 0, 0,
      
$today['mon'], ($today['mday'] - 7), $today['year']);

   
// we want items that occur only on the day in question, so  
    // calculate 1 day
   
$end_time = $start_time + 86400
   
// 60 * 60 * 24 = 86400 seconds in a day
   

   // more coming...
 
}
}
?>

If the site you are running on might not have content for the date exactly one week ago, you might want to change the end time to the following, in order to show something in the block:

$end_time = time(); // get all posts from one week ago to the present

The next step is the SQL statement that will retrieve the content we'd like to display from the database. We're selecting content from the node table, which is the central table for Drupal content. We'll get all sorts of content types with this query: blog entries, forum posts, etc. For this tutorial, this is okay. For a real module, you would adjust the SQL statement to select specific types of content (by adding the 'type' column and a WHERE clause checking the 'type' column).

Drupal uses database helper functions to perform database queries. This means that, for the most part, you can write your database SQL statement and not worry about the backend connections. We'll use db_query() to get the records (i.e. the database rows) with our SQL query:

<?php
  $query
= "SELECT nid, title, created FROM " .
    
"{node} WHERE created >= '%d' " .
    
" AND created <= '%d'";

 
$query_result db_query($query, $start_time, $end_time);
?>

This illustrates how to do a "safe" query in Drupal: create your query string with "placeholders" such as %d and %s, and then pass variables into the db_query() function to fill the "placeholders". This type of practice prevents SQL injection hacks, especially when user-generated content is used in a query. Another Drupal-specific custom to note here is that table names in Drupal database queries are always enclosed in curly braces, such as {node}. This is necessary so that your module will support database table name prefixes. You can find more information on the Drupal website by reading the Table Prefix (and sharing tables across instances) page in the Drupal handbook. Finally, note that in this example, we are not being careful about access permissions for nodes. Normally, all queries on nodes should employ the db_rewrite_sql() function, which makes sure the user viewing the page has permission to see each node that is returned, but this is beyond the scope of this tutorial.

Now, we'll use db_fetch_object() to look at the individual records returned by the query. For each node that we found, we'll generate a link to the node, with the node's title as the link text:

<?php
 
// content variable that will be returned for display   
 
$block_content = ''
  while (
$links = db_fetch_object($query_result)) {
   
$block_content .=  l($links->title, 'node/'. $links->nid) .'<br />';
  }
?>

Notice the actual link is created by the l() function. l generates <a href="link"> links from Drupal paths, adjusting the URL to the installation's URL configuration of either clean URLS: http://(sitename)/node/2 or not http://(sitename)/?q=node/2 (the path to any node is always "node/#", where # is the ID number of the node).

Finally, we need to return the content we just generated to Drupal for display:

<?php
 
// check to see if there was any content before returning
  //  the block view
 
if ($block_content == '') {   
   
/* No content from a week ago.  If we return nothing, the block  
     * doesn't show, which is what we want.
     */
   
return;
  }

 
// set up the block 
 
$block = array();
 
$block['subject'] = 'On This Date'
 
$block['content'] = $block_content;
  return
$block;
}
?>

We return an array that has 'subject' and 'content' elements, which is what Drupal expects from a block function. If you do not include both of these, the block will not render properly.

The example above assumes that if there is no content on the given date one week ago, you want to completely omit the block. Another possibility (especially during the testing phase) would be to display a message:

  if ($block_content == '') {   
     // no content from a week ago
     $block['subject'] = 'On This Date';
     $block['content'] = 'Sorry No Content';
     return $block;
  }

You may also notice the bad coding practice of combining content with layout. If you are writing a module for others to use, you will want to provide an easy way for others (in particular, non-programmers) to adjust the content's layout. An easy way to do this is to include a class attribute in your link, or surround the HTML with a <div> tag with a module-specific CSS class and not necessarily include the <br /> at the end of the link. Let's ignore this for now, but be aware of this issue when writing modules that others will use.

Putting it all together, our block function at this point looks like this:

<?php
function onthisdate_block($op='list', $delta=0) {

  if (
$op == "list") {
   
// Generate listing of blocks from this module, for the admin/block page
   
$block = array();
   
$block[0]["info"] = t('On This Date');
    return
$block;
  }
  else if (
$op == 'view') {

   
// Generate our block content

    // content variable that will be returned for display
   
$block_content = '';

   
// Get today's date
   
$today = getdate();

   
// calculate midnight one week ago
   
$start_time = mktime(0, 0, 0,$today['mon'],
                               (
$today['mday'] - 7), $today['year']);

   
// we want items that occur only on the day in question, so
    //calculate 1 day
   
$end_time = $start_time + 86400;
   
// 60 * 60 * 24 = 86400 seconds in a day

   
$query = "SELECT nid, title, created FROM " .
            
"{node} WHERE created >= '%d' " .
            
" AND created <= '%d'";

   
$query_result db_query($query, $start_time, $end_time);
    while (
$links = db_fetch_object($query_result)) {
     
$block_content .= l($links->title, 'node/'.$links->nid) . '<br />';
    }

   
// check to see if there was any content before returning
    //  the block view
   
if ($block_content == '') {
     
// no content from a week ago, return nothing.
     
return;
    }
   
// set up the block
   
$block['subject'] = 'On This Date';
   
$block['content'] = $block_content;
    return
$block;
  }
// end onthisdate_block
?>

Our module is now functional - we can install, enable and test it (see next tutorial page).

See Also

 
 

Drupal is a registered trademark of Dries Buytaert.