0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

とりあえず適当に本番データをローカル環境で入れることが出来るDB:seedを考えた

Posted at

概要

以下のニーズに答えた

  • 本番環境のデータをそのままローカル環境に流し込みたい
  • DB構造が変わるたびにSeeder書き換えるのが嫌
  • 適当に入ればいい(適当とは下記3の方)

1 ある条件・目的・要求などに、うまくあてはまること。かなっていること。ふさわしいこと。また、そのさま。「工場の建設に適当な土地」「この仕事に適当する人材」
2 程度などが、ほどよいこと。また、そのさま。「調味料を適当に加える」「一日の適当な仕事量」
3 やり方などが、いいかげんであること。また、そのさま。悪い意味で用いられる。「客を適当にあしらう」「適当な返事でごまかす」

適当(テキトウ)とは - コトバンク

〜 想定する流れ 〜

  1. データベースからcsvファイルでエクスポート
  2. laravelの storage/db_csv に設置
  3. php artisan DB:seed 実行

今回は1行目(カラム名が記述される)を含むcsvファイルをエクスポートしました。

環境

laravel 5.8
MySQL 5.7

つかった技術

CSVファイルを読み込む

SplFileObject クラスを利用して読み込む

$file = new SplFileObject("storage/db_csv/{$tableName}.csv");
// オプションをつける
$file->setFlags(
	\SplFileObject::READ_CSV |
	\SplFileObject::READ_AHEAD |
	\SplFileObject::SKIP_EMPTY |
	\SplFileObject::DROP_NEW_LINE
);

オプションについて

// CSV 列として行を読み込む
SplFileObject::READ_CSV

// 先読み/巻き戻しで読み出す
SplFileObject::READ_AHEAD

// ファイルの空行を読み飛す。READ_AHEADが有効であることが必須
SplFileObject::SKIP_EMPTY

// 行末の改行を読み飛ばす
SplFileObject::DROP_NEW_LINE

DBの情報を取得する

テーブルの一覧を取得

foreach (DB::select('SHOW TABLES') as $table) {
	$dbName = config('database.connections.mysql.database');
	$tableName = $table->{'Tables_in_' . $dbName};
}

カラム名とその型のリスト

$columns = Schema::connection('mysql')->getColumnListing($tableName)

注意

  • json型非対応
    • これを使って対応出来るとは思うのですが、とりあえず非対応です。そのうちがんばります。

コード

<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;

class DatabaseSeeder extends Seeder
{

    /**
     * storage/db_csv に{テーブル名}.csvを設置する
     */
    public function run()
    {
        foreach (DB::select('SHOW TABLES') as $table) {
            $dbName = config('database.connections.mysql.database');
            $tableName = $table->{'Tables_in_' . $dbName};

            // migrationsは除外
            if ($tableName === 'migrations') continue;

            // 存在しなければ除外
            if (!File::exists("storage/db_csv/{$tableName}.csv")) continue;

            // もう何か入っていれば除外
            if (DB::table($tableName)->count() > 0) continue;

            $this->saveByCsv($tableName);
        }

    }

    /**
     * csvからdbへ保存
     * @param $tableName
     */
    private function saveByCsv($tableName)
    {
        $file = new SplFileObject("storage/db_csv/{$tableName}.csv");
        $file->setFlags(
            \SplFileObject::READ_CSV |
            \SplFileObject::READ_AHEAD |
            \SplFileObject::SKIP_EMPTY |
            \SplFileObject::DROP_NEW_LINE
        );

        // 存在するカラムを取得
        $columns = Schema::connection('mysql')->getColumnListing($tableName);

        $columnTypes = [];
        foreach ($columns as $column) {
            $columnTypes[$column] = Schema::connection('mysql')->getConnection()->getDoctrineColumn($tableName, $column)->toArray()['type'];
        }

        $records = [];
        foreach($file as $key => $line) {
            // 1行目はカラム名なので除外
            if ($key === 0) continue;

            $record = [];
            foreach ($columns as $columnKey => $column) {

                // 存在しない場合は適当に値を入れる
                if (empty($line[$columnKey]))
                    $line[$columnKey] = $this->columnTypeToDummyValues($columnTypes[$column]);

                // nullが文字列になっているので修正
                if ($line[$columnKey] === "NULL")
                    $line[$columnKey] = null;

                $record += [$column => $line[$columnKey]];
            }
            $records[] = $record;

        }

        DB::table($tableName)->insert($records);

    }

    /**
     * カラムのタイプ毎にダミーの値を返す
     *
     * @param $type
     * @return mixed|string
     */
    private function columnTypeToDummyValues($type)
    {
        $ColumnTypes = [
            'Doctrine\DBAL\Types\StringType' => 'ダミーテキスト',
            'Doctrine\DBAL\Types\IntegerType' => 1,
            'Doctrine\DBAL\Types\SmallIntType' => 10,
            'Doctrine\DBAL\Types\DateTimeType' => \Carbon\Carbon::now(),
            'Doctrine\DBAL\Types\TextType' => 'ダミーテキスト',
            'Doctrine\DBAL\Types\BooleanType' => true,
            'Doctrine\DBAL\Types\DecimalType' => 0.1,
        ];
        $text = '';
        foreach ($ColumnTypes as $ColumnType => $value) {
            if ($type instanceof $ColumnType) {
                $text = $value;
            }
        }
        return $text;
    }
}

おわり

とりあえず適当に入ります。
エラーが怖くて空の時はダミーデータ挿入するようにしていますが、動いていないようです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?