Last updated August 10, 2012. Created by JerryH on January 5, 2008.
Edited by kietnguyen, drupalshrek, jhodgdon, Chris Einkauf. Log in to edit this page.
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, $edit = array()) {
// set up an empty array which will contain the block contents
$block = array();
switch ($op) {
case "list":
// Generate listing of blocks from this module, for the admin/block page
$block[0]["info"] = t('On This Date');
break;
case "view":
// Generate our block content
$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...
break;
case "save":
break;
case "configure":
break;
}
// return the built content
return $block;
}
?>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 presentThe 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
// set up an empty string to contain the block contents
$block_content = '';
// Loop round each link and concatenate to the 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).
You may also notice the bad coding practice of combining content (the link) with layout (the html line break). 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. An even better practice in Drupal is to make your module's output "themeable". Let's ignore this for now, but be aware of this issue when writing modules that others will use.
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
$block['subject'] = 'On This Date';
$block['content'] = 'Sorry No Content';
}
else {
$block['subject'] = 'On This Date';
$block['content'] = $block_content;
}
// return the built 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.
Of course we can refactor the code to take out the block subject line which is common to both cases, so the above code becomes:
<?php
// Fill in the subject of our block which is the same whether or not
// the block has any real content
$block['subject'] = 'On This Date';
// check to see if there was any content before returning
// the block view
if ($block_content == '') {
// no content from a week ago
$block['content'] = 'Sorry No Content';
}
else {
$block['content'] = $block_content;
}
// return the built content
return $block;
?>The example above assumes that if there is no content on the given date one week ago, you want the block to say "Sorry No Content". You may choose, instead, to simply omit the block completely if there are no results. To do that, you substitute this code into the appropriate section above:
<?php
if ($block_content == '') {
/* No content from a week ago. If we return nothing, the block
* doesn't show, which is what we want.
*
* Just leave the block array empty
*/
}
?>Putting it all together, our block function at this point looks like this:
<?php
function onthisdate_block($op='list', $delta=0, $edit = array()) {
// set up the block
$block = array();
switch ($op) {
case "list":
// Generate listing of blocks from this module, for the admin/block page
$block[0]["info"] = t("On This Date");
break;
case "view":
// Generate content for blocks from this module
$block_content = "";
//Get today's date
$today = getdate();
//Get timestamp from one week ago
$start_time = mktime(0,0,0, $today["mon"], ($today["mday"] -7), $today["year"]);
//Limit timespan to one day
$end_time = $start_time + 86400; //60*60*24 = seconds/day
//Get nodes in range
$query = "SELECT nid, title, created FROM " .
"{node} WHERE created >= '%d' " .
" AND created <= '%d'";
$query_result = db_query($query, $start_time, $end_time);
//Set Block content
while ($links = db_fetch_object($query_result)) {
$block_content .= l($links->title, "node/".$links->nid);
$block_content .= "<br />";
}
//Setup the block
$block["subject"] = t("On This Date");
//Check that content isn't empty
if ($block_content == "") {
$block_content = t("Sorry No Content");
}
$block["content"] = $block_content;
break;
case "save":
break;
case "configure":
break;
}
return $block;
} // end onthisdate_block
?>Our module is now functional (although it would be better to make the output "themeable", we're ignoring that in this tutorial) - we can install, enable and test it (see next tutorial page).
Comments
Use switch/case instead of if/else if
If we have several choices for $op: list, view, save, configure, it might be easier to use a switch/case structure instead of if/else if
for example
switch ($op) {
case "list":
// Generate listing of blocks from this module, for the admin/block page
$block = array();
$block[0]["info"] = t('On This Date');
return $block;
break;
case "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']);
... // more code here
break;
case "save":
... // some code here
break;
case "configure":
... // some code here
break;
}
Don't forget to put in the break; at the end of each case block.
Coding standards and syntax formatting
I just wanted to point out that while this tutorial is both helpful and informative, the author starts out by emphasizing coding standards then proceeds to format the code very sloppily, in addition to possibly using a switch as mentioned above there are obvious ways to improve this code, from a trying to learn stand point. Here is what I ended up with modifying the code as I read through the tutorial:
<?php
/**
* Implementation of hook_block().
* @param string $op one of "list", "view", "save" and "configure"
* @param integer $delta code to identify the block
*/
function onthisdate_block($op = "list", $delta = 0) {
$block = array();
if ($op == "list") {
// Generate listing of blocks from this module, for the admin/block page
$block[0]["info"] = t("On This Date");
} elseif ($op == "view") {
// Generate content for blocks from this module
$block_content = "";
//Get today's date
$today = getdate();
//Get timestamp from one week ago
$start_time = mktime(0,0,0, $today["mon"], ($today["mday"] -7), $today["year"]);
//Limit timespan to one day
$end_time = $start_time + 86400; //60*60*24 = seconds/day
//Get nodes in range
$query = "SELECT nid, title, created FROM " .
"{node} WHERE created >= '%d' " .
" AND created <= '%d'";
$query_result = db_query($query, $start_time, $end_time);
//Set Block content
while ($links = db_fetch_object($query_result)) {
$block_content .= l($links->title, "node/".$links->nid);
$block_content .="<br />";
}
//Setup the block
$block["subject"] = t("On This Date");
//Check that content isn't empty
if ($block_content == "") {
$block_content = t("Sorry No Content");
}
$block["content"] = $block_content;
}
return $block;
} //function onthisdate_block
?>
- CodeShare
Why don't you just update the
Why don't you just update the tutorial? It's easy. There's an edit tab at the top of the page. Then you can submit a documentation issue to have the comments removed that have been incorporated in the tutorial.
7 days before
I don't mean to be offensive and I really appreciate the work in this tutorial.
There's only one thing I'm not so sure in the code, if someone can help explain a bit further...
What if today is June 2nd, 2010, then mkdate would take (0,0,0,6,-5,2010), would that be a problem?
Many thanks
--
Ha Le
Glorimo, Inc.
Um, yeah, shouldn't the week
Um, yeah, shouldn't the week be subtracted from the result of the mktime function rather than the result of the getdate function?
Like this:
<?php
$start_time = mktime(0, 0, 0, $today['mon'], $today['mday'], $today['year']) - 604800;
// 60 * 60 * 24 * 7 = 604800 seconds in a week
?>
Good catch, Ha Le.
mktime accepts negative
mktime accepts negative values and does the calculation for you... See example #3 on http://www.php.net/manual/en/function.mktime.php
Link out of date
Hi,
The link in paragraph 7, the one about table prefixes, (http://drupal.org/node/2622) is out of date. It refers to Drupal 4.6.x. Is there a more recent post on the subject that this tutorial could link to?
where does it get called from
Where does this function...function onthisdate_block()
get called from?
it gets called from Drupal core
I'm only starting developing in Drupal, so could somebody correct me if I'm wrong.
I think onthisdate_block gets called from Drupal core as the hook is hook_block.
So when Drupal generates the page and finds that your block is included it calls the hook. For example if you have a region called content_top in your page.tpl and you place your block in that region this is when the hook gets called.
Could somebody confirm/correct this for us?
Thanks
Superb tutorial even better
Superb tutorial even better than the video tuts out der.
But i am stuck...
I am pulling content from the table and showing it in the block the same way it is shown in the tutorial
but instead of blogs i am extracting the user id from the database table and i have a problem in linking the user id with the user profile.
This is the code where the problem lies :-
while ($links = db_fetch_object($query_result)) {
$block_content .= l('user/'.$links->uid) . '
';
}
Since it is done on localhost i am only shown -> user/1 which is hyperlinked to the home page.
a link has a text and an href
a link has a text and an href attribute. So the definition of l is: function l($text, $path, $options = array()). So you're creating a link with text user/1 that links to the home page (empty path).