◀ 27.

Laravel ExcelでCSVを読み込む|旧2.xと3.1の違い・入力検証

▶

Laravel ExcelでCSVを受け取る際は、バージョン確認→アップロード検証→行の検証→保存判断を分けます。下の旧2.xコードは保持し、3.1方式の小容量CSVの件数確認例を追加しています。

CSVの受信、行の検証、件数確認とDB保存を分離する図

3.1方式:小さいCSVを検証して件数だけ返す

この追加例はUTF-8/カンマ区切り、先頭見出しheader1,header2、各列は80文字以内の空でない文字列、データ1〜1000行、ファイル256KiB以下という教材用の仕様です。自分の業務仕様へ無条件に適用しません。PHP8.4で隔離検証する構成を用い、3.1.70の依存PhpSpreadsheet1.30.7はPHP8.5を許可していません。要件を無視するinstallフラグで解決せず、実際のlockと全依存の対応範囲を確認します。本サイトのPHPや依存パッケージは変更していません。

app/Imports/CsvPreviewImport.phpへ配置する例です。1000行を超える入力を黙って途中まで採用せず、境界超過を検出して拒否します。見出しはパッケージの既定整形後のキーで照合します。生の見出しの重複/正規化前の一致をすべて検査する実装ではありません。

<?php

namespace App\Imports;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithLimit;

class CsvPreviewImport implements ToCollection, WithHeadingRow, WithLimit, WithCustomCsvSettings
{
    public int $rows = 0;

    public function limit(): int
    {
        // Include a sentinel beyond the 1,000-row application limit.
        return 1002;
    }

    public function getCsvSettings(): array
    {
        return ['input_encoding' => 'UTF-8', 'delimiter' => ','];
    }

    public function collection(Collection $rows): void
    {
        $data = $rows->toArray();
        if (count($data) < 1 || count($data) > 1000) {
            throw ValidationException::withMessages(['csv' => 'Data rows must be between 1 and 1000.']);
        }
        foreach ($data as $row) {
            if (array_keys($row) !== ['header1', 'header2']) {
                throw ValidationException::withMessages(['csv' => 'Expected header1,header2 in that order.']);
            }
        }
        Validator::make($data, [
            '*.header1' => ['required', 'string', 'max:80'],
            '*.header2' => ['required', 'string', 'max:80'],
        ])->validate();
        $this->rows = count($data);
    }
}

app/Http/Controllers/CsvPreviewController.php。既存のController基底・検証エラー処理と認証基盤があるLaravelアプリで使う例です。Excel Facadeは名前空間を明示し、入力名csvと一致させます。

<?php

namespace App\Http\Controllers;

use App\Imports\CsvPreviewImport;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
use Maatwebsite\Excel\Excel as ReaderType;

class CsvPreviewController extends Controller
{
    public function store(Request $request)
    {
        $request->validate(['csv' => ['required', 'file', 'max:256', 'mimes:csv,txt']]);
        $import = new CsvPreviewImport();
        Excel::import($import, $request->file('csv'), null, ReaderType::CSV);

        // Validation preview only: no DB writes and no uploaded row data in logs/responses.
        return response()->json(['rows' => $import->rows]);
    }
}

routes/web.phpへ追加するPOSTルート。既存の認証フローを用い、CSRFを無効化しません。

<?php

use App\Http\Controllers\CsvPreviewController;
use Illuminate\Support\Facades\Route;

// Place in routes/web.php; use the application's existing authentication flow.
Route::post('/csv/preview', [CsvPreviewController::class, 'store'])
    ->middleware(['auth', 'throttle:10,1'])->name('csv.preview');

フォームを表示する既存GET画面へ組み込むBlade断片です。エラー一覧等はアプリの標準表示に合わせます。

<form method="POST" action="{{ route('csv.preview') }}" enctype="multipart/form-data">
    @csrf
    <input type="file" name="csv" accept=".csv,text/csv" required>
    <button type="submit">件数を確認</button>
</form>

件数確認から実データ保存へ進む前に

  • acceptや拡張子/MIME検査だけを内容の証明にしない。CSVリーダーを指定しても、業務上の値の正しさ・安全な再出力は別に検証する。
  • 数値風の値がリーダーで型変換される場合がある。先頭ゼロ/長いID/日付/数式風の文字列をそのまま保持する必要があれば、値バインダーと再出力時の数式対策を別途設計する。例のstring検証は数値化された値を拒否し得る。
  • Shift_JIS系等は実際の入力文字コードを確認し、既知の文字コードに設定する。文字化けを適当な変換で隠さない。
  • 大容量はファイルサイズだけでなく行数・列数・セル長・処理時間・メモリを管理する。ToCollectionで全量を無制限に保持せず、対象版のchunk reading等を用途に合わせ検討する。
  • この例はDBへ保存しない。保存を追加する場合は権限・一意性・重複・全件/部分成功・トランザクション・再送方針を別途決め、プレビューの成功を保存成功と扱わない。
  • パース失敗/空ファイル/容量超過のエラー表示を確認する。アップロード内容・個人情報・秘密値を例外やログへ丸ごと残さない。

公式:導入条件 / UploadedFileとインポート / 見出しの整形 / CSVの設定。追加例の検証範囲は隔離したCSV読込と入力検証で、本番の認証付きアップロード/業務データ保存は未実施です。

旧2.xのコードと出力記録

以下は既存システムの保守用に残した旧例です。3.1のAPIと混ぜず、未完成な点は各節の注意を確認してください。

この記事の要点
  • Laravel maatwebsite/excel パッケージでのCSV アップロード・読み込み
  • 導入前にPHP/Laravel/依存ライブラリの対応版を確認。下の2.1は旧版保守の記録
  • 2.x 系: Excel::load($file)->get()
  • 3.x 系: Import クラスを作って Excel::import(new MyImport, $file)

 

本稿はLaravelの「maatwebsite/excel」パッケージを使用したCSVファイルの読み込み方法を説明します。

 

maatwebsite/excelの導入

次は旧2.1系列の保守資料です。新規導入へそのまま実行しません。現在のアプリのPHP/Laravel/依存要件と、対象版の公式導入手順を先に照合します。

composer require "maatwebsite/excel:~2.1.0"

これは2018年当時の記録です。現在の3.1系列にはインポート機能があり、新規導入に2.xを一律推奨しません。load/getとImportクラス方式を単純置換せず、行/見出し/検証/保存の仕様を確認します。

 

プロバイダー登録

下は旧構成での明示登録例です。対象版のパッケージ自動検出が利用可能なら、無条件にconfig/app.phpへ重複登録しません。現在のアプリ構成と公式の対応手順を確認します。

'providers' => [

Maatwebsite\Excel\ExcelServiceProvider::class,

]

...

'aliases' => [

'Excel' => Maatwebsite\Excel\Facades\Excel::class,

]

 

CSVの読み込み

以下は旧2.xの未完成例として保持します。フォームのname="csv"に対しサーバーがfile("file")を読み、名前が一致していません。CSRF/認証/容量/行検証も不足し、行の全内容をログに出すため、そのまま本番へ使いません。出力も2018年の記録で現行APIの検証結果ではありません。

 

【クライアント側】

<form method="POST" action="{{ url('...') }}" enctype="multipart/form-data">
    <input type="file" name="csv">
    <button type="submit">Submit</button>
</form>

 

【サーバー側】

use Excel;

...

class CsvController extends Controller

public function readCsv(Request $request) {

 

        $file = $request->file('file');

        $loadedFile = Excel::load(
                $file->getRealPath()
                ,function($reader){}
                )->get();

        $rows = $loadedFile->toArray();

        foreach ($rows as $row){
            \Log::info('row: ' . var_export($row, true));
        }        

...

}

}

 

【出力結果】

[2018-05-05 10:47:41] local.INFO: row: array (
  'header1' => 'val1-1',
  'header2' => 'val1-2',
)
[2018-05-05 10:47:41] local.INFO: row: array (
  'header1' => 'val2-1',
  'header2' => 'val2-2',
)

 

子ページ

子ページはありません

同階層のページ
  1. インストールと設定
  2. クイックスタート & チュートリアル(初心者向け)
  3. クイックスタート & チュートリアル(中級者向け)
  4. ルーティング
  5. Bladeテンプレート(ビュー/レイアウト)
  6. コントローラー
  7. マイグレーションとテーブル定義
  8. データベースの設定
  9. Eloquentモデル (ORM)
  10. SQLとクエリビルダー
  11. バリデーション
  12. .envファイルの設定値へのアクセス
  13. 動作環境による分岐処理
  14. configフォルダ配下の設定値へのアクセス
  15. assetヘルパーを利用したpublicフォルダへのアクセス
  16. storageフォルダへのアクセス
  17. アプリケーション名の変更
  18. メンテナンス
  19. ログイン画面(認証システム)の作成
  20. ログインの必須化
  21. ログインユーザー情報の取得
  22. ルートの認証化
  23. 本番サーバーへのデプロイ方法
  24. 多言語化
  25. csrf_field
  26. ファイルのダウンロード
  27. CSVのアップロードおよび読み込み(maatwebsite/excel)
  28. ページタイトルの設定
  29. コマンド一覧
  30. エラー一覧
  31. SQLの実行ログ出力方法
  32. キャッシュのクリア
  33. Selectの結果の最初もしくは最後に任意の値を追加する方法
  34. ajaxでPOST通信する際の注意点
  35. ソーシャルログインの実装
  36. セッション情報の確認
  37. ログイン、ユーザー登録、パスワードリセット後のリダイレクト先の変更方法
  38. redirectやreturn viewにメッセージを付与する方法
  39. クッキー(cookie)の設定と取得
  40. クラスの再読み込み
  41. csrfの有効時間を変更する方法
  42. ViewComposerを用いてviewに共通の値を付与する方法
  43. View::shareを用いて共通の値を各ビューに渡す方法
  44. ミドルウェアを用いた処理の共通化
  45. Middleware内でAuth::check()などを使用する方法
  46. Controller以外でリダイレクトする方法
  47. セッションの値の取得/保存/更新/削除
  48. $requestの値を変更する方法
  49. 常時SSL化
  50. ページング(ページネーション)をする方法
  51. vue.jsとの連携
  52. Vue.jsと連携するSPA実行環境構築
  53. .envの値をvue.jsで参照する方法
  54. vue.jsを本番環境にリリースする方法
  55. could not find driver(Windows, MySQL編)

最近更新/作成されたページ