Laravelを知らない中級（中年）プログラマーがマイグレーションファイルの仕組みを調べてみたけど全くわからない！その１ 「マイグレーションファイルを見てみよう」

# はじめに
はじめまして。ねねにゃんパパです。
新型コロナの影響で世の中いろいろ大変ですね。
僕も「お家にいよう！」にならって引き籠もっていて、少し時間が取れたのでなにかしてみようと思い、ほとんど触ったことのない「**Laravel**」を読んでみることにしました。
どうせ読むのでLaravel7です。まだ情報も少ないっぽいのでいいかなと思いました。
公式の[クイックスタート](https://readouble.com/laravel/4.2/ja/quick.html) を少し触ってみて、なんとなしに「**マイグレーションの作成**」をテーマに決めました。
読めるところまで読んでいきたいと思います。

## 前提
僕は「**Laravel**」はクイックスタートを数回読んだくらいで全く利用経験がありません。普段から使ってる方からしたら見当違いな事を書くかもしれません。
この記事の対象は PHP 中級者でLaravel未経験者及び初心者を想定しています。PHPの基礎的なシンタックスの説明とかは割愛しますのでご了承ください。
「**migrate**」コマンドが叩かれた箇所からなぞるように読んだものを記しているので説明の順序が通常の紹介記事とくらべると分かりづらい部分があるかもしれません。ご容赦ください。

※ コードリーディングの旅は一通り終わってます。清書されていませんが、全容を見たい方は[BLOG](https://xi.ayane.co.jp)で読めます。が、多分読みにくいので、整えたこちらが更新されるのをお待ちになられるのが良いと思います。

## 環境
Laravel Framework 7.2.0
PHP 7.3.15


### INDEX

- [その１「マイグレーションファイルを見てみよう」](https://qiita.com/xioncc/items/1dcec2fbe3485b7e769a)
- [その２「$app['db']って何者？」](https://qiita.com/xioncc/items/cd4a46e5aca16b13174d)
- [その３「Repositoryクラス」](https://qiita.com/xioncc/items/1ce2132594c4596a73ae)
- [その４「Larabelアプリケーションの初期化の流れ」](https://qiita.com/xioncc/items/a17d7f905d4a5abf8103)
- [その５「リポジトリの読込」](https://qiita.com/xioncc/items/dc7df63c2487b02052f3)
- [その６「データベース接続」](https://qiita.com/xioncc/items/3cf9336eecd5f6905831)
- [その７「スキーマビルダー」](https://qiita.com/xioncc/items/04c95ce9ff6d0b7c9d44)
- [その８「migrate コマンドの実行」](https://qiita.com/xioncc/items/4a5166a0963991e33eb5)
- [その９「Kernel::handle()」](https://qiita.com/xioncc/items/07c1f0ba9b40bd230608)




## マイグレーションって何？
マイグレーションとは「移行」「移動」等の意味だそうです。ＰＨＰのフレームワークとかだと対象が大抵データベースになるイメージです。ある環境があってそれと同じ構造のものを別に構築したい場合や、安定して同じ環境を作りたい場合に、テーブル作成、データのエクスポート、インポートの作業を手作業でやっていると工数も馬鹿になりませんし、なによりミスが起こる可能性があります。そのため、実行するだけで要件通りのデータを構築できる仕組みを作っておきます。その仕組みのことをマイグレーションと言います。今回は基本的な空のテーブルを作る簡易な処理ですが、コマンドを叩くとデータベース上にテーブルが出来るまでの流れを一通り追っていきます。

<img width="933" alt="マイグレーションのイメージ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/9041a3df-7430-895c-ccc0-8bf4b8645ee3.png" style="max-width: 70%; margin: auto;">


## マイグレーションファイルの生成
公式の[クイックスタート](https://readouble.com/laravel/4.2/ja/quick.html)に習って試しに``customers``テーブルを定義するためのマイグレーションファイルを生成してみます。

```php:COMMAND-LINE
$ php artisan make:migration create_customers_table --create=customers
Created Migration: 2020_03_23_050035_create_customers_table
```

``PROJECT_ROOT/database/migrations/`` に ``2020_03_23_050035_create_customers_table.php`` として、以下のようなファイルが生成されました。
どんなファイルが出来ているんでしょう。気になります。とても気になります。見てみましょう！

```php:生成されたファイル
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCustomersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('customers', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('customers');
    }
}
```

ん、思ったよりシンプルなファイルですね。これがどんなふうに動くんでしょう！？気になります。
端っこからちょっとずつ読んでみましょう。

## フレームワークのクラスファイルの配置場所はどこ？
生成されたマイグレーションファイルには以下の３つの名前空間が``use``で宣言されています。

```php:create_customers_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
```
Laravelのパッケージは``Illuminate``という名前空間の下で展開されているようです。
これの実体は、``PROJECT_ROOT/vendor/laravel/framework/src/Illuminate/``以下にありました。
``PROJECT_ROOT/vendor/laravel/framework/`` に ``composer.json`` があるので目を通してみると面白いかもしれません。


<img width="997" alt="CreateCustomersTable | Migration" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/110348be-b29d-1cc9-1490-40c066bf0240.png">





## Migrationクラス
生成された``CreateCustomersTable``クラスは``Migration``抽象クラスを継承しています。
``Migration``抽象クラスは以下のように定義されていました。

```php:Illuminate\Database\Migrations\Migration
<?php
namespace Illuminate\Database\Migrations;
abstract class Migration
{
    /**
     * The name of the database connection to use.
     *
     * @var string|null
     */
    protected $connection;
    /**
     * Enables, if supported, wrapping the migration within a transaction.
     *
     * @var bool
     */
    public $withinTransaction = true;
    /**
     * Get the migration connection name.
     *
     * @return string|null
     */
    public function getConnection()
    {
        return $this->connection;
    }
}
```

データベース接続名``$connection``とトランザクションを使用するかどうかの``$withinTransaction``(デフォルト:``true``)の２つの変数と、単純に``$connection``が返される``getConnection()``メソッドがが宣言されています。
もう少し色々書いてあるかなと思いましたがあてが外れました。引き返しましょう。

## Schemaクラス
``CreateCustomersTable``クラスは``Migration``抽象クラスに``up()``と``down()``の２つのメソッドを追加しています。どちらも``Schema``クラスの``static``メソッドを呼び出しています。``Schema``クラスは``Illuminate\Support\Facades\Schema`` で以下のように定義されています。

```php:Illuminate\Support\Facades\Schema
<?php
namespace Illuminate\Support\Facades;
/**
 * @method static \Illuminate\Database\Schema\Builder create(string $table, \Closure $callback)
 * @method static \Illuminate\Database\Schema\Builder drop(string $table)
 * @method static \Illuminate\Database\Schema\Builder dropIfExists(string $table)
 * @method static \Illuminate\Database\Schema\Builder table(string $table, \Closure $callback)
 * @method static \Illuminate\Database\Schema\Builder rename(string $from, string $to)
 * @method static void defaultStringLength(int $length)
 * @method static bool hasTable(string $table)
 * @method static bool hasColumn(string $table, string $column)
 * @method static bool hasColumns(string $table, array $columns)
 * @method static \Illuminate\Database\Schema\Builder disableForeignKeyConstraints()
 * @method static \Illuminate\Database\Schema\Builder enableForeignKeyConstraints()
 * @method static void registerCustomDoctrineType(string $class, string $name, string $type)
 *
 * @see \Illuminate\Database\Schema\Builder
 */
class Schema extends Facade
{
    /**
     * Get a schema builder instance for a connection.
     *
     * @param  string|null  $name
     * @return \Illuminate\Database\Schema\Builder
     */
    public static function connection($name)
    {
        return static::$app['db']->connection($name)->getSchemaBuilder();
    }
    /**
     * Get a schema builder instance for the default connection.
     *
     * @return \Illuminate\Database\Schema\Builder
     */
    protected static function getFacadeAccessor()
    {
        return static::$app['db']->connection()->getSchemaBuilder();
    }
```

こちらも簡素なクラスですね。なかなか扉が開きません。
``Schema``クラスは``Facade``抽象クラスを継承して``connection()``と ``getFacadeAccessor()``の２つのメソッドを定義しています。呼び出している``create()``メソッドと``dropIfExists()``メソッドは見当たりません。


## Facade抽象クラス
``Facade``抽象クラスを見てみましょう。
``Illuminate\Support\Facades\Facade``で定義されいてます。
ようやく少しだけ色々書かれています！
``Facade``ってくらいですから、これが「**正面玄関**」なのかもしれませんね！
少しだけコードが長いので中略します。

```php:Illuminate\Support\Facades\Facade
<?php
namespace Illuminate\Support\Facades;
use Closure;
use Mockery;
use Mockery\MockInterface;
use RuntimeException;
abstract class Facade
{
    /**
     * The application instance being facaded.
     *
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected static $app;
    /**
     * The resolved object instances.
     *
     * @var array
     */
    protected static $resolvedInstance;
 
    /* ========== 中略 ========== */
    /**
     * Get the root object behind the facade.
     *
     * @return mixed
     */
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }
    /**
     * Get the registered name of the component.
     *
     * @return string
     *
     * @throws \RuntimeException
     */
    protected static function getFacadeAccessor()
    {
        throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
    }
    /**
     * Resolve the facade root instance from the container.
     *
     * @param  object|string  $name
     * @return mixed
     */
    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }
        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }
        if (static::$app) {
            return static::$resolvedInstance[$name] = static::$app[$name];
        }
    }
 
    /* ========== 中略 ========== */
    /**
     * Handle dynamic, static calls to the object.
     *
     * @param  string  $method
     * @param  array  $args
     * @return mixed
     *
     * @throws \RuntimeException
     */
    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();
        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }
        return $instance->$method(...$args);
    }
}
```

ざっと見た感じ、呼び出したいメソッドは見当たりません。
最下部に**マジックメソッド**``__callStatic``が定義されています。
PHPの現場でも、ポリシーや担当によっては**マジックメソッド**を書く機会が無い方もいるかも知れません。これは、アクセス不能メソッドを静的コンテキストで実行したときに起動するものです。おそらくこれが叩かれるのでしょう。
今回のコードリーディングの旅では**マジックメソッド**が３〜４種類読み解く鍵として出てきます。無計画に使うのはおすすめできませんが、とても便利な機能です。馴染みがない方で興味のある方は[php.net](https://www.php.net/manual/ja/language.oop5.magic.php)を読んでみると楽しいと思います。


#### __callStatic()
マジックメソッド``__callStatic()``を見てみましょう。
第一引数はコールしたメソッド名、第二引数は渡されたパラメータが配列の形で格納されています。``__callStatic``のはじめに自身の``getFacadeRoot()``を呼び出して戻り値を``$instance``に格納しています。``getFacadeRoot()``は自身の ``resolveFacadeInstance()``に自身の``getFacadeAccessor()``を引数として渡しています。少しややこしくなってきましたね。一つずつ見ましょう。


```php:Illuminate\Support\Facades\Schema::getFacadeAccessor()
/**
 * Get a schema builder instance for the default connection.
 *
 * @return \Illuminate\Database\Schema\Builder
 */
 protected static function getFacadeAccessor()
 {
	return static::$app['db']->connection()->getSchemaBuilder();
	}
```

``getFacadeAccessor()``は継承された``Schema``クラスでオーバーライドされています。


```php:Illuminate\Support\Facades\Facade::getFacadeAccessor()
/**
 * Get the registered name of the component.
 *
 * @return string
 *
 * @throws \RuntimeException
 */
 protected static function getFacadeAccessor()
 {
	throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
	}
```

上書きされなかった場合、例外``RuntimeException``が「Facade does not implement getFacadeAccessor method.」というメッセージで投げられるように抽象クラスで定義されています。

``getFacadeAccessor()``メソッドが``return``しているスタティックメソッドで「ん？」と気になるヤツがいます。

**$app['db']**

こいつはなんでしょう。Laravel使いには当たり前の存在なのかもしれない曲者の匂いがします。正体を知りたくなりますね。調べてみましょう。




## 次回
次回は**$app['db']**について追ってみたいと思います。

続く☆