CakePHP 4 のシードで CSV ファイルを使う方法

はじめに

今日は CakePHP 4 のシードで CSV ファイルを使用する方法をご紹介します。

動作確認に使用したのは CakePHP 4.2.3 (cakephp/migrations 3.0.0) です。

目次
  1. 下準備
  2. CSV 読込方法
  3. おわりに

1. 下準備

今回のファイル構成は下記のような感じです。
CSV のフォルダ名やファイルパスは変更していただいても大丈夫です。

/path/to/cakephp4/
  ├ config/
  │  ├ Migrations/
  │  │  └ 20210131000001_CreateDrinks.php
  │  │
  │  ├ Seeds/
  │  │  └ DrinksSeed.php
  │  │
  │  ├ SeedsCSV/
  │  │  └ Drinks.csv
  │  │
  ~  ~

データベースは下記を使用しました。

/config/Migrations/20210131000001_CreateDrinks.php
<?php
declare(strict_types=1);

use Migrations\AbstractMigration;

class CreateDrinks extends AbstractMigration
{
    public function change()
    {
        $table = $this->table('drinks');
        $table->addColumn('name', 'string', [
            'default' => null,
            'limit' => 255,
            'null' => false,
        ]);
        $table->create();
    }
}
$ cd /path/to/cakephp4
$ bin/cake migrations migrate

CSV ファイルは文字コードが SJIS、改行コードが CRLF で、内容は下記のようにしています。

/config/SeedsCSV/Drinks.csv
1,"オレンジ
ジュース"
2,コーラ
3,Beer

2. CSV 読込方法

シーディングでの CSV 読み込みは、run() 関数内に直接処理を書けば OK です。

下記サンプルコードでは、CSV から配列に変換し、ドリンク名に対して mb_convert_kana で変更を加えて、テーブルに挿入しています。

/config/Seeds/DrinksSeed.php
<?php
declare(strict_types=1);

use Migrations\AbstractSeed;

class DrinksSeed extends AbstractSeed
{
    public function run()
    {
        // CSV から配列に変換
        // (定数 CONFIG は /config/paths.php で定義されています)
        $file = new SplFileObject(CONFIG . DS . 'SeedsCSV' . DS . 'Drinks.csv');
        $file->setFlags(SplFileObject::READ_CSV);
        $rows = [];
        foreach ($file as $row) {
            mb_convert_variables('UTF-8', 'SJIS-win', $row);
            $rows[] = $row;
        }

        // insert() 用のデータ作成
        // (カラム数が合わない場合はスキップ)
        foreach ($rows as $row) {
            if (count($row) !== 2) {
                continue;
            }
            list($id, $name) = $row;
            $data[] = [
                'id' => $id,
                'name' => mb_convert_kana($name, 'aKV')
            ];
        }

        $table = $this->table('drinks');
        $table->insert($data)->save();
    }
}
$ bin/cake migrations seed

3. おわりに

多くのシードで CSV 読み込みを行う場合には、共通処理としてどこかに書くと便利かもしれません。

AbstractSeed を継承した独自のクラスを実装するのがいいのかなと思っているのですが、その方法については後日ご紹介しようと思います。

余談ですが CakePHP にはファイルを扱うクラスとして Folder と File のクラスが用意されています。
しかし、これらは 5.0 で削除予定で、SplFileObject などを使うようにとされています。