[PHP]CSVファイル読み書きとファイル定義

## 目的
csv作成処理で出力項目の順番や固定値を設定しているのが煩わしすぎる
読み込み時の変数名を全機能で固定したい
## 対応
こんな感じで定義してみた。
メモリ節約には、読込不要項目やら1行ずつ読んだりしてるよ。

```php:config/file/kyaku
<?php
return [
    'user' => [
        'type'            => 'csv',
        'charCode'        => 'SJIS-WIN',
        'newLineCode'     => "\r\n",
        'formatName'      => '契約者情報',
        'headerExist'     => true, // 読込時ヘッダ有無
        'outputHeaderFlg' => true, // 出力時ヘッダ有無
        'fixedValue' => [ // contentのkey名 => 固定値出力内容
            'other' => '固定値',
        ],
        'guard' => [ // contentのkey名.読込不要項目
            'other',
        ],
        'content' => [ // ヘッダ => 入出力時のkey
            '契約番号' => 'user_id',
            '契約者名' => 'user_name',
            '性別'    => 'gender',
            'その他'  => 'other',
        ],
    ],
];
```
```php:書込処理
function createCsv($data, $fileName, $config): string
{
    $stream = fopen(config('filesystems.disks.local.root').'/'.$fileName, 'w');
    // ヘッダー書き込み
    if ($config['outputHeaderFlg']) {
        $header = array_keys($config['content']);
        mb_convert_variables($config['charCode'], mb_internal_encoding(), $header);
        fwrite($stream, implode(',', $header) . $config['newLineCode']);
    }
    // 本文書き込み
    $contentConfig = array_flip(array_values($config['content']));
    foreach ($data as $row) {
        // 出力項目に絞り、不足項目を空文字で埋め、固定値で上書き、出力順に並び替え
        $row = collect($row)->intersectByKeys($contentConfig)
            ->merge(array_fill_keys(array_flip(array_diff_key($contentConfig, $row)), ''))
            ->merge($config['fixedValue'])
            ->sortKeysUsing(fn($a,$b)=>$contentConfig[$a] <=> $contentConfig[$b])
            ->toArray();
        mb_convert_variables($config['charCode'], mb_internal_encoding(), $row);
        fwrite($stream, implode(',', $row) . $config['newLineCode']);
    }
    fclose($stream);
    return $fileName;
}
```
```php:読込処理
function readCsv($filePath, $config): Collection
{
    $csv = new \SplFileObject($filePath);
    $csv->setCsvControl(',', '"', '"');
    $csv->setFlags(\SplFileObject::READ_CSV | \SplFileObject::READ_AHEAD | \SplFileObject::SKIP_EMPTY | \SplFileObject::DROP_NEW_LINE);

    $data = collect();
    while (!$csv->eof() && $lineArray = $csv->fgetcsv()) {
        $tmp = array_diff_key(array_combine($config['content'], $lineArray), array_flip($config['guard']));
        mb_convert_variables(mb_internal_encoding(), $config['charCode'], $tmp);
        $data->push($tmp);
    }
    // ヘッダー削除
    if($config['headerExist']){
        $data->shift();
    }

    return $data;
}
```

```php:呼び出し
public function hoge()
{
    // ファイルを作成
    $data = [
        ['user_name' => '佐々木', 'user_id' => 1, 'gender' => '男', 'bug' => '不要'],
        ['user_id' => 2, 'user_name' => '佐藤', 'gender' => '男'],
        ['user_id' => 3, 'user_name' => '藤原', 'gender' => '女'],
    ];
    $this->createCsv($data, 'hoge.csv', config('file.kyaku.user'));
    
    // configの一部分だけ変えたいとき.
    config(['file.kyaku.user.charCode' => 'UTF-8']);
    // それか、取得して変更する. こっちの方が正しい気がする.
    $config = config('file.kyaku.user');
    $config['charCode'] = 'UTF-8';
        
    // ファイル読み込み
    $data = $this->readCsv(storage_path('hoge.csv'), $config);
    dd($data);
}
```

## 結果
書込
![スクリーンショット 2024-12-20 15.58.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222372/a04c140f-a5b6-d74e-3453-13d08a51394c.png)
読込
![スクリーンショット 2024-12-20 16.09.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222372/6a2ff8dd-008b-3bbc-012d-c30c582db056.png)




## 補足
- ファイル名をConfigに入れないのは、同一フォーマットでも別名な事もある為
- Configのcontentがスネークなのは、DBから取得したものを扱う機会が多い為