Author image
Senior Developer

Drupal Job Scheduler : Easily scheduling periodic tasks

Sometimes as a module developer you need to have some code execute periodically, like maybe every day or even once a week. This might be to optimise an external system, or pull in some external data or to compute some statistics every day etc. Drupal provides a very simple way of doing this: hook_cron but if you want your code to only execute say once a day or after 6pm only, then you have to add extra logic to your implementation. However, the Job Scheduler module provides a nice framework for setting up and running these periodic functions.

To use the Drupal Job Scheduler module all you need to do is install the module from the project page and then implement a new hook in your module code, and then set up your periodic job.

In this example, I'll be developing a module called: 'weekly_task', that wants to do run a certain bit of code once a week, every week, and ideally present a user interface for changing the day of the week that the code runs on.

I'll start by assuming that my weekly_task.module looks a bit like this:

/**
* Compute statistics and record, this can take a long time!
*
* This function should only be called once a week.
*/
function weekly_task_compute_statistics() {
  // Complicated code goes here
}

So I've got a module that needs to have this function executed once a week, first I declare this as a Job Scheduler task by implementing a hook:

/**
* Implements hook_cron_job_scheduler_info().
*/
function weekly_task_cron_job_scheduler_info() {
  $info = array();
  $info['weekly_task_statistics'] = array(
    'worker callback' => 'weekly_task_compute_statistics',
  );
  return $info;
}

I just return an array declaring each task with a globally unique name, and a worker callback that is the name of a function that will get called when the periodic job runs.

Next, I want to schedule this task when my module is enabled, so in my weekly_task.install file I implement a hook like this:

/**
* Implements hook_install().
*/
function weekly_task_install() {
  // Create a default scheduled job.
  $job = array(
    'type' => 'weekly_stats',
    'period' => 86400 * 7,
    'crontab' => '0 0 * * 0',
    'periodic' => TRUE,
  );
  JobScheduler::get('weekly_task_statistics')->set($job);
}

So here, I'm creating a new job definition, type can be used to identify different jobs that all use the same worker callback, but for this example, it can be anything really, crontab is the important thing here, it's saying that this job should be scheduled to occur once a week, after midnight on Sunday.

We can easily make the day of the week that this task runs configurable by adding a little admin UI to our weekly_task.module file:

/**
* Implements hook_menu().
*/
function weekly_task_menu() {
  $items = array();

  $items['admin/config/system/weekly-task'] = array(
    'title' => 'Weekly task',
    'description' => 'Change options relating to the weekly task',
    'access arguments' => array('administer site configuration'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array('weekly_task_admin_form'),
    'type' => MENU_NORMAL_ITEM,
  );

  return $items;
}

/**
* Administration form for the weekly task.
*/
function weekly_task_admin_form($form, &$form_state) {
  $form['weekly_task_day'] = array(
    '#type' => 'select',
    '#title' => t('Day of the week'),
    '#description' => t('Choose the day of the week that the compute statistics function will run on.'),
    '#options' => array(0 => t('Sunday'), 1 => t('Monday'), 2 => t('Tuesday'), 3 => t('Wednesday'), 4 => t('Thursday'), 5 => t('Friday'), 6 => t('Saturday')),
    '#default_value' => variable_get('weekly_task_day', '0'),
  );

  $form = system_settings_form($form);

  $form['#submit'][] = 'weekly_task_admin_form_submit';

  return $form;
}

/**
* Submit callback for Administration form.
*/
function weekly_task_admin_form_submit(&$form, &$form_state) {
  // The variables will have already been set by the time this callback runs.

  // Update the scheduled job.
  $job = array(
    'type' => 'weekly_stats',
    'period' => 86400 * 7,
    'crontab' => '0 0 * * ' . variable_get('weekly_task_day', 0),
    'periodic' => TRUE,
  );
  JobScheduler::get('weekly_task_statistics')->set($job);
}

This adds a simple menu item and admin form, which when submitted, gets the Job Scheduler job and sets the new schedule. Job Scheduler will make sure that there's only one of these tasks in the system, so we don't end up running multiple tasks per week.

Drupal's cron still needs to be run for all this to work, Job Scheduler just handles working out when it should call your worker callback, hopefully that means you can focus on your task, and not on writing code to make it run at the correct time.

Comments

Awesome, Exactly what I was looking for.

I have a small question though, Do we really need to set up the job in a hook_install()?

Thanks for the effort.

Well, until you register the job with job scheduler, it'll never run, if you only want it to start working when people submit the configuration form, then exclude the hook_install portion.

Comments on this article are now closed, if you want to give us feeback you can use our contact form instead.