node_example.module

  1. drupal
    1. 5

This is an example outlining how a module can be used to define a new node type.

Our example node type will allow users to specify a "color" and a "quantity" for their nodes; some kind of rudimentary inventory-tracking system, perhaps? To store this extra information, we need an auxiliary database table.

Database definition:

<?php
  CREATE TABLE node_example (
    vid int(10) unsigned NOT NULL default '0',
    nid int(10) unsigned NOT NULL default '0',
    color varchar(255) NOT NULL default '',
    quantity int(10) unsigned NOT NULL default '0',
    PRIMARY KEY (vid, nid),
    KEY `node_example_nid` (nid)
  )
?>

Functions & methods

NameDescription
node_example_accessImplementation of hook_access().
node_example_deleteImplementation of hook_delete().
node_example_formImplementation of hook_form().
node_example_insertImplementation of hook_insert().
node_example_loadImplementation of hook_load().
node_example_nodeapiImplementation of hook_nodeapi().
node_example_node_infoImplementation of hook_node_info(). This function replaces hook_node_name() and hook_node_types() from 4.6. Drupal 5 expands this hook significantly.
node_example_permImplementation of hook_perm().
node_example_updateImplementation of hook_update().
node_example_validateImplementation of hook_validate().
node_example_viewImplementation of hook_view().
theme_node_example_order_infoA custom theme function.
View source
<?php

/**
 * @file
 * This is an example outlining how a module can be used to define a new
 * node type.
 *
 * Our example node type will allow users to specify a "color" and a "quantity"
 * for their nodes; some kind of rudimentary inventory-tracking system, perhaps?
 * To store this extra information, we need an auxiliary database table.
 *
 * Database definition:
 * @code
 *   CREATE TABLE node_example (
 *     vid int(10) unsigned NOT NULL default '0',
 *     nid int(10) unsigned NOT NULL default '0',
 *     color varchar(255) NOT NULL default '',
 *     quantity int(10) unsigned NOT NULL default '0',
 *     PRIMARY KEY (vid, nid),
 *     KEY `node_example_nid` (nid)
 *   )
 * @endcode
 */

/**
 * Implementation of hook_node_info(). This function replaces hook_node_name()
 * and hook_node_types() from 4.6. Drupal 5 expands this hook significantly.
 *
 * This is a required node hook. This function describes the nodes provided by
 * this module.
 *
 * The "name" value provide a human readable name for the node,
 * while the "module" value tells Drupal how the module's functions map to hooks
 * (i.e. if the module is node_example_foo then node_example_foo_insert will be
 * called when inserting the node). The "description" value provides a brief
 * description of the node type, which will show up when a user accesses the
 * "create content" page for that node type. Other attributes can also be
 * defined through this hook, but only these ones are required.
 */
function node_example_node_info() {
  return array(
    'node_example' => array(
      'name' => t('example node'),
      'module' => 'node_example',
      'description' => t("This is an example node type with a few fields."),
    )
  );
}

/**
 * Implementation of hook_access().
 *
 * Node modules may implement node_access() to determine the operations
 * users may perform on nodes. This example uses a very common access pattern.
 */
function node_example_access($op, $node) {
  global $user;

  if ($op == 'create') {
    // Only users with permission to do so may create this node type.
    return user_access('create example node');
  }

  // Users who create a node may edit or delete it later, assuming they have the
  // necessary permissions.
  if ($op == 'update' || $op == 'delete') {
    if (user_access('edit own example nodes') && ($user->uid == $node->uid)) {
      return TRUE;
    }
  }
}

/**
 * Implementation of hook_perm().
 *
 * Since we are limiting the ability to create new nodes to certain users,
 * we need to define what those permissions are here. We also define a permission
 * to allow users to edit the nodes they created.
 */
function node_example_perm() {
  return array('create example node', 'edit own example nodes');
}

/**
 * Implementation of hook_form().
 *
 * Now it's time to describe the form for collecting the information
 * specific to this node type. This hook requires us to return an array with
 * a sub array containing information for each element in the form.
 */
function node_example_form(&$node) {
  $type = node_get_types('type', $node);

  // We need to define form elements for the node's title and body.
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => check_plain($type->title_label),
    '#required' => TRUE,
    '#default_value' => $node->title,
    '#weight' => -5
  );
  // We want the body and filter elements to be adjacent. We could try doing
  // this by setting their weights, but another module might add elements to the
  // form with the same weights and end up between ours. By putting them into a
  // sub-array together, we're able force them to be rendered together.
  $form['body_filter']['body'] = array(
    '#type' => 'textarea',
    '#title' => check_plain($type->body_label),
    '#default_value' => $node->body,
    '#required' => FALSE
  );
  $form['body_filter']['filter'] = filter_form($node->format);

  // Now we define the form elements specific to our node type.
  $form['color'] = array(
    '#type' => 'textfield',
    '#title' => t('Color'),
    '#default_value' => $node->color
  );
  $form['quantity'] = array(
    '#type' => 'textfield',
    '#title' => t('Quantity'),
    '#default_value' => $node->quantity,
    '#size' => 10,
    '#maxlength' => 10
  );

  return $form;
}

/**
 * Implementation of hook_validate().
 *
 * Our "quantity" field requires a number to be entered. This hook lets
 * us ensure that the user entered an appropriate value before we try
 * inserting anything into the database.
 *
 * Errors should be signaled with form_set_error().
 */
function node_example_validate(&$node) {
  if ($node->quantity) {
    if (!is_numeric($node->quantity)) {
      form_set_error('quantity', t('The quantity must be a number.'));
    }
  }
  else {
    // Let an empty field mean "zero."
    $node->quantity = 0;
  }
}

/**
 * Implementation of hook_insert().
 *
 * As a new node is being inserted into the database, we need to do our own
 * database inserts.
 */
function node_example_insert($node) {
  db_query("INSERT INTO {node_example} (vid, nid, color, quantity) VALUES (%d, %d, '%s', %d)", $node->vid, $node->nid, $node->color, $node->quantity);
}

/**
 * Implementation of hook_update().
 *
 * As an existing node is being updated in the database, we need to do our own
 * database updates.
 */
function node_example_update($node) {
  // if this is a new node or we're adding a new revision,
  if ($node->revision) {
    node_example_insert($node);
  }
  else {
    db_query("UPDATE {node_example} SET color = '%s', quantity = %d WHERE vid = %d", $node->color, $node->quantity, $node->vid);
  }
}

/**
 * Implementation of hook_nodeapi().
 *
 * When a node revision is deleted, we need to remove the corresponding record
 * from our table. The only way to handle revision deletion is by implementing
 * hook_nodeapi().
 */
function node_example_nodeapi(&$node, $op, $teaser, $page) {
  switch ($op) {
    case 'delete revision':
      // Notice that we're matching a single revision based on the node's vid.
      db_query('DELETE FROM {node_example} WHERE vid = %d', $node->vid);
      break;
  }
}

/**
 * Implementation of hook_delete().
 *
 * When a node is deleted, we need to remove all related records from out table.
 */
function node_example_delete($node) {
  // Notice that we're matching all revision, by using the node's nid.
  db_query('DELETE FROM {node_example} WHERE nid = %d', $node->nid);
}

/**
 * Implementation of hook_load().
 *
 * Now that we've defined how to manage the node data in the database, we
 * need to tell Drupal how to get the node back out. This hook is called
 * every time a node is loaded, and allows us to do some loading of our own.
 */
function node_example_load($node) {
  $additions = db_fetch_object(db_query('SELECT color, quantity FROM {node_example} WHERE vid = %d', $node->vid));
  return $additions;
}

/**
 * Implementation of hook_view().
 *
 * This is a typical implementation that simply runs the node text through
 * the output filters.
 */
function node_example_view($node, $teaser = FALSE, $page = FALSE) {
  $node = node_prepare($node, $teaser);
  $node->content['myfield'] = array(
    '#value' => theme('node_example_order_info', $node),
    '#weight' => 1,
  );

  return $node;
}

/**
 * A custom theme function.
 *
 * By using this function to format our node-specific information, themes
 * can override this presentation if they wish. We also wrap the default
 * presentation in a CSS class that is prefixed by the module name. This
 * way, style sheets can modify the output without requiring theme code.
 */
function theme_node_example_order_info($node) {
  $output = '<div class="node_example_order_info">';
  $output .= t('The order is for %quantity %color items.', array('%quantity' => check_plain($node->quantity), '%color' => check_plain($node->color)));
  $output .= '</div>';
  return $output;
}

?>