现在需要通过http“可用”的Laravel命令

时间:2019-02-13 12:06:19

标签: php laravel laravel-controller

我有一个在Laravel中创建的网络应用程序,可以进行信用卡付款。

我每天创建的计划命令都会运行以接受“今天”的付款(基本上,它向付款网关的每个待处理付款都提交一个http请求)。

现在,我需要允许通过仪表板上的按钮触发此付款提交过程。

该命令需要花费很长时间进行随机处理(取决于要处理的付款数量),所以我认为这不是从控制器调用它的选择。

我正在考虑对其进行重构:将所有命令代码移至“中间人”类,这样我就可以在命令和控制器上都调用该类。

PaymentsSubmissionHelper::submit()

PaymentsSubmissionCommand: PaymentsSubmissionHelper::submit()
PaymentsSubmissionController: PaymentsSubmissionHelper::submit()

但是,该命令显示进度条和估计的处理时间,我也需要在html界面中显示进度条。在Web界面中,我需要向服务器发出Ajax请求以获取当前进度,但是在命令中,使用以下命令以完全不同的方式跟踪此进度:

$bar = $this->output->createProgressBar($totalPayments);
$bar->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %message%');

以及每次处理的付款:

$bar->advance();

如何创建跟踪命令和控制器进度的信息?

任何帮助将不胜感激。

谢谢!

3 个答案:

答案 0 :(得分:1)

我建议在此用例中使用queued event listeners。您将在控制器中调度一个事件,并有一个可以触发命令的侦听器。通过对侦听器进行排队,可以避免较长的响应时间。无需重构命令本身!

关于进度条,您可能有一个静态进度条,该进度条会在页面加载时更新,您可以从数据库中读取状态并以类似于亚马逊在任何时候的显示方式显示状态。

对于实时更新的进度条,我建议实现Web套接字。 Socket.io看起来很棒。

答案 1 :(得分:1)

正如另一个答案中已经指出的那样,Laravel's queued event listeners是处理前端长期运行的进程的方法。您完全不需要重构控制台命令。

关于在前端显示进度,一种简单的解决方案是设置一些AJAX轮询。 AJAX每隔几秒钟就会发出对控制器方法的请求,该方法仅查看当今的付款,计算要处理的数量(大概您有某种status字段,它将显示正在运行的作业是否已处理),并返回代表已完成百分比的数字。然后,AJAX success处理程序将更新页面上的进度跟踪器。

// Check status every 2s
var timer = setInterval(function() {
    pollStatus();
}, 2000);

pollStatus = function() {
    $.ajax({
        url: 'somewhere/jobStatus',
        success: function(resp) {
            $('#progress').html(resp . '%');

            if (resp === 100) {
                // We've reached 100%, no need to keep polling now
                clearInterval(timer);
            }
        }
    });
}

以某种方式确保民意调查不会超支,这也许是明智的选择,也许您想调整民意调查的频率。

答案 2 :(得分:0)

当您使用进度条并前进时,您将在ajax中执行相同的操作,但是进度逻辑将偏离路线。

这两种情况的共同点是处理每笔卡付款。因此,我会说创建采用卡付款实例的单独的类或服务,例如PaymentProcess,对其进行处理,如果成功或失败,则返回。

然后在命令中可以执行(伪代码):

公共函数handle() {     $ pendingPayments = Payment :: where('status','pending');

$bar = $this->output->createProgressBar($pendingPayments->count());

$pendingPayments->chunk(10, function($payments) use($bar){

    $payments->each(function($payment) use ($bar){

        $process = (new PaymentProcess($payment))->process();

        $bar->advance();

    });
});

$bar->finish();

}

现在,如果您从前端触发此操作,则ajax响应应为您提供存储在某处的当前进程的ID。然后,您将继续发送间隔为1秒的另一个ajx请求,并获取当前进度,直到达到100%。 (如果您使用的是XMLHttpRequest2,则逻辑会有所不同)

为此,您可以创建另一个表来存储进度,然后继续对其进行更新。

现在类似地,您可以在控制器内部使用PaymentProcess。 :

公共功能processPendingPayments(Request $ request) {     //授权请求     $ this-> authorize('processPendingPayments',Payment :: class);

$pendingPayments = Payment::where('status', 'pending');

// Create a progress entry
$progress = PaymentProgress::create([
    'reference' => str_random('6')
    'total' => $pendingPayments->count(),
    'completed' => 0
]);

$pendingPayments->chunk(10, function($payments) use($bar){

    $payments->each(function($payment) use ($bar){

        $process = (new PaymentProcess($payment))->process();

        // Update a progress entry
        $progress->update([
            'completed' => $progress->completed + 1;
        ]);

    });
});

return response()->json([
    'progress_reference' => $progress->reference
], 200);

}

现在另一个端点获取进度

公共函数getProgress(Request $ request) {     //授权请求     $ this-> authorize('getProgress',Payment :: class);

$request->validate([
    'reference' => 'required|exists:payment_process,reference'
]);

$progress = PaymentProcess::where('reference', $request->reference)->first();

$percentage = $progress->completed / $progress->total * 100;

 return response()->json(compact('percentage'), 200);

}