背景
Laravelのseeder機能はとても便利です。
Factoryも用いればそこそこなテストデータの作成ができ、ローカル開発をする分には十分です。
しかし、時にはステージング・テスト環境のものを引っ張ってローカルで確認したい場合があると思います。
今回は、そのデータをCSVとして出力し、Seederでテスト投入する手段について紹介します。
ディレクトリの用意
以下のディレクトリを用意してください。
database/seeders/csv
database/seeders/csv/data
csvディレクトリ直下にはCSV投入用のSeederをセットします。
dataディレクトリには実際のcsvファイルを配置します。
初期設定
Gitのために以下の2ファイルを作成してください。
database/seeders/csv/data/.gitkeep
database/seeders/csv/data/.gitignore
*.csv
共通クラス(今回のメイン)
csv/data
に投入したcsvファイルをローカルに投入するクラスです。
CSV出力する都合nullやdate周りの変換処理を設定しています。
<?php
namespace Database\Seeders\csv;
use Illuminate\Support\Facades\Schema;
class SeederUtil
{
/**
* CSVファイルからデータを読み取り、指定されたモデルとテーブルに挿入します
*
* @param string $modelClass Fully Qualified Model Class Name (e.g. \App\Models\User::class)
* @param string $tableName テーブル名 (e.g. 'users')
* @return void
*/
public static function seedFromCsv(string $modelClass, string $tableName)
{
$csvFile = database_path('seeders/csv/data/' . $tableName . '.csv');
$fileHandle = fopen($csvFile, 'r');
$header = fgetcsv($fileHandle);
// テーブルのカラムを取得
$tableColumns = Schema::getColumnListing($tableName);
while (($row = fgetcsv($fileHandle)) !== false) {
$row = array_combine($header, $row);
// 'NULL'文字列をnullに変換
$row = array_map(function ($value) {
return $value === 'NULL' ? null : $value;
}, $row);
// 存在しないカラムを除外
$filteredRow = array_filter($row, function ($key) use ($tableColumns) {
return in_array($key, $tableColumns);
}, ARRAY_FILTER_USE_KEY);
// created_at, updated_atカラムがあれば現在日時を追加
$now = now();
if (in_array('created_at', $tableColumns)) {
$filteredRow['created_at'] = $now;
}
if (in_array('updated_at', $tableColumns)) {
$filteredRow['updated_at'] = $now;
}
$modelClass::create($filteredRow);
}
fclose($fileHandle);
}
}
DatabaseSeeder.php
csv投入用に作成します。namespaceにご注意ください。
<?php
namespace Database\Seeders\csv;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call(UserSeeder::class);
}
UserSeederの実装
<?php
namespace Database\Seeders\csv;
use Illuminate\Database\Seeder;
use App\Models\User;
class UserSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
SeederUtil::seedFromCsv(User::class, 'users');
}
}
これであとはCSVを配置することでデータ投入ができます。
また、DatabaseSeeder.phpの配置が違うので以下のようなコマンドで実行してください。
php artisan migrate:fresh
php artisan db:seed --class="Database\Seeders\csv\DatabaseSeeder"
一応今はSeederクラスを個別で実装するようにしましたが、DatabaseSeederにまとめてしまってもいいかと思います。
また、もし上記の共通実装では足りないテーブル別の変換処理が必要になった場合はseedFromCsv
関数をコピペして加工してください。