[譯] Laravel-Excel 3.0 文檔

Basics

最簡單的導出方法是建立一個自定義的導出類, 這裏咱們使用發票導出做爲示例. php

App/Exports 下建立一個 InvoicesExportlaravel

namespace App\Exports;

use Maatwebsite\Excel\Concerns\FromCollection;

class InvoicesExport implements FromCollection
{
    public function collection()
    {
        return Invoice::all();
    }
}

在控制器中你能夠使用以下方式來下載web

public function export() 
{
    return Excel::download(new InvoicesExport, 'invoices.xlsx');
}

或者存儲在 s3 磁盤中數組

public function storeExcel() 
{
    return Excel::store(new InvoicesExport, 'invoices.xlsx', 's3');
}

依賴注入

若是你的導出須要依賴, 你能夠注入導出類閉包

namespace App\Exports;

use Maatwebsite\Excel\Concerns\FromCollection;

class InvoicesExport implements FromCollection
{
    public function __construct(InvoicesRepository $invoices)
    {
        $this->invoices = $invoices;
    }

    public function collection()
    {
        return $this->invoices->all();
    }
}
public function export(Excel $excel, InvoicesExport $export) 
{
    return $excel->download($export, 'invoices.xlsx');
}

嚴格的 null 對比

若是你但願 0 在 excel 單元格中就是顯示 0, 而不是顯示 null(空單元格), 你能夠使用 WithStrictNullComparisonapp

namespace App\Exports;

use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithStrictNullComparison;

class InvoicesExport implements FromCollection, WithStrictNullComparison
{
    public function __construct(InvoicesRepository $invoices)
    {
        $this->invoices = $invoices;
    }

    public function collection()
    {
        return $this->invoices->all();
    }
}

Collection 全局定義/宏

這個包提供了 laravel collection 的一些額外的方法(宏) 來簡單的下載或者是存儲到 excel性能

把 collection 做爲 Excel 下載

(new Collection([[1, 2, 3], [1, 2, 3]]))->downloadExcel(
    $filePath,
    $writerType = null,
    $headings = false
)

在磁盤上存儲 collection

(new Collection([[1, 2, 3], [1, 2, 3]]))->storeExcel(
    $filePath,
    $disk = null,
    $writerType = null,
    $headings = false
)

在磁盤上存儲導出

導出能夠存儲到任何 Laravel 支持的 文件系統 中測試

public function storeExcel() 
{
    // Store on default disk
    Excel::store(new InvoicesExport(2018), 'invoices.xlsx');

    // Store on a different disk (e.g. s3)
    Excel::store(new InvoicesExport(2018), 'invoices.xlsx', 's3');

    // Store on a different disk with a defined writer type. 
    Excel::store(new InvoicesExport(2018), 'invoices.xlsx', 's3', Excel::XLSX);
}

Exportables / 可導出的

在以前的例子中, 咱們使用 Excel::download 這個 facade 來開始一個導出. 大數據

Laravel-Excel 一樣支持  Maatwebsite\Excel\Concerns\Exportable trait, 來讓一個類能夠直接導出, 固然, 這個類裏邊須要有 collection 方法.ui

namespace App\Exports;

use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\Exportable;

class InvoicesExport implements FromCollection
{
    use Exportable;

    public function collection()
    {
        return Invoice::all();
    }
}

咱們能夠不經過 facade 直接進行類的下載

return (new InvoicesExport)->download('invoices.xlsx');

或者是存儲到磁盤上.

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

Responsable / 可響應的

以前的例子能夠作的簡單一點, 例如咱們添加 Laravel 的 Responsable 到導出類中

namespace App\Exports;

use Illuminate\Contracts\Support\Responsable;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\Exportable;

class InvoicesExport implements FromCollection, Responsable
{
    use Exportable;

    /**
    * It's required to define the fileName within
    * the export class when making use of Responsable.
    */
    private $fileName = 'invoices.xlsx';

    public function collection()
    {
        return Invoice::all();
    }
}

你能夠更簡單的返回導出類,可是不須要調用 ->download() 方法.

return new InvoicesExport();

From Query / 從查詢輸出

在以前的例子中, 咱們在導出類中進行查詢, 固然這個解決方案能夠用在小的導出類中. 對於更大一點數據的導出類可能形成比較大的性能開銷.

經過使用 FromQuery 關係, 咱們能夠經過預查詢一個導出, 這個場景實現的原理是查詢能夠分塊執行.

InvoicesExport 類中,添加 FromQuery 關係, 而且添加一個查詢, 而且確保不要使用 ->get() 來獲取到數據!.

namespace App\Exports;

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

class InvoicesExport implements FromQuery
{
    use Exportable;

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

咱們能夠經過一樣的方式來下載

return (new InvoicesExport)->download('invoices.xlsx');

自定義查詢

這種方式能夠經過自定義的參數來進行查詢. 簡單的做爲依賴項傳入導出類便可.

做爲構造器參數

namespace App\Exports;

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

class InvoicesExport implements FromQuery
{
    use Exportable;

    public function __construct(int $year)
    {
        $this->year = $year;
    }

    public function query()
    {
        return Invoice::query()->whereYear('created_at', $this->year);
    }
}

$year 參數能夠傳遞給導出類.

return (new InvoicesExport(2018))->download('invoices.xlsx');

做爲設置項

namespace App\Exports;

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

class InvoicesExport implements FromQuery
{
    use Exportable;

    public function forYear(int $year)
    {
        $this->year = $year;

        return $this;
    }

    public function query()
    {
        return Invoice::query()->whereYear('created_at', $this->year);
    }
}

咱們能夠經過 forYear 方法來調全年份.

return (new InvoicesExport)->forYear(2018)->download('invoices.xlsx');

經過視圖

咱們能夠經過 blade 視圖來建立導出. 經過使用 FromView 關係.

namespace App\Exports;

use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;

class InvoicesExport implements FromView
{
    public function view(): View
    {
        return view('exports.invoices', [
            'invoices' => Invoice::all()
        ]);
    }
}

這種方式會導出一個 Html 表格到 Excel 單元表, 例如 users.blade.php:

<table>
    <thead>
    <tr>
        <th>Name</th>
        <th>Email</th>
    </tr>
    </thead>
    <tbody>
    @foreach($users as $user)
        <tr>
            <td>{{ $user->name }}</td>
            <td>{{ $user->email }}</td>
        </tr>
    @endforeach
    </tbody>
</table>

隊列

若是你處理更大數據量的數據, 很明智的方法就是使用隊列來運行.

例以下邊的導出類:

namespace App\Exports;

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

class InvoicesExport implements FromQuery
{
    use Exportable;

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

咱們只須要調用一個 ->queue() 方法便可.

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

後臺處理這些查詢的方式是經過多任務/多切割的方式來進行. 這些任務使用正確的順序來執行. 而且保證以前的查詢都是正確的.

另外一種方式的隊列實現

你能夠將導出做爲一個能夠扔到隊列中的實現(利用 Laravel), 能夠使用 ShouldQueue 約束.

namespace App\Exports;

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

class InvoicesExport implements FromQuery, ShouldQueue
{
    use Exportable;

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

在控制器中能夠調用普通的 ->store() 方法. 基於 ShouldQueue, 經過 laravel 的隊列方式來實現隊列處理. [ps:你須要首先自行配置隊列]

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

追加任務 / jobs

 queue() 方法返回一個 Laravel 的  PendingDispatch 實例, 這意味着你能夠把其餘的任務串聯起來, 僅僅當前一個任務執行成功的時候, 後續的任務纔可以被執行.

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

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;

class InvoiceExportCompletedJob implements ShouldQueue
{
    use Queueable;

    public function handle()
    {
        // Do something.
    }
}

自定義隊列

PendingDispatch 返回的時候, 咱們能夠改變咱們使用的隊列.

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

多單元表

爲了可以讓導出支持多單元表, 須要使用 WithMultipleSheets 關係來實現. 這個 sheets() 方法須要返回一個單元表數組.

namespace App\Exports;

use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;

class InvoicesExport implements WithMultipleSheets
{
    use Exportable;

    protected $year;

    public function __construct(int $year)
    {
        $this->year = $year;
    }

    /**
     * @return array
     */
    public function sheets(): array
    {
        $sheets = [];

        for ($month = 1; $month <= 12; $month++) {
            $sheets[] = new InvoicesPerMonthSheet($this->year, $month);
        }

        return $sheets;
    }
}

這個 InvoicesPerMonthSheet 能夠實現多種關係. 例如 FromQueryFromCollection, ...

namespace App\Exports;

use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithTitle;

class InvoicesPerMonthSheet implements FromQuery, WithTitle
{
    private $month;
    private $year;

    public function __construct(int $year, int $month)
    {
        $this->month = $month;
        $this->year  = $year;
    }

    /**
     * @return Builder
     */
    public function query()
    {
        return Invoice
            ::query()
            ->whereYear('created_at', $this->year)
            ->whereMonth('created_at', $this->month);
    }

    /**
     * @return string
     */
    public function title(): string
    {
        return 'Month ' . $this->month;
    }
}

如下能夠下載 2018 年的全部的發票, 它包含 12 單元表來顯示每月的數據.

public function download() 
{
    return (new InvoicesExport(2018))->download('invoices.xlsx');
}

數據遍歷

遍歷行

經過添加  WithMapping, 你能夠遍歷添加到單元行中的每一條數據而後並返回.
這種方法你能夠控制每一列的數據, 假設你使用 Eloquent 的 query builder.

use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithMapping;

class InvoicesExport implements FromQuery, WithMapping
{
    /**
    * @var Invoice $invoice
    */
    public function map($invoice): array
    {
        return [
            $invoice->invoice_number,
            Date::dateTimeToExcel($invoice->created_at),
        ];
    }
}

添加表頭

能夠經過添加一個  WithHeadings 約束來實現. 表頭會添加到全部數據的第一行的位置上.

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

class InvoicesExport implements FromQuery, WithHeadings

    public function headings(): array
    {
        return [
            '#',
            'Date',
        ];
    }
}

格式化列

你能夠格式化整列, 經過添加 WithColumnFormatting, 若是你想更多範圍的自定義. 推薦使用 AfterSheet 事件來直接和地城的 Worksheet 類進行交互.

namespace App\Exports;

use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Maatwebsite\Excel\Concerns\WithMapping;

class InvoicesExport implements WithColumnFormatting, WithMapping
{
    public function map($invoice): array
    {
        return [
            $invoice->invoice_number,
            Date::dateTimeToExcel($invoice->created_at),
            $invoice->total
        ];
    }

    /**
     * @return array
     */
    public function columnFormats(): array
    {
        return [
            'B' => NumberFormat::FORMAT_DATE_DDMMYYYY,
            'C' => NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE,
        ];
    }
}

日期

當操做日期的時候. 推薦使用 \PhpOffice\PhpSpreadsheet\Shared\Date::dateTimeToExcel() 來正確的解析你的日期數據.

導出關係

Interface Explanation
Maatwebsite\Excel\Concerns\FromCollection Use a Laravel Collection to populate the export.
Maatwebsite\Excel\Concerns\FromQuery Use an Eloquent query to populate the export.
Maatwebsite\Excel\Concerns\FromView Use a (Blade) view to to populate the export.
Maatwebsite\Excel\Concerns\WithTitle Set the Workbook or Worksheet title.
Maatwebsite\Excel\Concerns\WithHeadings Prepend a heading row.
Maatwebsite\Excel\Concerns\WithMapping Format the row before it's written to the file.
Maatwebsite\Excel\Concerns\WithColumnFormatting Format certain columns.
Maatwebsite\Excel\Concerns\WithMultipleSheets Enable multi-sheet support. Each sheet can have its own concerns (except this one).
Maatwebsite\Excel\Concerns\ShouldAutoSize Auto-size the columns in the worksheet.
Maatwebsite\Excel\Concerns\WithStrictNullComparison Uses strict comparisions when testing cells for null value.
Maatwebsite\Excel\Concerns\WithEvents Register events to hook into the PhpSpreadsheet process.

Traits

Trait Explanation
Maatwebsite\Excel\Concerns\Exportable Add download/store abilities right on the export class itself.
Maatwebsite\Excel\Concerns\RegistersEventListeners Auto-register the available event listeners.

擴展

事件

導出過程有一些事件,你能夠利用這些事件與底層類進行交互,以嚮導出添加自定義行爲。

經過使用事件,您能夠鏈接到父包。若是你須要徹底控制導出,則不須要使用諸如 "query" 或者 "view" 之類的便利方法。

事件將經過添加 WithEvents 關注來激活。在 registerEvents 方法中,你必須返回一系列事件。Key 是事件的徹底限定名(FQN),Value 是可調用的事件監聽器。這能夠是一個閉包、可調用的數組 或 invokable 類。

namespace App\Exports;

use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\BeforeExport;
use Maatwebsite\Excel\Events\BeforeWriting;
use Maatwebsite\Excel\Events\BeforeSheet;

class InvoicesExport implements WithEvents
{
    /**
     * @return array
     */
    public function registerEvents(): array
    {
        return [
            // Handle by a closure.
            BeforeExport::class => function(BeforeExport $event) {
                $event->writer->getProperties()->setCreator('Patrick');
            },

            // Array callable, refering to a static method.
            BeforeWriting::class => [self::class, 'beforeWriting'],

            // Using a class with an __invoke method.
            BeforeSheet::class => new BeforeSheetHandler()
        ];
    }

    public static function beforeWriting(BeforeWriting $event) 
    {
        //
    }
}

請注意,使用 Closure 將不可能與隊列導出合併,由於PHP不能序列化閉包。在這些狀況下,最好使用 RegistersEventListeners 特性。

自動註冊事件監聽器

經過使用 RegistersEventListeners trait ,你能夠自動註冊事件監聽器,而不須要使用 registerEvents 。只有在建立方法時,偵聽器纔會被註冊。

namespace App\Exports;

use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\RegistersEventListeners;
use Maatwebsite\Excel\Events\BeforeExport;
use Maatwebsite\Excel\Events\BeforeWriting;
use Maatwebsite\Excel\Events\BeforeSheet;
use Maatwebsite\Excel\Events\AfterSheet;

class InvoicesExport implements WithEvents
{
    use Exportable, RegistersEventListeners;

    public static function beforeExport(BeforeExport $event)
    {
        //
    }

    public static function beforeWriting(BeforeWriting $event)
    {
        //
    }

    public static function beforeSheet(BeforeSheet $event)
    {
        //
    }

    public static function afterSheet(AfterSheet $event)
    {
        //
    }
}

可用的事件

Event name Payload Explanation
Maatwebsite\Excel\Events\BeforeExport $event->writer : Writer Event gets raised at the start of the process.
Maatwebsite\Excel\Events\BeforeWriting $event->writer : Writer Event gets raised before the download/store starts.
Maatwebsite\Excel\Events\BeforeSheet $event->sheet : Sheet Event gets raised just after the sheet is created.
Maatwebsite\Excel\Events\AfterSheet $event->sheet : Sheet Event gets raised at the end of the sheet process.

WriterSheet 都是能夠進行宏操做的,這意味着它能夠很容易地擴展以知足你的須要。Writer 和 Sheet都有一個 ->getDelegate() 方法,它返回底層的PhpSpreadsheet 類。這將容許你爲 PhpSpreadsheets 方法添加快捷方法,而這個方法在這個包中是不可用的。

Writer / 寫入

use \Maatwebsite\Excel\Writer;

Writer::macro('setCreator', function (Writer $writer, string $creator) {
    $writer->getDelegate()->getProperties()->setCreator($creator);
});

Sheet / 單元表

use \Maatwebsite\Excel\Sheet;

Sheet::macro('setOrientation', function (Sheet $sheet, $orientation) {
    $sheet->getDelegate()->getPageSetup()->setOrientation($orientation);
});

你還能夠爲樣式單元添加一些快捷方法。你能夠自由使用這個宏,或者創造你本身的語法!

use \Maatwebsite\Excel\Sheet;

Sheet::macro('styleCells', function (Sheet $sheet, string $cellRange, array style) {
    $sheet->getDelegate()->getStyle($cellRange)->applyFromArray($style);
});

以上例子可做:

namespace App\Exports;

use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\BeforeExport;
use Maatwebsite\Excel\Events\AfterSheet;

class InvoicesExport implements WithEvents
{
    /**
     * @return array
     */
    public function registerEvents(): array
    {
        return [
            BeforeExport::class  => function(BeforeExport $event) {
                $event->writer->setCreator('Patrick');
            },
            AfterSheet::class    => function(AfterSheet $event) {
                $event->sheet->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE);

                $event->sheet->styleCells(
                    'B2:G8',
                    [
                        'borders' => [
                            'outline' => [
                                'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THICK,
                                'color' => ['argb' => 'FFFF0000'],
                            ],
                        ]
                    ]
                );
            },
        ];
    }
}

對於 PhpSpreadsheet 方法, 可查看文檔: https://phpspreadsheet.readthedocs.io/

測試 / Testing

The Excel facade can be used to swap the exporter to a fake.

測試下載

/**
* @test
*/
public function user_can_download_invoices_export() 
{
    Excel::fake();

    $this->actingAs($this->givenUser())
         ->get('/invoices/download/xlsx');

    Excel::assertDownloaded('filename.xlsx', function(InvoicesExport $export) {
        // Assert that the correct export is downloaded.
        return $export->collection()->contains('#2018-01');
    });
}

測試存儲導出

/**
* @test
*/
public function user_can_store_invoices_export() 
{
    Excel::fake();

    $this->actingAs($this->givenUser())
         ->get('/invoices/store/xlsx');

    Excel::assertStored('filename.xlsx', 'diskName');

    Excel::assertStored('filename.xlsx', 'diskName', function(InvoicesExport $export) {
        return true;
    });

    // When passing the callback as 2nd param, the disk will be the default disk.
    Excel::assertStored('filename.xlsx', function(InvoicesExport $export) {
        return true;
    });
}

測試隊列導出

/**
* @test
*/
public function user_can_queue_invoices_export() 
{
    Excel::fake();

    $this->actingAs($this->givenUser())
         ->get('/invoices/queue/xlsx');

    Excel::assertQueued('filename.xlsx', 'diskName');

    Excel::assertQueued('filename.xlsx', 'diskName', function(InvoicesExport $export) {
        return true;
    });

    // When passing the callback as 2nd param, the disk will be the default disk.
    Excel::assertQueued('filename.xlsx', function(InvoicesExport $export) {
        return true;
    });
}
相關文章
相關標籤/搜索