Overriding Queues in Drupal 7. Or How Not to Create Duplicate Queue Items
Have you ever needed to override a Queue in Drupal 7? For example, the reliable database SystemQueue? It's actually quite easy and very flexible as well. Let me first tell you the use case we are going to work with in this article.
Let's say that we want to make sure that when adding items to a queue, those items don't already exist in the queue table. That's actually a fair request in my opinion. An option would be to make a query and see if the item already exists before delegating its creation to the responsible Queue class. However, we'd have to deal with querying for serialised data and is just not performant.
A better way is to use our own DrupalQueueInterface
implementation:
/**
* Custom implementation of the DrupalQueueInterface
*/
class MyCustomQueue extends SystemQueue {
/**
* Overriding the method to make sure no duplicate queue items get created
* but that the items are updated if the exist.
*/
public function createItem($data) {
$serialized = serialize($data);
$query = db_merge('queue')
->key(array('name' => $this->name, 'data' => $serialized))
->fields(array(
'name' => $this->name,
'data' => $serialized,
'created' => time(),
));
return (bool) $query->execute();
}
}
As you can see, we are extending from the good ol' SystemQueue
but overriding it's createItem()
method. Instead of the db_insert()
statement with which items were persisted, we are using a db_merge()
statement to UPSERT the items. This means that if the items already exit, they get updated. If not, they get created. Which is exactly what we want.
Lastly, we need to make sure this class is being used for our queue. Again the solution is simple. Let's say our queue name is my_custom_queue
. When requesting it like so:
DrupalQueue::get('my_custom_queue');
... Drupal looks in the variables table for a variable called queue_class_my_custom_queue
. If it finds it, it will try to instantiate a class that has the name specified as the variable value (if it also implements DrupalQueueInterface
). If there is no such variable, it falls back to the default one which is SystemQueue
.
So this means that our module needs to create that variable so that when we are requesting this particular queue, we get an instance of our own class. We can do this inside install and uninstall hooks:
/**
* Implements hook_install().
*/
function my_module_install() {
variable_set('queue_class_my_custom_queue', 'MyCustomQueue');
}
/**
* Implements hook_uninstall().
*/
function my_module_uninstall() {
variable_del('queue_class_my_custom_queue');
}
And that is pretty much it. Now our queue will use the custom class we wrote and we can make sure no items are duplicated.
Hope this helps.