CAKEPHP3でCSV読込

## モデルなしフォーム作成
#### src/Form/HogeCsvForm.php
```php
<?php
namespace App\Form;

use Cake\Form\Form;
use Cake\Form\Schema;
use Cake\Validation\Validator;

class HogeCsvForm extends Form
{
    protected function _buildSchema(Schema $schema)
    {
        return $schema->addField('csv', ['type' => 'file']);
    }

    protected function _buildValidator(Validator $validator)
    {
        $validator->provider('upload', \Josegonzalez\Upload\Validation\DefaultValidation::class);
        $validator->add('csv', 'fileFileUpload', [
            'rule' => ['isFileUpload'],
            'message' => 'アップロードに失敗しました。',
            'provider' => 'upload'
        ])->add('csv', 'fileBelowMaxSize', [
            'rule' => ['isBelowMaxSize', 10240000],
            'message' => 'ファイルサイズが大きすぎます。',
            'provider' => 'upload'
        ])->add('csv', 'fileType', [
            'rule' => ['mimeType', ['text/plain']],
            'message' => 'ファイル形式が違います。'
        ]);
    }

    protected function _execute(array $data)
    {
        return true;
    }

}
```
## Controller作成
#### src/Controller/HogesController.php
```php
<?php
namespace App\Controller\Manage;

use App\Controller\AppController;
use App\Form\HogeCsvForm; // モデルなしフォームロード
use Cake\Filesystem\Folder;  // Folderユーティリティロード

class HogesController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Csv'); // CSVコンポートロード
    }
　   public function csv()
    {
        $hogesCsv = new HogesCsvForm();
        if ($this->request->is(['patch', 'post', 'put'])) {
            if ($hogesCsv ->execute($this->request->getData())) {
                // ファイルコピー
                $path = ROOT . DS . 'uploadfiles' . DS . 'tmp';
                $folder = new Folder();
                $folder->create($path);
                $file = $path . DS . sha1(uniqid(mt_rand(), true)) . '.csv';
                if (move_uploaded_file($this->request->getData('csv.tmp_name'), $file)) {
                    chmod($file, 0644);

                    // CSVデータ取込
                    $csvData = $this->Csv->getCsv($file);

                    // データ整形
                    $data = [];
                    foreach ($csvData as $vol) {
                        $data[] = [
                            'real_estate_id' => 0,
                            'aggregated_at'  => date('Y-m-d'),
                            'incomes'        => $vol['2'],
                            'expenses'       => $vol['3'],
                        ];
                    }

                    // データ登録
                    $this->loadModel('Hoges');
                    $entities = $this->MonthlyBalances->newEntities($data);
                    $result = $this->MonthlyBalances->saveMany($entities);

                    unlink($file);
                    $this->Flash->success('登録しました。');
                }else{
                    $this->Flash->error('登録に失敗しました。');
                }
            } else {
                $this->Flash->error('登録に失敗しました。');
            }
        }
        $this->set('hogesCsv', $hogesCsv);
    }
}
```
## view作成
#### src/Template/Hoges/csv.ctp
```php
<div>
<?= $this->Form->create($hogesCsv, ['type' => 'file']) ?>
    <fieldset>
        <legend><?= __('CSV登録') ?></legend>
        <?php
        echo $this->Form->control('csv', ['type' => 'file']);
        ?>
    </fieldset>
    <?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
```
## CSVデータ読込コンポーネント作成
#### Controller\Component\CsvComponent.php
```php
<?php
namespace App\Controller\Component;

use Cake\Controller\Component;
use Cake\Controller\ComponentRegistry;
use \SplFileObject;

/**
 * Csv component
 */
class CsvComponent extends Component
{

    /**
     * Default configuration.
     *
     * @var array
     */
    protected $_defaultConfig = [];

    public function getCsv($csvfile, $mode='sjis')
    {
        // ファイル存在確認
        if (!file_exists($csvfile)) {
            return false;
        }
        
        // 文字コードを変換しながら読み込めるようにPHPフィルタを定義
        if ($mode === 'sjis') {
            $filter = 'php://filter/read=convert.iconv.cp932%2Futf-8/resource='.$csvfile;
        } elseif ($mode === 'utf16') {
            $filter = 'php://filter/read=convert.iconv.utf-16%2Futf-8/resource='.$csvfile;
        } elseif ($mode === 'utf8') {
            $filter = $csvfile;
        }

        // SplFileObject()を使用してCSVロード
        $file = new SplFileObject($filter);
        if ($mode === 'utf16') {
            $file->setCsvControl("\t");
        }
        $file->setFlags(
            SplFileObject::READ_CSV |
            SplFileObject::SKIP_EMPTY |
            SplFileObject::READ_AHEAD
        );
     
        // 各行を処理
        $records = [];
        foreach ($file as $i => $row)
        {
            // 1行目はスキップ
            if ($i===0) {
                continue;
            }
     
            // 2行目以降はデータ行として取り込み
            $records[] = $row;
        }
        return $records;
    }
}
```
## 参考
[PHPでCSV読み込み関数の決定版を作ってみた。](https://kantaro-cgi.com/blog/php/super-csv-loader.html)