Create a custom web services project in Drupal 7

Take advantage of the flexible Services module

Web services are web-based software applications that exchange data with other web-based applications. Web services help link applications operating on different network platforms, hardware, software, and databases and combine software and services from different sources to provide a seamlessly integrated service. Learn how to create a custom web service that allows you to display content from one Drupal site on another using the Services module.

Timi Ogunjobi, Software Engineer and Author, Xceedia Limited

Photograph of Timi OgunjobiTimi Ogunjobi is an author and web applications developer. He is CEO of Xceedia Limited, a software engineering firm with interest in web and mobile applications. He is also the author of three Drupal books among several others.



31 January 2012

Also available in Chinese Russian Japanese Spanish

The Services module allows you to enable custom-built content servers and services on your Drupal site from one main module back-end configuration and administration area. The services included in the module allow you to call content and output data from Drupal's default and contributed File, Comment, Search, User, Menu, Node, System, Taxonomy, and Views modules. By calling these services, content can be fetched from your Drupal site and displayed on another Drupal site, either on the same local server or from an external location. However, you can also use the Services module to integrate applications that are not Drupal based with an external web service.

The Services module's flexibly permits you to programmatically create your own custom service module and integrate it with method calls (such as Representational State Transfer [REST], JavaScript Object Notation [JSON], and XML-remote procedure call [XML-RPC]) that come packaged with the main Services module. The main benefit of the Services module is that it enables web service integration with multiple applications while using the standard Drupal module code and programming. More importantly, the Services module helps to reduce the amount of time spent writing your own web service modules, as it provides a standard interface for a number of the common web service application environments.

This article looks at how you can create a web service module that will run a custom callback from a Drupal site using the Service module. You write a custom module to return a set of data from a node of a specific content type. You'll also learn how to:

  • Write a custom module for a service
  • Integrate this module with the Services module and test it to return an output (This is an example of a simple callback that returns a set of nodes.)

Obtaining and installing the required modules

First, you must download the following necessary modules from the Drupal.org site:

  • Services. This is the main Services module downloaded from the project page (see Resources for a link).
  • Chaos tool suite. The Services module depends on this suite, so you must actually install it first or at the same time as the Services module. The Chaos tool suite provides the framework for the endpoint definitions so that they can be defined and exported in both database and code. See Resources for a link to the download site.
  • REST server. You use the REST server, which is included in the Services download, to test the service. You can use other services, such as JSON and XML-RPC, as well.
  • Autoload. The REST server requires this utilities module. The Autoload module enables other modules to leverage PHP 5's autoloading class capabilities in a unified fashion. See Resources for a link.

Install these modules to your /sites/all/modules folder. After uploading the services, if you look inside the folder, you will find that it contains two subfolders: servers and services. The servers folder contains the web services module code and the core module contains support for the XML-RPC server. The services folder includes modules for providing web services of Drupal-based content, including files, comments, nodes, menus, search, users, system, taxonomy, and views (see Figure 1). These folders are all submodules of the Services module.

Figure 1. Contents of the Services module folder
Screenshot showing a file browser view of the Services module folder

When installation is complete, go to the main modules administration list in your Drupal site and enable the main core Services module, the Key Authentication module, the XML-RPC server, and the Services module. After enabling the modules, you will be able to view your installed and enabled servers and services on the Administration page along with the main core services settings. From the /admin/structure/services page of your site, you can easily browse your installed servers and services modules and view any application programming interface (API) keys you have added to your site to use with your servers and overall services. Figure 2 shows an example Browse page.

Figure 2. Adding a new service
Screenshot showing adding a new service with the +Add button highlighted

Click Add to list and title your web service and add an allowed external web server domain with which you will be communicating. The web service you're communicating with can then access your site to consume these services and access the data. You may already be conversant with social media websites such as Facebook and Twitter as well as a host of other web applications that enable seamless exchange and import of user date to facilitate login procedures. From the Admin > Configuration > Services section, the Settings page shows authentication methods and settings that have been enabled and asks whether you want to apply additional content permissions to your content when it is consumed. The field permissions on your content types will not automatically cascade during a web service call. By default, all content fields are returned without any exclusion, but you can apply specific content field-level permissions.


Creating a custom service module

A Drupal module is a collection of files written in PHP that effectively extend the functionality of the basic application. When viewed more closely, a module is no different in structure from any other PHP file and can be independently created, tested, and used in several different installations. The module code can typically access all variables and structures as well as use all the functions of the Drupal core. Similarly, the Drupal core can call—through fixed interfaces known as hooks—those functions that have been defined in modules to enhance its inherent basic functionality.

Hooks make it possible for Drupal to do perform such tasks as:

  • Add new URLs and pages to be defined by a module within the site (hook_menu)
  • Add content to Drupal pages (hook_block, hook_footer, and so on)
  • Create custom database tables (hook_schema)

You will see hook definitions in several places within the code of the new module you'll be creating. Like most other Drupal modules, the module file typically consists of three basic files: modulename.info, modulename.module, and modulename.inc. A more complex module may consist of several more files.

To begin creating your custom service or services module files, in the /sites/all/modules/services folder, add a new folder for this module named note_service.

The .info file

In addition to the note_service folder, create a new file named note_service.info. This file will contain all the metadata and information Drupal requires to list the module in the main modules administration page.

In the .info file, enter the code shown in Listing 1.

Listing 1. Note service .info file
name = Note Service
description = Services for the Note content type.
package = Note example
dependencies[] = services
files[] = note_service.inc
files[] = note_service.module
core = 7.x

Save the file as note_service.info. This code describes the name of the service, a description of what it does, the overall module package the custom module is part of, and the dependencies. In this case, this means that for your custom module to work at all, the Services module must be installed and enabled. Put this note_service.info file in your module folder.

The .install file

The .install file tells Drupal how the module is to be installed—in this case, by implementation of the hook schema. The .install file runs when a module is enabled for the first time, and its basic function is to run the setup procedures required by that module. The most common task the .install file performs is to create database tables and fields. The installation instructions are included in a _install() function. This hook function is called when the module is initially enabled. The file is also used to perform updates whenever a new version of a module is installed. The .install file has no special syntax: It is merely a PHP file with a different file extension. Listing 2 shows the code for the note service .install file.

Listing 2. Note service .install file
<?php
// note_service.install
/**
* Implementation of hook_schema().
*/
function note_service_schema() {
  $schema['note'] = array(
    'description' => t('Stores information about notes.'),
    'fields' => array(
      'id' => array(
        'description' => t('The primary identifier for a note.'),
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'uid' => array(
        'description' => t('The user that created the note.'),
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'created' => array(
        'description' => t('The timestamp for when the note was created.'),
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'modified' => array(
        'description' => t('The timestamp for when the note was modified.'),
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ),
      'subject' => array(
        'description' => t('The subject of the note'),
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
      ),
      'note' => array(
        'description' => t('The note'),
        'type' => 'text',
        'size' => 'medium',
      ),
    ),
    'primary key' => array('id'),
  );
  return $schema;
}
?>

Defining the service

Now, you create your module file. This file contains the PHP function to return a data array and output the Note nodes. The function mainly implements Drupal's hook_services(). A detailed understanding of Drupal module development is not required, although it's helpful. Needless to say, as the functions in the .module file are PHP functions, it is important to ensure that an opening <?php tag is present in the module file code.

Most services for the Services module implement the create, retrieve, update, delete, index (CRUD) method, though it is also possible to implement actions, targeted actions, and relationships by other means not covered in this article. Listing 3 shows the code to create a module file.

Listing 3. Note service .module file
<?php
// note_service.module
/** Gets a note object by id.
*
* @param int $id
* @return object
*/
function note_service_get_note($id) {
  return db_query("SELECT * FROM {note} WHERE id='".$id."'")->fetchAll();
}

/** Writes a note to the database
*
* @param object $note
* @return void
*/
function note_service_write_note($note) {
  $primary_key = !empty($note->id) ? array('id') : NULL;
  drupal_write_record('note', $note, $primary_key);
}

/**
* Deletes a note from the database.
*
* @param int $id
* @return void
*/
function note_service_delete_note($id) {
  db_query("DELETE FROM {note} WHERE id='".$id."'");
}

/**
* Implementation of hook_services_services().
*/
function note_service_services_services() {
  return array(
    'note' => array(
      'retrieve' => array(
        'help' => 'Retrieves a note',
        'file' => array('file' => 'inc', 'module' => 'note_service'),
        'callback' => '_note_service_retrieve',
        'access callback' => '_note_service_access',
        'access arguments' => array('view'),
        'access arguments append' => TRUE,
        'args' => array(
          array(
            'name' => 'id',
            'type' => 'int',
            'description' => 'The id of the note to get',
            'source' => array('path' => '0'),
            'optional' => FALSE,
          ),
        ),
      ),
      'create' => array(
        'help' => 'Creates a note',
        'file' => array('file' => 'inc', 'module' => 'note_service'),
        'callback' => '_note_service_create',
        'access arguments' => array('note service create'),
        'access arguments append' => FALSE,
        'args' => array(
          array(
            'name' => 'data',
            'type' => 'struct',
            'description' => 'The note object',
            'source' => 'data',
            'optional' => FALSE,
          ),
        ),
      ),
      'update' => array(
        'help' => 'Updates a note',
        'file' => array('file' => 'inc', 'module' => 'note_service'),
        'callback' => '_note_service_update',
        'access callback' => '_note_service_access',
        'access arguments' => array('update'),
        'access arguments append' => TRUE,
        'args' => array(
          array(
            'name' => 'id',
            'type' => 'int',
            'description' => 'The id of the node to update',
            'source' => array('path' => '0'),
            'optional' => FALSE,
          ),
          array(
            'name' => 'data',
            'type' => 'struct',
            'description' => 'The note data object',
            'source' => 'data',
            'optional' => FALSE,
          ),
        ),
      ),
      'delete' => array(
        'help' => 'Deletes a note',
        'file' => array('file' => 'inc', 'module' => 'note_service'),
        'callback' => '_note_service_delete',
        'access callback' => '_note_service_access',
        'access arguments' => array('delete'),
        'access arguments append' => TRUE,
        'args' => array(
          array(
            'name' => 'nid',
            'type' => 'int',
            'description' => 'The id of the note to delete',
            'source' => array('path' => '0'),
            'optional' => FALSE,
          ),
        ),
      ),
      'index' => array(
        'help' => 'Retrieves a listing of notes',
        'file' => array('file' => 'inc', 'module' => 'note_service'),
        'callback' => '_note_service_index',
        'access callback' => 'user_access',
        'access arguments' => array('access content'),
        'access arguments append' => FALSE,
        'args' => array(
          array(
            'name' => 'page',
            'type' => 'int',
            'description' => '',
            'source' => array(
              'params' => 'page',
            ),
            'optional' => TRUE,
            'default value' => 0,
          ),
          array(
           'name' => 'parameters',
           'type' => 'array',
           'description' => '',
           'source' => 'param',
           'optional' => TRUE,
           'default value' => array(),
          ),
        ),
      ),
    ),
  );
}
?>

Creating the callbacks

To create callbacks, you need to create the file note_service.inc file, as this is where you have told Services the callbacks can be found. The callbacks that will be incorporated in the .inc file (with respect to the CRUD method) include:

  • Create callback
  • Retrieve callback
  • Update callback
  • Delete callback

You should also include an index callback that fetches and returns all of a particular user's notes. Specify some arguments here in the method, even if they aren't used. The purpose of such arguments is to indicate that paging and filtering of an index listing are supported.

Obviously, not all users should have unhindered permission to view the Note nodes. This is why you should include access callbacks. These access callbacks ought actually to be included in the main module file. Note that neither create nor edit functions are represented, because these functions directly use user_access( ) statements. Also, unlike other methods, the file does not consider note ownership, it merely checks the Note service created for access permission. Listing 4 shows the .inc file code.

Listing 4. Note service .inc file
<?php
// note_service.inc
/**
* Callback for creating note services.
*
* @param object $data
* @return object
*/
function _note_service_create($data) {
  global $user;

  unset($data->id);
  $data->uid = $user->uid;
  $data->created = time();
  $data->modified = time();

  if (!isset($data->subject)) {
    return services_error('Missing note attribute subject', 406);
  }

  if (!isset($data->note)) {
    return services_error('Missing note attribute note', 406);
  }

  note_service_write_note($data);
  
  return (object)array(
    'id' => $data->id,
    'uri' => services_service_uri(array('note', $data->id)),
  );
}

// note_service.inc
/**
* Callback for updating note services.
*
* @param int $id
* @param object $data
* @return object
*/
function _note_service_update($id, $data) {
  global $user;
  $note = note_service_get_note($id);

  unset($data->created);
  $data->id = $id;
  $data->uid = $note->uid;
  $data->modified = time();

  note_service_write_note($data);
  
  return (object)array(
    'id' => $id,
    'uri' => services_service_uri(array('note', $id)),
  );
} 
/**
* Callback for retrieving note services.
*
* @param int $id
* @return object
*/
function _note_service_retrieve($id) {
  return note_service_get_note($id);
}

/**
* Callback for deleting note services.
*
* @param int $id
* @return object
*/
function _note_service_delete($id) {
  note_service_delete_note($id);
  
  return (object)array(
    'id' => $id,
  );
}

function _note_service_index($page, $parameters) {
  global $user;

  $notes = array();
  $res = db_query("SELECT * FROM {note} 
WHERE uid='".$user->uid."'ORDER BY modified DESC");
  foreach ($res as $note) {
    $notes[] = $note;
  }

  return $notes;
}

/**
* Access callback for the note service.
*
* @param string $op
*  The operation that's going to be performed.
* @param array $args
*  The arguments that will be passed to the callback.
* @return bool
*  Whether access is given or not.
*/

function _note_service_access($op, $args) {
  global $user;
  $access = FALSE;

  switch ($op) {
    case 'view':
      $note = note_service_get_note($args[0]);
      $access = user_access('note service view any note');
      $access = $access || $note->uid == $user->uid && 
      user_access('note service view own notes');
      break;
    case 'update':
      $note = note_service_get_note($args[0]->id);
      $access = user_access('note service edit any note');
      $access = $access || $note->uid == $user->uid &&  
      user_access('note service edit own notes');
      break;
    case 'delete':
      $note = note_service_get_note($args[0]);
      $access = user_access('note service delete any note');
      $access = $access || $note->uid == $user->uid && 
      user_access('note service delete own notes');
      break;
  }
  $access = TRUE;
  
  return $access;
}
?>

When the .info and .module files are in place in the new custom module folder, you can enable the module on the Drupal modules Administration page. Look for the custom Note service module, select the check box next to the module to enable it, and save the module configuration. Also, enable the Services and REST server modules. This should finalize the creation of your custom note_service module.


Creating the endpoint

Web services represent a set of software tools that can be used in several ways, the three most common of which are service-oriented architecture (SOA), RPC, and REST. RPC was the initial focus of the first web services tools and thus became the style more widely deployed and supported, even though it's too tightly coupled to language-specific or method calls. In contrast, REST uses HTTP and similar protocols through familiar operations such as GET, POST, PUT, and DELETE, thus interacting more with stateful resources rather than with operations and messages. Both server tools are included in the Services module (see the Servers folder). You can download other tools into this folder, but this example focuses on the REST method, because the endpoint is universal.

An endpoint indicates the specific location where a service can be accessed by using a specific protocol and data format. There are two ways to create an endpoint—either through code or by using the administrative interface. Creating an endpoint through the administrative interface is the easier option, as you can then export and paste the code into your module. To create an endpoint, perform the following steps:

  1. In Admin > Structure > Services, click Add.
  2. Name the endpoint note.
  3. Choose REST as your server, and place the path to endpoint at note.
  4. Clear the Debug Mode and Session Enabled check boxes.
  5. Click Save. Figure 3 shows the new service endpoint in progress.
    Figure 3. The new service endpoint
    Screenshot of editing the endpoint note with the Name=note, Server=REST, Path to endpoint=note

    View a larger version of Figure 3.

  6. Click the Services link, and select the services or service groups you want to enable. Clear the notes check box.
  7. Click Save.
  8. Click the Server tab, and then clear the application/x-www-form-urlencoded check box under Request parsing.
  9. Click Save.

You have now created a proper working endpoint in your REST server.


Conclusion

In this article, you learned how to use the Drupal Services module. You installed and enabled the contributed Services module and looked at the functionality of its servers and services functionality. You learned that you can add server modules to integrate with your Drupal site and content and serve your Drupal content to external websites using the prepackaged Services submodules. You have created a custom module that can integrate with your Services module and extend its default functionality and made a method call to return a callback that should output a listing all of the nodes and data for a specific content type.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=789772
ArticleTitle=Create a custom web services project in Drupal 7
publish-date=01312012