Jun 27, 2012

How to create a module in Drupal 7.x

What is a module in Drupal?
A module is nothing but a collection of functions that links into Drupal, providing all the additional functionality/behaviour for your Drupal installation or your Drupal site. After reading this tutorial, you will be able to create a basic block module and use it as a template for more advanced modules.


Things to be remember before we start:
This tutorial will not necessarily prepare you to write modules for public release it will only give you a step by step integration reagarding "How to create a module!". It does not cover caching, nor does it elaborate on permissions or security issues but you can simply use this tutorial as your starting point and extend your skills with other resources.


Our tutorial assumes you have:



  • Basic PHP knowledge, including syntax and the concept of PHP objects

  • Basic understanding of database tables, fields, records and SQL statements

  • A working Drupal 7 installation

  • Drupal administration access

  • Webserver access


Our tutorial does not assume you have any knowledge about the inner workings of a Drupal module.


Getting started


In this tutorial we'll create a module that lists links to content such as blog entries or forum discussions that were created recently (within the last week). This page in the tutorial describes how to create the initial module file and directory.


Name your module


The first step in creating a module is to choose a "short name" for it. This short name will be used in all file and function names in your module, so it must start with a letter, and it must contain only lower-case letters and underscores. For this example, we'll choose "current_posts" as the short name. Important note: Be sure you follow these guidelines and do not use upper case letters in your module's short name, since it is used for both the module's file name and as a function prefix. When you implement Drupal "hooks" (see later portions of tutorial), Drupal will only recognize your hook implementation functions if they have the same function name prefix as the name of the module file.


It's also important to make sure your module does not have the same short name as any theme you will be using on the site.


Create a folder and a module file


Given that our choice of short name is "current_posts" :



  1. Start the module by creating a folder in your Drupal installation at the path:

    • sites/all/modules/custom/current_posts



  2. Create the PHP file for the module :

    • Save it as current_posts.module in the directory sites/all/modules/custom/current_posts

    • As of Drupal 6.x, sites/all/modules is the preferred place for non-core modules (and sites/all/themes for non-core themes), because this places all site-specific files in the sites directory. This allows you to more easily update the core files and modules without erasing your customizations. Alternatively, if you have a multi-site Drupal installation and this module is for only one specific site, you can put it in sites/your-site-folder/modules.



  3. Add an opening PHP tag to the module :


    • <?php


    • Module files begin with the opening PHP tag. Do not place the CVS ID tag in your module. It is no longer needed with drupal.org's conversion to Git. If the coder module gives you error messages about it, then that module has not yet been updated to drupal.org's Git conventions.




The module is not operational yet: it hasn't been activated. We'll activate the module later in the tutorial.


Coding standards


As per the Coding standards, omit the closing ?> tag. Including the closing tag may cause strange runtime issues on certain server setups. (Note that the examples in documentation will show the closing tag for formatting reasons only and you should not include it in your real code.)


Telling Drupal about your module


All modules must have a 'modulename.info' file, which contains meta information about the module.


The general format is:


[php]name = Module Name<br />
description = A description of what your module does.<br />
core = 7.x[/php]



For our module, we will replace 'Module Name' in the example above with the name of our module, 'Current Posts'. Without this file, the module will not show up in the module listing. Here is our specific example:


[php]name = Current Posts<br />
description = A block module that lists links to recent posts.<br />
core = 7.x[/php]



Add the source above to a file named current_posts.info and save it into the module's directory at sites/all/modules/current_posts.


Note: If you copy and paste this code block, ensure that the description data does not contain a line break (turn off word-wrap on your text-editor to be sure). Otherwise, the .info file will not parse correctly.


.Info File Details


The details of what to put in a .info file can be found on the Writing .info files page.


Comments in Drupal modules


It's always a good idea to document how your module works in comments. Drupal uses Doxygen to draw documentation from source code, so contrib modules on drupal.org follow strict comment guidelines. See Doxygen and comment formatting conventions for more details. Following these guidelines is beneficial to anyone looking at your code even if it's not strictly necessary for your situation.


Your first comment:


[php]&lt;?php<br />
/**<br />
* @file<br />
* A block module that displays recent blog and forum posts.<br />
*/<br />
?&gt;[/php]



@file signifies that this comment pertains to the entire file. The doc block begins with a slash and two asterisks (/**) and ends with one asterisk and a slash (*/). Following Drupal guidelines, we will introduce each function in the module with such a comment.


Implementing your first hook


Hooks are fundamental to Drupal modules. They allow you to integrate your module into the actions of Drupal core.


[php]&lt;?php<br />
&lt;div&gt;<br />
&lt;pre&gt;function current_posts_help($path, $arg) {</p>
<p>}<br />
?&gt;[/php]


 


[php]&lt;?php<br />
/**<br />
* Implements hook_help.<br />
*<br />
* Displays help and module information.<br />
*<br />
* @param path<br />
*   Which path of the site we're using to display help<br />
* @param arg<br />
*   Array that holds the current path as returned from arg() function<br />
*/<br />
function current_posts_help($path, $arg) {<br />
switch ($path) {<br />
case &quot;admin/help#current_posts&quot;:<br />
return '&lt;p&gt;'.  t(&quot;Displays links to nodes created on this date&quot;) .'&lt;/p&gt;';<br />
break;<br />
}<br />
}<br />
?&gt;[/php]


(Note the closing ?> should not appear in your code.)


Declaring the block


To use this hook to define our block, go to your current_posts.module file and create the function current_posts_block_info() as follows:


[php]&lt;?php<br />
/**<br />
* Implements hook_block_info().<br />
*/<br />
function current_posts_block_info() {<br />
$blocks['current_posts'] = array(<br />
'info' =&gt; t('Current posts'), //The name that will appear in the block list.<br />
'cache' =&gt; DRUPAL_CACHE_PER_ROLE, //Default<br />
);<br />
return $blocks;<br />
}<br />
?&gt;[/php]



(Remember not to include the closing ?> in your code.)


Retrieving data


The function begins with getting the time numbers. Here's the first part:


[php]&lt;?php<br />
/**<br />
* Custom content function.<br />
*<br />
* Set beginning and end dates, retrieve posts from database<br />
* saved in that time period.<br />
*<br />
* @return<br />
*   A result set of the targeted posts.<br />
*/<br />
function current_posts_contents(){<br />
//Get today's date.<br />
$today = getdate();<br />
//Calculate the date a week ago.<br />
$start_time = mktime(0, 0, 0,$today['mon'],($today['mday'] - 7), $today['year']);<br />
//Get all posts from one week ago to the present.<br />
$end_time = time();<br />
?&gt;[/php]




Next we use Drupal's Database API to retrieve our list of current nodes. This is the second part of the custom function:


[php]&lt;?php<br />
//Use Database API to retrieve current posts.<br />
$query = db_select('node', 'n')<br />
-&gt;fields('n', array('nid', 'title', 'created'))<br />
-&gt;condition('status', 1) //Published.<br />
-&gt;condition('created', array($start_time, $end_time), 'BETWEEN')<br />
-&gt;orderBy('created', 'DESC') //Most recent first.<br />
-&gt;execute();<br />
return $query;<br />
}<br />
?&gt;[/php]



  1. We build the query using the db_select method, which takes a table name ('node') and alias ('n') as arguments.

  2. The fields method uses the table assigned the alias 'n' to select the fields listed in the array in the second argument.

  3. The condition method takes three arguments. The first is the field, the second the value, the third the operator. If no operator is specified, as in 'status' above, = is assumed.

  4. The orderBy method sorts according to the field in the first argument, in the order specified by the second argument.

  5. The execute method compiles and runs the query and returns a result set/statement object.


Here's the complete function:


[php]&lt;?php<br />
/**<br />
* Custom content function.<br />
*<br />
* Set beginning and end dates, retrieve posts from database<br />
* saved in that time period.<br />
*<br />
* @return<br />
*   A result set of the targeted posts.<br />
*/<br />
function current_posts_contents(){<br />
//Get today's date.<br />
$today = getdate();<br />
//Calculate the date a week ago.<br />
$start_time = mktime(0, 0, 0,$today['mon'],($today['mday'] - 7), $today['year']);<br />
//Get all posts from one week ago to the present.<br />
$end_time = time();</p>
<p>//Use Database API to retrieve current posts.<br />
$query = db_select('node', 'n')<br />
-&gt;fields('n', array('nid', 'title', 'created'))<br />
-&gt;condition('status', 1) //Published.<br />
-&gt;condition('created', array($start_time, $end_time), 'BETWEEN')<br />
-&gt;orderBy('created', 'DESC') //Most recent first.<br />
-&gt;execute();<br />
return $query;<br />
}<br />
?&gt;[/php]



(Remember not to include the closing ?> in your code.)


Generating block content


Access check


Here's the first part of the code:


[php]&lt;?php<br />
function current_posts_block_view($delta = '') {<br />
switch($delta){<br />
case 'current_posts':<br />
$block['subject'] = t('Current posts');<br />
if(user_access('access content')){<br />
//Retrieve and process data here.<br />
}<br />
?&gt;[/php]






Coding the data as links


Here's the next bit of code:


[php]&lt;?php<br />
//Use our custom function to retrieve data.<br />
$result = current_posts_contents();<br />
//Array to contain items for the block to render.<br />
$items = array();<br />
//Iterate over the resultset and format as links.<br />
foreach ($result as $node){<br />
$items[] = array(<br />
'data' =&gt; l($node-&gt;title, 'node/' . $node-&gt;nid),<br />
);<br />
}<br />
?&gt;[/php]






Theming the data


Here's the last section of code for current_posts_block_view:


[php]&lt;?php</p>
<p>if (empty($items)) { //No content in the last week.<br />
$block['content'] = t('No posts available.');<br />
} else {<br />
//Pass data through theme function.<br />
$block['content'] = theme('item_list', array(<br />
'items' =&gt; $items));<br />
}<br />
}<br />
}<br />
return $block;<br />
}<br />
?&gt;[/php]





The Final Steps


Testing and troubleshooting the module


It's time to enable and fully test your module!


Enable the module


Go to Modules, or http://example.com/admin/modules, and scroll down to the bottom of the list in the 'Other' category. You should see the module 'Current posts.' Click the checkbox to enable Current posts, and save your configuration. Now you should see a link to Help beside the module name. Click it to see the help text you entered in current_posts_help.


Enable the block


Next, navigate to Structure > Blocks, or http://example.com/admin/structure/block. Scroll down to the bottom of the list. Among the disabled blocks, you should find the name, 'Current posts'. Set its location for one of the page regions and save. Navigate to another page like your homepage to see your block. Congratulations! You have written a working module.


Troubleshooting


If you get a "white screen" or a PHP error when you enable this module, it probably means you have a syntax error in your .module file. Be sure all your punctuation is correct, semi-colons, commas, etc. all in the right places, and that you have all the hook names and module short names spelled correctly. (In the case of a white screen, you may be able to find out what the PHP error was by looking in your Apache error log. Or you can try changing PHP's error reporting level.)


If you cannot find and fix the syntax error, nothing on your site will display, because Drupal will try to load your module on every page request. The easiest way to get your site working again is to delete the module's folder or move it out of the site, in which case Drupal will figure out that it shouldn't load this module after all, and your site should work again.


Clear caches


Drupal caches a lot of data, and if you are not seeing changes appear, that could be why. In this phase of the module, the caches shouldn't be an issue, but they will be as we proceed. To get all the troubleshooting instructions in one place, we'll give you the instructions here that you'll need later.


To clear the caches, go to Configuration > Performance or http://example.com/admin/config/development/performance, and click the Clear all cachesbutton.

No comments :

Post a Comment