Запуск пакетных операций из обновления узла подключения()
При импорте контента с использованием каналов с использованием импортера, связанного с типом узла, сопоставление может быть настроено для назначения терминов таксономии из узла канала импортированным элементам канала. В проекте нам нужно расширить эту функцию, также обновив все уже импортированные элементы ленты при обновлении узла ленты.
Это можно сделать с помощью hook_node_update($node)
. Но так как один Узел подачи может иметь много элементов ленты , все они не могут быть обновлены в одном HTTP-запросе, иначе мы можем столкнуться с таймаутом. Очевидный способ избежать тайм-аутов - использовать API пакетных операций .
Большую часть времени реализации hook_node_update()
будут выполняться, потому что node_save
был вызван из обработчика отправки формы, поэтому в большинстве случаев нет необходимости вызывать batch_process из hook_node_update()
, поскольку он будет вызван автоматически в конце обработки формы. Вызов batch_progress
из hook_node_update()
также может привести к неожиданному результату, поскольку это предотвратит выполнение другой реализации того же крючка или других крючков.
Но как насчет случаев, когда реализации hook_node_update()
не выполняются из обработчика отправки формы. Например, когда node_save
вызывается из реализации hook_cron
? Есть ли способ обнаружить эти случаи, чтобы либо запустить все обновления сразу, либо отложить их в выделенную очередь?
2 answers
Как указано в ответе киамлалуно, информация о контексте выполнения не передается в реализации hook_node_update()
. Таким образом, мы закончили тем, что использовали debug_backtrace()
для поиска текущей обработанной формы (если таковая имеется). Если форма обработана, drupal_process_form()
будет обрабатывать пакетную обработку для нас. Если нет, мы не сможем безопасно начать постепенную обработку пакета. Но мы можем запустить всю партию за один проход. Это может привести к тайм-ауту и действительно должно быть заменено. Либо путем задержки выполнения операций ( в очереди операции) или путем запуска последовательной обработки пакета после выполнения всех операций обновления (но как?).
function _MODULE_get_currently_processed_form() {
$backtrace = debug_backtrace();
while ($frame = next($backtrace)) {
if (isset($frame['function']) && $frame['function'] == 'drupal_process_form') {
return array(
'form_id' => $frame['args'][0],
'form' => &$frame['args'][1],
'form_state' => &$frame['args'][2],
);
}
}
return FALSE;
}
function MODULE_node_update($node) {
if (some_condition_on($node)) {
$batch = array(
'operations' => array(
// ...
),
);
batch_set($batch);
$currently_processed_form = _MODULE_get_currently_processed_form();
if (!$currently_processed_form) {
// We are not currently processing a form, the batch processing won't be
// automatically started. But we cannot start processing it because it
// will end the request and prevent any other post node update code to
// run. So we process the batch in a single pass and hope for the best.
// FIXME: Start progressive processing of the batch after node update
// processing (using hook_exit?) or use other delayed execution (using
// queued operation?)
$batch =& batch_get();
$batch['progressive'] = FALSE;
batch_process();
}
}
}
Короче говоря, невозможно узнать, какая функция вызвала реализацию крючка; крючок не получает аргумент, который, например, равен TRUE
, когда крючок вызывается из обработчика отправки формы и FALSE
, когда вызов крючка вызван из другой реализации крючка.
Как правило, лучше запускать пакетные операции из обратного вызова меню, а не из функции, которая вызывается, например, во время задач cron, когда Drupal не выводит страницу в браузер. В на самом деле, чтобы избежать тайм-аутов PHP, Drupal заставляет браузер вызывать обратный вызов пакетного меню каждые X секунд; если в нем не задействован браузер (как это происходит с задачами cron, выполняемыми из утилиты cron), то пакетные операции не работают или работают как обычный вызов функции, который может вызвать тайм-ауты PHP.
Это исключает возможность использования пакетной операции в вашем случае.
Если вам действительно нужно использовать пакетные операции, то вы должны делать то, что делает основной код Drupal. Когда модуль необходимо использовать пакетные операции, он сообщает, что в реализации hook_requirements(), которая предоставляет пользователю ссылку на страницу, с которой начинаются пакетные операции. В качестве альтернативы аналогичное предупреждающее сообщение может быть размещено на странице административных настроек; также в этом случае пользователь начнет пакетные операции, нажав на ссылку, указанную в сообщении.
То, что я сообщал ранее о пакетных операциях, все еще актуально: пакетные операции зависят от браузера. Если есть проблемы с подключением к серверу или сбой браузера, пакетные операции будут прерваны; это также происходит, если компьютер, на котором запущен браузер, зависает или выключается/сбрасывается по какой-либо причине.
Пакетные операции следует использовать для относительно длительных операций, но не для операций, которые длятся 20-30 и более минут.