maatwebsite/excel使用:导出——队列

队列#

如果您正在处理大量数据,则整个过程排队可能是明智的选择。

假设我们有以下导出类:

namespace App\Exports;

use App\Invoice;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;

class InvoicesExport implements FromQuery
{
    use Exportable;

    public function query()
    {
        return Invoice::query();
    }
}

现在只需调用 ->queue() 方法即可。

(new InvoicesExport)->queue('invoices.xlsx');
return back()->withSuccess('Export started!');

在幕后,查询将被分块,并将链接多个作业。这些作业将按正确的顺序执行,仅在之前的作业没有失败的情况下才会执行。

隐式导出队列#

还可以将导出隐式标记为队列导出。您可以使用 LaravelShouldQueue 合约 来实现此目的。

namespace App\Exports;

use App\Invoice;
use Illuminate\Contracts\Queue\ShouldQueue;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;

class InvoicesExport implements FromQuery, ShouldQueue
{
    use Exportable;

    public function query()
    {
        return Invoice::query();
    }
}

现在在控制器中,你可以调用正常的 ->store() 方法。基于 ShouldQueue 合约的存在,导出将被排队。

(new InvoicesExport)->store('invoices.xlsx');

添加队列#

queue() 方法返回 Laravel 的 PendingDispatch 实例。这意味着您可以链接额外的作业,这些作业将被添加到队列的末尾,并且仅在所有导出作业正确执行时才会执行。

(new InvoicesExport)->queue('invoices.xlsx')->chain([
    new NotifyUserOfCompletedExport(request()->user()),
]);
namespace App\Jobs;

use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\SerializesModels;

class NotifyUserOfCompletedExport implements ShouldQueue
{
    use Queueable, SerializesModels;

    public $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function handle()
    {
        $this->user->notify(new ExportReady());
    }
}

处理队列导出中的失败#

在排队导出时,你可能希望有一种处理失败导出的方法。你可以通过向导出类添加 failed 方法来实现此目的。

use Throwable;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;

class UsersExport implements FromQuery, WithHeadings
{
    public function failed(Throwable $exception): void
    {
        // handle failed export
    }
}

自定义队列#

由于返回了 PendingDispatch,我们也可以更改应该使用的队列。

Laravel 8+:

(new InvoicesExport)->queue('invoices.xlsx')->onQueue('exports');

旧版本的 Laravel:

(new InvoicesExport)->queue('invoices.xlsx')->allOnQueue('exports');

多服务器设置#

如果你正在处理多服务器设置(例如使用负载均衡器),你可能希望确保用于存储每个数据块的临时文件对于每个作业都是相同的。你可以通过在配置中配置远程临时文件来实现这一点。

config/excel.php 中:

'temporary_files' => [
    'remote_disk' => 's3',
],

作业中间件#

如果您正在使用 Laravel 6,可以使用 middleware 方法将作业中间件作业中间件附加到导出类:

namespace App\Exports;

use App\Jobs\Middleware\RateLimited;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;

class ExportClass implements FromQuery
{
    use Exportable;

    public function middleware()
    {
        return [new RateLimited];
    }

    public function query()
    {
        // ...
    }
}

本地化导出队列#

如果要本地化导出队列,则应在导出类上实现 HasLocalePreference 契约:

namespace App\Exports;

use Illuminate\Contracts\Translation\HasLocalePreference;
use Maatwebsite\Excel\Concerns\Exportable;

class ExportClass implements HasLocalePreference
{
    use Exportable;

    public function __construct(string $locale)
    {
        $this->locale = $locale;
    }

    public function preferredLocale()
    {
        return $this->locale;
    }
}

自定义查询大小#

队列的可导出内容将被分块处理;每个块都是由 QueuedWriter 推送到队列的作业,对于实现 FromQuery 作业的数量通过将通过将 $query->count() 除以块大小来计算的。

何时使用#

根据 query() 方法的实现(例如,使用 groupBy 子句时),前面提到的计算可能不正确。

如果是这种情况你应该使用 WithCustomQuerySize 来提供查询大小的自定义计算。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。