😊

Laravelで実データと制御ファイル(ファイルサイズ量)をペアで出力する方法のメモ

3 min read

はじめに

以下のように、実データと制御データをペアで管理するCSV連携(※)を行うユースケースを考えてみます。
目的は、CSV読み取り側が実データを読み取る前に、制御データの内容を元に事前バリデーションを行うことです。
今回は、ファイルサイズでバリデーションを行うケースを想定します。

$ cd storage/app/public/
$ head *
==> output_20210703072428.csv <==
1,apple,100
2,banana,150
3,cherry,200
==> output_20210703072428.ctl <==
38

※こういうのって名前がついていたりするんだろうか

前提

  • Macでtreeコマンドを使用しています(動作確認用に。なければlsとかで代用可)
    brewでインストールできます
$ brew install tree

やってみたこと

Laravelプロジェクトを作成

$ composer create-project laravel/laravel --prefer-dist csv-with-ctl-file

処理部を実装

web.phpを編集します。

web.php
<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Route::get('/create', function () {
    // CSVを想定したテストデータを作成
    $items = array();
    $items[] = array(
        'id' => 1,
        'name' => 'apple',
        'price' => 100
    );
    $items[] = array(
        'id' => 2,
        'name' => 'banana',
        'price' => 150
    );
    $items[] = array(
        'id' => 3,
        'name' => 'cherry',
        'price' => 200
    );

    // ファイル出力設定
    $filename = 'output_' . date( 'YmdHis' ); // e.g. output_20210703072148
    $path = storage_path( 'app/public/' . $filename );

    // データファイルの出力
    $dataFile = new \SplFileObject( $path . '.csv', 'w' );
    $dataFile->setCsvControl( ',' );
    foreach( $items as $item ){
        $dataFile->fputcsv( $item );
    }

    // コントロールファイルの出力
    $ctrlFile = new \SplFileObject( $path . '.ctl', 'w' );
    $ctrlFile->fwrite( $dataFile->getSize() );
});

実装の補足

  • /create というパスを用意

  • $items = array(); で初期化した後に $items[] = <データ> と記述してテストデータをどんどん追記していきます

    // CSVを想定したテストデータを作成
   $items = array();
   $items[] = array(
       'id' => 1,
       'name' => 'apple',
       'price' => 100
   );
>> ```
  • SplFileObjectを使ってCSVとしてデータを出力します。
    第3引数で w を指定しているので書き出しモードでオープンし、ファイルが無ければ新規作成されます。他のモードのバリエーションはfopenのドキュメントを参照
  $dataFile = new \SplFileObject( $path . '.csv', 'w' );
  $dataFile->setCsvControl( ',' );
  • SplFileObjectのgetSize()でバイト数を取得できる
    $ctrlFile->fwrite( $dataFile->getSize() );

動作確認

  • treeコマンドで前後の状態を取得し、curlで/createを叩き、ファイルが出力されたことを確認
$ tree storage/app/public/
storage/app/public/
0 directories, 0 files
$ curl http://localhost:8000/create
$ tree storage/app/public/
storage/app/public/
├── output_20210703072428.csv
└── output_20210703072428.ctl
$ cd storage/app/public/
$ head *
==> output_20210703072428.csv <==
1,apple,100
2,banana,150
3,cherry,200
==> output_20210703072428.ctl <==
38

最後に

簡単にできそうだとは思ったけど本当にできた。
SplFileInfoのユーティリティ、めっちゃ揃ってるので便利。