XIONCC

【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか? :その6:スキーマビルダー

Schema::getFacadeAccessor()static::$app['db']->connection() の戻り値が接続を確立した Connection インスタンスであるというところまでは読みました。続きを見てみましょう。

Illuminate/Support/Facades/Schema::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() メソッドを見てみましょう。

Connection::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 を引数に自身を指定し生成します。SchemaBuilderIlluminate\Database\Schema\Builder が実体です。見てみましょう。

Builder::__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();
    }

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


Illuminate/Support/Facades/Facade::__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);
    }

さて、Schema::getFacadeAccessor() の戻り値がBuilder インスタンスであるというところまで読めました。
この記事の冒頭で叩いたコマンド、php artisan make:migration create_customers_table --create=customers によって生成されたマイグレーションファイルの up() 及び down() メソッドに記述されている staticメソッド Schema::create() 等は Schema クラスが継承している抽象クラス Facade__callStatic() がコールされ、その処理の中の $instance = static::getFacadeRoot()$instance には 今まで追ってきてたどり着いた Builder インスタンス が代入されるはずです。

Illuminate/Support/Facades/Facade::__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です。

インスタンスの代入に失敗した場合、「A facade root has not been set.」というメッセージを添えて、例外 RuntimeException をスローします。

インスタンスが無事代入された場合は、引数として受け取ったメソッド名からコールするメソッドを割り出し、引数として受け取った引数配列を指定してコールします。最初に戻って生成されたマイグレーションファイルを見てみましょう。

生成されたファイル::up()

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('customers', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }
TITLE
引数、戻り値はありません。

つまり、Builder::create() メソッドを第一引数としてテーブル名のストリング、第二引数としてクロージャーを指定してコールするという設計だということがわかりました。

では、次は実際にマイグレーションが実行される流れを見てみましょう。