Using a Drupal Cron job instead of an update hook
This article is a follow-up from my previous post
I’ve recently tried using cron jobs to run small tasks such as creating terms to a brand-new vocabulary that is not yet installed in the destination site during the deployment process as drush updb
is executed prior drush config-import
.
What we can do is commit the new vocabulary configurations into the codebase and add a hook_cron()
with some logic to create terms programmatically into that vocabulary.
Code
Create a new custom module.
app/modules/custom/my_module/my_module.module
In the hook_cron() we need to use a Drupal state storage service.
This is because we DO NOT want the same term creation method to be executed every time a cron is triggered.
Below comment is from app/core/lib/Drupal::state()
* Use this to store machine-generated data, local to a specific environment
* that does not need deploying and does not need human editing; for example,
* the last time cron was run. Data which needs to be edited by humans and
* needs to be the same across development, production, etc. environments
* (for example, the system maintenance message) should use \Drupal::config() instead.
Since we won’t be finding any need for the state variables to be changed manually, let’s use a new variable named my_module.test_vocabulary.status
/**
* Implements hook_cron().
*
* Add methods here to be executed via cron.
*/
function my_module_cron() {
// Execute if my_module.test_vocabulary.status is TRUE.
if (!\Drupal::state()->get('my_module.test_vocabulary.status')) {
_my_module_create_test_vocabulary_terms();
}
}
In the first run the my_module.test_vocabulary.status
is FALSE
therefore the _my_module_create_test_vocabulary_terms()
method will be executed. And in there we will set the my_module.test_vocabulary.status
to TRUE
/**
* Create 'Test vocabulary' terms.
*/
function _my_module_create_test_vocabulary_terms() {
// TERM CREATION LOGIC GOES HERE
// Set state variable so that cron will not execute this method again.
\Drupal::state()->set('my_module.test_vocabulary.status', TRUE);
\Drupal::messenger()->addMessage('Test vocabulary terms created.');
}
Now we can manually trigger the cron job from admin/config/system/cron
Important
According to app/core/core.api::hook_cron()
make sure no long-running task is added as cron task.
* Modules that require some commands to be executed periodically can
* implement hook_cron(). The engine will then call the hook whenever a cron
* run happens, as defined by the administrator. Typical tasks managed by
* hook_cron() are database maintenance, backups, recalculation of settings
* or parameters, automated mailing, and retrieving remote data.
*
* Short-running or non-resource-intensive tasks can be executed directly in
* the hook_cron() implementation.
*
* Long-running tasks and tasks that could time out, such as retrieving remote
* data, sending email, and intensive file tasks, should use the queue API
* instead of executing the tasks directly. To do this, first define one or
* more queues via a \Drupal\Core\Annotation\QueueWorker plugin. Then, add items
* that need to be processed to the defined queues.
Alternative solution
I will write a blog post about this solution, but for the moment please find below as a reference.
https://www.drush.org/latest/deploycommand/
Thanks Lee for this suggestion.
Resources
https://www.thirdandgrove.com/insights/best-practices-for-using-drupals-cron-system-hook_cron/
https://api.drupal.org/api/drupal/core%21core.api.php/function/hook_cron/10.0.x