そうだ、そうだ、Laravel Excelを使ってみよう(2)ヘッダ, map()の続きです。

日付のフォーマット

注文の多いマーケッターさんなら、日付の表示を「YYYY/MM/DD」に変更して欲しい、と言うかもしれません。やってみましょう。

前回エクスポートしたファイルを見てみると作成日欄は「標準」となっており「日付」ではありません。

カラムの形式を「日付」に変えた上で「YYYY/MM/DD」で表示させます。

カラムの形式を指定するにはWithColumnFormattingをimplementし、columnFormatsメソッドを追加します。

...
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;

class PurchaseHistoryExport implements FromCollection, WithHeadings, WithStrictNullComparison, WithMapping, WithColumnFormatting
{
    ...
    /**
     * @return array
     */
    public function columnFormats() :array
    {
        return [
            'B' => NumberFormat::FORMAT_DATE_YYYYMMDDSLASH,
        ];
    }
    ...

NumberFormatクラスに指定可能なフォーマットが定数で定義されているので適当なものを選びます。日付に関しては以下のフォーマットが定義されていました。今回は’YYYY/MM/DD’としたいので、FORMAT_DATE_YYYYMMDDSLASHを選択しました。

const FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd';
const FORMAT_DATE_YYYYMMDD = 'yyyy-mm-dd';
const FORMAT_DATE_DDMMYYYY = 'dd/mm/yyyy';
const FORMAT_DATE_DMYSLASH = 'd/m/yy';
const FORMAT_DATE_DMYMINUS = 'd-m-yy';
const FORMAT_DATE_DMMINUS = 'd-m';
const FORMAT_DATE_MYMINUS = 'm-yy';
const FORMAT_DATE_XLSX14 = 'mm-dd-yy';
const FORMAT_DATE_XLSX15 = 'd-mmm-yy';
const FORMAT_DATE_XLSX16 = 'd-mmm';
const FORMAT_DATE_XLSX17 = 'mmm-yy';
const FORMAT_DATE_XLSX22 = 'm/d/yy h:mm';
const FORMAT_DATE_DATETIME = 'd/m/yy h:mm';
const FORMAT_DATE_TIME1 = 'h:mm AM/PM';
const FORMAT_DATE_TIME2 = 'h:mm:ss AM/PM';
const FORMAT_DATE_TIME3 = 'h:mm';
const FORMAT_DATE_TIME4 = 'h:mm:ss';
const FORMAT_DATE_TIME5 = 'mm:ss';
const FORMAT_DATE_TIME6 = 'h:mm:ss';
const FORMAT_DATE_TIME7 = 'i:s.S';
const FORMAT_DATE_TIME8 = 'h:mm:ss;@';
const FORMAT_DATE_YYYYMMDDSLASH = 'yyyy/mm/dd;@';

一旦、tinkerからエクスポートしてみます。

Psy Shell v0.10.6 (PHP 7.2.34 — cli) by Justin Hileman
>>> use App\Exports\PurchaseHistoryExport;
>>> Excel::store(new PurchaseHistoryExport, 'purchase_history.xlsx');
=> true

するとカラムの表示形式が「標準」から「日付」変わりました。

しかし、セルの表示形式は「2021-03-15 09:42:24」のままです。どうすれば良いでしょうか?

シリアル値に変換

公式ドキュメントを確認すると、日付に関してPhpSpreadsheetパッケージのDate::dateTimeToExcel()を利用するように推奨されています。このメソッドはPHPのDateTimeオブジェクトをExcelのシリアル値に変換するメソッドです。なるほど、日付を正しくフォーマットするためには一度シリアル値に変換してエクスポートする必要があるようです。

シリアル値はExcel上で日時を管理するデータ形式です。1900年1月1日を1とみなし、そこから何日目か?によって日付を管理しています(つまり、Excel版のtimestampですね)。例えば、Excelでセルに数字の2を入力して表示形式を「標準」から「日付」に変更してみて下さい。すると、「1900/1/2」と表示されるはずです。1900年1月1日から2日目だからです。尚、時刻に関しては小数点以下で表現しています。

話を元に戻して、mapメソッドを以下の様に修正しました。

PhpOffice\PhpSpreadsheetパッケージからDateクラスをuseしています。このパッケージはLaravel Excelをインストールした際に一緒に入っています。(余談ですが、そもそもLaravel Excel自体がこのパッケージをLaravel用に使いやすくしたラッパーパッケージなのだそうです。)

...
use PhpOffice\PhpSpreadsheet\Shared\Date;
...
/**
 * @param PurchaseHistory $row
 * @return array
 */
public function map($row) :array
{
    return [
        $row->id,
        Date::dateTimeToExcel($row->created_at), // <-修正箇所
        $row->user_id,
        $row->product_name,
        $row->price * $row->quantity, // 合計金額
    ];
}
...

再度tinkerからエクスポートして確認すると、「作成日」欄が「YYYY/MM/DD」の形式になりました。表示上は年月日のみですが、内部的にシリアル値で管理されているのでセルを選択すると時刻まで確認できます。良さそうですね!

By hikaru