Hello World module in Drupal 6 Tutorial – Part 2

Hey all, welcome to part 2, a continuation of the Hello World module in Drupal 6 – Part 1 I know what you’re thinking: “A full 8 months later? wtf?”. If you werent thinking that then you are now. Or something along those lines. I apologise for the delay(if you can even call it that). Blah blah blah

{begin: excuse}
//TODO: enter excuses here
{end: excuse}

Now that we’ve got that out of the way, Shall we then get to the Semicontacts Module tutorial. Right.

In this tutorial we are going to learn the following

1. How to use the Drupal Database Abstraction Layer.
2. How to use the Drupal Form API to create and validate forms.
3. How to use the Drupal Schema API to create a database table during module installation.

NOTE: Please note that this tutorial is used for educational purposes only.<br />
 It is nowhere near production ready. Also what we are doing here can be done<br />
 without any coding at all using the <a href="http://drupal.org/project/cck">CCK module</a>. This is just meant to show you actual Drupal Code

Our module is an address book much like the one you have on your phone. It will require a first name, last name, phone number. These will then be saved in a database in a table called semicontacts that we will create.

We will also create a page to list all these contacts.

Okay less yada yada more code you say? Say no more. So were going to create module folder and the two required files like in part one.

So now I have a folder in sites/all/modules/ called semicontacts(the name of my module):

-sites/all/modules/semicontacts
—semicontacts.info
—semicontacts.module

Let’s start by telling Drupal about our module in the semicontacts.info file:

; $Id$
name = Semicolon Contacts Module
description = Contact Management Module(CMM :D )
core = 6.x
package = Semicolon

Now Drupal knows about our module. If you actually go into modules list you will see this new module.

Now lets edit the semicontacts.module file and add a page(tell Drupal the URL for the module), you will remember we do this by using a hook_menu() hook. If you remember in part 1, hooks naming follows nameofmodule_hookname() what does this mean for us?:

<?php
function semicontacts_menu(){
$items = array();
$items['semicontacts'] = array(
'title' => t('Semicontacts - Form'),
'page callback' => 'semicontacts_page',
 'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
 );
 return $items;
}

As you can see the module will be accessible from the http://mysite.com/semicontacts page. The page call-back is the function that will be called when this URL is accessed. So lets go ahead and create that. This page will contain our form. We will use the Form API to create the form.

function semicontacts_page($argument) {
    $content = "Hello World! Wow It really works
    This is a simple module to save contacts to the database.
    Enter Contact Details.";
    $content .= drupal_get_form('semicontacts_form');
    return $content;
}

There’s our function. So we put a little into text at the top and we add a form to that using the drupal_get_form() function, which takes a form function as a parameter. So we need to create this form function that will return our form.

function semicontacts_form(){
 $form = array();
 //create a text field called firstname with label First Name
 $form['firstname'] = array(
 '#type' => 'textfield',
 '#title' => t('First Nmae'),
 );
 //create a text field called lastname with label Last Name
 $form['lastname'] = array(
 '#type' => 'textfield',
 '#title' => t('Last Name'),
 );
 //create a text field called phonenumber with label Phone Number
 $form['phonenumber'] = array(
 '#type' => 'textfield',
 '#title' => t('Phone Number'),
 );
 //create submit button
 $form['submit'] = array(
 '#type' => 'submit',
 '#value' => t('Save Contact'),
 );
 //return the form
 return $form;
}

The Form API makes creating our form simpler and cleaner. The form is declared as an array of arrays(the fields are themselves arrays). By the ways this function can be called whatever you want. As long you fetch the right form in the page function above.

So now we should have a form. Lets try access the page. Go to http://www.my-site.com/semicontacts You should be greeted with a form which at the moment does absolutely nothing. Try click submit. It should just redirect you to the same page.

Okay. Now lets get to the fun stuff. Lets create a table in our database called semiccontacts to store our contacts:

CREATE TABLE `semicontacts`.`helloworld_contacts` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`firstname` VARCHAR( 255 ) NOT NULL ,
`lastname` VARCHAR( 255 ) NOT NULL ,
`phonenumber` VARCHAR( 255 ) NOT NULL
)

Copy sql query above and run in a mysql client(i use phpMyAdmin). Be sure you have already selected your drupal database.

Now we just need to create a function to save our form. We do this by implementing form api’s _submit() function

function semicontacts_form_submit($form_id, &$form_data){
 db_query("INSERT INTO semicontacts (firstname, lastname, phonenumber) VALUES ('%s', '%s', '%s')", $form_data['values']['firstname'], $form_data['values']['lastname'],  $form_data['values']['phonenumber']);
 drupal_set_message(t('Your contact has been saved to the database.'));
}

The data submitted by the form is stored in an array $form_data[‘values’] with field names as keys so to fetch the value of firstname field just $form_data[‘values’][‘firstname’], simple enough right? Yep.

In Drupal 7, We use the db_insert to save our data. The API allows for method chaining, much like RUBY’s active record pattern or Jqeury. Looks better and makes the code easier to follow.

So to save this in drupal 7 we would use:

db_insert('semicontacts')->fields($form_data)->execute();

So now our form should be saving to the database. Try it now. Works? Yes? No? If it doesnt then something went wrong up there. You or i(i hope not) missed something.

Wait don’t we need to validate our form data? We do. Lets. Form validation is done using the form api _validate() function. It means our function will be called semicontacts_validate().

function semicontacts_form_validate($form_id, &$form_data){
 //var_dump($form_value);
 if(!is_string($form_data['values']['firstname'])|| empty($form_data['values']['firstname'])){
 form_set_error('firstname', t("Please Enter a valid First Name"));
 }elseif(!is_string($form_data['values']['lastname']) || empty($form_data['values']['lastname'])){
 form_set_error('lastname', t("Please Enter a valid Last Name"));
 }elseif(!is_numeric($form_data['values']['phonenumber']) || empty($form_data['values']['phonenumber'])){
 form_set_error('phonenumber', t("Please Enter a valid Phone Number"));
 }
}

We used the the form_set_error function to set errors if any for the field we are currently validating. the method takes a fieldname and an error message. You will also notice like the submit function we did not have to call them. This is the beauty of hooks. They’re like that little kid in class with his hand up going “me,me,me, pick me sir”. And the class wont continue until they get picked. I thought that was deep. You should write that down 😉

So hooks break drupal flow of execution cause drupal will first check if there are any relevant hooks in your module before continuing to the next task. So its almost like you modified the Drupal core without really needing to see the core code.

Okay now try submitting without entering any values. You should get the error messages we defined above.

But wait would’nt it be cool if we had a page to list contacts? Lets go ahead and do that.

Remeber that secontacts_menu function up there, the one that defines our modules urls? Yeah that one. Lets add another url there. Say… http://www.my-site.com/semicontacts/list

function semicontacts_menu(){
$items = array();
$items['semicontacts'] = array(
'title' => t('Semicontacts - Form'),
 'page callback' => 'semicontacts_page',
 'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
 );
 $items['semicontacts/list'] = array(
 'title' => t('Semicontacts - List'),
 'page callback' => 'semicontacts_list_page',
 'page arguments' => array("1"),
 'access arguments' => array('access content'),
 'type' => MENU_CALLBACK,
 );
return $items;
}

Easy enough. If at this point you are thinking we need to create the call back function you are indeed correct young grasshoper 😀 Here it is:

function semicontacts_list_page($argument) {
 $content = t("List of contacts<br /><br />");
 $content .= t(l("Add New Contact","semicontacts"));
 $result = db_query('SELECT c.id, c.firstname, c.lastname, c.phonenumber FROM semicontacts c order by
lastname asc');
 $content .= "<ul>";
 while ($contact = db_fetch_object($result)) {
 $content .= "<li>$contact->lastname, $contact->firstname - $contact->phonenumber</li>";// Perform
operations on $node->body, etc. here.
 }
 $content .= "</ul>";
 $content .= l(t("Add New Contact"),"semicontacts");
 return $content;
}

At this point our module is fully functional. But we dont wan’t the end-user to always run a sql command to create the semicontacts table. We need this to happen during module installation. Enter the

semicontacts.install module with the Schema API Create a new file in the semicontacts folder called semicontacts.install with the following contents:

<?php
//$Id
/**
 * Implement hook_schema()
 */
function semicontacts_schema(){
 $schema['semicontacts'] = array(
 'description' => t('Store .'),
 'fields'=>array(
 'id'=>array(
 'type'=>'serial',
 'unsigned'=>TRUE,
 'not null'=>TRUE
 ),
 'firstname'=>array(
 'type'=>'varchar',
 'length'=>255,
 'default' => '',
 'not null'=>TRUE
 ),
 'lastname'=>array(
 'type'=>'varchar',
 'length'=>255,
 'default' => '',
 'not null'=>TRUE
 ),
 'phonenumber'=>array(
 'type'=>'varchar',
 'length'=>255,
 'default' => '',
 'not null'=>TRUE
 ),
 ),
 'primary key'=>array("id"),
 );
 return $schema;
}
/**
 * Implement hook_install()
 */
function semicontacts_install(){
 drupal_install_schema('semicontacts');
}
/**
 * Implement hook_uninstall
 */
function semicontacts_uninstall(){
 drupal_uninstall_schema('semicontacts');
}

This file contains implementation of 3 drupal hooks, hook_install(), hook_uninstall(), hook_schema().

hook_install() is used to run any configuration during install like say creating a database table for our module.

hook_uninstall() is used to run any configuration during install like say deleting a database table for our module.

hook_schema() is where we define our database table. The fields about are clearly self explanatory. Check a full list and description.

To test your new install file you must remove the module from Drupal system table. This makes sure that Drupal will run hook_install() since this only runs the first time a module is installed. To do that I usually just run this query

delete from system where name ="semicontacts"

Not the best way, not even advisable. to execute queries directly on the database, but it should suffice since we are on our localhost.

So now you need to package your module in to a .tar.gz file. If you are on windows download 7zip.
To create the file just right click on the semicontacts folder, go to the 7zip submenu and click on “add to archive”, on the window that pops up,choose “tar” format. you should have a newfile semicontacts.tar. Right click this file and do the same as above but choose gzip as the archive format. Good luck.

Comments welcome positive and negative. I apologise for the lenghth of the tutorial, was just so much stuff to cover. We are going to improve and extend semicontacts in the next tutorial(Part 3)

Download Source Code

  • Share post

A collection of particles named Tali Luvhengo

36 comments

  • Meshack says:

    Mayne i love me some of this site. Not that it’s useful or anything. i just like trolling around here 😀

  • […] an input form and save user the input to the database.You can download the complete drupal module.Once done with this easy bit Check out Part 2 of the tutorialEnjoyThanks Talifunction simple_hello_world_menu(){ $items = array(); $items['simple-hello-world'] = […]

  • Abhilash says:

    Ur site was really helpful for me to create a page,insert and validate …………

  • Irshad says:

    Really very help full article!!!

    Can you please let me know, how can I include javascript file (.js) for this module

    • Hey Irshad,

      To add a javascript file you need two functions
      1. drupal_get_path(‘module’, ‘module_name’) . ‘/module_name.js’); // returns the absolute path to the js file
      2. drupal_add_js(‘path_to_js_file’);

      e.g
      drupal_add_js(drupal_get_path(‘module’, ‘semicontacts’) . ‘/semicontacts.js’);

      if cause you have to create semicontacts.js file 🙂

  • Ronald says:

    It’s a good tutorial , sorry if i’m being newbie , but my drupal is not accepting the db_query command, it’s getting the following messagem : “fatal error: Call to undefined function db_query()”

    I don’t know what it could be …

  • Ronald says:

    Exactly Talifhani, i’m using the Drupal 6 … but i really have no idea why this error …

  • Ronald says:

    Btw, i’m using a MySql DB

  • wa2nlinux says:

    Hi great tutorial, how about add permission ? only user/role can access that module ..

    Regards

    • Check this code

      <?php
      function semicontacts_menu(){
      $items = array();
      $items['semicontacts'] = array(
      'title' => t('Semicontacts - Form'),
      'page callback' => 'semicontacts_page',
       'access arguments' => array('access content'),
      'type' => MENU_CALLBACK,
       );
       return $items;
      }
      

      This line => ‘access arguments’ => array(‘access content’) tells drupal that users need “access content” permission to view this page.

      So to define our own permissions we would use hook_perm() to tell drupal about THIS module’s permissions

      function semicontacts_perm(){
           return array('access semicontacts content');
      }
      

      And then we’d change the line about to:

       'access arguments' => array('access semicontacts content'),
      

      You new permissions will be listed in Drupal’s permissions page. I hope this makes sense.

  • wa2nlinux says:

    one more question ? how can I show the result after submit a form, not click a menu ?

  • Shobhit says:

    how can i show the value from the database on the form

  • Have a look at this function

    function semicontacts_list_page($argument) {
    $content = t(“List of contacts

    “);
    $content .= t(l(“Add New Contact”,”semicontacts”));
    $result = db_query(‘SELECT c.id, c.firstname, c.lastname, c.phonenumber FROM semicontacts c order by
    lastname asc’);
    $content .= “

      “;
      while ($contact = db_fetch_object($result)) {
      $content .= “

    • $contact->lastname, $contact->firstname – $contact->phonenumber
    • “;// Perform
      operations on $node->body, etc. here.
      }
      $content .= “

    “;
    $content .= l(t(“Add New Contact”),”semicontacts”);
    return $content;
    }

  • _Elena says:

    Я думаю, что в функцию надо переписать без аргумента. По крайней мере у меня Дрюпал ругался, пока я не изменила строчку
    function semicontacts_page($argument) {
    на
    function semicontacts_page() {

    function semicontacts_page($argument) {
    $content = “Hello World! Wow It really works
    This is a simple module to save contacts to the database.
    Enter Contact Details.”;
    $content .= drupal_get_form(‘semicontacts_form’);
    return $content; }

    У меня Дрюпал 6, php 5.3

    Очень буду благодарна вам за ответ

  • mata says:

    D7 need this:
    $content .= drupal_render(drupal_get_form(‘semicontacts_form’));

  • mata says:

    Insert data with D7:
    $nid = db_insert(‘semicontacts’)->fields(array(‘firstname’ => $form_data[‘values’][‘firstname’],’lastname’ => $form_data[‘values’][‘lastname’],’phonenumber’ => $form_data[‘values’][‘phonenumber’],))->execute();

  • mata says:

    List in D7:
    <?php
    function semicontacts_list_page($argument) {
    $content = t("List of contacts”);
    $content .= t(l(“Add New Contact”,”semicontacts”));
    $content .= “”;
    $query = db_select(‘semicontacts’, ‘c’);
    $query->fields(‘c’,array(‘id’,’firstname’,’lastname’,’phonenumber’))
    ->orderBy(‘lastname’, ‘ASC’)
    ->range(0,1000);//LIMIT to 1000 records
    $result = $query->execute();

    while($record = $result->fetchAssoc()) {
    $content .= “”.$record[‘lastname’].”, “.$record[‘firstname’].” – “.$record[‘phonenumber’].””;
    }
    $content .= “”;
    $content .= l(t(“Add New Contact”),”semicontacts”);

    return $content;
    }

  • Hey Mata thanks a lot for the update. I actually haven’t started with any Drupal 7 Development. Looks interesting. Love the chained methods.

  • Anup S. says:

    Nice tutorial. I followed your tutorial step by step and work like charm. Started to feel good with Drupal now.

    When is part 3 coming? Waiting for your part 3 tutorial.

    Cheers,
    Anup S.

  • sarath says:

    It’s Nice tutorial. you are simply showing how to create a from & displaying submitted data. Can u extend how to edit , delete & search the contact list.
    waiting for that implementation in next part.

  • Sarath says:

    Hi,
    It’s Nice tutorial. you are simply showing how to create a from & displaying submitted data. Can u extend how to edit , delete & search the contact list. waiting for that implementation in next part.

  • rishi says:

    U really saved me alot of time..

    ThanQ….

    Nice tutorial……….

  • krish says:

    how i can edit the form by getting values into the form..
    like adding a link edit contacts by clicking the record it should get values into the form and update them…how it should be done

    • You can do that by just creating a new page with an edit form by adding it to the menu hook.

      function semicontacts_menu(){
      $items = array();
      $items['semicontacts'] = array(
      'title' => t('Semicontacts - Form'),
       'page callback' => 'semicontacts_page',
       'access arguments' => array('access content'),
      'type' => MENU_CALLBACK,
       );
       $items['semicontacts/list'] = array(
       'title' => t('Semicontacts - List'),
       'page callback' => 'semicontacts_list_page',
       'page arguments' => array("1"),
       'access arguments' => array('access content'),
       'type' => MENU_CALLBACK,
       );
       $items['semicontacts/edit'] = array(
       'title' => t('Semicontacts - Edit'),
       'page callback' => 'semicontacts_edit_page',
       'page arguments' => array("1"),
       'access arguments' => array('access content'),
       'type' => MENU_CALLBACK,
       );
      return $items;
      }
      

      This will create a new page for you and all you have to do is define semicontacts_edit_page() function. You could then parge the row id in the url and fetch it from the db and populate the form.
      Make sense? Hope so.

  • Karthiha says:

    This is very helpful to understand the the basic module development. I did install this semicontacts module but the table doesn’t create automatically. Plz give me solutions. I m struggle with this for last one week.

  • rajiv says:

    Hello Talifhani Luvhengo,

    Sorry for this stupid question but i am quite fresher in Drupal 6.
    Well, thank you for this great tutorial.
    I successfully installed module in Drupal 6 as you mention in your tutorial but after installed i did not find the form to insert the data into database.
    Please help me as soon as possible because i really need to create module urgently.

    Regards,

  • colonic cleansing weight loss says:

    We are a group of volunteers and starting a new scheme
    in our community. Your website provided us with valuable info to work on.

    You’ve done a formidable job and our whole community will be grateful to you.

  • Antony Jeyaprakash says:

    Thanks a lot. I got much knowledge from this article. I have one doubt. That is, sub admin role user only have delete option. And another role, manager only can have edit option. How can i do that such role based option. Can you please explain? that will be very helpful for me.

  • sohan says:

    Thanks it’s a good tutorial. There is one slight mistake I would like to notice:

    In function semicontacts_page, the following line

    $content .= drupal_get_form(‘semicontacts_form’);
    should be:
    $content = drupal_get_form(‘semicontacts_form’);

Leave a Reply to Irshad Cancel reply

Your email address will not be published. Required fields are marked *

Page optimized by WP Minify WordPress Plugin