BeginnerEngineerBlog
中の人
中の人

laravelのmigrationsテーブルはいつどこで作成されているのか

公開: 2021-09-18 14:25
更新: 2023-04-06 15:00
3394
laravel6.x php migrations command ダイブ
laravelでテーブルを作成するといつの間にかmigrationsテーブルが作成されているので、いつ、どこで作成されているのか気になったので少し調べてみました。

こんにちは!

laravelのmigrationファイルでテーブルを作成すると、いつの間にかmigrationsディレクトリにないテーブルが作成されてますよね?
そう、migrationsテーブルです。

migrationsテーブルは、文字通りmigrationファイルを管理しているテーブルですが、勝手にlaravelチュートリアル #3 でlaravelのデフォルトで用意されているテーブルを作成する記事を書いている時に、どこで作成されているんだろう?と思って、結構ネット記事漁ったのですが全然見つかりませんでした。
結局ソースコードを追っていったら(なんとなく)解決しましたので、追っていった過程を含めて皆さんと共有したいと思います!


作られるタイミング


ひとまず、どのタイミングでmigrationsテーブルが作成されるか考えたところ、一番最初にテーブルを作成した時には既にmigrationsテーブルが出来上がっているので、おそらく一番最初に

php artisan migrate

のコマンドを打った時に作成されてそうだなーと感じました。

ということで、このコマンドの実行ファイルを見てみれば解決しそうだなと感じ、それっぽいファイルを探したらありました。


MigrateCommand.php


こいつです。
このファイルは、vendor/laravel/framework/src/Illuminate/Database/Console/Migrationsディレクトリにあります。

📁 vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/MigrateCommand.php

<?php


namespace Illuminate\Database\Console\Migrations;


use Illuminate\Console\ConfirmableTrait;
use Illuminate\Database\Migrations\Migrator;


class MigrateCommand extends BaseCommand
{
    use ConfirmableTrait;


    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'migrate {--database= : The database connection to use}
                {--force : Force the operation to run when in production}
                {--path=* : The path(s) to the migrations files to be executed}
                {--realpath : Indicate any provided migration file paths are pre-resolved absolute paths}
                {--pretend : Dump the SQL queries that would be run}
                {--seed : Indicates if the seed task should be re-run}
                {--step : Force the migrations to be run so they can be rolled back individually}';


    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Run the database migrations';


    /**
     * The migrator instance.
     *
     * @var \Illuminate\Database\Migrations\Migrator
     */
    protected $migrator;


    /**
     * Create a new migration command instance.
     *
     * @param  \Illuminate\Database\Migrations\Migrator  $migrator
     * @return void
     */
    public function __construct(Migrator $migrator)
    {
        parent::__construct();


        $this->migrator = $migrator;
    }


    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        if (! $this->confirmToProceed()) {
            return;
        }


        $this->prepareDatabase();


        // Next, we will check to see if a path option has been defined. If it has
        // we will use the path relative to the root of this installation folder
        // so that migrations may be run for any path within the applications.
        $this->migrator->setOutput($this->output)
                ->run($this->getMigrationPaths(), [
                    'pretend' => $this->option('pretend'),
                    'step' => $this->option('step'),
                ]);


        // Finally, if the "seed" option has been given, we will re-run the database
        // seed task to re-populate the database, which is convenient when adding
        // a migration and a seed at the same time, as it is only this command.
        if ($this->option('seed') && ! $this->option('pretend')) {
            $this->call('db:seed', ['--force' => true]);
        }
    }


    /**
     * Prepare the migration database for running.
     *
     * @return void
     */
    protected function prepareDatabase()
    {
        $this->migrator->setConnection($this->option('database'));


        if (! $this->migrator->repositoryExists()) {
            $this->call('migrate:install', array_filter([
                '--database' => $this->option('database'),
            ]));
        }
    }
}


/**
 * The name and signature of the console command.
 *
 * @var string
  */
protected $signature = 'migrate ~ 省略 ~';

これがコマンドを定義しているところです。
つまり、
php artisan ~~~~ <-この部分

です。つまり、

php artisan migrate

のコマンドを実行すると、このファイルが実行されるということですね。

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        if (! $this->confirmToProceed()) {
            return;
        }


        $this->prepareDatabase();


        // Next, we will check to see if a path option has been defined. If it has
        // we will use the path relative to the root of this installation folder
        // so that migrations may be run for any path within the applications.
        $this->migrator->setOutput($this->output)
                ->run($this->getMigrationPaths(), [
                    'pretend' => $this->option('pretend'),
                    'step' => $this->option('step'),
                ]);


        // Finally, if the "seed" option has been given, we will re-run the database
        // seed task to re-populate the database, which is convenient when adding
        // a migration and a seed at the same time, as it is only this command.
        if ($this->option('seed') && ! $this->option('pretend')) {
            $this->call('db:seed', ['--force' => true]);
        }
    }

で、これが実際に実行されるプログラムです。

ちなみに、コマンドは自身でも作成することができます。気になった方は調べてみてください。

で、色々と処理が書いてありますが、

$this->prepareDatabase();

この関数を見てみると、

    /**
     * Prepare the migration database for running.
     *
     * @return void
     */
    protected function prepareDatabase()
    {
        $this->migrator->setConnection($this->option('database'));


        if (! $this->migrator->repositoryExists()) {
            $this->call('migrate:install', array_filter([
                '--database' => $this->option('database'),
            ]));
        }
    }

こんな感じで書いてあって、

        if (! $this->migrator->repositoryExists()) {
            $this->call('migrate:install', array_filter([
                '--database' => $this->option('database'),
            ]));
        }

こんな記述があります。

ざっくりみてみると、もし、
repositoryExists()
関数がfalseを返したら、
$this->call('migrate:install', ~~省略~~

この関数を実行します。

という内容になっていますね。

ということで、
repositoryExists()
関数を見てみます。


repositoryExists()関数


この関数は、vendor/larave/framework/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.phpに定義されています。

📁 vendor/larave/framework/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php

~~省略~~

    /**
     * Determine if the migration repository exists.
     *
     * @return bool
     */
    public function repositoryExists()
    {
        $schema = $this->getConnection()->getSchemaBuilder();


        return $schema->hasTable($this->table);
    }

~~省略~~

$schema = $this->getConnection()->getSchemaBuilder();

は、まぁ、なんかデータベースとコネクションするクラスを$schemaへ代入しているのでしょう。(適当)

return $schema->hasTable($this->table);
で、

hasTable($this->table);

で、引数の$this->tableのテーブルが作成されているか判定しています。

php artisan migrate

を実行し、デバッグしたところ、$this->tableには'migrations'がセットされていました。(どこでセットされているかは面倒くさくてみてません。わかりませんでした。)

ということで、要するにここで'migrations'テーブルが既に作成されているか、されていないかを判定しています。
つまり、一番最初にテーブルを作成する時は、migrationsテーブルがないので、必ずfalseを返しますね。
この関数の意味がわかったので、再度MigrateCommand.phpに戻りましょう。


migrate:installコマンド


さて、
📁 vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/MigrateCommand.php

~~省略~~

        if (! $this->migrator->repositoryExists()) {
            $this->call('migrate:install', array_filter([
                '--database' => $this->option('database'),
            ]));
        }

~~省略~~

repositoryExists()

こいつは、一番最初に
php artisan migrate

コマンドを実行すると、必ずfalseが返ってくることがわかりましたので、次に

            $this->call('migrate:install', array_filter([
                '--database' => $this->option('database'),
            ]));

こいつが実行されるので、何してるかみてみましょう。

$this->call('~~~')

これは、コマンドをプログラムで実行する関数です。
ということは、
'migrate:install'

というコマンドが実行されるということですね。array_filterで指定している配列は、よくわかりませんがなんかcall関数に必要ななにかを渡しているんでしょう。(適当)

ということで
'migrate:install'

このコマンドは、vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/InstallCommand.phpに定義されていました。

📁 vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/InstallCommand.php

<?php


namespace Illuminate\Database\Console\Migrations;


use Illuminate\Console\Command;
use Illuminate\Database\Migrations\MigrationRepositoryInterface;
use Symfony\Component\Console\Input\InputOption;


class InstallCommand extends Command
{
    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'migrate:install';


    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create the migration repository';


    /**
     * The repository instance.
     *
     * @var \Illuminate\Database\Migrations\MigrationRepositoryInterface
     */
    protected $repository;


    /**
     * Create a new migration install command instance.
     *
     * @param  \Illuminate\Database\Migrations\MigrationRepositoryInterface  $repository
     * @return void
     */
    public function __construct(MigrationRepositoryInterface $repository)
    {
        parent::__construct();


        $this->repository = $repository;
    }


    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        $this->repository->setSource($this->input->getOption('database'));


        $this->repository->createRepository();


        $this->info('Migration table created successfully.');
    }


    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return [
            ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
        ];
    }
}

ファイルの見方は先ほどのMigrateCommand.phpで説明したとおりです。

(よくよく見返したら
/**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'migrate:install';

$nameて何!?て思ったんですけど、まー'The console command name'て書いてあるんでこのファイルが呼び出されてるんでしょう。(適当)正直よくわかりませんでした。🙇‍♂️

2021/09/21追記
なんとなくの情報で申し訳ないですが、extendsしているCommand.php(vendor/laravel/framework/src/Illuminate/Console/Command.php)に$signatureと$nameがプロパティとして定義されてて、コンストラクターの記述に、$signatureの値がある場合Parser.php(vendor/laravel/framework/src/Illuminate/Console/Paser.php)のparse関数で、$nameと$argumentsと$optionsに分解されて、$nameを親クラス(SymfonyCommand)に渡してて、$signatureではなく$nameが定義されている場合、$nameを親クラス(SymfonyCommand)へ渡してなんか処理しているみたいです。
つまり、$signatureと$nameは、結局SymfonyCommandへ$nameを渡しているみたいなので、オプションとか無い場合は$nameでコマンドを定義しても動くということでした。多分
)

で、handle関数の中身を見てみると、

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        $this->repository->setSource($this->input->getOption('database'));


        $this->repository->createRepository();


        $this->info('Migration table created successfully.');
    }

こんな感じになってて、ざっくり見ていくと、

$this->repository->setSource($this->input->getOption('database'));

これで、$this->repositoryになにかをセットしてて、(適当)

$this->repository->createRepository();

で、レポジトリをクリエイトして(適当)

$this->info('Migration table created successfully.');

で、インフォに「マイグレーションテーブルの作成に成功しました。」をどっかに書き込みか、出力をする(適当)

という処理をしているようです。

お、「マイグレーションテーブルの作成に成功しました。」ということは、

$this->repository->createRepository();

でmigrationsテーブルを作成してそうですね!

ということで、createRepository()関数を見てみましょう。


createRepository()関数


はい、こいつがmigrationsテーブルを作成している関数になります。
この関数は、vendor/laravel/framework/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.phpに書いてあります。

早速中身を見てみましょう。

📁 vendor/laravel/framework/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php

~~省略~~

    /**
     * Create the migration repository data store.
     *
     * @return void
     */
    public function createRepository()
    {
        $schema = $this->getConnection()->getSchemaBuilder();


        $schema->create($this->table, function ($table) {
            // The migrations table is responsible for keeping track of which of the
            // migrations have actually run for the application. We'll create the
            // table to hold the migration file's path as well as the batch ID.
            $table->increments('id');
            $table->string('migration');
            $table->integer('batch');
        });
    }

~~省略~~

がっつりmigrationファイルと同じ書き方してますね!

多分

$schema->create($this->table, function~~~

$this->table

には、

'migrations'

という文字列が入っているんでしょう。(面倒くさくて確認はしてません。🙇‍♂️ )

      $table->increments('id');
      $table->string('migration');
      $table->integer('batch');

このフィールドたちは、migrationsテーブルのフィールドと一致していますので、まず間違いなく、こいつがmigrationsテーブルでしょう。
ようやく、どこでmigrationsテーブルが作成されているのかがわかりましたね!


ちなみに


デフォルトで用意されているmigrationファイルや、私たち開発者が作成したmigrationファイルは、上記の一連の処理の後に実行されています。

📁 vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/MigrateCommand.php

~~省略~~

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        if (! $this->confirmToProceed()) {
            return;
        }


        $this->prepareDatabase();


        // Next, we will check to see if a path option has been defined. If it has
        // we will use the path relative to the root of this installation folder
        // so that migrations may be run for any path within the applications.
        $this->migrator->setOutput($this->output)
                ->run($this->getMigrationPaths(), [
                    'pretend' => $this->option('pretend'),
                    'step' => $this->option('step'),
                ]);


        // Finally, if the "seed" option has been given, we will re-run the database
        // seed task to re-populate the database, which is convenient when adding
        // a migration and a seed at the same time, as it is only this command.
        if ($this->option('seed') && ! $this->option('pretend')) {
            $this->call('db:seed', ['--force' => true]);
        }
    }

~~省略~~


        // Next, we will check to see if a path option has been defined. If it has
        // we will use the path relative to the root of this installation folder
        // so that migrations may be run for any path within the applications.
        $this->migrator->setOutput($this->output)
                ->run($this->getMigrationPaths(), [
                    'pretend' => $this->option('pretend'),
                    'step' => $this->option('step'),
                ]);


->run

が、私たちが作成したmigrationファイルを実行しているっぽいので(適当)、必然的にmigrationsテーブルが一番最初に作成されるというわけです。

また、一度migrationsテーブルが作成されると、上記で説明した

repositoryExists()

が、常にtrueを返すので、もうmigrationsテーブルが作成されないということですね。

あーコード追うの面倒だったけど、わかるとスッキリしますね。これで夜も仕事中もぐっすり寝れそうです。(☝︎ ՞ਊ ՞)☝︎< ギャクニhigh!!


要するに


php artisan migrate

を実行すると、

migrationsテーブルが作成済みかどうかを判定して、作成されてなければmigrationsテーブルを作成して、その次に私たちが作成したmigrationファイルを実行する

という処理をしているということでした。


終わりに


冒頭で説明したように、勝手にlaravelチュートリアル #3 を書いている時に疑問に思って調べてみたのですが、全然ネットに情報がなくて困った(調べ方の問題もあるかと思います。)ので、今回記事にしてみました。
というか、いつどこでmigrationsテーブルが作成されているかなんて、あまり興味ないのかもしれませんね。またはいちいち記事にするような内容でもなかったのかもしれません。
そもそもmigrationファイルを管理するテーブルなんだから、一番初めに作成されるのは当たり前だろって感じですよね。

まぁ、とりあえず、私と同じ疑問を持った方の助けになれば幸いです!

ということで今回の記事は以上になります!

かなりざっくりした内容だったかと思いますが、足りないと感じる部分はご自身で調べてみてください!
0
0
0
0
通信エラーが発生しました。
似たような記事