Drupal 8:通过 AJAX 请求运行批处理
这个问题来自我最近在做的一个项目。我不得不代表用户执行一堆API查找,这可能需要一分钟左右的时间才能完成。API查找的结果被缓存,因此一旦完成,站点就会非常快,不幸的是,最初的API查找显着减慢了页面加载速度,因此产生了问题。
我创建了一个批处理过程,以更易于管理的方式加载API结果,而不是仅仅在页面加载过程中进行API加载并让用户坐下来。这产生了另一个问题,尽管Drupal中的批处理运行器非常好,但仅仅向用户展示并期望他们了解正在发生的事情可能有点太多了。这让我想到是否可以通过AJAX回调从他们试图加载的页面运行批处理。
不得不说,这个问题的解决方法我找了好久。事实证明,以前没有人解决过这个问题(我可以看到)。
第一步是创建一个库来控制批处理的AJAX回调。
loader: js: js/loader.js: {} dependencies: - core/jquery - core/drupalSettings
连同名为loader.js的相关JavaScript文件。我不知道我需要在这里填写什么,所以只是一个存根,所以我首先使用最小的AJAX回调来创建它,该回调会触发批处理过程。
(function ($, Drupal) { 'use strict'; Drupal.behaviors.account = { attach: function attach(context, settings) { $.ajax({ url: Drupal.url('loading/ajax'), type: 'POST', contentType: 'application/json; charset=utf-8', dataType: 'json', success: function success(value) { //做东西... } }); } }; })(jQuery, Drupal);
这是通过将其附加到页面的输出而加载到页面上的。在这种情况下,我只是使用静态插页式页面来运行AJAX请求。
public function myLoading() { return [ '#theme' => 'my_loading', '#attached' => [ 'library' => [ 'my_module/loader', ], ], ]; }
这是一个标准的Drupal控制器,所以它没有做任何特别的事情。这可能在一个块或其他东西中,但我们希望将用户带到“加载”页面,同时我们从API获取内容,然后将它们发送回他们试图访问的页面。
最后一步是设置AJAX端点,以便它可以触发批处理。
public function ajaxBatchProcess() { //设置批处理。 $this->batchService->setupLengthyBatchProcess(); //获取我们刚刚创建的批次。 $batch =& batch_get(); //确保完成的响应不会产生任何消息。 $batch['sets'][0]['finished'] = NULL; //创建batch_process(),并为它提供一个它将要去的URL。 $url = Url::fromRoute('user.page'); $response = batch_process($url); //将响应返回到ajax输出。 $ajaxResponse = new AjaxResponse(); return $ajaxResponse->addCommand(new BaseCommand('batchcustomer', $response->getTargetUrl())); }
我错过了这里的一些复杂性,但基本上我已经将批量创建包装在一个服务中。该服务本质上是一个设置和管理批处理运行的类。该函数setupLengthyBatchProcess()本质上只是包装了batch_set($batch);调用并且可以在提交处理程序或类似的东西中使用。批处理完成功能还报告它刚刚完成的内容,因此将其删除以防止向用户显示这些消息。
经过一番调查,我发现该batch_process()函数的结果是返回批处理运行器的路径(例如batch?id=12345&op=start)。我不想向用户显示这个,所以我不能只向AJAX请求返回重定向响应。
解决此问题的一种方法是在批处理设置中设置“渐进式”选项。这实际上是一次处理整个批次,这并不是我真正想做的。我们失去的(除了能够以小块处理正在处理的数据量之外)是能够告诉用户他们必须等待多长时间,因为我们无法报告我们在批处理过程中走了多远。一个简单的进度指示器非常有用,即使用户只需要等待10秒钟。
如果您确实想沿着这条渐进式路线走下去,那么只需更改上面代码中的几个文件,到目前为止我所展示的一切都将起作用。请注意,我不再向batch_process()函数发送URL,因为我们不会进行任何重定向。您还需要确保当AJAX请求完成时,它会正确重定向,因为它目前什么都不做。
$batch =& batch_get(); $batch['progressive'] = TRUE; $response = batch_process();
我最终做的是对Drupal8的批处理运行器进行逆向工程。这使用了一个名为ProgressBar的插件,该插件基本上监视端点并报告进度。该updateCallback()函数用于更新用户的进度或执行重定向。这有效地以与Drupal通常运行它的方式相同的方式运行批处理。
(function ($, Drupal) { 'use strict'; Drupal.behaviors.account = { attach: function attach(context, settings) { var progressBar = void 0; function updateCallback(progress, status, pb) { $('#updateprogress').html(progress + '%'); if (progress === '100') { pb.stopMonitoring(); window.location= '/user'; } } function errorCallback(pb) { } $.ajax({ url: Drupal.url('account/loading/ajax'), type: 'POST', contentType: 'application/json; charset=utf-8', dataType: 'json', success: function success(value) { progressBar = new Drupal.ProgressBar('updateprogress', updateCallback, 'POST', errorCallback); progressBar.startMonitoring(value[0].data + '&op=do', 10); } }); } }; })(jQuery, Drupal);
最终,这非常有效。当用户登录时(我们检测到他们还没有缓存),他们被发送到这个中间页面,批处理在后台运行,同时他们等待API调用完成。该过程会报告它已经完成了多远,一旦完成,用户就会被发送回他们的帐户页面。