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

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

- [その１「マイグレーションファイルを見てみよう」](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)


## スキーマビルダー
前回はデータベース接続について読みました。
``Schema::getFacadeAccessor()``の``static::$app['db']->connection()``の戻り値が接続を確立した``Connection``インスタンスであるというところまでは読みました。続きを見てみましょう。

```php:Illuminate/Support/Facades/Schema&#x3a;&#x3a;getFacadeAccessor()
    /**
     * 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();
    }
```

``Connection::getSchemaBuilder()``メソッドを見てみましょう。

```php:Connection&#x3a;&#x3a;getSchemaBuilder()|関連メソッド
    /**
     * Get a schema builder instance for the connection.
     *
     * @return \Illuminate\Database\Schema\Builder
     */
    public function getSchemaBuilder()
    {
        if (is_null($this->schemaGrammar)) {
            $this->useDefaultSchemaGrammar();
        }
        return new SchemaBuilder($this);
    }
    /**
     * Set the schema grammar to the default implementation.
     *
     * @return void
     */
    public function useDefaultSchemaGrammar()
    {
        $this->schemaGrammar = $this->getDefaultSchemaGrammar();
    }
    /**
     * Get the default schema grammar instance.
     *
     * @return \Illuminate\Database\Schema\Grammars\Grammar
     */
    protected function getDefaultSchemaGrammar()
    {
        //
    }
```

### Connection::getSchemaBuilder()
> 引数はありません。
戻り値は``Builder``インスタンスです。

``$this->schemaGrammar``が``null``の場合、``useDefaultSchemaGrammar()``をコールします。その先では``getDefaultSchemaGrammar()``メソッドがコールされていますが、ロジックは空です。オーバーロードして使うのでしょうか。ちょっと意図がわかりません。
その後に``SchemaBuilder``を引数に自身を渡して生成します。``SchemaBuilder``は``Illuminate\Database\Schema\Builder``が実体です。見てみましょう。

```php:Builder&#x3a;&#x3a;__construct()
    /**
     * Create a new database Schema manager.
     *
     * @param  \Illuminate\Database\Connection  $connection
     * @return void
     */
    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
        $this->grammar = $connection->getSchemaGrammar();
    }
```

```php:Connection&#x3a;&#x3a;getSchemaGrammar()
    /**
     * Get the schema grammar used by the connection.
     *
     * @return \Illuminate\Database\Schema\Grammars\Grammar
     */
    public function getSchemaGrammar()
    {
        return $this->schemaGrammar;
    }
    /**
     * Set the schema grammar used by the connection.
     *
     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
     * @return $this
     */
    public function setSchemaGrammar(Schema\Grammars\Grammar $grammar)
    {
        $this->schemaGrammar = $grammar;
        return $this;
    }
```

### Builder::__construct()
> 第一引数は``Connection``インスタンスです。
戻り値はありません。

``Connection``インスタンスを受け取り、``$this->connection``に代入します。
受け取った``Connection``の``getSchemaGrammar()``メソッドの返り値を``$this->grammar``に代入しています。ただ、先程``Connection::getSchemaBuilder()``を読んだ時に、``$schemaGrammar``は設定されていませんでした。``Builder``クラスには``$grammar``を変更するインターフェースがコンストラクタ以外に見当たりません。``Connection::setSchemaGrammar()``メソッドが用意されているので、何かのタイミングで``Connection``インスタンスにセットした後に``Builder``インスタンスを生成するのかもしれません。とりあえず保留して読みすすめましょう。

```php:Illuminate/Support/Facades/Facade&#x3a;&#x3a;__callStatic()
    /**
     * 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);
    }
```

### Facade::__callStatic()
> 第一引数はストリング型でメソッド名です。
第二引数は配列型で引数をまとめたものです。
戻り値は``mix``です。

さて、``Schema::getFacadeAccessor()``の戻り値が``Builder``インスタンスであるというところまで読めました。
この記事の冒頭で叩いたコマンド、

```php
php artisan make:migration create_customers_table --create=customers
```

によって生成されたマイグレーションファイルの``up()``及び``down()``メソッドに記述されている``static``メソッド``Schema::create()``等は``Schema``クラスが継承している抽象クラス``Facade``の``__callStatic()``がコールされ、その処理の中の``$instance = static::getFacadeRoot()``のステップで``$instance``には今まで追ってきてたどり着いた``Builder``インスタンス が代入されるはずです。

インスタンスの代入に失敗した場合、「A facade root has not been set.」というメッセージを添えて、例外``RuntimeException``をスローします。
インスタンスが無事代入された場合は、引数として受け取ったメソッド名からコールするメソッドを割り出し、引数として受け取った引数配列を指定してコールします。最初に戻って生成されたマイグレーションファイルを見てみましょう。

```php:生成されたファイル&#x3a;&#x3a;up()
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('customers', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }
```

### 生成されたファイル::up()
> 引数、戻り値はありません。

つまり、``Builder::create()``メソッドを第一引数としてテーブル名のストリング、第二引数としてクロージャーを指定してコールするという設計だということがわかりました。
すばらしい！何をやっているのかちょっとわかってしまいましたね！
本日は少し短いですが、次回からだんだん区切りが長くなってきそうです。

## 次回
次は実際にマイグレーションが実行される流れを見てみましょう！
楽しみですね！



続く☆