だいたい死んでる

渋谷で働いているWEBプログラマーです。

Cakephp3でCSVのデータをSeedで利用する

最近、巨大なCSVをSeedとして利用することがあり、そのさい行ったことをメモ程度にまとめていきます。

環境は以下です。

最初のプログラム

1番最初に作成したプログラムでは、CSVをfopen関数で呼び出し、内容をループで取得しながらインサートしていました。

<?php
use Migrations\AbstractSeed;

class TestSeed extends AbstractSeed
{
    public function run()
    {
        $table = $this->table('test');
        $fp = fopen("seedファイルのパス", "rb");
        while(($line = fgetcsv($fp)) !== false) {
             $insertData = [
                'data1'  => (int)$data[1],
                'data2' => (string)$data[2],
            ];
            $table->insert($insertData)->save();
        }
    }
}
?>

Seedに使用するCSVファイルが複数ある場合に同じ処理を複数回書いていました。 関数化することで他のクラスでも使用できるようにしたかったため、Generatorを使用してCSVの読み込み処理を関数化しました。 Generatorを使用しようと思ったのは、メモリ効率が良さそうなのと、使ってみたかったからです。

Generatorを使った処理。

<?php
use Migrations\AbstractSeed;

class TestSeed extends AbstractSeed
{
    public function run()
    {
        $table = $this->table('test');
        foreach ($this->fileGetLines("seedファイルのパス") as $data) {
            $insertData = [
                  'data1'  => (int)$data[1],
                  'data2' => (string)$data[2],
            ];
            $table->insert($insertData)->save();
        }
    }
    
    public function fileGetLines($filepath) {
        $lines = [];
        $fp = fopen($filepath, "rb");
        while(($line = fgets($fp)) !== false) {
            yield $line;
        }
        fclose($fp);
    }
}
?>

Generatorを使用することで、処理がスッキリし読みやすくなりました。 また、変更後処理時間を測定した結果、Generatorを使用したほうが、若干処理が早くなりました。

バルクインサートする

先程までの処理は1行ずつ保存していたため、処理が遅かったです。 そのため、バルクインサートにすることで処理を早くしました。 バルクインサートにした結果2分かかっていた処理が、40秒ほどになりました。

<?php
use Migrations\AbstractSeed;

class TestSeed extends AbstractSeed
{
    public function run()
    {
        $table = $this->table('test');
        foreach ($this->fileGetLines("seedファイルのパス") as $data) {
            $insertData = [
                  'data1'  => (int)$data[1],
                  'data2' => (string)$data[2],
            ];
            if (count($insertData) >= 1000) {
                $this->insert('test', $insertData);
                $insertData = [];
            }
        }
        $this->insert('test', $insertData);
    }
    
    public function fileGetLines($filepath) {
        $lines = [];
        $fp = fopen($filepath, "rb");
        while(($line = fgets($fp)) !== false) {
            yield $line;
        }
        fclose($fp);
    }
}
?>

参考にしたサイト

lab.adn-mobasia.net

PHP: ジェネレータとは - Manual