Drupal 7 Tutorial: Using Entityqueue with Views
Last time I gave a quick introduction on how to create and manage queues using Entityqueue.
Entityqueue uses Ctools plugins for what we call an EntityQueueHandler. In this post we are going to see how to create a custom EntityQueueHandler.
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 as an example.
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. 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.
/** * 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);
/** * Returns the label of a given subqueue. */ public function getSubqueueLabel(EntitySubqueue $subqueue);
/** * Returns TRUE if subqueue can be deleted, otherwise returns FALSE. */ public function canDeleteSubqueue(EntitySubqueue $subqueue);
/** * 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();
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() 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.
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".
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 {
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; }
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; }
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.
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; }
That's it for our implementation of an EntityQueueHandler. Feel free to checkout the rest of the code for Entityqueue Scheduler. Also, you can help get it to a full release by reviewing this patch to the Date module.
If you need additional help understanding ctools plugins (who doesn't?), Ctools has a Plugin Examples module. I also found slides for a Ctools plugins presentation that gives a good overview.
Last time I gave a quick introduction on how to create and manage queues using Entityqueue.
Now that Entityqueue has been released, I wanted to give a quick tutorial on how to use it.
This post was inspired by a question our VP Tony asked me while I was working on Entityqueue, he asked, "how is it different from Draggable Views?"...
Be the first to know about new B2B SaaS Marketing insights to build or refine your marketing function with the tools and knowledge of today’s industry.