Programming Drupal Part 2 - Create a Hello world page
1 - 2
This tutorial is our start in Drupal 7 module development. I aim to facilitate learning by example, and over the coming weeks will show how to do standard web application functionality with Drupal and then will dive deeper into the Drupal APIs.
We'll start by making a page and exploring URL routing and how the menu system works.
What is a page exactly?
In Drupal, which is more a PAC than MVC architecture, the page layout is split into regions and into those regions you can place any number of blocks. The main content for the page goes into the main content area and this is where our hello world output from this tutorial will go. We will look at layouts, blocks and other elements like breadcrumb, navigation menus and the HTML head tags in other tutorials.
Click to enlarge
Create a "Hello World" page
So let's create a page that puts custom content into the main content area. The first thing we need to do is create container for our code, Symfony 2 has the concept of bundles,
A bundle is similar to a plugin in other software, but even better. The key difference is that everything is a bundle in Symfony2, including both the core framework functionality and the code written for your application. Bundles are first-class citizens in Symfony2. more..
and in Drupal the same thing is called a module, and like Symfony with its bundles, everything in Drupal is written as module, both core and custom code. First create a directory named helloworld inside your sites/default/modules directory, in my case on windows with WAMP that's here:
C<span style="color: #339933;">:</span>\wamp\www\d7\sites\<span style="color: #b1b100;">default</span>\modules
Then add two files inside that helloworld directory
helloworld.info
name = Hello world
description = A container that contains some tutorial code
package = helloworld
core = 7.x
files[] = helloworld.module
This contains some information that should be self explanatory, most of the text will appear on the modules page (see below). Then add the actual module file which as you can see doesn't do anything yet.
helloworld.module
<?php
/**
* Helloworld module contains some tutorial code
*/
Now go to the modules page, scroll down and enable the module (or use drush like you saw in the last tutorial) and hit submit:
/d7/admin/modules
This registers the module, which so far isn't actually doing anything.
Now for the page code. To make a custom page in Drupal, a URL route is mapped to a callback function. This callback in turn returns an array to the theme layer which takes care of the display. More about that later, and for now we'll just worry about the callback, menu item and url route.
helloworld.module
<?php
/**
* Implements hook_menu()
* @return array
*/
function helloworld_menu() {
$aItems = array(
'hello_world' => array(
'title' => 'Hello world',
'page callback' => '_page_hello_world',
'access callback' => TRUE,
),
);
return $aItems;
}
/**
* Page callback mapped to the url /hello_world
*
* @return array
*/
function _page_hello_world() {
return array(
'#markup' => '<p>Hello world</p>'
);
}
In Zend Framework or Symfony you write url routes that map to actions and Drupal has its own equivalent mechanism, the slightly misnamed menu system. The menu system handles both menu entries in the navigation menus, url routes and callbacks and permissions.
The way it works is that individual modules implement hook_menu and this way Drupal knows about routes, menu items and callbacks. The function helloworld_menu() is an implementation of the hook_menu and the format of the function name as you can see is:
function <MODULENAME>_menu() {
}
Add the helloworld.module code above to the module file, save and enter this url into your browser:
/hello_world
and...
it doesn't work! The reason is that Drupal doesn't know about this new url route, we missed one step namely registering the array. So let's fix this by clearing the menu cache with Drush like this:
drush cc menu
or from the menu toolbar here.
When doing this, Drupal invokes all menu hook implementations gathers the array data and stores it in its internal cache. Currently this is one of the mysql tables, but it could very easily be memcached. Now the page should will work:
Click to enlarge
Note: Generally if your new menu entries or pages aren't appearing, check that the module is enabled, that the permissions are correct and flush the menu cache. More on permissions in the next tutorial.
Let's look a bit more closely at how this works:
/**
* Implements hook_menu()
* @return array
*/
function helloworld_menu() {
// A hook_menu array can contain any number of menu entries. By default
// entries are added to the navigation menu.
$aItems = array(
// the array key is the actual url
'hello_world' => array(
// the title appears in a navigation menu, the <h1> and <title> tags.
'title' => 'Hello world',
// this is the function that returns the output data
'page callback' => '_page_hello_world',
// the user can see this page
'access callback' => TRUE,
),
);
return $aItems;
}
If we have a multi-lingual install, the title value will automatically be translatable from the translation interface. I usually start all page callback function names with _page_ to make the code clearer, in fact I prefix all non public functions with a _ to indicate that they are private to the current module. This is function visibility by convention and developer discipline rather than by enforcement through PHP, which is one of those Drupal things we have to live with. I will talk more about organising code in future tutorials.
We can remove the visible navigation menu link by changing the menu type like this:
$aItems = array(
'hello_world' => array(
'title' => 'Hello world',
'page callback' => '_page_hello_world',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
),
);
Note: I encountered an issue where making this change and subsequently refreshing the menu cache, as the menu item didn't disappear. To resolve it I commented out the array, flushed the menu, added it back and flushed once more.
By default, Drupal adds menu items to the navigation menu. You could add it to another menu, like this:
function helloworld_menu() {
$aItems = array(
'hello_world' => array(
'title' => 'Hello world',
'page callback' => '_page_hello_world',
'access callback' => TRUE,
'menu_name' => 'main-menu',
),
);
return $aItems;
}
Make the change, and refresh the menu cache. Now you'll see this:
Click to enlarge
Now let's do one more thing, go to the modules page and enable the menu module:
Click to enlarge
now visit the main-menu- page /admin/structure/menu/manage/main-menu
Click to enlarge
and you see the menu entry you added via the hook_menu callback. From here you can override the code values to hide and rename it, or even move it to another menu. We'll stick with doing things programmatically, but it is possible.
Click to enlarge
and finally have a look in the database to see what this menu entry looks like:
// the router table containing the path, callback information
SELECT * FROM `menu_router` WHERE path = 'hello_world';
// the links table storing parent child link information
SELECT * FROM `menu_links` WHERE link_path = 'hello_world';
If you want to make the link return to the navigation menu, it isn't enough to just just remove this line:
'menu_name' => 'main-menu',
you have to explicitly set it to the navigation menu:
'menu_name' => 'navigation',
and don't forget, refresh the menu cache.
That concludes part two, we've touched the tip of the iceberg. In part three we'll look at user permissions on pages. Click to download the the code for this tutorial.
Further reading
Drupal 7 hook menu APIDrupal examples project - contains amongst others menu api examples. Very worth a look.
Downloads: The complete code for part 2 - create a hello world pageBlog tags:
Link tags: