Oh, The Huge Manatee

A blog about technology, open source, and the web... from someone who works with all three.

Drupal Quick Learning Guides: Custom Modules

Ah, it’s nice to be done with the basic structure stuff. At this point, I’m going to assume that you know the basics of how to administer a Drupal site. You understand the file structure, you know where to go to download themes and modules, and you know how to make sites out of the big pieces you can find that way. Awesome. This is where it gets fun. I’m going to assume some basic knowledge of PHP, but there’s nothing here that you can’t pick up with some quick glances over at w3schools, and maybe some experimentation on your own.

I mentioned in the file structure post that the whole point of Drupal is that Everything Is Override-able. If you want to customize the site beyond what you get out of the box with some contributed modules, you’re going to want to do it in your own module. That’s right - it’s incredibly easy to write your own Drupal module to insert whatever code you want into your particular Drupal site. In fact, it’s the recommended way to customize your site, because it means that your code won’t get mixed up with anything in Drupal’s “core”. If you update Drupal, you don’t have to worry about refactoring your patches or anything like that, you’ve got one place to look for your custom behavior. More importantly, if someone else comes along to take care of the site after you’re off the project, there’s only one place for them to look for customizations.

A module at its most basic consists of a directory under sites/whatever/modules , a self-named .info file giving basic information about the module, and a self-named .module file with your code in it. In this quick learning guide, we’ll build a “Hello World” module that prints some text on every page load. This is a quick guide - I’ll get more into how to leverage Drupal in your code in later posts.

So let’s create your custom module. Consider your module’s name - Drupal has a single namespace for all modules and features, and it’s easy to get confused with the theme namespace as well, so it’s best to pick a name that is definitely not going to be used by any modules, features, or themes. Most big shops will have a custom module for most any site they build, and they just name it “Sitename Custom” or something similar. We’re going to make a module called “Swearing Custom”, to follow that convention.

First create the module directory. I like to keep my custom modules all under sites/all/modules/custom . Drupal will search subdirectories, so take advantage of that fact to keep your codebase tidy! We’ll create the directory sites/all/modules/swearing_custom . Note the underscore instead of a space.

Then create the module’s .info file. This file informs Drupal about your module’s name, description, requirements, and other fundamentals. I like to think of it as anything that will go on the site’s Modules listing. This file has to be named after the module, just like the directory was. Here’s my swearing_custom.info file:

name = Swearing at Computers Custom Module description = Custom tweaks and functionality for Swearing at Computers project = "swearing_custom" version = "7.x-1.x-dev" core = "7.x" package = "custom" dependencies[] = token

Most of those variables can be omitted, to be honest. All Drupal REALLY needs is name, description, project, and core. Notice how I defined a dependency, just for fun. I also like to put my custom code into a module group called “custom”, so my modules list stays nice and tidy. The norm is to add to the dependencies array with one dependency per line, for readability. You can similarly have a files[] array to add your own files to the module. By default this contains just the .info and .module files, which is all we’re using so I’m not adding a files[] line at all.

Now we’ll create the module file itself. This is just a php file, with an opening tag. The trouble is, how do you get your code to run? You can write all the functions you want, but how should Drupal know to call your code?

Aha, now we get to the interesting bit. Drupal modules are built around a system of “hooks”. Each module defines a set of hooks, places for you to insert your custom code. And this isn’t an edge case - it’s totally core to how Drupal works. Big modules will have tens of hooks at your disposal… but we’ll cover those a bit later. For now, we’re just going to use one simple hook to get our “Hello world” code displayed: hook_init().

All hooks are named hook_something. It’s how you know it’s a hook. All the core hooks, and many of the contributed hooks, are documented on Drupal’s excellent API resource site. When you’re writing your custom module, you just declare a function named after the hook, replacing the word “hook” with your module’s name. Drupal will find that function and run it for you. Simple as that.

Hook_init() is called early in the page build process, on every page of your site. This is a very powerful hook, usually used to define variables that you’ll use later on. But we’re just going to use it to print a message to the screen. To start with, my swearing_custom.module file looks like this:

/** * Implementation of hook_init(). */ swearing_custom_init() { print "Hello world!"; }

Pretty simple, right? That comment at the top is the standard format for Drupal. It’s so that you can easily find where you used particular hooks (or where other contrib modules used them!), and it’s used for a lot of Drupal’s automated help and API resources. It’s also nice and tidy, so it’s a good habit to get into.

Then you see that I just declared a function named after the hook, added my code, and got out of there. At this point, if I enable this custom module I’ll see “Hello world” in unformatted text at the top of every page. Simple, but ugly.

Enter another great feature of Drupal: a million and one helper functions. These are also well documented at api.drupal.org, and they’re the functions that everyone - even Drupal’s core itself - uses to get things done in a standardized way. For example, I hate the way that text appears. So let’s update the module to use Drupal’s own messaging system. This means using drupal_set_message(). Have a look at the API page to see the arguments for this function, but 90% of your use cases will be following this example. Here’s a new version of swearing_custom.module:

/** * Implementation of hook_init(). */ swearing_custom_init() { print "Hello world!"; drupal_set_message('Hello world!', 'status'); }

That second argument tells Drupal the severity of the message: ‘status’, ‘warning’, or ‘error’. Status is actually the default, so I’ll cut it out of the next iteration of this code. The module now prints your message in a fancy, Drupal formatted way. It looks just like all the other Drupal modules, because that’s how all of Drupal displays messages.

I’m going to add one more layer of complexity here, with the t() function. You’ll see t() everywhere: it’s the way you declare text strings to Drupal as user-facing text. It makes the text available for translation, and makes it easy to sanitize any variables or user-entered text. An example t() string would be:

t('Welcome to %sitename', array('%sitename' => 'Swearing at Computers');

You just include replacement patterns in the text itself, and then declare them in an array. The replacement strings get sanitized, so that’s the right place to include user-entered text, URLs, or just variable output in general. So here is a final version of our custom module, using drupal_set_message() the way it was intended.

/** * Implementation of hook_init(). */ swearing_custom_init() { print "Hello world!"; drupal_set_message(t('Hello world! This is %name!', array(%name => 'Swearing at Computers'))); }

That’s it for this quick guide. The basics of writing a module. The hardest part here is figuring out which hooks to use. Google is your friend: try searching for things like “drupal views hooks” to see how to tweak Views at various stages, or “drupal form hooks” to see how forms work in Drupal. Or don’t - I’ll be covering some of those hooks in the next posts.

Comments