Enqueuing items programmaticallyfor Drupal 11 , 10 , 9 , and 8

Last updated :  

Last lesson introduced the queue as a ticket rail — a place to drop work for later. This lesson zooms in on the "dropping work" half specifically: the realistic patterns you'll actually use for adding items programmatically, beyond a single hardcoded string.

What you'll learn in this lesson

  • Adding items from a loop (bulk enqueuing)
  • Adding items conditionally, based on business logic
  • Checking queue size before and after, and why that matters operationally
  • The real form-submit pattern from the Examples module's queue_example

The real submit handler

Here's the actual "add to queue" submit handler from queue_example's form:

public function submitAddQueueItem(array &$form, FormStateInterface $form_state) {
  $queue = $this->queueFactory->get($form_state->getValue('queue_name'));
  // There is no harm in trying to recreate existing.
  $queue->createQueue();

  // Queue the string.
  $queue->createItem($form_state->getValue('string_to_add'));
  $count = $queue->numberOfItems();

  $this->messenger()->addMessage(
    $this->t(
      'Queued your string (@string_to_add). There are now @count items in the queue.',
      ['@count' => $count, '@string_to_add' => $form_state->getValue('string_to_add')]
    )
  );
  $form_state->setRebuild();
}

Notice it immediately calls numberOfItems() right after adding — that's not decoration, it's a genuinely useful pattern: reporting the queue's new size back to whoever triggered the add gives them confidence the item actually landed, without needing to inspect the database directly.

Bulk enqueuing from a loop

$queue = $this->queueFactory->get('my_module_notifications');
$queue->createQueue();

foreach ($user_ids as $uid) {
  $queue->createItem([
    'uid' => $uid,
    'message' => $message,
  ]);
}

This is the realistic version of what a bulk operation looks like: get the queue and create it once, outside the loop, then call createItem() once per unit of work inside it. Each item is a small associative array — enough data for a worker to redo the notification later, nothing more.

Adding conditionally

if ($node->isPublished() && !$node->get('field_already_notified')->value) {
  $queue->createItem(['nid' => $node->id()]);
}

There's nothing special about conditional enqueuing — it's just an ordinary if around a createItem() call. The important habit is deciding eligibility at enqueue time using data you already have, rather than queuing everything and making the worker re-check the same condition later, which just wastes a queue slot on work that was never going to happen.

Watch queue growth in production: if items are added faster than cron can process them, the queue grows unbounded. numberOfItems() is cheap to call and worth exposing on an admin status page or logging periodically — a queue that keeps climbing, never draining, is an early warning sign worth catching before it becomes a real backlog.

Quick check: you're enqueuing 50,000 items in a single batch operation. Should you call $queue->createQueue() once before the loop, or once per item inside it? If you said once before — you've got it. It's idempotent and cheap, but there's still no reason to call it 50,000 times when once guarantees the same result.

Key takeaways

  • Call createQueue() once before a loop of createItem() calls, not inside the loop.
  • Queue small, self-contained arrays or scalars — enough data to redo the work, not live objects.
  • Decide eligibility (which items are actually worth queuing) at enqueue time, not inside the worker.
  • numberOfItems() is a cheap, useful health signal — watch it for unbounded growth in production.

Coming up next

Items are piling up on the queue — now you need something to actually take them off and do the work. Next: writing a real QueueWorker plugin, the piece that processes each item.