Skip to main content

Nodequeues for developers

An article from ComputerMinds - Building with Drupal in the UK since 2005
27th Aug 2009

Steven Jones

Senior Developer
Steven Jones
Hey, you seem to look at this article a lot! Why not Bookmark this article so you can find it easily in the future?

Nodequeues are a really useful way of building ordered lists of content and they integrate amazingly with views and have quite a nice interface for adding/removing nodes. Out of the box you can create queues and choose node types that can be added to the queues, and roles that can manipulate the queues, with a little more effort and some code you can do a lot more. We had built a media competition feature for a client; they can create a competition into which users can post photos or videos that can then be voted and commented on. However we never really gave much thought to how winners would be picked, but it seemed logical to use a nodequeue to allow moderators to choose the winners by adding them into a queue.

We decided that rather than forcing our client to create a new nodequeue for each competition (which are nodes themselves) we'd have a single queue with subqueues for each competition. This approach, using subqueues, has several advantages, among which is that it makes it stupidly easy to build a single view that takes a competition node id as an argument for which subqueue to show.

The first step was to define our own queue type, using hook_nodequeue_info():


/**
 * Implementation of hook_nodequeue_info()
 */
function media_competition_nodequeue_info() {
  return array('media_competition' => array(
    'title' => t('Media competition queue'),
    'description' => t('Each media competition item on the site will get its own subqueue. This is to allow flagging of the entries for e.g. the winning entries.'),
    ));
}

You just define the queue types that you want to handle, and some simple meta data.

Then you'll want to define hook_nodequeue_form() so that you can change the form the user sees when they add/edit a media_competition nodequeue. Ours looks a little like this:


/**
 * Implementation of hook_nodequeue_form()
 */
function media_competition_nodequeue_form($queue, &$form) {
  $form['subqueue_title'] = array(
    '#type' => 'textfield',
    '#title' => t('Subqueue title'),
    '#default_value' => $queue->subqueue_title,
    '#size' => 50,
    '#maxlength' => 64,
    '#description' => t('What to display for the subqueue title; use %subqueue to embed the actual subqueue title. This is used to distinguish multiple nodequeues with subqueues from each other, as internal subqueue title is filled automatically.'),
  );
}

I've removed some other code that sets the node types that you're allowed to add into the queue, but the format of the hook is that of a hook_form_alter().

Finally, you'll need to let nodequeue know what subqueues a node can be added to, you do this by implementing hook_nodequeue_subqueues()


/**
 * Implementation of hook_nodequeue_subqueues()
 *
 * @return An array of subqueue references.
 */
function media_competition_nodequeue_subqueues(&$queue, $node) {
  media_competition_ensure_all_subqueues($queue);
 
  // Lookup the subqueues that match our competition:
  if ($node->competitions && is_array($node->competitions)) {
    return array_keys($node->competitions);
  }
}

The important bit here is to return an array of subqueue REFERENCES. You see, for each subqueue you create you give it a reference, it is this that you return. We keep track of which competitions our entries belong to by loading that into the node in $node->competitions. The media_competition_ensure_all_subqueues() function is a simple helper that ensures that the given queue has a subqueue for each competition on the site, it looks like:


function media_competition_ensure_all_subqueues(&$queue) {
  // Get all the possible competitions:
  $competitions = array();
  $result = db_query("SELECT n.nid FROM {node} n WHERE n.type = '%s'", MEDIA_COMPETITION_NODE_TYPE);
  while ($row = db_fetch_object($result)) {
    $competitions[] = $row->nid;
  }
 
  // Now check to see if they exist in the nodequeue DB already:
  $exists = array();
  $subqueues = nodequeue_load_subqueues_by_reference(array($queue->qid => $competitions));
  foreach ($subqueues as $subqueue) {
    $exists[$subqueue->reference] = TRUE;
  }
  foreach ($competitions as $competition) {
    if (empty($exists[$competition])) {
      nodequeue_add_subqueue($queue, media_competition_nodequeue_subqueue_title($queue, $competition), $competition);
    }
  }
}

function media_competition_nodequeue_subqueue_title(&$queue, $competition_nid) {
  if ($competition = node_load($competition_nid)) {
    return $competition->title;
  }
}

There's still a little work to do to hook into the nodeapi so that you remove or rename subqueues when the competitions are removed or renamed, but I leave that as an exercise to the reader!

Lovely, a nodequeue with subqueues corresponding to one type of nodes on your site.

Hi, thanks for reading

ComputerMinds are the UK’s Drupal specialists with offices in Bristol and Coventry. We offer a range of Drupal services including Consultancy, Development, Training and Support. Whatever your Drupal problem, we can help.