---
title: "Drupal 7 Tutorial: Creating a Custom Entityqueue Handler"
description: Learn how to create a custom EntityQueueHandler in Drupal 7 using Entityqueue and Ctools plugins in this step-by-step tutorial.
---

[Blog ](https://www.metaltoad.com/blog)

# [Drupal 7 Tutorial: Creating a Custom Entityqueue Handler](https://www.metaltoad.com/blog/drupal-tutorial-custom-entityqueue)

 Written by [Metal Toad Staff](https://www.metaltoad.com/blog/author/metal-toad-staff) | Jun 27, 2015 12:00:00 AM

Entityqueue uses Ctools plugins for what we call an EntityQueueHandler. In this post we are going to see how to create a custom EntityQueueHandler. We will be looking at the [Entityqueue Scheduler module](https://www.drupal.org/project/entityqueue_scheduler) as an example.

### What is an EntityQueueHandler?

When you create a new Entityqueue, there is an option to select which handler to use.

First lets look at what an EntityQueueHandler can do. To do that, take a peek at the [EntityQueueHandlerInterface](http://cgit.drupalcode.org/entityqueue/tree/plugins/entityqueue/handler/base.inc?h=7.x-1.x#n11). While you can implement that interface directly, most likely you will extend the `EntityQueueHandlerBase` abstract class or one of its subclasses and only override the methods you care about.

Here is a quick overview of the most likely methods you'll want to override.

#### Methods for overriding Entityqueue forms

```
/**
 * Generates a settings form for this handler.
 */
public function settingsForm();
 
/**
 * Generates an add/edit subqueue form for this handler.
 */
public function subqueueForm(EntitySubqueue $subqueue, &$form_state);
```

#### Method for overriding a Subqueue label

```
/**
 * Returns the label of a given subqueue.
 */
public function getSubqueueLabel(EntitySubqueue $subqueue);
```

#### Method for determining if a Subqueue can be deleted

```
/**
 * Returns TRUE if subqueue can be deleted, otherwise returns FALSE.
 */
public function canDeleteSubqueue(EntitySubqueue $subqueue);
```

#### Methods for CRUD on an Entityqueue

```
/**
 * Act on creating a queue.
 */
public function create();
 
/**
 * Act on loading a queue.
 */
public function load();
 
/**
 * Act on loading a queue that is defined only in code.
 */
public function loadFromCode();
 
/**
 * Act before a queue is saved.
 */
public function preSave();
 
/**
 * Act after a new queue is saved.
 */
public function insert();
 
/**
 * Act after a queue is updated.
 */
public function update();
 
/**
 * Act before deleting a queue.
 */
public function preDelete();
 
/**
 * Act after deleting a queue.
 */
public function postDelete();
```

### Getting Started

Like all Ctools plugins when you want to implement your own custom plugin you first have to let ctools know where to look for your plugins.

Implement [hook_ctools_plugin_directory()](http://www.drupalcontrib.org/api/drupal/contributions%21ctools%21ctools.api.php/function/hook_ctools_plugin_directory/7) to let ctools know where to look.

An example from Entityqueue Scheduler.

```
/**
 * Implements hook_ctools_plugin_directory().
 */
function entityqueue_scheduler_ctools_plugin_directory($module, $plugin) {
  return 'plugins/' . $module . '/' . $plugin;
}
```

The common convention is to put plugins in a plugin folder and subfolders for the different modules and plugin types that your module implements. In this case `$module = 'entityqueue'` and `$plugin = 'handler'`, so ctools will look for `plugins/entityqueue/handler` within our module folder for any `.inc` files.

Inside our include file, we define our `$plugin` array.

```
// plugins/entityqueue/handler/scheduled.inc
$plugin = array(
  'title' => t('Scheduled queue'),
  'class' => 'EntityQueueSchedulerEntityQueueHandler',
  'weight' => -99,
);
```

Entityqueue handler plugins are pretty simple. Give it a title, tell it the name of the class that implements EntityQueueHandler and optionally give it a weight. There is also a `'queue type'` which can be set to `'single'` if the EntityQueueHandler only supports 1 Subqueue, otherwise it defaults to `'multiple'` which means the EntityQueueHandler supports multiple Subqueues.

### Overview of Entityqueue Scheduler

Entityqueue Scheduler allows users to schedule the items and order of the queue in the future. It does this using subqueues. Each subqueue has a date on which it will be "published".

### Defining EntityQueueSchedulerEntityQueueHandler

We create our class in the same directory as our plugin definition, the file must be named `ClassName.class.php`. For `EntityQueueSchedulerEntityQueueHandler` that is `EntityQueueSchedulerEntityQueueHandler.class.php`

First we extend `EntityQueueHandlerBase` which implements the `EntityQueueHandlerInterface`.

```
class EntityQueueSchedulerEntityQueueHandler extends EntityQueueHandlerBase {
```

#### Define our Subqueue Form

Since we want our Subqueues to have a date on which to be published, we need to override the Subqueue form. The `subqueueForm` method takes the `EntitySubqueue` and `$form_state` as parameters and returns the `$form` array. Keep in mind that the `EntitySubqueue` may not be saved yet, so may not have a `subqueue_id`.

```
/**
 * Overrides EntityQueueHandlerBase::subqueueForm().
 */
public function subqueueForm(EntitySubqueue $subqueue, &$form_state) {
  // Get the current timezone
  $timezone = date_default_timezone_object();
  // The subqueue has a schedule date, use that as the default
  if (isset($subqueue->scheduler_date)) {
    $date_object = new DateObject($subqueue->scheduler_date, 'UTC');
    $date_object->setTimezone($timezone);
    $default_date = date_format_date($date_object, 'custom', 'Y-m-d H:i');
  }
  // Otherwise set the default to an hour from now
  else {
    $now = date_now($timezone);
    // Add an hour.
    $now->add(new DateInterval('PT1H'));
    $default_date = date_format_date($now, 'custom', 'Y-m-d H:i');
  }
  $form = array();
  // Don't add the date field to the currently published subqueue.
  if (!$this->isLiveQueue($subqueue)) {
    $form['date'] = array(
      '#title' => t('Publish Date'),
      '#type' => 'date_select',
      '#default_value' => $default_date,
      '#date_format' => 'm d, Y H:iA',
      '#date_label_position' => 'above',
      '#date_increment' => 15,
      '#date_year_range' => '-0:+3',
      '#required' => TRUE,
      '#weight' => -50,
      '#disabled' => isset($subqueue->subqueue_id),
      '#description' => t('This is the date when this subqueue should be published. Once you set the date for the subqueue it cannot be changed. You will need to create a new subqueue for a new date.'),
    );
    $form['#validate'][] = 'entityqueue_scheduler_subqueue_form_validate';
  }
  return $form;
}
```

#### Define our Subqueue Label

Since our subqueues are only distiguishable by the date, we want to use that in the label and display it in a user-friendly way. To do that, we override the `getSubqueueLabel` method which takes a `EntitySubqueue` and returns a translated string.

```
/**
 * Overrides EntityQueueHandlerBase::getSubqueueLabel().
 */
public function getSubqueueLabel(EntitySubqueue $subqueue) {
  if (!empty($subqueue->scheduler_date)) {
    // Use site timezone for label.
    $timezone = date_default_timezone_object(FALSE);
    $date_object = new DateObject($subqueue->scheduler_date, 'UTC');
    $date_object->setTimezone($timezone);
    $label = t('@queue: @date queue', array(
      '@queue' => $this->queue->label,
      '@date' => date_format_date($date_object, 'short'),
    ));
  }
  else {
    $label = t('@queue: Live queue', array(
      '@queue' => $this->queue->label,
    ));
  }
  return $label;
}
```

#### Ensure there is always a Published Subqueue

Entityqueue Scheduler uses a special Subqueue that it calls the "Live queue". This is the currently published version of the queue. We want to make sure this Subqueue exists as soon as a new queue using our EntityQueueSchedulerEntityQueueHandler is created.

```
/**
 * Overrides EntityQueueHandlerBase::loadFromCode().
 */
public function loadFromCode() {
  $this->ensureSubqueue();
}
 
/**
 * Overrides EntityQueueHandlerBase::insert().
 */
public function insert() {
  $this->ensureSubqueue();
}
 
/**
 * Makes sure that every queue has a subqueue.
 */
protected function ensureSubqueue() {
  global $user;
  static $queues = array();
 
  if (!isset($queues[$this->queue->name])) {
    $queues[$this->queue->name] = TRUE;
 
    $transaction = db_transaction();
    $query = new EntityFieldQuery();
    $query
      ->entityCondition('entity_type', 'entityqueue_subqueue')
      ->entityCondition('bundle', $this->queue->name);
    $result = $query->execute();
 
    // If we don't have a subqueue already, create the live one.
    if (empty($result['entityqueue_subqueue'])) {
      $subqueue = entityqueue_subqueue_create();
      $subqueue->queue = $this->queue->name;
      $subqueue->name = $this->queue->name . '__live';
      $subqueue->label = $this->getSubqueueLabel($subqueue);
      $subqueue->module = 'entityqueue_scheduler';
      $subqueue->uid = $user->uid;
 
      entity_get_controller('entityqueue_subqueue')->save($subqueue, $transaction);
    }
  }
}
```

We define a helper method to create the Live subqueue if it doesn't exist and call it whenever a new queue is inserted and when a queue is loaded from a code export. We override the `insert` and `loadFromCode` methods.

#### Prevent Users from Deleting the Published Subqueue

We also want to make sure users cannot accidentally delete our Live subqueue. To do that override the `canDeleteSubqueue` method.

```
/**
 * Overrides EntityQueueHandlerBase::canDeleteSubqueue().
 */
public function canDeleteSubqueue(EntitySubqueue $subqueue) {
  if ($this->isLiveQueue($subqueue)) {
    return FALSE;
  }
  return TRUE;
}
```

### What Next?

That's it for our implementation of an EntityQueueHandler. Feel free to checkout the rest of the [code for Entityqueue Scheduler](https://www.drupal.org/project/entityqueue_scheduler/git-instructions). Also, you can help get it to a full release by [reviewing this patch to the Date module](https://www.drupal.org/node/2325207#comment-9329971).

If you need additional help understanding ctools plugins (who doesn't?), [Ctools has a Plugin Examples module](http://cgit.drupalcode.org/ctools/tree/ctools_plugin_example). I also found slides for a Ctools plugins presentation that gives a good overview.

[View full post](https://www.metaltoad.com/blog/drupal-tutorial-custom-entityqueue)

```json
{
  "@context" : "http://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Metal Toad Staff"
  },
  "dateModified" : "2025-05-07T23:35:11.073Z",
  "datePublished" : "2015-06-27T00:00:00Z",
  "headline" : "Drupal 7 Tutorial: Creating a Custom Entityqueue Handler",
  "image" : {
    "@type" : "ImageObject",
    "height" : 500,
    "url" : "https://6104926.fs1.hubspotusercontent-na1.net/hubfs/6104926/Imported_Blog_Media/drupal-logo-blog-header-Aug-11-2023-10-24-19-4041-PM.jpg",
    "width" : 870
  },
  "mainEntityOfPage" : "https://www.metaltoad.com/blog/drupal-tutorial-custom-entityqueue",
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "height" : 60.0,
      "url" : "https://6104926.fs1.hubspotusercontent-na1.net/hubfs/6104926/social-suggested-images/6104926.fs1.hubspotusercontent-na1.nethub6104926hubfsMetal_Toad_Logo_orange_horizontal-50-1.jpg",
      "width" : 240.00002
    },
    "name" : "Blog"
  }
}
```