Creating a Basic Drupal Module with HTML Output
Now that you’ve gotten your feet wet with HTML, this chapter shows you how to use HTML to create your own custom web pages within the Drupal framework. In this chapter, I’m actually going to jump ahead a bit. Even though I’m going to assume you don’t know any PHP (a server-side scripting language designed for web development), I’m going to give you just enough right now so you can play around with showing basic HTML in a programmatically created Drupal page—that is, via a Drupal module.
In order to create your module, you first need to locate your Drupal docroot directory, which is where your Drupal files are housed. You’ll be placing your own files you create within the Drupal directory structure so that you can integrate your custom pages into your Drupal site.
Working with the Drupal Docroot Directory
So what is a docroot directory? Well, to begin, you need to know that there are two docroot directories: an Apache docroot and a Drupal docroot. Let’s look at the Apache docroot directory first.
The Apache docroot is the location from which your local Apache web server will serve up web pages—any web pages, not just your Drupal installation. Often the name of the folder for the Apache docroot directory will be htdocs or the /var/www/ directory, which is the most commonplace on an Ubuntu server (the location of the docroot directory can also be configured to be in any other directory, but usually it is in the default location at /var/www).
Your Drupal docroot directory is the base, or “home,” directory of your Drupal installation and it’s where all your Drupal files and folders reside. You can really put the Drupal docroot anywhere within the Apache docroot directory. If you use Acquia Dev Desktop out of the box on a Mac, the Drupal docroot directory will just be within your Sites/devdesktop directory, which is within your user’s home directory. On my Mac, mine is at, for example, /Users/jbarnett/Sites/devdesktop. On a Windows machine, the Drupal docroot directory is most often at C:/Users/your_username/Sites. Acquia Dev Desktop for Windows, puts the Drupal docroot within C:/Users/your_username/Sites/devdesktop. Within this devdesktop directory you’ll find all the site folders for each Drupal installation you’ve created using Acquia Dev Desktop.
When you need to configure where your Drupal docroot is or you want to create another instance of Drupal on your local machine or on an Ubuntu server in the cloud (at Amazon, Rackspace, Linode, or any other server farm in the cloud), you will set up an Apache virtual host (something I won’t get into now). Fortunately, Acquia Dev Desktop already sets this up for you, which allows you to proceed more quickly. An Apache virtual host is configured to “wire” it so your web server at a particular web URL serves up the correct files within your Apache docroot—whether they be Drupal files, HTML files, straight up PHP files, and so on.
Now that you know where your Drupal files are stored you can begin creating your first Drupal module.
Creating a Basic Drupal Module
In this section, we are going to create a basic Drupal module. Recall that a module is like a plug-in that extends Drupal’s functionality. In this case, the module you create will be used to create a simple web page that looks like the one shown in Figure 4-1. Simply, this module will output the following HTML to the screen (as shown in Figure 4-1):
<p><b>Saying Hello World in Drupal 8 is cool!</b></p>
Figure 4-1. A bare bones custom-created Drupal module showing some basic HTML
Note The code for this Drupal module is on Github: https://github.com/barnettech/drupal8_book.
Within a subfolder of your Drupal docroot directory (likely under /Users/your_username/Sites/devdesktop on a Mac or within C:/Users/your_username/Sites on a Windows machine), you’ll see a file list that looks as follows:
README.txt example.gitignore robots.txt
composer.json index.php sites
composer.lock modules themes
core profiles web.config
You can navigate to your Drupal docroot folder within the file browser that comes with your Mac or with Windows, or you can use the command line if you’re comfortable with that.
Note The Drupal 8 directory structure is different from previous versions of Drupal. In Drupal 7 and prior, the modules directory in Drupal’s docroot directory contained core modules (modules that come with Drupal) only, and the contrib (community contributed) and custom modules used to live in the sites/all/modules directory. The Drupal community thought this was confusing and wanted something more intuitive. Now when a developer sees the Drupal docroot directory, and within it the modules directory, he or she can think, “Oh, that is where my modules I develop or download should go.” Drupal core modules now live within the core/modules directory.
Note The code for this Drupal module is on Github: https://github.com/barnettech/drupal8_book. The code will be formatted better at Github compared to reading it on your Kindle device/e-reader because it includes syntax highlighting.
name: Hello World
type: module
description: 'A basic Drupal 8 Hello World Module.'
package: Custom Modules
version: 1.0
core: 8.x
The file hello_world.info.yml has a .yml extension and is a YAML file. YAML files are used to store metadata about all themes and modules, something that was done in .info files prior to Drupal 8.
Note For users who’ve used Drupal 7 and previous versions, you may wonder why Drupal switched in Drupal 8 to using YAML rather than continuing to use just an .info file with standard text file format. I found this answer: “Why YAML, that’s also by exclusion. First, we are looking for a file format that is both human editable and computer parsable. We did not want a Drupal-specific format (info files). We did not want XML because then the schema is the Drupalism (and XML has cooties). JSON (JavaScript Object Notation) doesn’t allow comments and non-ASCII characters need escaping, that’s not nice for humans. So, YAML.” (Excerpt by NK at www.drupal4hu.com/node/377.)
hello_world_settings:
path: '/hello/world'
defaults:
_controller: 'Drupalhello_worldControllerHelloWorldController::
myCallbackMethod'
_title: 'Hello World'
requirements:
_permission: 'access content'
Note This directory structure is very different from Drupal 7 and prior versions; we never needed to create this kind of directory structure previously, but with the new object-oriented way of doing things, it is now necessary. If the directory structure is at all confusing, look at the code in Github—you’ll be able to see the directory structure. You can also download (“clone”) the project from Github, which will bring the files and directory structure down to your own computer.
Note A method in object-oriented coding is very similar to a function in the procedural (non-object-oriented) code you may be more familiar with. It is a block of code that is grouped together for easier invocation. The hello_world.routing.yml file (specifically, the line following _controller: invokes this myCallbackMethod) you created earlier is the file that invokes this myCallbackMethod so its code will get executed.
<?php
/**
* @file
* Contains Drupalhello_worldHelloWorldController.
*/
namespace Drupalhello_worldController;
/**
* Provides route responses for the hello world page example.
*/
class HelloWorldController {
/**
* Returns a simple hello world page.
*
* @return array
* A very simple renderable array is returned.
*/
public function myCallbackMethod() {
$element = array(
'#markup' => '<p><b>Saying Hello World in Drupal 8 is
cool!</b></p>',
);
return $element;
}
}
$content = '<p><b>Saying Hello World in Drupal 8 is
cool!</b></p>';
Note The code you’ve seen so far is the amount of coding it takes to put some basic HTML on a page in Drupal 8. The boilerplate code is a bit more complex than in previous versions of Drupal, as Drupal 8 incorporates the Symphony routing system. Drupal 8 has taken on new, more “modern” architecture from the Symphony PHP framework (http://symfony.com/), and Drupal 8 leaps to try to adopt more modern architectural paradigms, which Symphony already handles well. This leap for the Drupal community brings Drupal more up to date with other object-oriented frameworks. Don’t worry for now if you don’t understand every line of PHP code presented.
Figure 4-2. Enabling your new custom module, which outputs some HTML to the screen
You’ll now see your simple Drupal page in all its glory, showing some basic HTML on the page (see Figure 4-3). Your screen may look different if you’ve installed a different Drupal theme.
Figure 4-3. The module outputted the basic HTML into the center of the Drupal page
Now when I introduce you to CSS, PHP, JavaScript, and so on later in the book, I can show you the featured technology as used outside Drupal, and also within a Drupal module, within the Drupal framework. We’ll be adding to this Drupal module to showcase each new technology. The example code you just looked at is pretty simple, only displaying some HTML on the screen, but this code is the framework you’ll add to in order to create more complex web pages.
If any of the code presented just now is confusing, don’t worry too much. For now just imagine this code as a bunch of magic and put your HTML within the quotes within the line $content = '<p><b>Saying Hello World in Drupal 8 is cool!</b></p>', and that HTML will be the output on the web page.
Looking at the Model, View, Controller Design Pattern
To get into the wisdom behind the architecture you’re seeing in Drupal 8, it’s necessary to talk about the controller you just saw in the Hello World module (the HelloWorldController.php file you just created). The controller is the “C” in the Model, View, Controller (MVC) architecture, which is popular in almost all coding languages, and it separates “concerns,” separating the view code, logic, and data. By separating these different pieces of architecture, the code is more organized, and it helps in keeping “themers” in the “view” and developers on the back end, which means themers and developers should experience less collision when working together.
Note You can read more about controllers at https://drupal.org/project/controller.
The system separates the view (the theming/presentation layer) from the back-end data (the model), and the logic/strategy (in the controller) of responding to the user’s actions they initiate in the view. For example, the view—the look and feel of the site—can be replaced or heavily modified, and the core data and logic behind the view can be unaffected by the change. This system also allows you to have multiple views of the back-end data (the model) and allows a change to the model to affect all views of the data—the key idea of the “observer pattern” in software architecture. You can read more about MVC in the book Design Patterns by Richard Helm, Ralph Johnson, John M. Vlissides, and Craig Larman (often referred to as “The Gang of Four”) (Addison-Wesley, 1994).
Showcasing Different HTML Elements Within the Drupal Module
Most HTML elements you saw in Chapter 3, like tables and lists, can be easily put into your new Drupal module’s output. The exception is forms, which are more complicated and will be covered in Chapter 14.
Note When you put HTML into your Drupal module you omit the <html>, <head>, and <body> tags. Drupal puts these tags in for you.
Take, for example, the code to create anchors (see Chapter 3). You can easily put this code into the new Drupal module in the HelloWorldController.php controller file. The new file will now look as follows:
<?php
/**
* @file
* Contains Drupalhello_worldHelloWorldController.
*/
namespace Drupalhello_worldController;
/**
* Provides route responses for the hello world page example.
*/
class HelloWorldController {
/**
* Returns a simple hello world page.
*
* @return array
* A very simple renderable array is returned.
*/
public function myCallbackMethod() {
$content = '
<a id="top"></a>
<h2>
<p><a href="#section1">Visit Section 1</a></p>
<p><a href="#section2">Visit Section 2</a></p>
<p><a href="#section3">Visit Section 3</a></p>
<p><a href="#section4">Visit Section 4</a></p>
<p><a href="#section5">Visit Section 5</a></p>
</h2>
<p><h2><a id="section1">Section 1</a></h2><p>
<p>
Veggies sunt bona vobis, proinde vos postulo esse magis
tigernut wakame jícama spring onion tatsoi zucchini yarrow.
Komatsuna amaranth catsear celery quandong zucchini chickweed
chard coriander spring onion winter purslane turnip greens swiss chard
radicchio bok choy mustard squash. Rock melon carrot tomatillo cabbage
rock melon leek courgette. Chickweed beetroot tigernut epazote bitterleaf
courgette dandelion bell pepper earthnut pea salsify radicchio soko sea
lettuce okra pumpkin. Veggies sunt bona vobis, proinde vos postulo esse
magis tigernut wakame jícama spring onion tatsoi zucchini yarrow.
... text ommitted here to save space ...
</p>
<p><h2><a href="#top">Take me back to the top!</a></h2></p>
<h2><a id="section2">Section 2</a></h2>
<p>
Peanut mustard chickweed lotus root yarrow summer purslane desert
raisin endive corn green bean rutabaga pumpkin lettuce. Arugula zucchini
courgette leek bunya nuts eggplant water spinach tatsoi yarrow potato rock
melon kohlrabi jícama bell pepper shallot burdock. Pumpkin chicory caulie
tigernut courgette celery. Tigernut salad cress komatsuna earthnut pea
cauliflower bell pepper spring onion cucumber. Kale taro cress broccoli
beetroot corn salsify water spinach chickpea beet greens cucumber dandelion
arugula prairie turnip caulie cauliflower. Peanut mustard chickweed lotus
root yarrow summer purslane desert raisin endive corn green bean rutabaga
pumpkin lettuce. Arugula zucchini courgette leek bunya nuts eggplant
water spinach tatsoi yarrow potato rock melon kohlrabi jícama bell pepper
shallot burdock. Pumpkin chicory caulie tigernut courgette celery. Tigernut
salad cress komatsuna earthnut pea cauliflower bell pepper spring onion
cucumber. Kale taro cress broccoli beetroot corn salsify water spinach
chickpea beet greens cucumber dandelion arugula prairie turnip caulie
cauliflower.
... text ommitted here to save space ...
</p>
<p><h2><a href="#top">Take me back to the top!</a></h2></p>
<h2><a id="section4">Section 4</a></h2>
<p>
Artichoke arugula collard greens kale tomato scallion catsear
celery turnip bunya nuts endive seakale wattle seed nori daikon fava bean
parsnip. Burdock zucchini caulie garlic rock melon pumpkin. Eggplant
endive kombu asparagus tomatillo artichoke. Artichoke arugula collard
greens kale tomato scallion catsear celery turnip bunya nuts endive
seakale wattle seed nori daikon fava bean parsnip. Burdock zucchini caulie
garlic rock melon pumpkin. Eggplant endive kombu asparagus tomatillo
artichoke.
... text ommitted here to save space ...
<h2><a id="section5">Section 5</a></h2>
<p>
Artichoke parsley ricebean desert raisin caulie grape kakadu plum
collard greens leek courgette seakale mustard nori sorrel amaranth
courgette peanut beetroot. Maize corn beet greens lettuce broccoli parsnip
garlic chickweed groundnut garbanzo brussels sprout kombu rock melon bamboo
shoot seakale endive cauliflower. Celery cucumber coriander cress desert
raisin silver beet pumpkin quandong cauliflower kombu garlic squash.
Artichoke parsley ricebean desert raisin caulie grape kakadu plum collard
greens leek courgette seakale mustard nori sorrel amaranth courgette peanut
beetroot. Maize corn beet greens lettuce broccoli parsnip garlic chickweed
groundnut garbanzo brussels sprout kombu rock melon bamboo shoot seakale
endive cauliflower. Celery cucumber coriander cress desert raisin silver
beet pumpkin quandong cauliflower kombu garlic squash.
... text ommitted here to save space ...
</p>
<p><h2><a href="#top">Take me back to the top!</a></h2></p>
';
$element = array(
'#markup' => $content,
);
return $element;
}
Notice the code ported over from the HTML from Chapter 3; we only needed to omit the <html>, <head>, and <body> tags. Figure 4-4 shows what the anchor code from Chapter 3 looks like in the Drupal module.
Figure 4-4. HTML anchors in a simple Drupal module
Now, for example, if you click “Visit Section 5” at the top of the screen, you are sent to Section 5, which is much farther down the page (see Figure 4-5). The anchor HTML code works just as expected within the HelloWorldController.php file.
Figure 4-5. The anchor at Section 5
Note In Chapter 12, you’ll learn a better way to handle HTML output—by creating theme functions—to further separate the view of the web page from the controller logic.
If you want to put a basic HTML table into the HelloWorldController.php file, it would look as follows:
<?php
/**
* @file
* Contains Drupalhello_worldHelloWorldController.
*/
namespace Drupalhello_worldController;
/**
* Provides route responses for the hello world page example.
*/
class HelloWorldController {
/**
* Returns a simple hello world page.
*
* @return array
* A very simple renderable array is returned.
*/
public function myCallbackMethod() {
$content = '
<table border="1">
<tr>
<th>Header 1</th>
<th>Header 2</th>
</tr>
<tr>
<td>row 1, cell 1</td>
<td>row 1, cell 2</td>
</tr>
<tr>
<td>row 2, cell 1</td>
<td>row 2, cell 2</td>
</tr>
</table>
';
$element = array(
'#markup' => $content,
);
return $element;
}
Again, you omit the <body>, <html>, and <head> tags. Figure 4-6 shows what the Drupal page looks like at the ...hello/world URL.
Figure 4-6. The Basic Drupal module with HTML in it to create a table
And finally, here is our HelloWorldController.php file with HTML code to create both unordered lists and ordered lists.
<?php
/**
@file
* Contains Drupalhello_worldHelloWorldController.
*/
namespace Drupalhello_worldController;
/**
Provides route responses for the hello world page example.
*/
class HelloWorldController {
/**
* Returns a simple hello world page.
*
* @return array
* A very simple renderable array is returned.
*/
public function myCallbackMethod() {
$content = '
ORDERED LIST:
<ol>
<li>Item 1</li>
<li>Item 2</li>
</ol>
UNORDERED LIST:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
';
$element = array(
'#markup' => $content,
);
return $element;
See Figure 4-7 for an example of what the Drupal page looks like with the HTML creating both ordered and unordered lists.
Figure 4-7. Our simple Drupal module with HTML output creating first an ordered list and then an unordered list
Renaming a Module—and How to Create a Module Not Named “hello_world”
If you would like to change the name of your module, you must change all the filenames and function names in the module. You also must change the folder name that houses the module’s files. So In the case of the example module, the folder name was hello_world. So if you wanted to change the module name to my_module, you would need to change the folder name to my_module. Then, within the my_module folder, you would need to change the hello_world.info.yml file to my_module.info.yml instead. Also the hello_world.module file would be renamed to be my_module.module. Then within the my_module.info.yml file, you would change the name of the module if you wish to. Then within the my_module.module file, you would change all the function names to begin with my_module rather than hello_world. So if you have functions called hello_world_cron (to have Drupal run new cron tasks; cron tasks refer to tasks to be run on a schedule) and hello_world_permission (to create new Drupal permissions) which implement the hook_cron and hook_permission Drupal hooks respectively, you would change these function names to my_module_cron and my_module_permission.
Note Hooks are bits of code that allow you to alter or add to Drupal functionality. It’s important to preface all your hooks with your module’s name; otherwise, they will not run.
Drupal looks for any hook declarations that are in your Drupal installation, and runs the code at the appropriate time based on what type of hook it is. hook_cron is a hook in the Drupal system that has Drupal run the code within the hook_cron function on a schedule. hook_permission is a Drupal hook which allows you to define new Drupal permissions.
If you create a new module from scratch, the same conventions apply: The folder name must match the prefix of your function names, and the filenames must contain the name of your module.
Summary
In this chapter you took the HTML skills you acquired in Chapter 3 and learned to output HTML in a Drupal module. You learned the basic structure of a Drupal module and about the different pieces needed to create a module that included a controller and some YAML configuration files. We learned that the directory structure and how we name our files matter to wire up a Drupal module to work. Finally, you took a first look at how hooks work in Drupal to alter and add to Drupal.
As you learn about PHP later in the book, the boilerplate code used to create a module will make more sense to you. For a beginning Drupaler, copying and pasting boilerplate code and editing the code to do what you need it to do can take you a long way toward being a successful Drupal programmer. To fully understand the new code needed to create a Drupal hello world module, you would need to study object-oriented PHP programming. My favorite book on the subject is PHP 5 Objects, Patterns, and Practice by Matt Zandstra (Apress, 2005) (see www.apress.com/9781590593806). This chapter fearlessly showed you how to create a Drupal module without assuming you had read a book on object-oriented programming. Know that a primary paradigm in object- oriented programming is to create a bit of code (a class) that can be extended easily by other programmers to accomplish a task. In fact, the whole point is to abstract the coding behind the scenes, so other programmers can build upon what has already been done, not needing to concern themselves necessarily with what has already been built.
Abstraction is key to all engineering. We can program in a programming language because other engineers have already created a computer for us. All we need to do is write Drupal code. I don’t concern myself (often) with how the computer I’m using was made. This is abstraction, and in my mind is the basic idea behind the quote, “We stand on the backs of giants.” So don’t be overly concerned if the boilerplate code in creating a Drupal module is a bit of a black box. You now have learned the basic skills of creating a Drupal module, which is a highly valuable skill by itself.