<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>XIONCC</title>
	<atom:link href="https://xi.ayane.co.jp/feed" rel="self" type="application/rss+xml" />
	<link>https://xi.ayane.co.jp</link>
	<description>AYANE-PROJECT</description>
	<lastBuildDate>Sun, 12 Apr 2020 13:25:18 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://xi.ayane.co.jp/wp-content/uploads/2020/03/cropped-ico-32x32.png</url>
	<title>XIONCC</title>
	<link>https://xi.ayane.co.jp</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">136451185</site>	<item>
		<title>【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１８：Migrator::runUp()</title>
		<link>https://xi.ayane.co.jp/archives/2223</link>
		
		<dc:creator><![CDATA[xi]]></dc:creator>
		<pubDate>Sun, 12 Apr 2020 08:09:02 +0000</pubDate>
				<category><![CDATA[フレームワーク]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[laravel]]></category>
		<category><![CDATA[フレームワークを読む]]></category>
		<guid isPermaLink="false">https://xi.ayane.co.jp/?p=2223</guid>

					<description><![CDATA[<p>runUp() メソッドです。見てみましょう。 Illuminate\Database\Migrations\Migrator::runUp() &#124; 関連メソッド /** * Run "up" a migration i [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/2223">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１８：Migrator::runUp()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><code>runUp()</code> メソッドです。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::runUp() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run "up" a migration instance.
     *
     * @param  string  $file
     * @param  int  $batch
     * @param  bool  $pretend
     * @return void
     */
    protected function runUp($file, $batch, $pretend)
    {
        // First we will resolve a "real" instance of the migration class from this
        // migration file name. Once we have the instances we can run the actual
        // command such as "up" or "down", or we can just simulate the action.
        $migration = $this->resolve(
            $name = $this->getMigrationName($file)
        );

        if ($pretend) {
            return $this->pretendToRun($migration, 'up');
        }

        $this->note("<comment>Migrating:</comment> {$name}");

        $startTime = microtime(true);

        $this->runMigration($migration, 'up');

        $runTime = round(microtime(true) - $startTime, 2);

        // Once we have run a migrations class, we will log that it was run in this
        // repository so that we don't try to run it next time we do a migration
        // in the application. A migration repository keeps the migrate order.
        $this->repository->log($name, $batch);

        $this->note("<info>Migrated:</info>  {$name} ({$runTime} seconds)");
    }
    /**
     * Resolve a migration instance from a file.
     *
     * @param  string  $file
     * @return object
     */
    public function resolve($file)
    {
        $class = Str::studly(implode('_', array_slice(explode('_', $file), 4)));

        return new $class;
    }
    /**
     * Get the name of the migration.
     *
     * @param  string  $path
     * @return string
     */
    public function getMigrationName($path)
    {
        return str_replace('.php', '', basename($path));
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>以前読んだ処理の中で、実行対象のマイグレーションファイルは <code>require_once()</code> で読み込まれていました。<br />
それを実行する手続きを踏むはずです。<br />
<code>getMigrationName()</code> メソッドで拡張子を削除します。<br />
<code>resolve()</code> メソッドでクラス名を取り出します。<br />
マイグレーションファイルはファイルの前半に生成した日付の情報が数字とアンダースコアで連結してあります。まずこれを取り除きます。その後 <code>Str::studly()</code> メソッドでパスカルケースのクラス名に変換しつつキャッシュします。<br />
出来たクラス名を使い生成したインスタンスを変数 <code>$migration</code> に代入します。<br />
次に、 <code>$pretend</code> が <code>true</code> ならば、<code>pretendToRun()</code> メソッドをコールします。<br />
これはおそらくドライランのような使い方をするのでしょう。今回は通りません。<br />
<code>note()</code> メソッドでメッセージを出力します。「Migrating: 2014_10_12_000000_create_users_table」 のような感じですね。</p>
<p>変数 <code>$startTime</code> にクエリ開始時間を代入します。</p>
<p><code>runMigration()</code> メソッドを実行します。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::runMigration()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run a migration inside a transaction if the database supports it.
     *
     * @param  object  $migration
     * @param  string  $method
     * @return void
     */
    protected function runMigration($migration, $method)
    {
        $connection = $this->resolveConnection(
            $migration->getConnection()
        );

        $callback = function () use ($migration, $method) {
            if (method_exists($migration, $method)) {
                $this->fireMigrationEvent(new MigrationStarted($migration, $method));

                $migration->{$method}();

                $this->fireMigrationEvent(new MigrationEnded($migration, $method));
            }
        };

        $this->getSchemaGrammar($connection)->supportsSchemaTransactions()
            && $migration->withinTransaction
                    ? $connection->transaction($callback)
                    : $callback();
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>resolveConnection()</code> メソッドで接続を変数 <code>$connection</code> に代入します。<br />
変数 <code>$callback</code> にコールバックを代入しています。メイン処理を行うクロージャーです。<br />
<code>getSchemaGrammar()</code> メソッドでスキーマグラマーを取得しています。<br />
今回は <code>MySqlGrammar</code> ですね。<br />
取得した <code>MySqlGrammar</code> の <code>supportsSchemaTransactions()</code> メソッドをコールしています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\Grammar::supportsSchemaTransactions()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Check if this Grammar supports schema changes wrapped in a transaction.
     *
     * @return bool
     */
    public function supportsSchemaTransactions()
    {
        return $this->transactions;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>supportsSchemaTransactions()</code> メソッドは <code>$this->transactions</code> を戻しているだけです。<br />
<code>Grammar</code> クラスのこのパラメータの初期値は <code>false</code> です。継承している <code>MySqlGrammar</code> もこのパラメータを上書きはしていません。おそらく、現状の Laravel のグラマーはトランザクションをカバーしていないのでしょう。<br />
このパラメーターが <code>true</code> 且つ 対象マイグレーションインスタンスの <code>$withinTransaction</code> パラメーターが <code>true</code> だった場合、接続の <code>transaction()</code> メソッドにコールバックを渡します。<br />
そうでない場合はコルバックをそのまま実行します。<br />
マイグレーションファイルのスーパークラスとなる <code>Illuminate\Database\Migrations\Migration</code> クラスの <code>$withinTransaction</code> パラメータの初期値は <code>true</code> となっています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Concerns\ManagesTransactions::transactionI()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Execute a Closure within a transaction.
     *
     * @param  \Closure  $callback
     * @param  int  $attempts
     * @return mixed
     *
     * @throws \Throwable
     */
    public function transaction(Closure $callback, $attempts = 1)
    {
        for ($currentAttempt = 1; $currentAttempt <= $attempts; $currentAttempt++) {
            $this->beginTransaction();

            // We'll simply execute the given callback within a try / catch block and if we
            // catch any exception we can rollback this transaction so that none of this
            // gets actually persisted to a database or stored in a permanent fashion.
            try {
                $callbackResult = $callback($this);
            }

            // If we catch an exception we'll rollback this transaction and try again if we
            // are not out of attempts. If we are out of attempts we will just throw the
            // exception back out and let the developer handle an uncaught exceptions.
            catch (Throwable $e) {
                $this->handleTransactionException(
                    $e, $currentAttempt, $attempts
                );

                continue;
            }

            try {
                $this->commit();
            } catch (Throwable $e) {
                $this->handleCommitTransactionException(
                    $e, $currentAttempt, $attempts
                );

                continue;
            }

            return $callbackResult;
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>一応 <code>transaction()</code> メソッドも見てみましょう。 <code>beginTransaction()</code> メソッドをコールしてコールバックを実行し、 <code>commit()</code> メソッドをコールしています。<br />
流れはわかりますね。今回はここは通りません。</p>
<p>コールバックの実行に入ります。クロージャーを読みましょう。以下のようの内容です。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
function () use ($migration, $method) {
    if (method_exists($migration, $method)) {
        $this->fireMigrationEvent(new MigrationStarted($migration, $method));
        $migration->{$method}();
        $this->fireMigrationEvent(new MigrationEnded($migration, $method));
    }
};
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>引数として渡されたマイグレーションクラスとメソッド名が実行可能か検証し、可能なら処理をします。<br />
<code>fireMigrationEvent()</code> はイベントをディスパッチするものでした。<br />
マイグレーション開始と終了のクラスをディスパッチする間で対象メソッドが実行されています。</p>
<p>今回のマイグレーション実行予定は以下のようなものでした。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
2014_10_12_000000_create_users_table.php
2019_08_19_000000_create_failed_jobs_table.php
2020_03_23_050035_create_customers_table.php
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>create_users_table.php</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>class=&#8221;codeTitle&#8221;>PROJECT_ROOT\database\magrations\2014_10_12_000000_create_users_table.php</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>Schema::create()</code> を見てみましょう。<br />
もはや懐かしいですね。初期の頃に <code>Facade::__callStatic()</code> から <code>static::$app['db']->connection()->getSchemaBuilder()</code> がコールされ、戻り値の <code>create()</code> メソッドがコールされるんでしたね。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Builder::create() | createBlueprint()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new table on the schema.
     *
     * @param  string  $table
     * @param  \Closure  $callback
     * @return void
     */
    public function create($table, Closure $callback)
    {
        $this->build(tap($this->createBlueprint($table), function ($blueprint) use ($callback) {
            $blueprint->create();

            $callback($blueprint);
        }));
    }
    /**
     * Create a new command set with a Closure.
     *
     * @param  string  $table
     * @param  \Closure|null  $callback
     * @return \Illuminate\Database\Schema\Blueprint
     */
    protected function createBlueprint($table, Closure $callback = null)
    {
        $prefix = $this->connection->getConfig('prefix_indexes')
                    ? $this->connection->getConfig('prefix')
                    : '';

        if (isset($this->resolver)) {
            return call_user_func($this->resolver, $table, $callback, $prefix);
        }

        return new Blueprint($table, $callback, $prefix);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>tap()</code> ヘルパ関数を<code>createBlueprint()</code> メソッドの戻り値とクロージャーを渡してコールしています。<br />
その戻り値を <code>build()</code> メソッドに引数として渡してコールしています。<br />
ここの流れは <a href="https://xi.ayane.co.jp/archives/1573">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１３：MigrateCommand::handle()</a> で詳細に追いました。</p>
<p><code>createBlueprint()</code> メソッドは、<code>Blueprint</code> インスタンスをテーブル名とクロージャー、プレフィックスを引数として渡して生成し戻しています。</p>
<p><code>tap()</code> ヘルパ関数は第一引数を第二引数のクロージャーに引数として渡してコールします。<br />
クロージャーでは、渡された <code>Blueprint</code> インスタンスの <code>create()</code> メソッドをコールしています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::create()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Indicate that the table needs to be created.
     *
     * @return \Illuminate\Support\Fluent
     */
    public function create()
    {
        return $this->addCommand('create');
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>create</code> コマンドを追加していますね。<br />
その後、 <code>create()</code> メソッドに渡された クロージャーを自身を引数として渡してコールしています。<br />
ややこしいですね。<br />
つまり、マイグレーションファイルのに定義されているクラス、今回は <code>CreateUsersTable</code> の <code>up()</code> メソッドの第二引数として渡されているクロージャーです。テーブルにカラムを追加する処理が入っているようです。</p>
<p><code>tap()</code> ヘルパ関数は第二引数に渡した第一引数を戻します。<br />
こうして手続きを踏んだ <code>Blueprint</code> インスタンスを引数として渡して <code>build()</code> メソッドがコールされます。</p>
<p>SQL文を構築するメソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint SQL生成用メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    public function id($column = 'id')
    {
        return $this->bigIncrements($column);
    }
    public function bigIncrements($column)
    {
        return $this->unsignedBigInteger($column, true);
    }
    public function unsignedBigInteger($column, $autoIncrement = false)
    {
        return $this->bigInteger($column, $autoIncrement, true);
    }
    public function bigInteger($column, $autoIncrement = false, $unsigned = false)
    {
        return $this->addColumn('bigInteger', $column, compact('autoIncrement', 'unsigned'));
    }
    public function addColumn($type, $name, array $parameters = [])
    {
        $this->columns[] = $column = new ColumnDefinition(
            array_merge(compact('type', 'name'), $parameters)
        );

        return $column;
    }
    public function string($column, $length = null)
    {
        $length = $length ?: Builder::$defaultStringLength;

        return $this->addColumn('string', $column, compact('length'));
    }
    public function timestamp($column, $precision = 0)
    {
        return $this->addColumn('timestamp', $column, compact('precision'));
    }
    public function rememberToken()
    {
        return $this->string('remember_token', 100)->nullable();
    }
    public function timestamps($precision = 0)
    {
        $this->timestamp('created_at', $precision)->nullable();

        $this->timestamp('updated_at', $precision)->nullable();
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>以下のようなSQLが生成されそうです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
CREATE TABLE users (
    id bigint unsigned auto_increment primary key not null,
    name varchar(255) not null,
    email varchar(255) not null,
    email_verified_at timestamp null,
    password varchar(255) not null,
    remember_token varchar(100) null,
    created_at timestamp null,
    updated_at timestamp null
);
ALTER TABLE users ADD unique users_email_unique (email);
</pre>
<div class="su-spacer" style="height:20px"></div>
<div class="su-spacer" style="height:30px"></div>
<p>残りのファイルも見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">PROJECT_ROOT\database\magrations\2019_08_19_000000_create_failed_jobs_table.php</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
class CreateFailedJobsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('failed_jobs', function (Blueprint $table) {
            $table->id();
            $table->text('connection');
            $table->text('queue');
            $table->longText('payload');
            $table->longText('exception');
            $table->timestamp('failed_at')->useCurrent();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('failed_jobs');
    }
}
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>以下のようなSQLが生成されそうです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
CREATE TABLE failed_jobs (
    id bigint unsigned auto_increment primary key not null,
    connection text not null,
    queue text not null,
    payload longtext not null,
    exception longtext not null,
    failed_at timestamp  default CURRENT_TIMESTAMP
);
</pre>
<div class="su-spacer" style="height:20px"></div>
<div class="su-spacer" style="height:30px"></div>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">PROJECT_ROOT\database\magrations\2020_03_23_050035_create_customers_table.php</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
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');
    }
}
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>以下のようなSQLが生成されそうです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
CREATE TABLE customers (
    id bigint unsigned auto_increment primary key not null,
    created_at timestamp null,
    updated_at timestamp null
);
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>生成されたSQLを実行しテーブルを３つ作成します。<br />
生成後、現在時間から、処理開始時間を引き、実行にかかった時間を割り出します。<br />
<code>$this->repository</code> 、つまり <code>DatabaseMigrationRepository</code> インスタンスの <code>log()</code> メソッドをマイグレーション名とバッチ番号を引数として渡しコールします。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\DatabaseMigrationRepository::log()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Log that a migration was run.
     *
     * @param  string  $file
     * @param  int  $batch
     * @return void
     */
    public function log($file, $batch)
    {
        $record = ['migration' => $file, 'batch' => $batch];

        $this->table()->insert($record);
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::insert()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Insert a new record into the database.
     *
     * @param  array  $values
     * @return bool
     */
    public function insert(array $values)
    {
        // Since every insert gets treated like a batch insert, we will make sure the
        // bindings are structured in a way that is convenient when building these
        // inserts statements by verifying these elements are actually an array.
        if (empty($values)) {
            return true;
        }

        if (! is_array(reset($values))) {
            $values = [$values];
        }

        // Here, we will sort the insert keys for every record so that each insert is
        // in the same order for the record. We need to make sure this is the case
        // so there are not any errors or problems when inserting these records.
        else {
            foreach ($values as $key => $value) {
                ksort($value);

                $values[$key] = $value;
            }
        }

        // Finally, we will run this query against the database connection and return
        // the results. We will need to also flatten these bindings before running
        // the query so they are all in one huge, flattened array for execution.
        return $this->connection->insert(
            $this->grammar->compileInsert($this, $values),
            $this->cleanBindings(Arr::flatten($values, 1))
        );
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Query\Grammars\MySqlGrammar::compileInsert()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Compile an insert statement into SQL.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  array  $values
     * @return string
     */
    public function compileInsert(Builder $query, array $values)
    {
        if (empty($values)) {
            $values = [[]];
        }

        return parent::compileInsert($query, $values);
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Query\Grammars\MySqlGrammar::compileInsert()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Compile an insert statement into SQL.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  array  $values
     * @return string
     */
    public function compileInsert(Builder $query, array $values)
    {
        // Essentially we will force every insert to be treated as a batch insert which
        // simply makes creating the SQL easier for us since we can utilize the same
        // basic routine regardless of an amount of records given to us to insert.
        $table = $this->wrapTable($query->from);

        if (empty($values)) {
            return "insert into {$table} default values";
        }

        if (! is_array(reset($values))) {
            $values = [$values];
        }

        $columns = $this->columnize(array_keys(reset($values)));

        // We need to build a list of parameter place-holders of values that are bound
        // to the query. Each insert should have the exact same amount of parameter
        // bindings so we will loop through the record and parameterize them all.
        $parameters = collect($values)->map(function ($record) {
            return '('.$this->parameterize($record).')';
        })->implode(', ');

        return "insert into $table ($columns) values $parameters";
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>もう読むのは難しくありませんね。</p>
<p>以下のようなSQLが生成されそうです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
INSERT INTO migrations (migration, batch) values (FILE_NAME, BATCH_NO);
</pre>
<div class="su-spacer" style="height:20px"></div>
<div class="su-spacer" style="height:30px"></div>
<p>その後、<code>note()</code> メソッドを引数に 「<code>Migrated: マイグレーション名 (実行時間 seconds)</code>」 というメッセージを渡しコールしてメッセージを出力し <code>Migrator::runUp()</code> メソッドは完了です。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>runUp()</code> メソッドの処理が終わると、<code>runPending()</code> メソッドは <code>fireMigrationEvent()</code> メソッドでマイグレーション終了をディスパッチして終了します。<br />
<code>runPending()</code> メソッドの実行が狩猟すると、<code>Migrator::run()</code> メソッドはマイグレーションファイル配列を戻します。<br />
コール元は <code>MigrateCommand::handle()</code> でした。マイグレーション処理完了後、 0 を戻します。</p>
<p><code>MigrateCommand::handle()</code> メソッドのコール元は <code>Command::execute()</code> でした。<br />
受け取った値をそのまま戻します。<br />
<code>Command::execute()</code> メソッドのコール元は <code>Symfony\Component\Console\Command\Command::run()</code> でした。<br />
<code>Symfony\Component\Console\Command\Command::run()</code> メソッドは戻り値が整数ならそれを、そうでなけれが 0 を戻します。<br />
<code>Symfony\Component\Console\Command\Command::run()</code> メソッドのコール元は <code>Illuminate\Console\Command::run()</code> でした。<code>Illuminate\Console\Command::run()</code> は受け取った戻り値をそのまま戻します。<br />
<code>Illuminate\Console\Command::run()</code> のコール元は <code>Symfony\Component\Console\Application::doRunCommand()</code> です。<br />
<code>Symfony\Component\Console\Application::doRunCommand()</code> は受け取った戻り値をそのまま戻します。<br />
<code>Symfony\Component\Console\Application::doRunCommand()</code> のコール元は <code>Symfony\Component\Console\Application::doRun()</code> です。<br />
<code>Symfony\Component\Console\Application::doRun()</code> は <code>$this->runningCommand</code> を <code>null</code> に上書きし、戻り値を戻します。<br />
<code>Symfony\Component\Console\Application::doRun()</code> のコール元は <code>Symfony\Component\Console\Application::run()</code> です。<br />
<code>Symfony\Component\Console\Application::run()</code> は受け取った戻り値をそのまま戻します。<br />
<code>Symfony\Component\Console\Application::run()</code> のコール元は <code>Illuminate\Console\Application::run()</code> です。<br />
<code>Illuminate\Console\Application::run()</code> はイベントディスパッチャーにコマンド終了インスタンスをディスパッチした後、戻り値をそのまま戻します。<br />
<code>Illuminate\Console\Application::run()</code> のコール元は <code>Illuminate\Foundation\Console\Kernel::handle()</code> です。<br />
<code>Illuminate\Foundation\Console\Kernel::handle()</code> は戻り値をそのまま戻します。<br />
<code>Illuminate\Foundation\Console\Kernel::handle()</code> のコール元は <code>PROJECT_ROOT/artisan</code> です。<br />
<code>PROJECT_ROOT/artisan</code> は戻り値を変数 <code>$status</code> に代入します。<br />
その後 <code>terminate()</code> メソッドを入力と戻り値のステータス値を引数に渡しコールします。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Foundation\Console\Kernel::terminate()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Terminate the application.
     *
     * @param  \Symfony\Component\Console\Input\InputInterface  $input
     * @param  int  $status
     * @return void
     */
    public function terminate($input, $status)
    {
        $this->app->terminate();
    }
</pre>
<h4 class="codeTitle">Illuminate\Foundation\Application::terminate()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Terminate the application.
     *
     * @return void
     */
    public function terminate()
    {
        foreach ($this->terminatingCallbacks as $terminating) {
            $this->call($terminating);
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>terminate()</code> メソッドはコマンド終了時にコールするコールバックが登録されていた場合はそれをコールします。<br />
そしてとうとう、 <code>exit()</code> メソッドをステータス値を引数として渡してコールして終了です。<br />
長い旅でした。お疲れさまです。</p>

	<div class="st-post-tags">
	TAGS: <a href="https://xi.ayane.co.jp/archives/tag/php" title="php" rel="tag">php</a> / <a href="https://xi.ayane.co.jp/archives/tag/laravel" title="laravel" rel="tag">laravel</a> / <a href="https://xi.ayane.co.jp/archives/tag/%e3%83%95%e3%83%ac%e3%83%bc%e3%83%a0%e3%83%af%e3%83%bc%e3%82%af%e3%82%92%e8%aa%ad%e3%82%80" title="フレームワークを読む" rel="tag">フレームワークを読む</a><br /></div>

<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/2223">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１８：Migrator::runUp()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2223</post-id>	</item>
		<item>
		<title>【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１７：Migrator:: runPending()</title>
		<link>https://xi.ayane.co.jp/archives/2163</link>
		
		<dc:creator><![CDATA[xi]]></dc:creator>
		<pubDate>Sat, 11 Apr 2020 12:35:24 +0000</pubDate>
				<category><![CDATA[フレームワーク]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[laravel]]></category>
		<category><![CDATA[フレームワークを読む]]></category>
		<guid isPermaLink="false">https://xi.ayane.co.jp/?p=2163</guid>

					<description><![CDATA[<p>Migrator::run() の続きを読みましょう。 Illuminate\Database\Migrations\Migrator::run() /** * Run the pending migrations at [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/2163">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１７：Migrator:: runPending()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><code>Migrator::run()</code> の続きを読みましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::run()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run the pending migrations at a given path.
     *
     * @param  array|string  $paths
     * @param  array  $options
     * @return array
     */
    public function run($paths = [], array $options = [])
    {
        // Once we grab all of the migration files for the path, we will compare them
        // against the migrations that have already been run for this package then
        // run each of the outstanding migrations against a database connection.
        $files = $this->getMigrationFiles($paths);

        $this->requireFiles($migrations = $this->pendingMigrations(
            $files, $this->repository->getRan()
        ));

        // Once we have all these migrations that are outstanding we are ready to run
        // we will go ahead and run them "up". This will execute each migration as
        // an operation against a database. Then we'll return this list of them.
        $this->runPending($migrations, $options);

        return $migrations;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->repository->getRan()</code> の戻り値は以下のSQLの結果を配列にしたものでした。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
SELECT migration FROM migrations ORDER BY batch ASC, migration ASC
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>pendingMigrations()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::pendingMigrations()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the migration files that have not yet run.
     *
     * @param  array  $files
     * @param  array  $ran
     * @return array
     */
    protected function pendingMigrations($files, $ran)
    {
        return Collection::make($files)
                ->reject(function ($file) use ($ran) {
                    return in_array($this->getMigrationName($file), $ran);
                })->values()->all();
    }
</pre>
<h4 class="codeTitle">Illuminate\Support\Traits\EnumeratesValues::reject()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a collection of all elements that do not pass a given truth test.
     *
     * @param  callable|mixed  $callback
     * @return static
     */
    public function reject($callback = true)
    {
        $useAsCallable = $this->useAsCallable($callback);

        return $this->filter(function ($value, $key) use ($callback, $useAsCallable) {
            return $useAsCallable
                ? ! $callback($value, $key)
                : $value != $callback;
        });
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>マイグレーションファイル名の配列を引数として渡して <code>Collection::make()</code> メソッドをスタティックコールし、<code>Collection</code> インスタンスを生成したものの <code>reject()</code> メソッドをコールします。<br />
引数として<br />
<code>reject()</code> メソッドは受け取った引数がコールバックならば、それを使ってフィルターします。<br />
マイグレーションファイル名の配列のなかで、先程のSQL実行結果に無いもののみを配列にしてそれを引数として生成した <code>Collection</code> インスタンスを戻します。</p>
<p>戻された <code>Collection</code> インスタンスを <code>$migrations</code> に代入し、<code>requireFiles()</code> メソッドをコールします。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::requireFiles()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Require in all the migration files in a given path.
     *
     * @param  array  $files
     * @return void
     */
    public function requireFiles(array $files)
    {
        foreach ($files as $file) {
            $this->files->requireOnce($file);
        }
    }
</pre>
<h4 class="codeTitle">Illuminate\Filesystem\Filesystem::requireOnce()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Require the given file once.
     *
     * @param  string  $file
     * @return mixed
     */
    public function requireOnce($file)
    {
        require_once $file;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>requireFiles()</code> メソッドは受け取ったファイル名配列を <code>foreach</code> で回して <code>require_once()</code> するだけですね。</p>
<div class="su-spacer" style="height:30px"></div>
<p>さて、<code>runPending()</code> を読みましょう！</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::runPending()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run an array of migrations.
     *
     * @param  array  $migrations
     * @param  array  $options
     * @return void
     */
    public function runPending(array $migrations, array $options = [])
    {
        // First we will just make sure that there are any migrations to run. If there
        // aren't, we will just make a note of it to the developer so they're aware
        // that all of the migrations have been run against this database system.
        if (count($migrations) === 0) {
            $this->fireMigrationEvent(new NoPendingMigrations('up'));

            $this->note('<info>Nothing to migrate.</info>');

            return;
        }

        // Next, we will get the next batch number for the migrations so we can insert
        // correct batch number in the database migrations repository when we store
        // each migration's execution. We will also extract a few of the options.
        $batch = $this->repository->getNextBatchNumber();

        $pretend = $options['pretend'] ?? false;

        $step = $options['step'] ?? false;

        $this->fireMigrationEvent(new MigrationsStarted);

        // Once we have the array of migrations, we will spin through them and run the
        // migrations "up" so the changes are made to the databases. We'll then log
        // that the migration was run so we don't repeat it next time we execute.
        foreach ($migrations as $file) {
            $this->runUp($file, $batch, $pretend);

            if ($step) {
                $batch++;
            }
        }

        $this->fireMigrationEvent(new MigrationsEnded);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>引数として渡されているのは、マイグレーションファイル名の配列と、<code>MigrateCommand::handle()</code> メソッドから渡された第二引数の配列です。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\MigrateCommand::handle()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
$this->migrator->setOutput($this->output)
    ->run($this->getMigrationPaths(), [
        'pretend' => $this->option('pretend'),
          'step' => $this->option('step'),
    ]);
</pre>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::fireMigrationEvent() | note()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Fire the given event for the migration.
     *
     * @param  \Illuminate\Contracts\Database\Events\MigrationEvent  $event
     * @return void
     */
    public function fireMigrationEvent($event)
    {
        if ($this->events) {
            $this->events->dispatch($event);
        }
    }
    /**
     * Write a note to the console's output.
     *
     * @param  string  $message
     * @return void
     */
    protected function note($message)
    {
        if ($this->output) {
            $this->output->writeln($message);
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>runPending()</code> に渡された第一引数、マイグレーションファイルの配列が空の場合、<code>NoPendingMigrations</code> インスタンスを引数に「<code>up</code>」を渡して生成したものを引数に <code>fireMigrationEvent()</code> メソッドをコールします。<code>NoPendingMigrations</code> クラスはコンストラクタに渡されたメソッド名のストリングを保持するだけの小さなクラスです。<br />
<code>fireMigrationEvent()</code> メソッドはイベントにディスパッチしているだけですね。<br />
そして <code>note()</code> メソッドを 「Nothing to migrate.」 をいうメッセージを引数にコールします。<br />
これは、<code>output->writeln()</code> のラッパー関数ですね。メッセージを出力します。<br />
メッセージを出力後に <code>return</code> します。</p>
<div class="su-spacer" style="height:20px"></div>
<p>マイグレーションファイル配列に値が入っていた場合、<code>DatabaseMigrationRepository</code> インスタンスの <code>getNextBatchNumber()</code> メソッドをコールします。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\DatabaseMigrationRepository::getNextBatchNumber()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the next migration batch number.
     *
     * @return int
     */
    public function getNextBatchNumber()
    {
        return $this->getLastBatchNumber() + 1;
    }
    /**
     * Get the last migration batch number.
     *
     * @return int
     */
    public function getLastBatchNumber()
    {
        return $this->table()->max('batch');
    }
    /**
     * Get a query builder for the migration table.
     *
     * @return \Illuminate\Database\Query\Builder
     */
    protected function table()
    {
        return $this->getConnection()->table($this->table)->useWritePdo();
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::max()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Retrieve the maximum value of a given column.
     *
     * @param  string  $column
     * @return mixed
     */
    public function max($column)
    {
        return $this->aggregate(__FUNCTION__, [$column]);
    }
    /**
     * Execute an aggregate function on the database.
     *
     * @param  string  $function
     * @param  array  $columns
     * @return mixed
     */
    public function aggregate($function, $columns = ['*'])
    {
        $results = $this->cloneWithout($this->unions ? [] : ['columns'])
                        ->cloneWithoutBindings($this->unions ? [] : ['select'])
                        ->setAggregate($function, $columns)
                        ->get($columns);

        if (! $results->isEmpty()) {
            return array_change_key_case((array) $results[0])['aggregate'];
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>getNextBatchNumber()</code> メソッドは <code>getLastBatchNumber()</code> メソッドの戻り値に１を加算した値を戻します。<br />
<code>getLastBatchNumber()</code> メソッドは <code>table()</code> メソッドをコールして戻ってきた <code>Builder</code> インスタンスの <code>max()</code> メソッドをコールします。<br />
<code>table()</code> メソッドは接続に対象テーブル名、今回は 「<code>migrations</code>」 をセットして書込PDO接続を指定しています。<br />
<code>max()</code> メソッドは <code>aggregate()</code> メソッドを関数名「<code>max</code>」とカラム名を配列にしたもの、今回は 「<code>['batch']</code>」 を引数として渡してコールします。</p>
<p><code>aggregate()</code> メソッドは SQL文を組み立てて実行していると推測されます。<br />
一つずつ読んでみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::cloneWithout()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Clone the query without the given properties.
     *
     * @param  array  $properties
     * @return static
     */
    public function cloneWithout(array $properties)
    {
        return tap(clone $this, function ($clone) use ($properties) {
            foreach ($properties as $property) {
                $clone->{$property} = null;
            }
        });
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>cloneWithout()</code> を <code>['columns']</code> を引数として渡してコールしています。<br />
<code>cloneWithout()</code> は <code>tap()</code> ヘルパ関数を使い、自身のクローンを加工しています。<br />
引数として渡された配列、今回は <code>['columns']</code> を <code>foreach</code> で回し、その値をプロパティー名としてクローンのプロパティーを <code>null</code> で上書きしています。つまり、指定カラム名をリセットすることになります。戻り値はクローンした <code>Builder</code> インスタンスです。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::cloneWithoutBindings()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Clone the query without the given bindings.
     *
     * @param  array  $except
     * @return static
     */
    public function cloneWithoutBindings(array $except)
    {
        return tap(clone $this, function ($clone) use ($except) {
            foreach ($except as $type) {
                $clone->bindings[$type] = [];
            }
        });
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>次は <code>cloneWithoutBindings()</code> メソッドを <code>['select']</code> を引数として渡してコールしています。<br />
<code>cloneWithoutBindings()</code> も <code>tap()</code> ヘルパ関数を使い、自身のクローンを加工しています。<br />
引数として渡された配列、今回は <code>['select']</code> を <code>foreach</code> で回し、クローンのバインドタイプを空配列に上書きしています。つまり、指定したバインドタイプをリセットすることになります。戻り値はクローンした <code>Builder</code> インスタンスです。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::setAggregate()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Set the aggregate property without running the query.
     *
     * @param  string  $function
     * @param  array  $columns
     * @return $this
     */
    protected function setAggregate($function, $columns)
    {
        $this->aggregate = compact('function', 'columns');

        if (empty($this->groups)) {
            $this->orders = null;

            $this->bindings['order'] = [];
        }

        return $this;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>setAggregate()</code> メソッドは受け取った引数を <code>compact()</code> で配列化し、<code>$this->aggregate</code> に代入します。この <code>$this</code> はクローンした <code>Builder</code> インスタンスです。<br />
もし、 <code>groups</code> パラメーターが空だった場合、 <code>orders</code> を <code>null</code> に、<code>bindings['order']</code> を空配列にセットし、自身を返します。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::get()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Execute the query as a "select" statement.
     *
     * @param  array|string  $columns
     * @return \Illuminate\Support\Collection
     */
    public function get($columns = ['*'])
    {
        return collect($this->onceWithColumns(Arr::wrap($columns), function () {
            return $this->processor->processSelect($this, $this->runSelect());
        }));
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>最後に <code>get()</code> メソッドを引数に <code>['batch']</code> を渡しコールします。<br />
<code>collect()</code> ヘルパ関数を使い、結果を引数にして生成した <code>Collection</code> インスタンスを戻しています。中を見てみましょう。<br />
<code>onceWithColumns()</code> メソッドをコールしています。これは少し前に読みましたね。引数として渡しているのは <code>Arr::wrap()</code> staticメソッドとクロージャーです。<br />
<code>Arr::wrap()</code> は引数を配列にキャストして戻します。今回は 「<code>['batch']</code>」 ですのでそのままですね。<br />
クロージャーは <code>MySqlProcessor::processSelect()</code> をコールしています。<br />
引数に自身と <code>$this->runSelect()</code> を渡しています。</p>
<p><code>MySqlProcessor::processSelect()</code> メソッドは第二引数をそのまま戻すものでした。<br />
つまり、主役は <code>$this->runSelect()</code> ということになります。</p>
<p>もう一度見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::runSelect()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run the query as a "select" statement against the connection.
     *
     * @return array
     */
    protected function runSelect()
    {
        return $this->connection->select(
            $this->toSql(), $this->getBindings(), ! $this->useWritePdo
        );
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>今回のポイントは <code>aggregate</code> が設定されているところだと思います。そこを主点に見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::toSql()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the SQL representation of the query.
     *
     * @return string
     */
    public function toSql()
    {
        return $this->grammar->compileSelect($this);
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Query\Grammars\Grammar::compileSelect() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * The components that make up a select clause.
     *
     * @var array
     */
    protected $selectComponents = [
        'aggregate',
        'columns',
        'from',
        'joins',
        'wheres',
        'groups',
        'havings',
        'orders',
        'limit',
        'offset',
        'lock',
    ];
    /**
     * Compile a select query into SQL.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @return string
     */
    public function compileSelect(Builder $query)
    {
        if ($query->unions && $query->aggregate) {
            return $this->compileUnionAggregate($query);
        }

        // If the query does not have any columns set, we'll set the columns to the
        // * character to just get all of the columns from the database. Then we
        // can build the query and concatenate all the pieces together as one.
        $original = $query->columns;

        if (is_null($query->columns)) {
            $query->columns = ['*'];
        }

        // To compile the query, we'll spin through each component of the query and
        // see if that component exists. If it does we'll just call the compiler
        // function for the component which is responsible for making the SQL.
        $sql = trim($this->concatenate(
            $this->compileComponents($query))
        );

        if ($query->unions) {
            $sql = $this->wrapUnion($sql).' '.$this->compileUnions($query);
        }

        $query->columns = $original;

        return $sql;
    }
    /**
     * Compile the components necessary for a select clause.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @return array
     */
    protected function compileComponents(Builder $query)
    {
        $sql = [];

        foreach ($this->selectComponents as $component) {
            // To compile the query, we'll spin through each component of the query and
            // see if that component exists. If it does we'll just call the compiler
            // function for the component which is responsible for making the SQL.
            if (isset($query->$component) && ! is_null($query->$component)) {
                $method = 'compile'.ucfirst($component);

                $sql[$component] = $this->$method($query, $query->$component);
            }
        }

        return $sql;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>このあたりはだいぶ読みましたのでわりと読めるようになってきていると思います。<br />
<code>compileSelect()</code> メソッドがコールされ、ロジックの手順を踏み <code>compileComponents()</code> メソッドがコールされます。<br />
<code>$this->selectComponents[]</code> にはコンパイル対象のSQL句が格納されていてそれを <code>foreach</code> で回します。その中に今回のポイントとなる <code>aggregate</code> があります。ロジックの中で 「<code>compileAggregate()</code>」 というメソッド名が整形されそれを対象に <code>Builder</code> インスタンスと <code>$query->aggregate</code> が引数として渡されてコールされます。<code>$query->aggregate</code> の内容は以下のようなイメージです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
array(2) {
  ["function"]=>
  string(3) "max"
  ["columns"]=>
  string(5) "batch"
}
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>compileAggregate()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Grammars\Grammar::compileAggregate()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Compile an aggregated select clause.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  array  $aggregate
     * @return string
     */
    protected function compileAggregate(Builder $query, $aggregate)
    {
        $column = $this->columnize($aggregate['columns']);

        // If the query has a "distinct" constraint and we're not asking for all columns
        // we need to prepend "distinct" onto the column name so that the query takes
        // it into account when it performs the aggregating operations on the data.
        if (is_array($query->distinct)) {
            $column = 'distinct '.$this->columnize($query->distinct);
        } elseif ($query->distinct && $column !== '*') {
            $column = 'distinct '.$column;
        }

        return 'select '.$aggregate['function'].'('.$column.') as aggregate';
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>columnize()</code> してます。これは <code>$this->wrap()</code> したものを <code>implode()</code> するものでした。特に変化はなさそうです。<br />
次に <code>$query->distinct</code> パラメータを見ています。 DISTINCT句を使っているところはありませんのでここも通りません。<br />
戻されるのは以下のようなSQLだと思います。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
SELECT MAX(batch) as aggregate FROM migrations
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>SQL文を戻された <code>compileComponents()</code> メソッドは変数 <code>$sql[]</code> 配列に代入して戻します。<br />
配列を戻された <code>compileSelect()</code> メソッドは <code>implode()</code> して文字列に変換しトリムしたものを変数 <code>$sql</code> に代入したものを戻します。<code>toSql()</code> は受け取った戻り値をそのまま戻します。<br />
<code>runSelect()</code> は受け取った SQL文を使いPDOステートメントを生成しクエリを実行し <code>fetchAll()</code> た結果の配列を戻します。 <code>get()</code> メソッドは受け取った配列を引数として生成した <code>Collection</code> インスタンスを戻します。<br />
<code>aggregate()</code> メソッドは受け取った <code>Collection</code> インスタンスに情報が含まれていた場合、その結果のキーを全て小文字に変換した配列の 「<code>aggregate</code>」 がキーの値を戻します。<br />
<code>max()</code> <code>getLastBatchNumber()</code> と値が戻されて、<code>getNextBatchNumber()</code> メソッドで戻り値に 1 を加算した数が戻されます。<code>null</code> のケースの例外処理が抜けているような気がします。気にせず進みましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>Migrator::runPending()</code> メソッドの戻り値が <code>$batch</code> に代入されました。<br />
これは、最終バッチ番号をインクリメントし、次のバッチ番号を求める値ですね。</p>
<p>次にオプションの <code>$pretend</code> と <code>$step</code> を整えます。今回は使わなそうですね。<br />
<code>fireMigrationEvent()</code> メソッドで <code>MigrationsStarted</code> インスタンスをディスパッチしてます。<br />
<code>MigrationsStarted</code> は定義がほぼ空っぽのクラスです。イベント登録処理のためのものでしょう。</p>
<div class="su-spacer" style="height:30px"></div>
<p>いよいよマイグレーションファイルの処理の開始ですね。<br />
<code>runPending()</code> メソッドが引数として受け取ったマイグレーションファイルの配列を <code>foreach</code> で回します。<br />
<code>$this->runUp()</code> メソッドをファイルとバッチ番号、<code>pretend</code> オプションを引数として渡しコールします。<br />
ステップオプションが付いている場合は処理ごとにバッチ番号をインクリメントします。</p>
<p>その１８に続きます。</p>

	<div class="st-post-tags">
	TAGS: <a href="https://xi.ayane.co.jp/archives/tag/php" title="php" rel="tag">php</a> / <a href="https://xi.ayane.co.jp/archives/tag/laravel" title="laravel" rel="tag">laravel</a> / <a href="https://xi.ayane.co.jp/archives/tag/%e3%83%95%e3%83%ac%e3%83%bc%e3%83%a0%e3%83%af%e3%83%bc%e3%82%af%e3%82%92%e8%aa%ad%e3%82%80" title="フレームワークを読む" rel="tag">フレームワークを読む</a><br /></div>

<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/2163">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１７：Migrator:: runPending()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2163</post-id>	</item>
		<item>
		<title>【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１６：Migrator::run()</title>
		<link>https://xi.ayane.co.jp/archives/2074</link>
		
		<dc:creator><![CDATA[xi]]></dc:creator>
		<pubDate>Fri, 10 Apr 2020 12:07:07 +0000</pubDate>
				<category><![CDATA[フレームワーク]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[laravel]]></category>
		<category><![CDATA[フレームワークを読む]]></category>
		<guid isPermaLink="false">https://xi.ayane.co.jp/?p=2074</guid>

					<description><![CDATA[<p>run() メソッドの続きです。随分離れてしまったのでもう一度見てみましょう。 Illuminate\Database\Migrations\Migrator::run() /** * Run the pending m [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/2074">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１６：Migrator::run()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><code>run()</code> メソッドの続きです。随分離れてしまったのでもう一度見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::run()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run the pending migrations at a given path.
     *
     * @param  array|string  $paths
     * @param  array  $options
     * @return array
     */
    public function run($paths = [], array $options = [])
    {
        // Once we grab all of the migration files for the path, we will compare them
        // against the migrations that have already been run for this package then
        // run each of the outstanding migrations against a database connection.
        $files = $this->getMigrationFiles($paths);

        $this->requireFiles($migrations = $this->pendingMigrations(
            $files, $this->repository->getRan()
        ));

        // Once we have all these migrations that are outstanding we are ready to run
        // we will go ahead and run them "up". This will execute each migration as
        // an operation against a database. Then we'll return this list of them.
        $this->runPending($migrations, $options);

        return $migrations;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>getMigrationFiles()</code> メソッドの戻り値を <code>$files</code> に代入するところまで読みました。<br />
次は <code>requireFiles()</code> メソッドがコールされています。引数から見てみましょう。<br />
<code>pendingMigrations()</code> メソッドをマイグレーションファイル配列と <code>$this->repository->getRan()</code> の戻り値を渡してコールした戻り値を <code>$migrations</code> に代入しています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::pendingMigrations()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the migration files that have not yet run.
     *
     * @param  array  $files
     * @param  array  $ran
     * @return array
     */
    protected function pendingMigrations($files, $ran)
    {
        return Collection::make($files)
                ->reject(function ($file) use ($ran) {
                    return in_array($this->getMigrationName($file), $ran);
                })->values()->all();
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Migrations\DatabaseMigrationRepository::getRan()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the completed migrations.
     *
     * @return array
     */
    public function getRan()
    {
        return $this->table()
                ->orderBy('batch', 'asc')
                ->orderBy('migration', 'asc')
                ->pluck('migration')->all();
    }
    /**
     * Get a query builder for the migration table.
     *
     * @return \Illuminate\Database\Query\Builder
     */
    protected function table()
    {
        return $this->getConnection()->table($this->table)->useWritePdo();
    }
    /**
     * Resolve the database connection instance.
     *
     * @return \Illuminate\Database\Connection
     */
    public function getConnection()
    {
        return $this->resolver->connection($this->connection);
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\ConnectionResolver::connection()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get a database connection instance.
     *
     * @param  string|null  $name
     * @return \Illuminate\Database\ConnectionInterface
     */
    public function connection($name = null)
    {
        if (is_null($name)) {
            $name = $this->getDefaultConnection();
        }

        return $this->connections[$name];
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>まず、<code>getRan()</code> メソッドを読みましょう。<br />
<code>table()</code> メソッドは、<code>getConnection</code> で接続を取得し、戻り値の<code>table()</code> メソッドをコールします。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Connection::table() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Begin a fluent query against a database table.
     *
     * @param  \Closure|\Illuminate\Database\Query\Builder|string  $table
     * @param  string|null  $as
     * @return \Illuminate\Database\Query\Builder
     */
    public function table($table, $as = null)
    {
        return $this->query()->from($table, $as);
    }

    /**
     * Get a new query builder instance.
     *
     * @return \Illuminate\Database\Query\Builder
     */
    public function query()
    {
        return new QueryBuilder(
            $this, $this->getQueryGrammar(), $this->getPostProcessor()
        );
    }
    /**
     * Get the query grammar used by the connection.
     *
     * @return \Illuminate\Database\Query\Grammars\Grammar
     */
    public function getQueryGrammar()
    {
        return $this->queryGrammar;
    }
    /**
     * Get the query post processor used by the connection.
     *
     * @return \Illuminate\Database\Query\Processors\Processor
     */
    public function getPostProcessor()
    {
        return $this->postProcessor;
    }
    /**
     * Create a new database connection instance.
     *
     * @param  \PDO|\Closure  $pdo
     * @param  string  $database
     * @param  string  $tablePrefix
     * @param  array  $config
     * @return void
     */
    public function __construct($pdo, $database = '', $tablePrefix = '', array $config = [])
    {
        $this->pdo = $pdo;

        // First we will setup the default properties. We keep track of the DB
        // name we are connected to since it is needed when some reflective
        // type commands are run such as checking whether a table exists.
        $this->database = $database;

        $this->tablePrefix = $tablePrefix;

        $this->config = $config;

        // We need to initialize a query grammar and the query post processors
        // which are both very important parts of the database abstractions
        // so we initialize these to their default values while starting.
        $this->useDefaultQueryGrammar();

        $this->useDefaultPostProcessor();
    }
    /**
     * Set the query post processor to the default implementation.
     *
     * @return void
     */
    public function useDefaultPostProcessor()
    {
        $this->postProcessor = $this->getDefaultPostProcessor();
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\MySqlConnection::getDefaultPostProcessor() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the default post processor instance.
     *
     * @return \Illuminate\Database\Query\Processors\MySqlProcessor
     */
    protected function getDefaultPostProcessor()
    {
        return new MySqlProcessor;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>table()</code> メソッドは <code>query()</code> メソッドの戻り値の <code>from()</code> メソッドをコールします。<br />
<code>from()</code> メソッドは後で読みましょう。<br />
<code>query()</code> メソッドは 自身と、 <code>getQueryGrammar()</code> メソッドの戻り値、今回は <code>MySqlGrammar</code> インスタンスと、 <code>getPostProcessor()</code> メソッドの戻り値、今回は <code>MySqlProcessor</code> インスタンスを引数に渡し <code>QueryBuilder</code> インスタンスを生成し戻します。<br />
<code>MySqlProcessor</code> は <code>Connection</code> クラスのコンストラクタで <code>useDefaultPostProcessor()</code> メソッドがコールされ <code>useDefaultPostProcessor()</code> メソッドから <code>getDefaultPostProcessor()</code> メソッドがコールされ <code>MySqlConnection::getDefaultPostProcessor</code> で <code>MySqlProcessor</code> インスタンスが生成され戻されます。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>QueryBuilder::from()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::from()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Set the table which the query is targeting.
     *
     * @param  \Closure|\Illuminate\Database\Query\Builder|string  $table
     * @param  string|null  $as
     * @return $this
     */
    public function from($table, $as = null)
    {
        if ($this->isQueryable($table)) {
            return $this->fromSub($table, $as);
        }

        $this->from = $as ? "{$table} as {$as}" : $table;

        return $this;
    }
    /**
     * Determine if the value is a query builder instance or a Closure.
     *
     * @param  mixed  $value
     * @return bool
     */
    protected function isQueryable($value)
    {
        return $value instanceof self ||
               $value instanceof EloquentBuilder ||
               $value instanceof Closure;
    }
    /**
     * Makes "from" fetch from a subquery.
     *
     * @param  \Closure|\Illuminate\Database\Query\Builder|string  $query
     * @param  string  $as
     * @return $this
     *
     * @throws \InvalidArgumentException
     */
    public function fromSub($query, $as)
    {
        [$query, $bindings] = $this->createSub($query);

        return $this->fromRaw('('.$query.') as '.$this->grammar->wrapTable($as), $bindings);
    }
    /**
     * Add a raw from clause to the query.
     *
     * @param  string  $expression
     * @param  mixed  $bindings
     * @return $this
     */
    public function fromRaw($expression, $bindings = [])
    {
        $this->from = new Expression($expression);

        $this->addBinding($bindings, 'from');

        return $this;
    }
    /**
     * Creates a subquery and parse it.
     *
     * @param  \Closure|\Illuminate\Database\Query\Builder|string  $query
     * @return array
     */
    protected function createSub($query)
    {
        // If the given query is a Closure, we will execute it while passing in a new
        // query instance to the Closure. This will give the developer a chance to
        // format and work with the query before we cast it to a raw SQL string.
        if ($query instanceof Closure) {
            $callback = $query;

            $callback($query = $this->forSubQuery());
        }

        return $this->parseSub($query);
    }
    /**
     * Create a new query instance for a sub-query.
     *
     * @return \Illuminate\Database\Query\Builder
     */
    protected function forSubQuery()
    {
        return $this->newQuery();
    }
     /**
     * Get a new instance of the query builder.
     *
     * @return \Illuminate\Database\Query\Builder
     */
    public function newQuery()
    {
        return new static($this->connection, $this->grammar, $this->processor);
    }
    /**
     * Parse the subquery into SQL and bindings.
     *
     * @param  mixed  $query
     * @return array
     *
     * @throws \InvalidArgumentException
     */
    protected function parseSub($query)
    {
        if ($query instanceof self || $query instanceof EloquentBuilder) {
            return [$query->toSql(), $query->getBindings()];
        } elseif (is_string($query)) {
            return [$query, []];
        } else {
            throw new InvalidArgumentException(
                'A subquery must be a query builder instance, a Closure, or a string.'
            );
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>isQueryable()</code> メソッドをコールして戻り値を検証しています。<br />
<code>isQueryable()</code> は引数が、 <code>Builder</code> インスタンス若しくは <code>EloquentBuilder</code> インスタンス若しくはクロージャーであれば <code>true</code> そうでなければ <code>false</code> を戻します。<br />
戻り値が <code>true</code> の場合、 <code>fromSub()</code> メソッドを引数をそのまま渡してコールします。<br />
<code>false</code> の場合は、第二引数の <code>$as</code> が渡されていた場合はテーブル名にエイリアス設定を追加したSQL文を <code>$this->from</code> に代入し 自身を戻します。<br />
<code>fromSub()</code> メソッドを読みましょう。<br />
<code>createSub()</code> メソッドを第一引数を渡してコールした戻り値をクエリ文とバインド値に代入しています。<br />
<code>createSub()</code> メソッドは受け取った引数がクロージャーだった場合、 <code>forSubQuery()</code> メソッドをコールし <code>forSubQuery()</code> から <code>newQuery()</code> をコールし <code>Builder</code> インスタンスを生成して戻します。<br />
クロージャーでない場合は <code>parseSub()</code> メソッドに引数を渡してコールした戻り値を戻します。<br />
<code>parseSub()</code> メソッドは受け取った引数が <code>Builder</code> インスタンス若しくは <code>EloquentBuilder</code> インスタンスだった場合は、 <code>toSql()</code> メソッドと <code>getBuildings()</code> メソッドの戻り値を配列にしたものを戻します。<code>toSql()</code> から見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::toSql()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the SQL representation of the query.
     *
     * @return string
     */
    public function toSql()
    {
        return $this->grammar->compileSelect($this);
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Query\Grammars\Grammar::compileSelect()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Compile a select query into SQL.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @return string
     */
    public function compileSelect(Builder $query)
    {
        if ($query->unions && $query->aggregate) {
            return $this->compileUnionAggregate($query);
        }

        // If the query does not have any columns set, we'll set the columns to the
        // * character to just get all of the columns from the database. Then we
        // can build the query and concatenate all the pieces together as one.
        $original = $query->columns;

        if (is_null($query->columns)) {
            $query->columns = ['*'];
        }

        // To compile the query, we'll spin through each component of the query and
        // see if that component exists. If it does we'll just call the compiler
        // function for the component which is responsible for making the SQL.
        $sql = trim($this->concatenate(
            $this->compileComponents($query))
        );

        if ($query->unions) {
            $sql = $this->wrapUnion($sql).' '.$this->compileUnions($query);
        }

        $query->columns = $original;

        return $sql;
    }
    /**
     * Compile a union aggregate query into SQL.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @return string
     */
    protected function compileUnionAggregate(Builder $query)
    {
        $sql = $this->compileAggregate($query, $query->aggregate);

        $query->aggregate = null;

        return $sql.' from ('.$this->compileSelect($query).') as '.$this->wrapTable('temp_table');
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>Grammar</code> インスタンスの <code>compileSelect()</code> メソッドに自身を引数として渡してコールしています。<br />
<code>$query->unions</code> と <code>$query->aggregate</code> を検証しています。このパラメーターを更新するのは <code>union()</code> と <code>setAggregate()</code> です。こちらをコールした場所はありませんでしたので今回はここは通りません。<br />
ここを通る場合は、 <code>compileUnionAggregate()</code> メソッドがコールされます。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Grammars\Grammar::compileAggregate() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Compile an aggregated select clause.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  array  $aggregate
     * @return string
     */
    protected function compileAggregate(Builder $query, $aggregate)
    {
        $column = $this->columnize($aggregate['columns']);

        // If the query has a "distinct" constraint and we're not asking for all columns
        // we need to prepend "distinct" onto the column name so that the query takes
        // it into account when it performs the aggregating operations on the data.
        if (is_array($query->distinct)) {
            $column = 'distinct '.$this->columnize($query->distinct);
        } elseif ($query->distinct && $column !== '*') {
            $column = 'distinct '.$column;
        }

        return 'select '.$aggregate['function'].'('.$column.') as aggregate';
    }
    /**
     * Convert an array of column names into a delimited string.
     *
     * @param  array  $columns
     * @return string
     */
    public function columnize(array $columns)
    {
        return implode(', ', array_map([$this, 'wrap'], $columns));
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>columnize()</code> メソッドを <code>$aggregate['columns']</code> を引数として渡しコールします。<br />
<code>columnize()</code> メソッドは <code>array_map()</code> で渡されたカラム配列を <code>$this->wrap()</code> を通し、 <code>implode()</code> で連結し戻します。<code>wrap()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Grammars\Grammar::wrap()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Wrap a value in keyword identifiers.
     *
     * @param  \Illuminate\Database\Query\Expression|string  $value
     * @param  bool  $prefixAlias
     * @return string
     */
    public function wrap($value, $prefixAlias = false)
    {
        if ($this->isExpression($value)) {
            return $this->getValue($value);
        }

        // If the value being wrapped has a column alias we will need to separate out
        // the pieces so we can wrap each of the segments of the expression on its
        // own, and then join these both back together using the "as" connector.
        if (stripos($value, ' as ') !== false) {
            return $this->wrapAliasedValue($value, $prefixAlias);
        }

        // If the given value is a JSON selector we will wrap it differently than a
        // traditional value. We will need to split this path and wrap each part
        // wrapped, etc. Otherwise, we will simply wrap the value as a string.
        if ($this->isJsonSelector($value)) {
            return $this->wrapJsonSelector($value);
        }

        return $this->wrapSegments(explode('.', $value));
    }
    /**
     * Determine if the given value is a raw expression.
     *
     * @param  mixed  $value
     * @return bool
     */
    public function isExpression($value)
    {
        return $value instanceof Expression;
    }
    /**
     * Get the value of a raw expression.
     *
     * @param  \Illuminate\Database\Query\Expression  $expression
     * @return string
     */
    public function getValue($expression)
    {
        return $expression->getValue();
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Query\Expression::getValue()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the value of the expression.
     *
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }
    /**
     * Wrap the given JSON selector.
     *
     * @param  string  $value
     * @return string
     *
     * @throws \RuntimeException
     */
    protected function wrapJsonSelector($value)
    {
        throw new RuntimeException('This database engine does not support JSON operations.');
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>このメソッドは見覚えありますね。スキーマーのグラマーにも同じようなものがありました。処理も似ているのかもしれません。読んでみましょう。<br />
<code>isExpression()</code> メソッドで引数を調べています。 <code>Expression</code> インスタンスだった場合は、<code>getValue()</code> メソッドに引数を渡し、<code>getValue()</code> メソッドで引数の <code>getValue()</code> メソッドをコールした戻り値を戻しています。<code>Expression::getValue()</code> は <code>$this->value</code> を返しているだけですね。<br />
見たところ、スキーマの時の処理とほぼ同じようです。<br />
１点違う部分は、 <code>isJsonSelector()</code> メソッドを検証しています。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Grammars\Grammar::isJsonSelector()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Determine if the given string is a JSON selector.
     *
     * @param  string  $value
     * @return bool
     */
    protected function isJsonSelector($value)
    {
        return Str::contains($value, '->');
    }
</pre>
<h4 class="codeTitle">Illuminate\Support\Str::contains()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Determine if a given string contains a given substring.
     *
     * @param  string  $haystack
     * @param  string|string[]  $needles
     * @return bool
     */
    public static function contains($haystack, $needles)
    {
        foreach ((array) $needles as $needle) {
            if ($needle !== '' && mb_strpos($haystack, $needle) !== false) {
                return true;
            }
        }

        return false;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>Str::contains()</code> に引数と 「<code>-></code>」 を渡しています。<br />
<code>Str::contains()</code> メソッドは第二引数を <code>foreach</code> で回し 第一引数の中にその文字列があった場合 <code>true</code> を、ない場合は <code>false</code> を返します。つまり <code>mb_strpos()</code> の配列対応関数ですね。</p>
<p>集計カラム用のSQL文が生成されました。<br />
次は <code>$this->distinct</code> が配列か検証します。配列だった場合はこちらも <code>columnize()</code> メソッドでSQL文を生成します。<br />
配列でない場合で且つ <code>null</code> でなく、整形しているSQL文が 「<code>*</code>」 でなかった場合は、 「distinct」文にカラム名を連結しSQL文を生成します。<br />
出来上がったSQL文を使いセブクエリの文字列を戻します。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>compileSelect()</code> に戻りましょう。<br />
引数として受け取った <code>Builder</code> インスタンスのカラムパラメータを検証し、<code>null</code> だった場合、「<code>*</code>」 を代入します。</p>
<p><code>concatenate()</code> に引数として <code>compileComponents()</code> に引数として受け取った <code>Builder</code> インスタンスを渡してコールした戻り値を渡してコールしています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Grammars\Grammar::compileComponents() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * The components that make up a select clause.
     *
     * @var array
     */
    protected 
        $selectComponents = [
        'aggregate',
        'columns',
        'from',
        'joins',
        'wheres',
        'groups',
        'havings',
        'orders',
        'limit',
        'offset',
        'lock',
    ];
    /**
     * Compile the components necessary for a select clause.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @return array
     */
    protected function compileComponents(Builder $query)
    {
        $sql = [];

        foreach ($this->selectComponents as $component) {
            // To compile the query, we'll spin through each component of the query and
            // see if that component exists. If it does we'll just call the compiler
            // function for the component which is responsible for making the SQL.
            if (isset($query->$component) && ! is_null($query->$component)) {
                $method = 'compile'.ucfirst($component);

                $sql[$component] = $this->$method($query, $query->$component);
            }
        }

        return $sql;
    }
    /**
     * Concatenate an array of segments, removing empties.
     *
     * @param  array  $segments
     * @return string
     */
    protected function concatenate($segments)
    {
        return implode(' ', array_filter($segments, function ($value) {
            return (string) $value !== '';
        }));
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>compileComponents()</code> を見てみましょう。<br />
<code>selectComponents</code> を<code>foreach</code> で回して <code>Builder</code> インスタンスに <code>$selectComponents</code> のパラメーターが設定されていた場合、文字列 「compile」 と<code>$selectComponents</code> の値を結合しメソッド名を生成し、そのメソッドをコールした戻り値を SQL配列に代入したものを戻します。<br />
<code>concatenate()</code> メソッドは受け取ったSQL文の配列を <code>implode()</code> したものを戻します。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>compileSelect()</code> の続きです。<br />
引数として受け取った <code>Builder</code> インスタンスに <code>unions</code> のパラメータがあった場合は <code>wrapUnion()</code> メソッドと <code>compileUnions()</code> メソッドの戻り値を連結してSQL文を生成します。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Grammars\Grammar::wrapUnion()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Wrap a union subquery in parentheses.
     *
     * @param  string  $sql
     * @return string
     */
    protected function wrapUnion($sql)
    {
        return '('.$sql.')';
    }
    /**
     * Compile the "union" queries attached to the main query.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @return string
     */
    protected function compileUnions(Builder $query)
    {
        $sql = '';

        foreach ($query->unions as $union) {
            $sql .= $this->compileUnion($union);
        }

        if (! empty($query->unionOrders)) {
            $sql .= ' '.$this->compileOrders($query, $query->unionOrders);
        }

        if (isset($query->unionLimit)) {
            $sql .= ' '.$this->compileLimit($query, $query->unionLimit);
        }

        if (isset($query->unionOffset)) {
            $sql .= ' '.$this->compileOffset($query, $query->unionOffset);
        }

        return ltrim($sql);
    }
    /**
     * Compile a single union statement.
     *
     * @param  array  $union
     * @return string
     */
    protected function compileUnion(array $union)
    {
        $conjunction = $union['all'] ? ' union all ' : ' union ';

        return $conjunction.$this->wrapUnion($union['query']->toSql());
    }
    /**
     * Compile the "order by" portions of the query.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  array  $orders
     * @return string
     */
    protected function compileOrders(Builder $query, $orders)
    {
        if (! empty($orders)) {
            return 'order by '.implode(', ', $this->compileOrdersToArray($query, $orders));
        }

        return '';
    }
    /**
     * Compile the "limit" portions of the query.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  int  $limit
     * @return string
     */
    protected function compileLimit(Builder $query, $limit)
    {
        return 'limit '.(int) $limit;
    }
    /**
     * Compile the "offset" portions of the query.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  int  $offset
     * @return string
     */
    protected function compileOffset(Builder $query, $offset)
    {
        return 'offset '.(int) $offset;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>wrapUnion()</code> メソッドは受け取ったSQL文を「()」で囲んで戻すだけです。<br />
<code>compileUnions()</code> は受け取った <code>Builder</code> インスタンスをforeachで回し、パラメーターごとに文字列整形してSQL文を組み立てていく流れになっていますね。</p>
<p>こうして整えられた SQL文を <code>compileSelect()</code> メソッドは戻しています。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>parseSub()</code> メソッドに戻ります。<br />
もう一つの戻り値 <code>$query->getBindings()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::getBindings()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the current query value bindings in a flattened array.
     *
     * @return array
     */
    public function getBindings()
    {
        return Arr::flatten($this->bindings);
    }
</pre>
<h4 class="codeTitle">Illuminate\Support\Arr::flatten()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Flatten a multi-dimensional array into a single level.
     *
     * @param  iterable  $array
     * @param  int  $depth
     * @return array
     */
    public static function flatten($array, $depth = INF)
    {
        $result = [];

        foreach ($array as $item) {
            $item = $item instanceof Collection ? $item->all() : $item;

            if (! is_array($item)) {
                $result[] = $item;
            } else {
                $values = $depth === 1
                    ? array_values($item)
                    : static::flatten($item, $depth - 1);

                foreach ($values as $value) {
                    $result[] = $value;
                }
            }
        }

        return $result;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>Arr::flatten()</code> メソッドを引数にバインド値を渡して静的コールしています。<br />
<code>Arr::flatten()</code> は多次元配列を再帰的に一次配列に変換して返す関数ですね。</p>
<p><code>parseSub()</code> メソッドの続きです。</p>
<p>受け取った引数が <code>Builder</code> インスタンスでも <code>EloquentBuilder</code> インスタンスでもなかった場合、それがストリング型か検証し、そうだった場合は引数と空の配列を配列に入れて戻します。どれでもなかった場合は「A subquery must be a query builder instance, a Closure, or a string.」 をメッセージを添えて例外 <code>InvalidArgumentException</code> をスローします。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>parseSub()</code> メソッドから戻されたSQL文配列を <code>createSub()</code> メソッドはそのまま <code>fromSub()</code> に戻します。<code>fromSub()</code> は受け取ったクエリ文を整形したものとバインド値を引数として <code>fromRow()</code> メソッドに渡し戻り値を戻します。<br />
<code>fromRaw()</code> メソッドは 受け取ったSQL文を元に <code>Expression</code> インスタンスを生成し、<code>$this->from</code> に代入します。<br />
次に受け取ったバインド値を引数として渡して <code>addBinding()</code> メソッドをコールします。<br />
<code>addBinding()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::addBinding()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * The current query value bindings.
     *
     * @var array
     */
    public $bindings = [
        'select' => [],
        'from' => [],
        'join' => [],
        'where' => [],
        'groupBy' => [],
        'having' => [],
        'order' => [],
        'union' => [],
        'unionOrder' => [],
    ];
    /**
     * Add a binding to the query.
     *
     * @param  mixed  $value
     * @param  string  $type
     * @return $this
     *
     * @throws \InvalidArgumentException
     */
    public function addBinding($value, $type = 'where')
    {
        if (! array_key_exists($type, $this->bindings)) {
            throw new InvalidArgumentException("Invalid binding type: {$type}.");
        }

        if (is_array($value)) {
            $this->bindings[$type] = array_values(array_merge($this->bindings[$type], $value));
        } else {
            $this->bindings[$type][] = $value;
        }

        return $this;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>第二引数として受けたっとタイプが <code>$this->bindings</code> に含まれていない場合は、「<code>Invalid binding type: {$type}.</code>」 とメッセージを添えて例外 <code>InvalidArgumentException</code> をスローします。<br />
受け取った第一引数が配列の場合、<code>$this->bindings[]</code> の第二引数をキーにした値に第一引数を <code>array_merge()</code> したものを <code>array_values()</code> したものを代入します。<br />
配列でなかった場合、<code>$this->bindings[]</code> の第二引数をキーにした配列に第一引数を追加します。<br />
<code>$this->bindings[]</code> へ値の追加をした後自身を戻します。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>addBinding()</code> メソッドから自身を戻された <code>fromRaw()</code> メソッドは <code>fromSub()</code> に自身を戻し、<code>fromSub()</code> メソッドはさらにそれを戻します。</p>
<p><code>Builder::from()</code> メソッドに戻ります。結構経ってしまったのでもう一度見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::from()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Set the table which the query is targeting.
     *
     * @param  \Closure|\Illuminate\Database\Query\Builder|string  $table
     * @param  string|null  $as
     * @return $this
     */
    public function from($table, $as = null)
    {
        if ($this->isQueryable($table)) {
            return $this->fromSub($table, $as);
        }
        $this->from = $as ? "{$table} as {$as}" : $table;
        return $this;
    }
    /**
     * Determine if the value is a query builder instance or a Closure.
     *
     * @param  mixed  $value
     * @return bool
     */
    protected function isQueryable($value)
    {
        return $value instanceof self ||
               $value instanceof EloquentBuilder ||
               $value instanceof Closure;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>今回は第一引数として渡されている <code>$table</code> には <code>$this->table</code> 、つまり 「<code>migrations</code>」 が入っていました。 <code>isQueryable()</code> の戻り値は <code>false</code> ですのでここは通りません。<br />
第二引数 <code>$as</code> は <code>null</code> ですので、<code>$this->from</code> には 「<code>migrations</code>」 が代入されます。<br />
<code>$this->from</code> に適切な値が代入された後に <code>Connection::table()</code> に自身を戻します。<br />
<code>Connection::table()</code> は受け取った戻り値をそのまま <code>DatabaseMigrationRepository::table()</code> メソッドに戻します。</p>
<p>もう一度見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\DatabaseMigrationRepository::table() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the completed migrations.
     *
     * @return array
     */
    public function getRan()
    {
        return $this->table()
                ->orderBy('batch', 'asc')
                ->orderBy('migration', 'asc')
                ->pluck('migration')->all();
    }
    /**
     * Get a query builder for the migration table.
     *
     * @return \Illuminate\Database\Query\Builder
     */
    protected function table()
    {
        return $this->getConnection()->table($this->table)->useWritePdo();
    }
    /**
     * Resolve the database connection instance.
     *
     * @return \Illuminate\Database\Connection
     */
    public function getConnection()
    {
        return $this->resolver->connection($this->connection);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>DatabaseMigrationRepository::table()</code> メソッドは受け取った戻り値の <code>useWritePdo()</code> メソッドをコールし戻り値を戻します。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::useWritePdo()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Use the write pdo for query.
     *
     * @return $this
     */
    public function useWritePdo()
    {
        $this->useWritePdo = true;

        return $this;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>useWritePdo()</code> メソッドは、 <code>useWritePdo</code> パラメータを <code>true</code> に上書きし自身を返します。<br />
<code>DatabaseMigrationRepository::table()</code> メソッドは受け取った戻り値を <code>DatabaseMigrationRepository::getRan()</code> メソッドに戻します。<br />
とても長かったですが、ようやく戻ってきました。戻されるのは <code>Builder</code> インスタンスですね。<br />
<code>Builder</code> インスタンスに、 <code>OrderBy()</code> メソッドをコールしたあと、 <code>pluck()</code> して <code>all()</code> してます。<code>OrderBy()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::orderBy()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Add an "order by" clause to the query.
     *
     * @param  \Closure|\Illuminate\Database\Query\Builder|string  $column
     * @param  string  $direction
     * @return $this
     *
     * @throws \InvalidArgumentException
     */
    public function orderBy($column, $direction = 'asc')
    {
        if ($this->isQueryable($column)) {
            [$query, $bindings] = $this->createSub($column);

            $column = new Expression('('.$query.')');

            $this->addBinding($bindings, $this->unions ? 'unionOrder' : 'order');
        }

        $direction = strtolower($direction);

        if (! in_array($direction, ['asc', 'desc'], true)) {
            throw new InvalidArgumentException('Order direction must be "asc" or "desc".');
        }

        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = [
            'column' => $column,
            'direction' => $direction,
        ];

        return $this;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>isQueryable()</code> メソッドで検証しています。このメソッドは、引数が <code>Builder</code> インスタンス若しくは <code>EloquentBuilder</code> インスタンス若しくはクロージャーの場合は <code>true</code> を、そうでなければ <code>false</code> を戻すものでしたね。<br />
もしその場合は、先程読んだ流れのようにサブクエリを生成する処理をします。<br />
ソート指定をSQL文として整形します。もし、昇順降順以外の文字列が指定されていた場合は「<code>Order direction must be "asc" or "desc".</code>」 とメッセージを添えて例外 <code>InvalidArgumentException</code> をスローします。<br />
<code>$this->unions</code> を検証し、<code>true</code> なら 「<code>unionOrders</code>」、<code>false</code> なら 「orders」 のパラメーター配列に第一引数をカラムとして、第二引数をソート順として代入し、自身を戻します。<br />
今回は、 <code>$this->orders[]</code> に <code>['column' => 'batch', 'direction' => 'asc'], ['column' => 'migration', 'direction' => 'asc']</code> が追加される感じでしょう。<br />
その後、戻された <code>Builder</code> インスタンスの <code>pluck()</code> メソッドがコールされる流れですね。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::pluck() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get an array with the values of a given column.
     *
     * @param  string  $column
     * @param  string|null  $key
     * @return \Illuminate\Support\Collection
     */
    public function pluck($column, $key = null)
    {
        // First, we will need to select the results of the query accounting for the
        // given columns / key. Once we have the results, we will be able to take
        // the results and get the exact data that was requested for the query.
        $queryResult = $this->onceWithColumns(
            is_null($key) ? [$column] : [$column, $key],
            function () {
                return $this->processor->processSelect(
                    $this, $this->runSelect()
                );
            }
        );

        if (empty($queryResult)) {
            return collect();
        }

        // If the columns are qualified with a table or have an alias, we cannot use
        // those directly in the "pluck" operations since the results from the DB
        // are only keyed by the column itself. We'll strip the table out here.
        $column = $this->stripTableForPluck($column);

        $key = $this->stripTableForPluck($key);

        return is_array($queryResult[0])
                    ? $this->pluckFromArrayColumn($queryResult, $column, $key)
                    : $this->pluckFromObjectColumn($queryResult, $column, $key);
    }
    /**
     * Execute the given callback while selecting the given columns.
     *
     * After running the callback, the columns are reset to the original value.
     *
     * @param  array  $columns
     * @param  callable  $callback
     * @return mixed
     */
    protected function onceWithColumns($columns, $callback)
    {
        $original = $this->columns;

        if (is_null($original)) {
            $this->columns = $columns;
        }

        $result = $callback();

        $this->columns = $original;

        return $result;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>まず、<code>onceWithColumns()</code> メソッドをコールしています。引数にはカラム名とクロージャーを渡しています。<br />
<code>onceWithColumns()</code> は、自身の<code>columns</code> の値を一時的に逃してコールバックを実行します。<br />
その後 <code>$this->columns</code> をもとに戻し、コールバックの結果を戻しています。</p>
<p>コールバックを見てみましょう。<br />
<code>$this->processor</code> の <code>processSelect()</code> メソッドをコールしています。<br />
<code>$this->processor</code> は <code>MySqlProcessor</code> でしたね。<br />
引数として渡しているのは自身と <code>$this->runSelect()</code> です。<br />
<code>$this->runSelect()</code> から見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::runSelect()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run the query as a "select" statement against the connection.
     *
     * @return array
     */
    protected function runSelect()
    {
        return $this->connection->select(
            $this->toSql(), $this->getBindings(), ! $this->useWritePdo
        );
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>MySqlConnection::select()</code> をコールしています。引数に <code>$this->toSql()</code> 、<code>$this->getBindings()</code> 、<code>$this->useWritePdo</code> を渡しています。この３つは先程読みましたね。<code>MySqlConnection::select()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Connection::select()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run a select statement against the database.
     *
     * @param  string  $query
     * @param  array  $bindings
     * @param  bool  $useReadPdo
     * @return array
     */
    public function select($query, $bindings = [], $useReadPdo = true)
    {
        return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
            if ($this->pretending()) {
                return [];
            }

            // For select statements, we'll simply execute the query and return an array
            // of the database result set. Each element in the array will be a single
            // row from the database table, and will either be an array or objects.
            $statement = $this->prepared($this->getPdoForSelect($useReadPdo)
                              ->prepare($query));

            $this->bindValues($statement, $this->prepareBindings($bindings));

            $statement->execute();

            return $statement->fetchAll();
        });
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>このメソッドは以前読みましたね。クエリ文とバインド値からPDOステートメントを生成して実行するコールバックを実行して処理時間を計測する処理でした。今回は 「ORDER BY」句が入っています。クエリ生成メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Grammars\Grammar::compileOrders() | compileOrdersToArray()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Compile the "order by" portions of the query.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  array  $orders
     * @return string
     */
    protected function compileOrders(Builder $query, $orders)
    {
        if (! empty($orders)) {
            return 'order by '.implode(', ', $this->compileOrdersToArray($query, $orders));
        }

        return '';
    }
    /**
     * Compile the query orders to an array.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  array  $orders
     * @return array
     */
    protected function compileOrdersToArray(Builder $query, $orders)
    {
        return array_map(function ($order) {
            return $order['sql'] ?? $this->wrap($order['column']).' '.$order['direction'];
        }, $orders);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>渡された第二引数 <code>$orders</code> が設定されていた場合、<code>compileOrdersToArray()</code> メソッドの戻り値の配列を <code>implode()</code> したものを 「<code>ORDER BY</code> 」 と連結して戻します。<br />
設定されていない場合は空文字を戻します。<br />
<code>compileOrdersToArray()</code> は渡された第二引数に「&#8217;sql&#8217;」がキーの値があればそれを、なければカラム名とソート順を半角スペースで連結した文字列を戻します。おそらく以下のようなSQL文が出来るのでしょう。<br />
「<code>SELECT migration FROM migrations ORDER BY batch ASC, migration ASC</code>」</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>runSelect()</code> は出来上がったSQL文の実行結果を配列にしたものを <code>pluck()</code> メソッドの <code>onceWithColumns()</code> の第二引数のコールバックの戻り値としてコールされる関数の第二引数として戻します。</p>
<p><code>processSelect()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder\Processor::processSelect()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Process the results of a "select" query.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  array  $results
     * @return array
     */
    public function processSelect(Builder $query, $results)
    {
        return $results;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>単純に第二引数を戻しているだけですね。</p>
<p><code>onceWithColumns()</code> メソッドか渡されるのは <code>migration</code> と 先程のSQLの結果を配列で戻すコールバックということになります。<br />
<code>onceWithColumns()</code> メソッドはコールバックの結果をそのまま戻していますので、<code>pluck()</code> メソッド内の <code>$queryResult</code> には先程のSQLの実行結果配列が代入されます。</p>
<p><code>pluck()</code> メソッドをもう一度見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::pluck() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    public function pluck($column, $key = null)
    {
        // First, we will need to select the results of the query accounting for the
        // given columns / key. Once we have the results, we will be able to take
        // the results and get the exact data that was requested for the query.
        $queryResult = $this->onceWithColumns(
            is_null($key) ? [$column] : [$column, $key],
            function () {
                return $this->processor->processSelect(
                    $this, $this->runSelect()
                );
            }
        );
        if (empty($queryResult)) {
            return collect();
        }
        // If the columns are qualified with a table or have an alias, we cannot use
        // those directly in the "pluck" operations since the results from the DB
        // are only keyed by the column itself. We'll strip the table out here.
        $column = $this->stripTableForPluck($column);
        $key = $this->stripTableForPluck($key);
        return is_array($queryResult[0])
                    ? $this->pluckFromArrayColumn($queryResult, $column, $key)
                    : $this->pluckFromObjectColumn($queryResult, $column, $key);
    }
    /**
     * Strip off the table name or alias from a column identifier.
     *
     * @param  string  $column
     * @return string|null
     */
    protected function stripTableForPluck($column)
    {
        if (is_null($column)) {
            return $column;
        }

        $seperator = strpos(strtolower($column), ' as ') !== false ? ' as ' : '\.';

        return last(preg_split('~'.$seperator.'~i', $column));
    }
</pre>
<h4 class="codeTitle">Illuminate\Support\helpers.php 抜粋</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the last element from an array.
     *
     * @param  array  $array
     * @return mixed
     */
    function last($array)
    {
        return end($array);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$queryResult</code> が空の場合は情報の入っていない <code>Collection</code> インスタンスを生成してそのまま戻します。</p>
<p>空でない場合は、<code>stripTableForPluck()</code> メソッドをコールします。<br />
カラムとキーをどちらとも通します。<br />
<code>stripTableForPluck()</code> は引数が <code>null</code> の場合そのまま戻します。<br />
<code>null</code> でない場合は引数に 「<code> as </code>」が含まれている場合はセパレーターとして 「<code> as </code>」に、そうでない場合は 「<code>\.</code>」に設定します。<br />
引数をセパレーターで <code>preg_split()</code> で配列にし、最後の値を戻します。</p>
<p>今回の場合はカラムが「<code>migration</code>」、キーは <code>null</code> ですね。</p>
<p>SQLの実行結果配列の 0番目が配列ならば、<code>pluckFromArrayColumn()</code> メソッド、そうでなければ <code>pluckFromObjectColumn()</code> メソッドをコールします。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Query\Builder::pluckFromArrayColumn() | pluckFromObjectColumn()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Retrieve column values from rows represented as arrays.
     *
     * @param  array  $queryResult
     * @param  string  $column
     * @param  string  $key
     * @return \Illuminate\Support\Collection
     */
    protected function pluckFromArrayColumn($queryResult, $column, $key)
    {
        $results = [];

        if (is_null($key)) {
            foreach ($queryResult as $row) {
                $results[] = $row[$column];
            }
        } else {
            foreach ($queryResult as $row) {
                $results[$row[$key]] = $row[$column];
            }
        }

        return collect($results);
    }
    /**
     * Retrieve column values from rows represented as objects.
     *
     * @param  array  $queryResult
     * @param  string  $column
     * @param  string  $key
     * @return \Illuminate\Support\Collection
     */
    protected function pluckFromObjectColumn($queryResult, $column, $key)
    {
        $results = [];

        if (is_null($key)) {
            foreach ($queryResult as $row) {
                $results[] = $row->$column;
            }
        } else {
            foreach ($queryResult as $row) {
                $results[$row->$key] = $row->$column;
            }
        }

        return collect($results);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>pluckFromArrayColumn()</code> を見てみましょう。<br />
渡されたキーが <code>null</code> だった場合は、<code>foreach</code> で回して結果配列のカラム名キーを取得します。<br />
<code>null</code> でない場合は、<code>foreach</code> で回して結果配列の指定キーを取得します。<br />
取得した配列を引数として渡した <code>Collection</code> インスタンスを生成して戻します。<br />
ということで、 <code>getRan()</code> メソッドは以下のSQLが実行された結果の配列が取得されます。<br />
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
SELECT migration FROM migrations ORDER BY batch ASC, migration ASC
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>その１７に続きます</p>

	<div class="st-post-tags">
	TAGS: <a href="https://xi.ayane.co.jp/archives/tag/php" title="php" rel="tag">php</a> / <a href="https://xi.ayane.co.jp/archives/tag/laravel" title="laravel" rel="tag">laravel</a> / <a href="https://xi.ayane.co.jp/archives/tag/%e3%83%95%e3%83%ac%e3%83%bc%e3%83%a0%e3%83%af%e3%83%bc%e3%82%af%e3%82%92%e8%aa%ad%e3%82%80" title="フレームワークを読む" rel="tag">フレームワークを読む</a><br /></div>

<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/2074">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１６：Migrator::run()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2074</post-id>	</item>
		<item>
		<title>【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１５：Connection::statement()</title>
		<link>https://xi.ayane.co.jp/archives/1960</link>
		
		<dc:creator><![CDATA[xi]]></dc:creator>
		<pubDate>Thu, 09 Apr 2020 07:19:15 +0000</pubDate>
				<category><![CDATA[フレームワーク]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[laravel]]></category>
		<category><![CDATA[フレームワークを読む]]></category>
		<guid isPermaLink="false">https://xi.ayane.co.jp/?p=1960</guid>

					<description><![CDATA[<p>Connection::statement() メソッドを見てみましょう。 Illuminate\Database\Connection::statement() /** * Execute an SQL stateme [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1960">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１５：Connection::statement()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><code>Connection::statement()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Connection::statement()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Execute an SQL statement and return the boolean result.
     *
     * @param  string  $query
     * @param  array  $bindings
     * @return bool
     */
    public function statement($query, $bindings = [])
    {
        return $this->run($query, $bindings, function ($query, $bindings) {
            if ($this->pretending()) {
                return true;
            }

            $statement = $this->getPdo()->prepare($query);

            $this->bindValues($statement, $this->prepareBindings($bindings));

            $this->recordsHaveBeenModified();

            return $statement->execute();
        });
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Connection::statement()</h6>
<p>第一引数はストリング型でクエリ文です。<br />
第二引数は配列でバインドする値です<br />
戻り値はブーリアンです。
</p></div>
<div class="su-spacer" style="height:20px"></div>
<p>受け取った引数にクロージャーを加えて <code>run()</code> メソッドをコールしています。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Connection::run()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run a SQL statement and log its execution context.
     *
     * @param  string  $query
     * @param  array  $bindings
     * @param  \Closure  $callback
     * @return mixed
     *
     * @throws \Illuminate\Database\QueryException
     */
    protected function run($query, $bindings, Closure $callback)
    {
        $this->reconnectIfMissingConnection();

        $start = microtime(true);

        // Here we will run this query. If an exception occurs we'll determine if it was
        // caused by a connection that has been lost. If that is the cause, we'll try
        // to re-establish connection and re-run the query with a fresh connection.
        try {
            $result = $this->runQueryCallback($query, $bindings, $callback);
        } catch (QueryException $e) {
            $result = $this->handleQueryException(
                $e, $query, $bindings, $callback
            );
        }

        // Once we have run the query we will calculate the time that it took to run and
        // then log the query, bindings, and execution time so we will report them on
        // the event that the developer needs them. We'll log time in milliseconds.
        $this->logQuery(
            $query, $bindings, $this->getElapsedTime($start)
        );

        return $result;
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Connection::statement()</h6>
<p>第一引数はストリング型でクエリ文です。<br />
第二引数は配列でバインドする値です<br />
第三引数はクロージャーです。<br />
戻り値は <code>mix</code> です。
</div>
<p><code>reconnectIfMissingConnection()</code> メソッドは接続が切れていた場合再接続を試みるものでしたね。<br />
次に <code>$start</code> にクエリ実行前のマイクロタイムを代入しています。<br />
そして <code>runQueryCallback()</code> メソッドを TRY して <code>$result</code> に代入しています。<br />
渡されたクロージャーを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
function ($query, $bindings) {
    if ($this->pretending()) {
        return true;
    }
    $statement = $this->getPdo()->prepare($query);
    $this->bindValues($statement, $this->prepareBindings($bindings));
    $this->recordsHaveBeenModified();
    return $statement->execute();
}
</pre>
<h4 class="codeTitle">Illuminate\Database\Connection::runQueryCallback()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run a SQL statement.
     *
     * @param  string  $query
     * @param  array  $bindings
     * @param  \Closure  $callback
     * @return mixed
     *
     * @throws \Illuminate\Database\QueryException
     */
    protected function runQueryCallback($query, $bindings, Closure $callback)
    {
        // To execute the statement, we'll simply call the callback, which will actually
        // run the SQL against the PDO connection. Then we can calculate the time it
        // took to execute and log the query SQL, bindings and time in our memory.
        try {
            $result = $callback($query, $bindings);
        }

        // If an exception occurs when attempting to run a query, we'll format the error
        // message to include the bindings with SQL, which will make this exception a
        // lot more helpful to the developer instead of just the database's errors.
        catch (Exception $e) {
            throw new QueryException(
                $query, $this->prepareBindings($bindings), $e
            );
        }

        return $result;
    }
    /**
     * Determine if the connection is in a "dry run".
     *
     * @return bool
     */
    public function pretending()
    {
        return $this->pretending === true;
    }
    /**
     * Indicate if any records have been modified.
     *
     * @param  bool  $value
     * @return void
     */
    public function recordsHaveBeenModified($value = true)
    {
        if (! $this->recordsModified) {
            $this->recordsModified = $value;
        }
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Connection::runQueryCallback()</h6>
<p>第一引数はストリング型でクエリ文です。<br />
第二引数は配列でバインドする値です<br />
第三引数はクロージャーです。<br />
戻り値は mix です。
</p></div>
<p>渡されたクロージャーをクエリ文とバインドを渡し TRY しています。<br />
クロージャーは <code>pretending()</code> メソッドをコールしています。戻り値が <code>true</code> ならば <code>true</code> を戻しています。<br />
<code>pretending()</code> は <code>$this->pretending</code> が <code>true</code> かどうかをブーリアンで返しています。 <code>$this->pretending</code> は初期値は <code>false</code> で <code>pretend()</code> メソッドで更新します。<br />
今回は初期値 <code>false</code> なので次に行きます。</p>
<p><code>getPdo()</code> メソッドをコールした戻り値の <code>prepare()</code> メソッドを引数にクエリ文を渡してコールしています。<br />
<code>getPdo()</code> メソッドは <code>PDO</code> インスタンスを戻します。MySQL用のステートメントオブジェクトが生成され <code>$statement</code> に代入されます。 <code>bindValues()</code> は以前読みましたね。今回はバインド用の配列は空ですので処理はされません。<br />
<code>recordsHaveBeenModified()</code> メソッドをコールして <code>recordsModified</code> に <code>true</code> を代入します。<br />
準備された <code>$statement</code> インスタンスの <code>execute()</code> メソッドをコールして SQL を実行し結果を戻します。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>run()</code> メソッドに戻ります。<br />
<code>logQuery()</code> をクエリ文とバインド内容と <code>getElapsedTime()</code> にクエリ返し時間を引数に渡しコールした戻り値を引数に渡しコールします。</p>
<p><code>getElapsedTime()</code> からみてみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Connection::getElapsedTime()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the elapsed time since a given starting point.
     *
     * @param  int  $start
     * @return float
     */
    protected function getElapsedTime($start)
    {
        return round((microtime(true) - $start) * 1000, 2);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>現在時間からクエリ開始時間を引いて整形してます。クエリ実行にかかった時間を割り出してます。</p>
<p>次は <code>logQuery()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Connection::logQuery()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Log a query in the connection's query log.
     *
     * @param  string  $query
     * @param  array  $bindings
     * @param  float|null  $time
     * @return void
     */
    public function logQuery($query, $bindings, $time = null)
    {
        $this->event(new QueryExecuted($query, $bindings, $time, $this));

        if ($this->loggingQueries) {
            $this->queryLog[] = compact('query', 'bindings', 'time');
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>event()</code> メソッドを <code>QueryExecuted</code> インスタンスを生成したものを引数として渡しコールしています。<br />
<code>QueryExecuted</code> インスタンスは、SQL、実行時間、バインドした値、接続、接続名を記録するためのクラスのようです。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Events\QueryExecuted::__construct()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new event instance.
     *
     * @param  string  $sql
     * @param  array  $bindings
     * @param  float|null  $time
     * @param  \Illuminate\Database\Connection  $connection
     * @return void
     */
    public function __construct($sql, $bindings, $time, $connection)
    {
        $this->sql = $sql;
        $this->time = $time;
        $this->bindings = $bindings;
        $this->connection = $connection;
        $this->connectionName = $connection->getName();
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>event()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Connection::event()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Fire the given event if possible.
     *
     * @param  mixed  $event
     * @return void
     */
    protected function event($event)
    {
        if (isset($this->events)) {
            $this->events->dispatch($event);
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>$this->events</code> がセットされていたばあい、 <code>dispatch()</code> メソッドに <code>$event</code> を渡してコールしています。 <code>$this->events</code> は <code>DatabaseManager::configure()</code> で <code>$this->app['events']</code> が渡されていました。 <code>Dispatcher</code> インスタンスです。<br />
渡された <code>QueryExecuted</code> インスタンスをディスパッチしています。<br />
<code>$this->loggingQueries</code> が <code>ture</code> ならば、 <code>$this->queryLog[]</code> に クエリ文、バインドする値、時間を配列にして代入します。</p>
<p><code>logQuery()</code> は戻りは無いので、 <code>run()</code> メソッドに戻ります。<br />
<code>run()</code> メソッドは PDOステートメントを実行した戻り値を戻します。</p>
<p><code>statement()</code> メソッドは <code>run()</code> メソッドの戻り値を戻します。<br />
これで <code>Blueprint::build()</code> メソッドの処理は完了です。<br />
長い道のりでしたが、<code>Illuminate\Database\Migrations\DatabaseMigrationRepository::createRepository()</code> メソッドの実行が完了しました。<br />
コール元の <code>Illuminate\Console\Command::handle()</code> の続きです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    public function handle()
    {
        $this->repository->setSource($this->input->getOption('database'));
        $this->repository->createRepository();
        $this->info('Migration table created successfully.');
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->info()</code> メソッドを 「Migration table created successfully.」 というメッセージを引数として渡してコールしています。<br />
これは、 <code>Illuminate\Console\Concerns\InteractsWithIO</code> トレイトに定義されています。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Console\Concerns\InteractsWithIO::info() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Write a string as information output.
     *
     * @param  string  $string
     * @param  int|string|null  $verbosity
     * @return void
     */
    public function info($string, $verbosity = null)
    {
        $this->line($string, 'info', $verbosity);
    }
    /**
     * Write a string as standard output.
     *
     * @param  string  $string
     * @param  string|null  $style
     * @param  int|string|null  $verbosity
     * @return void
     */
    public function line($string, $style = null, $verbosity = null)
    {
        $styled = $style ? "<$style>$string</$style>" : $string;

        $this->output->writeln($styled, $this->parseVerbosity($verbosity));
    }
    /**
     * Get the verbosity level in terms of Symfony's OutputInterface level.
     *
     * @param  string|int|null  $level
     * @return int
     */
    protected function parseVerbosity($level = null)
    {
        if (isset($this->verbosityMap[$level])) {
            $level = $this->verbosityMap[$level];
        } elseif (! is_int($level)) {
            $level = $this->verbosity;
        }

        return $level;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>info()</code> メソッドは受け取ったメッセージと 「<code>info</code>」 というスタイル定数と <code>null</code> を引数として渡して <code>line()</code> メソッドをコールします。<br />
<code>line()</code> メソッドは受け取ったメッセージをスタイルを用いて装飾して、 出力の <code>writeln()</code> メソッドをコールしています。<br />
第二引数で渡している <code>parseVerbosity()</code> メソッドですが、受け取った引数から <code>OutputInterface</code> の定数を返します。<br />
<code>writeln()</code> メソッドは出力に受け取った文字列に改行を追加して表示するものでしたね。<br />
コマンドラインに 「Migration table created successfully.」 と表示されます。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>Illuminate/Database/Console/Migrations/MigrateCommand::prepareDatabase()</code> メソッドの <code>call()</code> メソッドの処理が終わりました。続きを読みましょう。</p>
<p><code>prepareDatabase()</code> メソッドをコールしていたのは <code>Illuminate\Database\Console\Migrations\MigrateCommand::handle() </code> メソッドでした。<br />
もう一度見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\MigrateCommand::handle()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        if (! $this->confirmToProceed()) {
            return 1;
        }

        $this->migrator->usingConnection($this->option('database'), function () {
            $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]);
            }
        });

        return 0;
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::setOutput()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Set the output implementation that should be used by the console.
     *
     * @param  \Symfony\Component\Console\Output\OutputInterface  $output
     * @return $this
     */
    public function setOutput(OutputInterface $output)
    {
        $this->output = $output;

        return $this;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->migrator</code> インスタンスの <code>setOutput()</code> メソッドを出力を引数として渡してコールし、戻ってくる <code>Migrator</code> インスタンスの <code>run()</code> メソッドをコールします。<br />
引数として、<code>getMigrationPaths()</code> の戻り値とオプション <code>pretend</code> <code>step</code> を配列にしたものを渡します。</p>
<p><code>getMigrationPaths()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\BaseCommand::getMigrationPaths()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get all of the migration paths.
     *
     * @return array
     */
    protected function getMigrationPaths()
    {
        // Here, 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 the installation folder so our database
        // migrations may be run for any customized path from within the application.
        if ($this->input->hasOption('path') && $this->option('path')) {
            return collect($this->option('path'))->map(function ($path) {
                return ! $this->usingRealPath()
                                ? $this->laravel->basePath().'/'.$path
                                : $path;
            })->all();
        }

        return array_merge(
            $this->migrator->paths(), [$this->getMigrationPath()]
        );
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->input->hasOption('path')</code> がコールされています。<br />
これはなんでしょうか。<code>MigrateCommand</code> インスタンスが生成される時に、スーパークラスの <code>Command</code> クラスのコンストラクタで <code>$this->signature</code> が設定されていた場合 <code>configureUsingFluentDefinition()</code> メソッドがコールされます。そこで、 <code>$this->signature</code> が <code>Parser::parse()</code> メソッドを通してオプションの説明文の配列が取得されます。これを <code>$this->getDefinition()->addOptions($options)</code> したものを参照します。つまり、<code>$this->input->hasOption('path')</code> はコマンド説明を参照して 「<code>--path</code>」 があるか検証しています。<br />
<code>$this-option()</code> メソッドは <code>InteractsWithIO</code> クラスで以下のように定義されています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Console\Concerns\InteractsWithIO::option()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the value of a command option.
     *
     * @param  string|null  $key
     * @return string|array|bool|null
     */
    public function option($key = null)
    {
        if (is_null($key)) {
            return $this->input->getOptions();
        }

        return $this->input->getOption($key);
    }
</pre>
<h4 class="codeTitle">Symfony\Component\Console\Input\Input::getOption()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Returns the option value for a given option name.
     *
     * @return string|string[]|bool|null The option value
     *
     * @throws InvalidArgumentException When option given doesn't exist
     */

    public function getOption(string $name)
    {
        if (!$this->definition->hasOption($name)) {
            throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
        }

        return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>引数としてキーが渡されていなければ、<code>getOptions()</code> の戻り値を、渡されていれば <code>getOption()</code> の戻り値を戻しています。 <code>getOption()</code> メソッドはオプションに指定キーの値があればそれを、なければオプション定義のデフォルト値を返します。<br />
オプション値が返された場合は、環境に合わせてパスを整形しそれを返します。<br />
今回はここは通らず以下を戻します。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
        return array_merge(
            $this->migrator->paths(), [$this->getMigrationPath()]
        );
</pre>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::run()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="paths">
    /**
     * Get all of the custom migration paths.
     *
     * @return array
     */
    public function paths()
    {
        return $this->paths;
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\BaseCommand::getMigrationPath()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="paths">
    /**
     * Get the path to the migration directory.
     *
     * @return string
     */
    protected function getMigrationPath()
    {
        return $this->laravel->databasePath().DIRECTORY_SEPARATOR.'migrations';
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>paths()</code> メソッドと <code>getMigrationPath()</code> メソッドの戻り値を <code>array_merge()</code> してます。 <code>path()</code> メソッドは <code>$this->paths</code> を戻すだけのものです。 <code>$this->paths</code> は代入した場所はありませんでしたので空です。 <code>getMigrationPath()</code> はアプリケーションのデータベースディレクトリ下の 「<code>migrations</code>」 を戻します。今回は、 「<code>PROJECT_ROOT/database/migrations/</code>」 のみの配列が戻る結果となります。 </p>
<p><code>run()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::run()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run the pending migrations at a given path.
     *
     * @param  array|string  $paths
     * @param  array  $options
     * @return array
     */
    public function run($paths = [], array $options = [])
    {
        // Once we grab all of the migration files for the path, we will compare them
        // against the migrations that have already been run for this package then
        // run each of the outstanding migrations against a database connection.
        $files = $this->getMigrationFiles($paths);

        $this->requireFiles($migrations = $this->pendingMigrations(
            $files, $this->repository->getRan()
        ));

        // Once we have all these migrations that are outstanding we are ready to run
        // we will go ahead and run them "up". This will execute each migration as
        // an operation against a database. Then we'll return this list of them.
        $this->runPending($migrations, $options);

        return $migrations;
    }
    /**
     * Get all of the migration files in a given path.
     *
     * @param  string|array  $paths
     * @return array
     */
    public function getMigrationFiles($paths)
    {
        return Collection::make($paths)->flatMap(function ($path) {
            return Str::endsWith($path, '.php') ? [$path] : $this->files->glob($path.'/*_*.php');
        })->filter()->values()->keyBy(function ($file) {
            return $this->getMigrationName($file);
        })->sortBy(function ($file, $key) {
            return $key;
        })->all();
    }
</pre>
<h4 class="codeTitle">Illuminate\Support\Traits\EnumeratesValues::make()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new collection instance if the value isn't one already.
     *
     * @param  mixed  $items
     * @return static
     */
    public static function make($items = [])
    {
        return new static($items);
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Migrations\Migrator::run()</h6>
<p>第一引数はストリング型若しくは配列でマイグレーションファイルが配置されているディレクトリのパスです。<br />
第二引数は配列でオプションです。<br />
戻り値は配列です。
</p></div>
<p><code>getMigrationFiles()</code> メソッドを第一引数として受け取ったパス情報を引数として渡しコールしています。<br />
パスは 「<code>PROJECT_ROOT/database/migrations/</code>」 でした。</p>
<p><code>getMigrationFiles()</code> メソッドは受け取った引数を <code>Collection::make()</code> に引数として渡しスタティックコールしてます。 <code>Collection::make()</code> メソッドは <code>EnumeratesValues</code> トレイトで定義されている、受け取った引数を渡して自身を生成し戻すメソッドです。<br />
戻された <code>Collection</code> インスタンスの <code>flatMap()</code> メソッドをコールバックを渡しコールします。</p>
<p>コールバックから見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
function ($path) {
    return Str::endsWith($path, '.php') ? [$path] : $this->files->glob($path.'/*_*.php');
}
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>Str::endsWith()</code> が使われています。これは第一引数のストリング型変数の終わりの文字列が第二引数と等価だった場合 <code>true</code> をそうでなければ <code>false</code> を返すものでした。つまり、受け取ったパスが拡張子 「.php」 のファイルパスだった場合はそのパスを配列にして戻します。<br />
そうでなかった場合、 <code>Filesystem</code> インスタンスの <code>glob()</code> メソッドを引数にパスと「<code>/*_*.php</code> 」 を連結した文字列を渡してコールしています。ファイル名をワイルドカードとPHP拡張子でフィルタルールを渡しています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Filesystem\Filesystem::glob()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Find path names matching a given pattern.
     *
     * @param  string  $pattern
     * @param  int  $flags
     * @return array
     */
    public function glob($pattern, $flags = 0)
    {
        return glob($pattern, $flags);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>これは <code>glob()</code> しているだけですね。「<code>PROJECT_ROOT/database/</code>」 ディレクトリにあるマイグレーションコマンドで生成されたマイグレーション用のPHPファイルの一覧のパスを配列にして戻しています。<br />
おそらく現状は、先頭にタイムスタンプが入った 「<code>create_user_table.php</code>」「<code>create_failed_jobs_table.php</code>」「<code>create_customers_table.php</code>」 の３ファイルが配置されていると思います。</p>
<p><code>flatMap()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Support\Traits\EnumeratesValues::flatMap()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Map a collection and flatten the result by a single level.
     *
     * @param  callable  $callback
     * @return static
     */
    public function flatMap(callable $callback)
    {
        return $this->map($callback)->collapse();
    }
</pre>
<h4 class="codeTitle">Illuminate\Support\Collection::map()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run a map over each of the items.
     *
     * @param  callable  $callback
     * @return static
     */
    public function map(callable $callback)
    {
        $keys = array_keys($this->items);

        $items = array_map($callback, $this->items, $keys);

        return new static(array_combine($keys, $items));
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>引数として渡されたコールバックを自身の <code>map()</code> メソッドに渡して戻り値の <code>collapse()</code> メソッドをコールしています。<br />
<code>map()</code> メソッドは <code>$items</code> を渡されたコールバックでフィルタリングした後、<code>array_combine()</code> したものを引数として生成した <code>Collection</code> インスタンスを戻します。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Support\Collection::filter() | values()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run a filter over each of the items.
     *
     * @param  callable|null  $callback
     * @return static
     */
    public function filter(callable $callback = null)
    {
        if ($callback) {
            return new static(Arr::where($this->items, $callback));
        }

        return new static(array_filter($this->items));
    }
    /**
     * Reset the keys on the underlying array.
     *
     * @return static
     */
    public function values()
    {
        return new static(array_values($this->items));
    }
</pre>
<h4 class="codeTitle">Illuminate\Support\Arr::where()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Filter the array using the given callback.
     *
     * @param  array  $array
     * @param  callable  $callback
     * @return array
     */
    public static function where($array, callable $callback)
    {
        return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>filter()</code> メソッドは、引数にコールバックがあった場合は <code>Arr::where()</code> に <code>$this->items</code> とコールバックを渡して戻り値を引数として渡した <code>Collection</code> インスタンスを戻します。<br />
<code>Arr::where()</code> メソッドは <code>array_filter()</code> を引数に <code>ARRAY_FILTER_USE_BOTH</code> フラグを添えてコールした戻り値を戻すだけです。<br />
<code>filter()</code> メソッドに引数が渡されていなかった場合は、そのまま <code>array_filter()</code> で <code>$this->items</code> 配列の値が <code>false</code> のものを削除した配列を引数として渡し生成した <code>Collection</code> インスタンスを戻します。</p>
<p><code>values()</code> メソッドは渡された引数を <code>array_values</code> に渡してコールした戻り値を引数として生成した <code>Collection</code> インスタンスを戻します。</p>
<p><code>keyBy()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Support\Collection::keyBy()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Key an associative array by a field or using a callback.
     *
     * @param  callable|string  $keyBy
     * @return static
     */
    public function keyBy($keyBy)
    {
        $keyBy = $this->valueRetriever($keyBy);

        $results = [];

        foreach ($this->items as $key => $item) {
            $resolvedKey = $keyBy($item, $key);

            if (is_object($resolvedKey)) {
                $resolvedKey = (string) $resolvedKey;
            }

            $results[$resolvedKey] = $item;
        }

        return new static($results);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>渡されているコールバックは以下でした。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
function ($file) {
    return $this->getMigrationName($file);
}
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>getMigrationName()</code> メソッドをコールしています。こちらも見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::getMigrationName()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the name of the migration.
     *
     * @param  string  $path
     * @return string
     */
    public function getMigrationName($path)
    {
        return str_replace('.php', '', basename($path));
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>引数として受け取ったパスの最後にある名前の部分だけ取り出し、「<code>.php</code>」 を削除しています。<br />
続きの <code>sortBy()</code> を引数として渡すコールバックと一緒に見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
function ($file, $key) {
    return $key;
}
</pre>
<h4 class="codeTitle">Illuminate\Support\Collection::sortBy()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Sort the collection using the given callback.
     *
     * @param  callable|string  $callback
     * @param  int  $options
     * @param  bool  $descending
     * @return static
     */
    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
    {
        $results = [];

        $callback = $this->valueRetriever($callback);

        // First we will loop through the items and get the comparator from a callback
        // function which we were given. Then, we will sort the returned values and
        // and grab the corresponding values for the sorted keys from this array.
        foreach ($this->items as $key => $value) {
            $results[$key] = $callback($value, $key);
        }

        $descending ? arsort($results, $options)
            : asort($results, $options);

        // Once we have sorted all of the keys in the array, we will loop through them
        // and grab the corresponding model so we can set the underlying items list
        // to the sorted version. Then we'll just return the collection instance.
        foreach (array_keys($results) as $key) {
            $results[$key] = $this->items[$key];
        }

        return new static($results);
    }
</pre>
<h4 class="codeTitle">Illuminate\Support\Traits\EnumeratesValues::valueRetriever() | useAsCallable()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get a value retrieving callback.
     *
     * @param  callable|string|null  $value
     * @return callable
     */
    protected function valueRetriever($value)
    {
        if ($this->useAsCallable($value)) {
            return $value;
        }

        return function ($item) use ($value) {
            return data_get($item, $value);
        };
    }
    /**
     * Determine if the given value is callable, but not a string.
     *
     * @param  mixed  $value
     * @return bool
     */
    protected function useAsCallable($value)
    {
        return ! is_string($value) && is_callable($value);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>sortBy()</code> メソッドは受け取ったコールバックを <code>valueRetriever()</code> メソッドに渡してコールします。<br />
<code>valueRetriever()</code> メソッドは渡された引数が実行可能なコールバックか検証し、問題ない場合はそのまま戻します。<br />
実行可能なコールバックでなかった場合、 <code>data_get()</code> ヘルパ関数を通すクロージャーを戻します。 <code>data_get()</code> は以前も出てきましたね。配列やオブジェクトにドットシンタックスでアクセス出来るようにするものです。<br />
引数として渡されたクロージャーは第二引数をそのまま戻すものでした。<br />
<code>$this->items</code> を <code>foreach</code> で回し、キーと値が同じ配列を生成します。<br />
生成された配列を指定のソート方法でソートします。<br />
ソートされた配列を <code>foreach</code> で回し、キーに対応した <code>$this->items</code> の値を代入し、出来た配列を引数として生成した <code>Collection</code> インスタンスを戻します。<br />
最後の <code>all()</code> メソッドは <code>$this->items</code> をそのまま戻すだけでしたね。</p>
<p>結果、<code>getMigrationFiles()</code> の戻り値は、<code>PROJECT_ROOT/database/migrations</code> のディレクトリにあるマイグレーションファイルを昇順に並べ替えた配列ということですね。</p>
<div class="su-spacer" style="height:30px"></div>
<p>その１６に続きます。</p>

	<div class="st-post-tags">
	TAGS: <a href="https://xi.ayane.co.jp/archives/tag/php" title="php" rel="tag">php</a> / <a href="https://xi.ayane.co.jp/archives/tag/laravel" title="laravel" rel="tag">laravel</a> / <a href="https://xi.ayane.co.jp/archives/tag/%e3%83%95%e3%83%ac%e3%83%bc%e3%83%a0%e3%83%af%e3%83%bc%e3%82%af%e3%82%92%e8%aa%ad%e3%82%80" title="フレームワークを読む" rel="tag">フレームワークを読む</a><br /></div>

<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1960">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１５：Connection::statement()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1960</post-id>	</item>
		<item>
		<title>【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１４：Builder::build()</title>
		<link>https://xi.ayane.co.jp/archives/1730</link>
		
		<dc:creator><![CDATA[xi]]></dc:creator>
		<pubDate>Mon, 06 Apr 2020 14:24:12 +0000</pubDate>
				<category><![CDATA[フレームワーク]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[laravel]]></category>
		<category><![CDATA[フレームワークを読む]]></category>
		<guid isPermaLink="false">https://xi.ayane.co.jp/?p=1730</guid>

					<description><![CDATA[<p>いよいよ builder クラスの build() メソッドです。 なんか同じようなこと何回も言ってる気がしますが、どんどん読んでいきましょう。 Illuminate\Database\Schema\Builder::b [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1730">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１４：Builder::build()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>いよいよ <code>builder</code> クラスの <code>build()</code> メソッドです。<br />
なんか同じようなこと何回も言ってる気がしますが、どんどん読んでいきましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Builder::build()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Execute the blueprint to build / modify the table.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @return void
     */
    protected function build(Blueprint $blueprint)
    {
        $blueprint->build($this->connection, $this->grammar);
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::build() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Execute the blueprint against the database.
     *
     * @param  \Illuminate\Database\Connection  $connection
     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
     * @return void
     */
    public function build(Connection $connection, Grammar $grammar)
    {
        foreach ($this->toSql($connection, $grammar) as $statement) {
            $connection->statement($statement);
        }
    }
    /**
     * Get the raw SQL statements for the blueprint.
     *
     * @param  \Illuminate\Database\Connection  $connection
     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
     * @return array
     */
    public function toSql(Connection $connection, Grammar $grammar)
    {
        $this->addImpliedCommands($grammar);

        $statements = [];

        // Each type of command has a corresponding compiler function on the schema
        // grammar which is used to build the necessary SQL statements to build
        // the blueprint element, so we'll just call that compilers function.
        $this->ensureCommandsAreValid($connection);

        foreach ($this->commands as $command) {
            $method = 'compile'.ucfirst($command->name);

            if (method_exists($grammar, $method) || $grammar::hasMacro($method)) {
                if (! is_null($sql = $grammar->$method($this, $command, $connection))) {
                    $statements = array_merge($statements, (array) $sql);
                }
            }
        }

        return $statements;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>Builder::build()</code> メソッドは先程準備した <code>Blueprint</code> インスタンスを引数に受け取ります。<br />
<code>Blueprint::build()</code> メソッドを <code>MySqlConnection</code> と <code>MySqlGrammar</code> インスタンスを引数に渡しコールします。<br />
<code>Blueprint::build()</code> メソッドは受け取った引数２つを <code>toSql()</code> メソッドに渡し戻り値を <code>foreach</code> で回します。</p>
<p><code>toSql()</code> メソッドは引数として受け取った <code>MySqlGrammar</code> インスタンスを <code>addImpliedCommands()</code> メソッドに渡します。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint:: addImpliedCommands()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Add the commands that are implied by the blueprint's state.
     *
     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
     * @return void
     */
    protected function addImpliedCommands(Grammar $grammar)
    {
        if (count($this->getAddedColumns()) > 0 && ! $this->creating()) {
            array_unshift($this->commands, $this->createCommand('add'));
        }
        if (count($this->getChangedColumns()) > 0 && ! $this->creating()) {
            array_unshift($this->commands, $this->createCommand('change'));
        }
        $this->addFluentIndexes();
        $this->addFluentCommands($grammar);
    }
    /**
     * Get the columns on the blueprint that should be added.
     *
     * @return \Illuminate\Database\Schema\ColumnDefinition[]
     */
    public function getAddedColumns()
    {
        return array_filter($this->columns, function ($column) {
            return ! $column->change;
        });
    }
</pre>
<h4 class="codeTitle">Illuminate\Support\Fluent::__get() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Dynamically retrieve the value of an attribute.
     *
     * @param  string  $key
     * @return mixed
     */
    public function __get($key)
    {
        return $this->get($key);
    }
    /**
     * Get an attribute from the fluent instance.
     *
     * @param  string  $key
     * @param  mixed  $default
     * @return mixed
     */
    public function get($key, $default = null)
    {
        if (array_key_exists($key, $this->attributes)) {
            return $this->attributes[$key];
        }

        return value($default);
    }

</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>addImpliedCommands()</code> メソッドは <code>getAddedColumns()</code> の戻り値の配列の数を検証します。<br />
<code>getAddedColumns()</code> は <code>$this->columns</code> 配列に含まれる <code>ColumnDefinition</code> インスタンスの中で、<code>change</code> のパラメーターがあるものを省いた配列を戻します。<br />
<code>ColumnDefinition</code> クラスは <code>Fluent</code> クラスを継承しています。<br />
<code>Fluent</code> クラスはマジックメソッド <code>__get()</code> が定義してあり、<code>get()</code> メソッドの戻り値を戻します。 <code>get()</code> メソッドは <code>$this->attributes[]</code> に引数をキーとした値があればそれを、なければデフォルト指定された値を。指定がなければ <code>null</code> を戻します。<code>array_filter()</code> はコールバックが <code>true</code> を戻すもののみを配列にしたものを戻しますので、<code>$column->change</code> が <code>null</code> のもののみ配列にしたものが戻ってきます。<code>$this->columns</code> には <code>id</code> <code>migration</code> <code>batch</code> が登録された <code>ColumnDefinition</code> が入っています。そのどれも <code>change</code> をセットはしていませんので、そのまま戻ってきます。</p>
<p><code>getAddedColumns()</code> の戻り値の配列の数が 0 より大きかった場合、さらに <code>$this->creating()</code> メソッドを検証します。</p>
<p><code>creating()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint:: creating()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Determine if the blueprint has a create command.
     *
     * @return bool
     */
    protected function creating()
    {
        return collect($this->commands)->contains(function ($command) {
            return $command->name === 'create';
        });
    }
</pre>
<h4 class="codeTitle">Illuminate\Support\helpers.php 抜粋</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a collection from the given value.
     *
     * @param  mixed  $value
     * @return \Illuminate\Support\Collection
     */
    function collect($value = null)
    {
        return new Collection($value);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>collect()</code> ヘルパ関数を使っています。<br />
<code>collect()</code> ヘルパ関数は引数として受け取った値を引数として渡して <code>Collection</code> インスタンスを生成し、それを戻しています。<br />
つまり、 <code>$this->commands</code> を引数として渡して生成した <code>Collection</code> インスタンスの <code>contains()</code> メソッドをクロージャーを引数として渡してコールするという流れになります。<br />
<code>Collection</code> クラスを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Support\Collection 抜粋</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    use EnumeratesValues, Macroable;
    /**
     * The items contained in the collection.
     *
     * @var array
     */
    protected $items = [];

    /**
     * Create a new collection.
     *
     * @param  mixed  $items
     * @return void
     */
    public function __construct($items = [])
    {
        $this->items = $this->getArrayableItems($items);
    }
    /**
     * Determine if an item exists in the collection.
     *
     * @param  mixed  $key
     * @param  mixed  $operator
     * @param  mixed  $value
     * @return bool
     */
    public function contains($key, $operator = null, $value = null)
    {
        if (func_num_args() === 1) {
            if ($this->useAsCallable($key)) {
                $placeholder = new stdClass;

                return $this->first($key, $placeholder) !== $placeholder;
            }

            return in_array($key, $this->items);
        }

        return $this->contains($this->operatorForWhere(...func_get_args()));
    }
    /**
     * Get the first item from the collection passing the given truth test.
     *
     * @param  callable|null  $callback
     * @param  mixed  $default
     * @return mixed
     */
    public function first(callable $callback = null, $default = null)
    {
        return Arr::first($this->items, $callback, $default);
    }
</pre>
<h4 class="codeTitle">Illuminate\Support\Traits\EnumeratesValues::getArrayableItems</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Results array of items from Collection or Arrayable.
     *
     * @param  mixed  $items
     * @return array
     */
    protected function getArrayableItems($items)
    {
        if (is_array($items)) {
            return $items;
        } elseif ($items instanceof Enumerable) {
            return $items->all();
        } elseif ($items instanceof Arrayable) {
            return $items->toArray();
        } elseif ($items instanceof Jsonable) {
            return json_decode($items->toJson(), true);
        } elseif ($items instanceof JsonSerializable) {
            return (array) $items->jsonSerialize();
        } elseif ($items instanceof Traversable) {
            return iterator_to_array($items);
        }

        return (array) $items;
    }
    /**
     * Determine if the given value is callable, but not a string.
     *
     * @param  mixed  $value
     * @return bool
     */
    protected function useAsCallable($value)
    {
        return ! is_string($value) && is_callable($value);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>Collection</code> クラスはコンストラクタで受け取った引数を <code>getArrayableItems()</code> メソッドに渡した戻り値を変数 <code>$this->items</code> に代入しています。今回入るのは、「<code>create</code> 」が登録された<code>Fluent</code> インスタンスが入った配列です。<br />
<code>getArrayableItems()</code> はトレイト <code>EnumeratesValues</code> に定義されています。これは前にも見たことがありますね。引数の型に合わせて適切に配列型に直し戻すメソッドでしたね。つまり、<code>Collection::$items</code> は配列型であることが保証されます。<br />
では <code>contains()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<p>受け取った引数が１つのみでそれがコールバック関数だった場合、<code>stdClass</code> を生成し、<code>first()</code> メソッドをコールバックと <code>stdClass</code> を引数として渡してコールし、戻ってきたものが <code>stdClass</code> と等価であるか検証し、等価でなければ <code>true</code> そうでなければ <code>false</code> を戻します。<br />
<code>first()</code> メソッドを見てみましょう。</p>
<p><code>Arr::first()</code> を登録されたコマンド配列とコールバックを引数として渡してコールしています。<br />
<code>Arr::first()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Support\Arr::first()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Return the first element in an array passing a given truth test.
     *
     * @param  iterable  $array
     * @param  callable|null  $callback
     * @param  mixed  $default
     * @return mixed
     */
    public static function first($array, callable $callback = null, $default = null)
    {
        if (is_null($callback)) {
            if (empty($array)) {
                return value($default);
            }

            foreach ($array as $item) {
                return $item;
            }
        }

        foreach ($array as $key => $value) {
            if ($callback($value, $key)) {
                return $value;
            }
        }

        return value($default);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>引数として渡されたコールバックが <code>null</code> で、渡された配列が空ならばデフォルトを、空でなければ１番目の配列の中身を戻しています。<br />
コールバック関数が渡されていた場合、配列を <code>foreach</code> で回し、 値とキーを引数にコールバックをコールした戻り値が <code>false</code> でなければその値を返します。<br />
<code>foreach</code> が全て回されコールバックの戻り値が全て <code>false</code> だった場合、デフォルト値を戻します。<br />
今回渡されたコールバックは以下でした。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
return collect($this->commands)->contains(function ($command) {
    return $command->name === 'create';
});
</pre>
<div class="su-spacer" style="height:20px"></div>
配列として渡されたものはコマンド配列で中身は <code>ColumnDefinition</code> インスタンスでした。<code>ColumnDefinition</code> インスタンスで <code>name</code> が「<code>create</code>」 のものを返すという処理のようです。今回は <code>name</code> が 「<code>create</code>」 ですので、戻り値は 「<code>create</code>」ですね。<br />
ということで、<code>addImpliedCommands()</code> メソッドのはじめの処理は、<br />
<code>$this->columns</code> 配列の中に含まれる <code>ColumnDefinition</code> インスタンスの中で、「<code>change</code>」 パラメーターが無いものが存在し、且つ 「<code>create</code> 」のパラメータが含まれていない場合は <code>$this->commands</code> に <code>$this->createCommand('add')</code> の戻り値を先頭に加えるというものですね。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>$this->createCommand('add')</code> は前に読みましたね。<br />
引数にコマンド名と引数配列を受け取り、<code>compact()</code> で「<code>name</code> 」というキーにコマンド名を代入した配列と引数配列を <code>array_marge()</code> したものを引数として <code>Fluent</code> インスタンスを生成して戻すものでした。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>addImpliedCommands()</code> メソッドの次の処理を読みましょう。<br />
最初のものと似ていますね。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
if (count($this->getChangedColumns()) > 0 && ! $this->creating()) {
    array_unshift($this->commands, $this->createCommand('change'));
}
</pre>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::getChangedColumns()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the columns on the blueprint that should be changed.
     *
     * @return \Illuminate\Database\Schema\ColumnDefinition[]
     */
    public function getChangedColumns()
    {
        return array_filter($this->columns, function ($column) {
            return (bool) $column->change;
        });
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>getChangedColumns()</code> メソッドは <code>$this->columns</code> 配列に含まれる <code>ColumnDefinition</code> インスタンスの中で 「<code>change</code>」 パラメーターがあるものが存在し、且つ「<code>create</code>」 パラメーターが含まれていない場合は<code>$this->commands</code> に <code>$this->createCommand('change')</code> の戻り値を先頭に加えるというものです。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>addImpliedCommands()</code> の続きです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$this->addFluentIndexes();
$this->addFluentCommands($grammar);
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>addFluentIndexes()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::addFluentIndexes()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Add the index commands fluently specified on columns.
     *
     * @return void
     */
    protected function addFluentIndexes()
    {
        foreach ($this->columns as $column) {
            foreach (['primary', 'unique', 'index', 'spatialIndex'] as $index) {
                // If the index has been specified on the given column, but is simply equal
                // to "true" (boolean), no name has been specified for this index so the
                // index method can be called without a name and it will generate one.
                if ($column->{$index} === true) {
                    $this->{$index}($column->name);
                    $column->{$index} = false;

                    continue 2;
                }

                // If the index has been specified on the given column, and it has a string
                // value, we'll go ahead and call the index method and pass the name for
                // the index since the developer specified the explicit name for this.
                elseif (isset($column->{$index})) {
                    $this->{$index}($column->name, $column->{$index});
                    $column->{$index} = false;

                    continue 2;
                }
            }
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->columns</code> を <code>foreach</code> で回し、<code>ColumnDefinition::$attributes</code> に <code>primary</code> <code>unique</code> <code>index</code> <code>spatialIndex</code> が含まれているか確認し、含まれている場合、<code>Blueprint</code> の各インデックス名メソッドでカラム名を登録します。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint インデックス登録メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Specify the primary key(s) for the table.
     *
     * @param  string|array  $columns
     * @param  string|null  $name
     * @param  string|null  $algorithm
     * @return \Illuminate\Support\Fluent
     */
    public function primary($columns, $name = null, $algorithm = null)
    {
        return $this->indexCommand('primary', $columns, $name, $algorithm);
    }
    /**
     * Specify a unique index for the table.
     *
     * @param  string|array  $columns
     * @param  string|null  $name
     * @param  string|null  $algorithm
     * @return \Illuminate\Support\Fluent
     */
    public function unique($columns, $name = null, $algorithm = null)
    {
        return $this->indexCommand('unique', $columns, $name, $algorithm);
    }
    /**
     * Specify an index for the table.
     *
     * @param  string|array  $columns
     * @param  string|null  $name
     * @param  string|null  $algorithm
     * @return \Illuminate\Support\Fluent
     */
    public function index($columns, $name = null, $algorithm = null)
    {
        return $this->indexCommand('index', $columns, $name, $algorithm);
    }
    /**
     * Specify a spatial index for the table.
     *
     * @param  string|array  $columns
     * @param  string|null  $name
     * @return \Illuminate\Support\Fluent
     */
    public function spatialIndex($columns, $name = null)
    {
        return $this->indexCommand('spatialIndex', $columns, $name);
    }
    /**
     * Add a new index command to the blueprint.
     *
     * @param  string  $type
     * @param  string|array  $columns
     * @param  string  $index
     * @param  string|null  $algorithm
     * @return \Illuminate\Support\Fluent
     */
    protected function indexCommand($type, $columns, $index, $algorithm = null)
    {
        $columns = (array) $columns;

        // If no name was specified for this index, we will create one using a basic
        // convention of the table name, followed by the columns, followed by an
        // index type, such as primary or index, which makes the index unique.
        $index = $index ?: $this->createIndexName($type, $columns);

        return $this->addCommand(
            $type, compact('index', 'columns', 'algorithm')
        );
    }
    /**
     * Create a default index name for the table.
     *
     * @param  string  $type
     * @param  array  $columns
     * @return string
     */
    protected function createIndexName($type, array $columns)
    {
        $index = strtolower($this->prefix.$this->table.'_'.implode('_', $columns).'_'.$type);

        return str_replace(['-', '.'], '_', $index);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>addCommand()</code> メソッドは <code>$this->commands[]</code> に<code>createCommand()</code> メソッドの戻り値を代入しそれを戻すものでした。戻り値は <code>Fluent</code> インスタンです。</p>
<p>続きの <code>addFluentCommands()</code> メソッドを見てみましょう。引数として <code>MySqlGrammar</code> インスタンスを渡しています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::addFluentCommands()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Add the fluent commands specified on any columns.
     *
     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
     * @return void
     */
    public function addFluentCommands(Grammar $grammar)
    {
        foreach ($this->columns as $column) {
            foreach ($grammar->getFluentCommands() as $commandName) {
                $attributeName = lcfirst($commandName);

                if (! isset($column->{$attributeName})) {
                    continue;
                }

                $value = $column->{$attributeName};

                $this->addCommand(
                    $commandName, compact('value', 'column')
                );
            }
        }
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\Grammar::getFluentCommands()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * The commands to be executed outside of create or alter command.
     *
     * @var array
     */
    protected $fluentCommands = [];
    /**
     * Get the fluent commands for the grammar.
     *
     * @return array
     */
    public function getFluentCommands()
    {
        return $this->fluentCommands;
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\PostgresGrammar</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * The commands to be executed outside of create or alter command.
     *
     * @var array
     */
    protected $fluentCommands = ['Comment'];
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->columns</code> を <code>foreach</code> で回し、<code>$grammar->getFluentCommands()</code> で <code>$fluentCommands[]</code> を取得しています。これは、各データベースに合わせた <code>Grammar</code> クラスで設定されているようですが、設定されているのは Postgres のみです。設定されている場合はコマンドに追加します。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>toSql()</code> メソッドの続きを見ましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::toSql()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the raw SQL statements for the blueprint.
     *
     * @param  \Illuminate\Database\Connection  $connection
     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
     * @return array
     */
    public function toSql(Connection $connection, Grammar $grammar)
    {
        $this->addImpliedCommands($grammar);

        $statements = [];

        // Each type of command has a corresponding compiler function on the schema
        // grammar which is used to build the necessary SQL statements to build
        // the blueprint element, so we'll just call that compilers function.
        $this->ensureCommandsAreValid($connection);

        foreach ($this->commands as $command) {
            $method = 'compile'.ucfirst($command->name);

            if (method_exists($grammar, $method) || $grammar::hasMacro($method)) {
                if (! is_null($sql = $grammar->$method($this, $command, $connection))) {
                    $statements = array_merge($statements, (array) $sql);
                }
            }
        }

        return $statements;
    }
    /**
     * Ensure the commands on the blueprint are valid for the connection type.
     *
     * @param  \Illuminate\Database\Connection  $connection
     * @return void
     *
     * @throws \BadMethodCallException
     */
    protected function ensureCommandsAreValid(Connection $connection)
    {
        if ($connection instanceof SQLiteConnection) {
            if ($this->commandsNamed(['dropColumn', 'renameColumn'])->count() > 1) {
                throw new BadMethodCallException(
                    "SQLite doesn't support multiple calls to dropColumn / renameColumn in a single modification."
                );
            }

            if ($this->commandsNamed(['dropForeign'])->count() > 0) {
                throw new BadMethodCallException(
                    "SQLite doesn't support dropping foreign keys (you would need to re-create the table)."
                );
            }
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$statements</code> に空の配列を代入します。<br />
<code>ensureCommandsAreValid()</code> メソッドをコールしていますが、このメソッドの中身は SQLite 接続の場合の例外処理のようです。外部キーの削除や複数のテーブルドロップ、カラム名変更が仕様上出来ないための対応のようです。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>$this->commands</code> を <code>foreach</code> で回します。中身はコマンド名を登録した <code>Fluent</code> インスタンスです。回したコマンド名からメソッド名を整形し、<code>MySqlGrammar</code> クラスにそのメソッドが実装されている若しくはトレイトの <code>$macros</code> に登録されているか検証します。<br />
存在した場合、<code>MySqlGrammar</code> の指定メソッドを自身とコマンドが登録された <code>Fluent</code> と接続を引数として渡しコールします。<br />
今回は 「&#8217;create&#8217;」 コマンドが登録されていますので、<code>MySqlGrammar::compileCreate()</code> ですね。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\MySqlGrammar::compileCreate()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Compile a create table command.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @param  \Illuminate\Support\Fluent  $command
     * @param  \Illuminate\Database\Connection  $connection
     * @return string
     */
    public function compileCreate(Blueprint $blueprint, Fluent $command, Connection $connection)
    {
        $sql = $this->compileCreateTable(
            $blueprint, $command, $connection
        );

        // Once we have the primary SQL, we can add the encoding option to the SQL for
        // the table.  Then, we can check if a storage engine has been supplied for
        // the table. If so, we will add the engine declaration to the SQL query.
        $sql = $this->compileCreateEncoding(
            $sql, $connection, $blueprint
        );

        // Finally, we will append the engine configuration onto this SQL statement as
        // the final thing we do before returning this finished SQL. Once this gets
        // added the query will be ready to execute against the real connections.
        return $this->compileCreateEngine(
            $sql, $connection, $blueprint
        );
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Schema\Grammars\MySqlGrammar::compileCreate()</h6>
<p>第一引数は <code>Blueprint</code> インスタンスです。<br />
第二引数は <code>Fluent</code> インスタンスでコマンドです。<br />
第三引数は <code>Connection</code> インスタンスで接続です。<br />
戻り値はストリング型で SQL文です。
</div>
<p><code>compileCreateTable()</code> をコールしています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\MySqlGrammar::compileCreateTable()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create the main create table clause.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @param  \Illuminate\Support\Fluent  $command
     * @param  \Illuminate\Database\Connection  $connection
     * @return string
     */
    protected function compileCreateTable($blueprint, $command, $connection)
    {
        return sprintf('%s table %s (%s)',
            $blueprint->temporary ? 'create temporary' : 'create',
            $this->wrapTable($blueprint),
            implode(', ', $this->getColumns($blueprint))
        );
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\Grammar::wrapTable()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Wrap a table in keyword identifiers.
     *
     * @param  mixed  $table
     * @return string
     */
    public function wrapTable($table)
    {
        return parent::wrapTable(
            $table instanceof Blueprint ? $table->getTable() : $table
        );
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars::wrapTable()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Wrap a table in keyword identifiers.
     *
     * @param  \Illuminate\Database\Query\Expression|string  $table
     * @return string
     */
    public function wrapTable($table)
    {
        if (! $this->isExpression($table)) {
            return $this->wrap($this->tablePrefix.$table, true);
        }

        return $this->getValue($table);
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::getTable()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the table the blueprint describes.
     *
     * @return string
     */
    public function getTable()
    {
        return $this->table;
    }
    /**
     * Determine if the given value is a raw expression.
     *
     * @param  mixed  $value
     * @return bool
     */
    public function isExpression($value)
    {
        return $value instanceof Expression;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>SQL文を生成します。<br />
<code>Blurprint::$temporary</code> が <code>true</code> ならば、「create temporary」 <code>false</code> ならば<br />
「create 」が入ります。一時テーブルの指定が出来るようですね。今回は使いません。</p>
<p><code>wrapTable()</code> メソッドを引数に <code>Blueprint</code> インスタンスを渡しコールしています。<br />
<code>Illuminate\Database\Schema\Grammars\Grammar::wrapTable()</code> メソッドは受け取った引数が <code>Blueprint</code> インスタンスなら、 <code>Blueprint::getTable()</code> の戻り値つまりテーブル名を、そうでなければ引数として渡されているストリング型のテーブル名を、そのままスーパークラスの <code>wrapTable()</code> メソッドに引数として渡し戻り値を戻しています。<br />
スーパークラスの <code>Illuminate\Database\Schema\Grammars::wrapTable()</code> メソッドは、引数として渡されたテーブルが <code>Expression</code> インスタンスかどうか検証ます。<br />
<code>Expression</code> インスタンスでない場合は、<code>Grammars::wrap()</code> メソッドをテーブルプレフィックスとテーブル名を連結したストリング、及び <code>true</code> を引数として渡してコールした戻り値を戻します。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Grammar::wrap()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Wrap a value in keyword identifiers.
     *
     * @param  \Illuminate\Database\Query\Expression|string  $value
     * @param  bool  $prefixAlias
     * @return string
     */
    public function wrap($value, $prefixAlias = false)
    {
        if ($this->isExpression($value)) {
            return $this->getValue($value);
        }

        // If the value being wrapped has a column alias we will need to separate out
        // the pieces so we can wrap each of the segments of the expression on its
        // own, and then join these both back together using the "as" connector.
        if (stripos($value, ' as ') !== false) {
            return $this->wrapAliasedValue($value, $prefixAlias);
        }

        return $this->wrapSegments(explode('.', $value));
    }
    /**
     * Get the value of a raw expression.
     *
     * @param  \Illuminate\Database\Query\Expression  $expression
     * @return string
     */
    public function getValue($expression)
    {
        return $expression->getValue();
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Query\Expression::getValue()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the value of the expression.
     *
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>まず重複ですが、第一引数が <code>Expression</code> インスタンスであった場合は、 <code>getValue()</code> メソッドでっ取得した値を戻します。<br />
そうでなかった場合は、第一引数に 「<code> as </code> 」が含まれるか検証します。<br />
含まれる場合は <code>wrapAliasedValue()</code> メソッドをコールします。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Grammar::wrapAliasedValue()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Wrap a value that has an alias.
     *
     * @param  string  $value
     * @param  bool  $prefixAlias
     * @return string
     */
    protected function wrapAliasedValue($value, $prefixAlias = false)
    {
        $segments = preg_split('/\s+as\s+/i', $value);

        // If we are wrapping a table we need to prefix the alias with the table prefix
        // as well in order to generate proper syntax. If this is a column of course
        // no prefix is necessary. The condition will be true when from wrapTable.
        if ($prefixAlias) {
            $segments[1] = $this->tablePrefix.$segments[1];
        }

        return $this->wrap($segments[0]).' as '.$this->wrapValue($segments[1]);
    }
    /**
     * Wrap a single string in keyword identifiers.
     *
     * @param  string  $value
     * @return string
     */
    protected function wrapValue($value)
    {
        if ($value !== '*') {
            return '"'.str_replace('"', '""', $value).'"';
        }

        return $value;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>受け取ったテーブル名を正規表現で 「<code> as </code> 」で分割し配列にしたものを <code>$segments</code> に代入します。<br />
プレフィックスが指定されていた場合は、<code>$segments[]</code> 配列の１番目の先頭にプレフィックスを連結します。<br />
連結したものに「<code> as </code> 」と <code>wrapValue()</code> メソッドを通した <code>$segments[]</code> 配列の１番目 を <code>wrap()</code> メソッドに渡した引数を戻します。と書いてあるのですが、ぱっと見た感じ無限ループしそうなのですが、動くんでしょうか。僕の読みが浅いのかもしれません。<br />
今回は 「<code> as </code> 」は含まれないのでここは通りません。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>wrapSegments()</code> こちらを読みましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Grammar::wrapSegments()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Wrap the given value segments.
     *
     * @param  array  $segments
     * @return string
     */
    protected function wrapSegments($segments)
    {
        return collect($segments)->map(function ($segment, $key) use ($segments) {
            return $key == 0 && count($segments) > 1
                            ? $this->wrapTable($segment)
                            : $this->wrapValue($segment);
        })->implode('.');
    }

</pre>
<div class="su-spacer" style="height:20px"></div>
<p>テーブル名を 「<code>.</code> 」で <code>explode()</code> したものを引数として受け取ります。</p>
<p><code>collect()</code> ヘルパ関数に受け取った引数を渡してます。受け取った引数を <code>$items</code> 変数に代入した <code>Collection</code> インスタンスを戻すものでしたね。 <code>Collection::map()</code> メソッドをコールしています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Support\Collection::map()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run a map over each of the items.
     *
     * @param  callable  $callback
     * @return static
     */
    public function map(callable $callback)
    {
        $keys = array_keys($this->items);

        $items = array_map($callback, $this->items, $keys);

        return new static(array_combine($keys, $items));
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$items</code> 配列のキーを取得し配列にしたものを <code>$keys</code> に代入します。<br />
引数として渡されたコールバックに <code>$items</code> と <code>$keys</code> を渡し結果を <code>$items</code> に代入します。<br />
コールバックの内容は、キーが 0 つまり０番目の配列で且つ 引数として渡された <code>$items</code> 中身の値が配列で１より大きければ <code>wrapTable()</code> メソッドに <code>$items</code> の 0 番目を渡してコールした戻り値を、そうでなければ 引数として渡された <code>$items</code> の中身の値を引数として<code>wrapValue()</code> に渡しコールした戻り値を戻します。<br />
返ってきた値を <code>array_combine()</code> したものを引数として生成した <code>Collection</code> インスタンスを戻します。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>map()</code> の戻り値 <code>Collection</code> インスタンスの <code>implode()</code> メソッドを引数に「<code>.</code>」を渡してコールします。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Support\Collection::implode()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Concatenate values of a given key as a string.
     *
     * @param  string  $value
     * @param  string|null  $glue
     * @return string
     */
    public function implode($value, $glue = null)
    {
        $first = $this->first();

        if (is_array($first) || is_object($first)) {
            return implode($glue, $this->pluck($value)->all());
        }

        return implode($value, $this->items);
    }
    /**
     * Get the values of a given key.
     *
     * @param  string|array  $value
     * @param  string|null  $key
     * @return static
     */
    public function pluck($value, $key = null)
    {
        return new static(Arr::pluck($this->items, $value, $key));
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>first()</code> メソッドをコールしています。<code>$this->items</code> の最初の値を取得するものでしたね。<br />
取得した値が配列、若しくはオブジェクトの場合、引数として渡された「<code>.</code>」 を引数として渡して <code>pluck()</code> メソッドをコールし、戻り値の <code>all()</code> メソッドの戻り値を 第二引数で連結した文字列を戻します。<br />
そうでなければ、<code>$this->items</code> 配列を 「<code>.</code> 」で連結した文字列を戻します。<br />
今回は、「<code>maigrations</code>」 というストリング型の文字列ですので、ここは通らず、<code>wrapValue()</code> に行きます。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Grammar::wrapValue()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Wrap a single string in keyword identifiers.
     *
     * @param  string  $value
     * @return string
     */
    protected function wrapValue($value)
    {
        if ($value !== '*') {
            return '"'.str_replace('"', '""', $value).'"';
        }
        return $value;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>引数として受け取った文字列が 「<code>*</code>」 でなかった場合は整形して戻してますね。<br />
今回の場合「<code>"migration"</code>」 になります。<br />
ようやく <code>MySqlGrammar::compileCreateTable()</code> が戻す SQL を生成する <code>sprintf()</code> の２つ目の <code>%s</code> までわかりました。今わかっているのは 「create table &#8220;migration&#8221; (%s)」 ですね。最後の <code>%s</code> を読みましょう。以下となっていました。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
implode(', ', $this->getColumns($blueprint))
</pre>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\Grammar::getColumns() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Compile the blueprint's column definitions.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @return array
     */
    protected function getColumns(Blueprint $blueprint)
    {
        $columns = [];

        foreach ($blueprint->getAddedColumns() as $column) {
            // Each of the column types have their own compiler functions which are tasked
            // with turning the column definition into its SQL format for this platform
            // used by the connection. The column's modifiers are compiled and added.
            $sql = $this->wrap($column).' '.$this->getType($column);

            $columns[] = $this->addModifiers($sql, $blueprint, $column);
        }

        return $columns;
    }
    /**
     * Get the columns on the blueprint that should be added.
     *
     * @return \Illuminate\Database\Schema\ColumnDefinition[]
     */
    public function getAddedColumns()
    {
        return array_filter($this->columns, function ($column) {
            return ! $column->change;
        });
    }
    /**
     * Get the SQL for the column data type.
     *
     * @param  \Illuminate\Support\Fluent  $column
     * @return string
     */
    protected function getType(Fluent $column)
    {
        return $this->{'type'.ucfirst($column->type)}($column);
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Schema\Grammars\Grammar::getColumns()</h6>
<p>第一引数は <code>Blueprint</code> インスタンスです。<br />
戻り値は配列でカラムです。
</div>
<p><code>Grammar::getColumns()</code> は引数に <code>Blueprint</code> インスタンスを受け取り、それの <code>getAddedColumns()</code> メソッドの戻り値を <code>foreach</code> で回します。<br />
<code>getAddedColumns()</code> は受け取った <code>Blueprint</code> インスタンスの <code>$columns</code> 配列の内容から <code>change</code> パラメーターの存在するものを削除した配列を戻します。<br />
つまり、 <code>$blueprint->columns</code> で <code>change</code> パラメーターの無いものを <code>foreach</code> します。<br />
<code>foreach</code> した値を <code>wrap()</code> しています。先程読みましたね。ただのストリング型ですので「<code>"</code>」で囲まれた値が返ってきます。それに半角スペースと <code>getType()</code> の戻り値を連結しています。<br />
<code>getType()</code> は 各 <code>$column</code> の <code>type</code> に合わせたメソッドをコールします。<br />
今回追加されるカラムは <code>id</code> <code>migration</code> <code>batch</code> の３つでした。<code>id</code> を生成した時に使われた配列は以下です。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
array(4) {
  ["type"]=> string(7) "integer"
  ["name"]=> string(2) "id"
  ["autoIncrement"]=> bool(true)
  ["unsigned"]=> bool(true)
}
</pre>
<div class="su-spacer" style="height:20px"></div>
つまりコールされるメソッド名は 「<code>typeInteger()</code>」 ですね。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">TITLE</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create the column definition for an integer type.
     *
     * @param  \Illuminate\Support\Fluent  $column
     * @return string
     */
    protected function typeInteger(Fluent $column)
    {
        return 'int';
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>単純にストリング型で 「<code>int</code>」 と返しているだけですね。<br />
<code>$sql</code> に代入される文字列は 「<code>"id" int</code> 」 になりそうです。<br />
その後に <code>$columns[]</code> 配列に <code>$this->addModifiers()</code> をコールした戻り値を代入します。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\Grammar::addModifiers()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Add the column modifiers to the definition.
     *
     * @param  string  $sql
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @param  \Illuminate\Support\Fluent  $column
     * @return string
     */
    protected function addModifiers($sql, Blueprint $blueprint, Fluent $column)
    {
        foreach ($this->modifiers as $modifier) {
            if (method_exists($this, $method = "modify{$modifier}")) {
                $sql .= $this->{$method}($blueprint, $column);
            }
        }

        return $sql;
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Schema\Grammars\Grammar::addModifiers()</h6>
<p>第一引数はストリング型で SQL です。<br />
第二引数は <code>Blueprint</code> インスタンスです。<br />
第三引数は <code>Fluent</code> 型でカラムです。<br />
戻り値はストリング型でSQLです。
</div>
<p><code>$this->modifiers</code> を <code>foreach</code> で回しています。<br />
<code>modifiers</code> は各データベースの種類に合わせて <code>Grammar</code> クラスで定義されています。<br />
以下は <code>MySql</code> のものです。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\MySqlGrammar::$modifiers</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * The possible column modifiers.
     *
     * @var array
     */
    protected $modifiers = [
        'Unsigned', 'Charset', 'Collate', 'VirtualAs', 'StoredAs', 'Nullable',
        'Srid', 'Default', 'Increment', 'Comment', 'After', 'First',
    ];
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>modifiers</code> 配列に登録された文字列と 「<code>modifier</code>」を連結したメソッドが <code>MySqlGrammar</code> クラスに存在した場合は、そのメソッドを <code>blueprint</code> インスタンスと <code>column</code> インスタンスを引数として渡してコールします。<br />
今回 「<code>id</code>」 に含まれている <code>Unsigned</code> 見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    /**
     * Get the SQL for an unsigned column modifier.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @param  \Illuminate\Support\Fluent  $column
     * @return string|null
     */
    protected function modifyUnsigned(Blueprint $blueprint, Fluent $column)
    {
        if ($column->unsigned) {
            return ' unsigned';
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>もし、渡された <code>$column</code> に 「<code>unsigned</code>」 が含まれている場合は 「<code> unsigned</code>」という文字列を戻しています。</p>
<p>各カラムに対して同様の処理を行ったものを代入した配列が戻され、<code>compileCreateTable()</code> で <code>implode()</code> され SQLにが生成されます。おそらく以下のような SQL になるのでしょう。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
create table “migration” (
  "id" integer unsigned auto_increment primary key,
  "migration" varchar(255),
  "batch" integer
)
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>MySqlGrammar::compileCreateTable()</code> の戻り値がわかりました。<br />
<code>MySqlGrammar::compileCreate()</code> の続きです。<br />
次は <code>compileCreateEncoding()</code> がコールされています。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\MySqlGrammar::compileCreateEncoding()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Append the character set specifications to a command.
     *
     * @param  string  $sql
     * @param  \Illuminate\Database\Connection  $connection
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @return string
     */
    protected function compileCreateEncoding($sql, Connection $connection, Blueprint $blueprint)
    {
        // First we will set the character set if one has been set on either the create
        // blueprint itself or on the root configuration for the connection that the
        // table is being created on. We will add these to the create table query.
        if (isset($blueprint->charset)) {
            $sql .= ' default character set '.$blueprint->charset;
        } elseif (! is_null($charset = $connection->getConfig('charset'))) {
            $sql .= ' default character set '.$charset;
        }

        // Next we will add the collation to the create table statement if one has been
        // added to either this create table blueprint or the configuration for this
        // connection that the query is targeting. We'll add it to this SQL query.
        if (isset($blueprint->collation)) {
            $sql .= " collate '{$blueprint->collation}'";
        } elseif (! is_null($collation = $connection->getConfig('collation'))) {
            $sql .= " collate '{$collation}'";
        }

        return $sql;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>Blueprint</code> インスタンスに <code>charset</code> が登録されていればそれを、なければ設定ファイルの文字コードを使い、デフォルト文字コードを設定する SQL を追加します。</p>
<p><code>Blueprint</code> インスタンスに <code>collation</code> が登録されていればそれを、なければ設定ファイルに照合順序が登録されていればそれを使い照合順序を設定する SQL を追加します。</p>
<p>上記２処理を施した SQL を戻します。<br />
おそらく今回は以下のような SQL になっているでしょう。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
create table “migration” (
  "id" integer unsigned auto_increment primary key,
  "migration" varchar(255),
  "batch" integer
) default character set utf8mb4 collate utf8mb4_unicode_ci

</pre>
<div class="su-spacer" style="height:20px"></div>
<p>次は <code>compileCreateEngine()</code> です。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\MySqlGrammar::compileCreateEngine()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Append the engine specifications to a command.
     *
     * @param  string  $sql
     * @param  \Illuminate\Database\Connection  $connection
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @return string
     */
    protected function compileCreateEngine($sql, Connection $connection, Blueprint $blueprint)
    {
        if (isset($blueprint->engine)) {
            return $sql.' engine = '.$blueprint->engine;
        } elseif (! is_null($engine = $connection->getConfig('engine'))) {
            return $sql.' engine = '.$engine;
        }

        return $sql;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$blueprint->engine</code> が登録されていればそれを、登録されてない場合、設定ファイルに登録されていればそれを使いストレージエンジンを設定する SQL文を追加します。今回は指定されておらず、設定ファイルにも記述がないので追加されません。</p>
<p><code>compileCreate()</code> メソッドが戻す SQL文がわかりました。<code>toSql()</code> メソッドに戻りましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::toSql()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the raw SQL statements for the blueprint.
     *
     * @param  \Illuminate\Database\Connection  $connection
     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
     * @return array
     */
    public function toSql(Connection $connection, Grammar $grammar)
    {
        $this->addImpliedCommands($grammar);

        $statements = [];

        // Each type of command has a corresponding compiler function on the schema
        // grammar which is used to build the necessary SQL statements to build
        // the blueprint element, so we'll just call that compilers function.
        $this->ensureCommandsAreValid($connection);

        foreach ($this->commands as $command) {
            $method = 'compile'.ucfirst($command->name);

            if (method_exists($grammar, $method) || $grammar::hasMacro($method)) {
                if (! is_null($sql = $grammar->$method($this, $command, $connection))) {
                    $statements = array_merge($statements, (array) $sql);
                }
            }
        }

        return $statements;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->commands</code> を <code>foreach</code> で回し登録されたコマンドのSQL文を生成し配列に入れたものを戻している処理ということがわかりました。</p>
<p><code>Blueprint::build()</code> に戻りましょう。</p>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::build() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Execute the blueprint against the database.
     *
     * @param  \Illuminate\Database\Connection  $connection
     * @param  \Illuminate\Database\Schema\Grammars\Grammar  $grammar
     * @return void
     */
    public function build(Connection $connection, Grammar $grammar)
    {
        foreach ($this->toSql($connection, $grammar) as $statement) {
            $connection->statement($statement);
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>$this->toSql()</code> メソッドは登録されたコマンドから生成されたSQL文が入った配列でした。 <code>foreach</code> でまわして <code>$connection->statement()</code> メソッドをSQL文を引数として渡してコールします。<br />
<code>statement()</code> メソッドを見てみましょう。</p>
<p>その１５に続きます。</p>

	<div class="st-post-tags">
	TAGS: <a href="https://xi.ayane.co.jp/archives/tag/php" title="php" rel="tag">php</a> / <a href="https://xi.ayane.co.jp/archives/tag/laravel" title="laravel" rel="tag">laravel</a> / <a href="https://xi.ayane.co.jp/archives/tag/%e3%83%95%e3%83%ac%e3%83%bc%e3%83%a0%e3%83%af%e3%83%bc%e3%82%af%e3%82%92%e8%aa%ad%e3%82%80" title="フレームワークを読む" rel="tag">フレームワークを読む</a><br /></div>

<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1730">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１４：Builder::build()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1730</post-id>	</item>
		<item>
		<title>【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１３：MigrateCommand::handle()</title>
		<link>https://xi.ayane.co.jp/archives/1573</link>
		
		<dc:creator><![CDATA[xi]]></dc:creator>
		<pubDate>Sat, 04 Apr 2020 06:07:12 +0000</pubDate>
				<category><![CDATA[フレームワーク]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[laravel]]></category>
		<category><![CDATA[フレームワークを読む]]></category>
		<guid isPermaLink="false">https://xi.ayane.co.jp/?p=1573</guid>

					<description><![CDATA[<p>いよいよ MigrateCommand::handle() を読むときです。少しばかり長かったですね。 見てみましょう。 Illuminate\Database\Console\Migrations\MigrateCom [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1573">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１３：MigrateCommand::handle()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>いよいよ <code>MigrateCommand::handle()</code> を読むときです。少しばかり長かったですね。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\MigrateCommand::handle()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        if (! $this->confirmToProceed()) {
            return 1;
        }

        $this->migrator->usingConnection($this->option('database'), function () {
            $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]);
            }
        });

        return 0;
    }
    /**
     * Resolve the database connection instance.
     *
     * @return \Illuminate\Database\Connection
     */
    public function getConnection()
    {
        return $this->resolver->connection($this->connection);
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Console\Migrations\MigrateCommand()</h6>
<p>引数はありません。<br />
戻り値は整数型です。
</p></div>
<p>まず、<code>$this->confirmToProceed()</code> メソッドがコールされています。<br />
これは、<code>Illuminate\Console\ConfirmableTrait</code> トレイトに定義されています。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Console\ConfirmableTrait::confirmToProceed()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Confirm before proceeding with the action.
     *
     * This method only asks for confirmation in production.
     *
     * @param  string  $warning
     * @param  \Closure|bool|null  $callback
     * @return bool
     */
    public function confirmToProceed($warning = 'Application In Production!', $callback = null)
    {
        $callback = is_null($callback) ? $this->getDefaultConfirmCallback() : $callback;

        $shouldConfirm = value($callback);

        if ($shouldConfirm) {
            if ($this->hasOption('force') && $this->option('force')) {
                return true;
            }

            $this->alert($warning);

            $confirmed = $this->confirm('Do you really wish to run this command?');

            if (! $confirmed) {
                $this->comment('Command Canceled!');

                return false;
            }
        }

        return true;
    }
    /**
     * Get the default confirmation callback.
     *
     * @return \Closure
     */
    protected function getDefaultConfirmCallback()
    {
        return function () {
            return $this->getLaravel()->environment() === 'production';
        };
    }
</pre>
<div class="description">
<h6>Illuminate\Console\ConfirmableTrait::confirmToProceed()</h6>
<p>第一引数はストリング型で警告メッセージです。<br />
第二引数はコールバック関数若しくはブーリアン若しくは <code>null</code> です。<br />
戻り値はブーリアンです。
</div>
<p>引数として渡されたコールバック関数が <code>null</code> だった場合、デフォルトのコールバック関数をセットします。<br />
<code>value()</code> メソッドは Laravel のヘルパ関数です。引数がクロージャならばそれの戻り値、そうでなければそのままを戻します。<br />
今回はコールバックは指定されていないのでデフォルトコールバックが入っています。内容は コマンドアプリケーションコンテナが <code>production</code> であるかの検証がブーリアンで返されるものですね。現在 <code>APP_ENV</code> は <code>production</code> に設定されているので <code>true</code> です。<br />
<code>value($callback)</code> が <code>true</code> だった場合、オプションに <code>force</code> 指定があるか検証します。もし、あった場合は <code>true</code> を戻します。<br />
つまりコマンド実行確認のメソッドですね。<br />
キャンセルされた場合は <code>false</code> 実行承認した場合は <code>true</code> が戻されます。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>handle()</code> メソッドの続きに戻ります。<br />
<code>$this->confirmToProceed()</code> でキャンセルされた場合は 整数 1 を戻します。<br />
キャンセルされなかった場合は以下が実行されます。<br />
今回の記事の主題ですね。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\MigrateCommand::handle() 抜粋</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
        $this->migrator->usingConnection($this->option('database'), function () {
            $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]);
            }
        });
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->migrator</code> は <code>Illuminate\Database\MigrationServiceProvider::registerMigrator()</code> で以下のようにコマンドアプリケーションコンテナにシングルトン結合したものを、 <code>Illuminate\Database\MigrationServiceProvider::registerMigrateCommand()</code> で引数として渡して <code>MigrateCommand</code> インスタンスを生成し、コンストラクタで <code>$migrator</code> に代入したものです。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\MigrationServiceProvider::registerMigrator() | registerMigrateCommand()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Register the migrator service.
     *
     * @return void
     */
    protected function registerMigrator()
    {
        // The migrator is responsible for actually running and rollback the migration
        // files in the application. We'll pass in our database connection resolver
        // so the migrator can resolve any of these connections when it needs to.
        $this->app->singleton('migrator', function ($app) {
            $repository = $app['migration.repository'];

            return new Migrator($repository, $app['db'], $app['files'], $app['events']);
        });
    }
    /**
     * Register the command.
     *
     * @return void
     */
    protected function registerMigrateCommand()
    {
        $this->app->singleton('command.migrate', function ($app) {
            return new MigrateCommand($app['migrator']);
        });
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\MigrateCommand::__construct() | registerMigrateCommand()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new migration command instance.
     *
     * @param  \Illuminate\Database\Migrations\Migrator  $migrator
     * @return void
     */
    public function __construct(Migrator $migrator)
    {
        parent::__construct();

        $this->migrator = $migrator;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>Illuminate\Database\Migrations\Migrator</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::__construct()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new migrator instance.
     *
     * @param  \Illuminate\Database\Migrations\MigrationRepositoryInterface  $repository
     * @param  \Illuminate\Database\ConnectionResolverInterface  $resolver
     * @param  \Illuminate\Filesystem\Filesystem  $files
     * @param  \Illuminate\Contracts\Events\Dispatcher|null  $dispatcher
     * @return void
     */
    public function __construct(MigrationRepositoryInterface $repository,
                                Resolver $resolver,
                                Filesystem $files,
                                Dispatcher $dispatcher = null)
    {
        $this->files = $files;
        $this->events = $dispatcher;
        $this->resolver = $resolver;
        $this->repository = $repository;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
コンストラクタで引数として渡されたものを変数に代入していますね。<br />
リポジトリ、データベース、ファイルシステム、イベント、一通りあります。<br />
<code>usingConnection()</code> を見てみます。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::usingConnection()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Execute the given callback using the given connection as the default connection.
     *
     * @param  string  $name
     * @param  callable  $callback
     * @return mixed
     */
    public function usingConnection($name, callable $callback)
    {
        $previousConnection = $this->resolver->getDefaultConnection();

        $this->setConnection($name);

        return tap($callback(), function () use ($previousConnection) {
            $this->setConnection($previousConnection);
        });
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Migrations\Migrator::usingConnection()</h6>
<p>第一引数はストリングでデータベース名です。<br />
第二引数はコールバック関数です。<br />
戻り値は です。
</p></div>
<p><code>$this->resolver</code> には <code>$app['db']</code> が代入されています。エイリアス設定されていて、中身は <code>Illuminate\Database\DatabaseManager</code> でした。<code>DatabaseManager::getDefaultConnection()</code> の内容は以下です。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    /**
     * Get the default connection name.
     *
     * @return string
     */
    public function getDefaultConnection()
    {
        return $this->app['config']['database.default'];
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
つまり、 <code>PROJECT_ROOT/config/database.app</code> の 「<code>default</code>」キーの内容です。デフォルトでは「mysql」ですね。<br />
次に <code>setConnection()</code> に <code>$name</code> を渡しています。<code>$name</code> は <code>MigrateCommand()</code> から <code>$this->option('database')</code> が渡されています。<br />
<code>option()</code> は、<code>Illuminate\Console\Concerns\InteractsWithIO</code> トレイトで以下のように定義されています。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    /**
     * Get the value of a command option.
     *
     * @param  string|null  $key
     * @return string|array|bool|null
     */
    public function option($key = null)
    {
        if (is_null($key)) {
            return $this->input->getOptions();
        }

        return $this->input->getOption($key);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>$this->input</code> は <code>Illuminate\Console\Command::run()</code> メソッドがコールされた時に渡された <code>Symfony\Component\Console\Input\InputInterface</code> が入っています。 <code>getOption()</code> に 「<code>database</code>」 が引数に渡しています。このオプションを設定した箇所は見当たりませんでしたのでデフォルト値 <code>null</code> が入っていると思われます。<br />
<code>setConnection()</code> を引数に <code>null</code> が渡されコールされます。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\Migrator::setConnection()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Set the default connection name.
     *
     * @param  string  $name
     * @return void
     */
    public function setConnection($name)
    {
        if (! is_null($name)) {
            $this->resolver->setDefaultConnection($name);
        }

        $this->repository->setSource($name);

        $this->connection = $name;
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Migrations\Migrator::setConnection()</h6>
<p>第一引数はストリングで接続名です。<br />
戻り値はありません。
</p></div>
<p><code>$name</code> が <code>null</code> でなければ <code>$this->resolver->setDefaultConnection()</code> メソッドをコールしています。<br />
<code>resolver</code> は <code>DatabaseManager</code> インスタンスでした。 <code>setDefaultConnection()</code>は以下です。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    /**
     * Set the default connection name.
     *
     * @param  string  $name
     * @return void
     */
    public function setDefaultConnection($name)
    {
        $this->app['config']['database.default'] = $name;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>$this->app['config']['database.default']</code> を引数で上書きしてます。<br />
今回は <code>null</code> ですのでここは通りません。</p>
<p><code>$this->repository->setSource()</code><br />
<code>$this->repository</code> は <code>Illuminate\Database\Migrations\DatabaseMigrationRepository</code> インスタンスです。<code>setSource()</code> は引数を変数に代入しているだけです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    /**
     * Set the information source to gather data.
     *
     * @param  string  $name
     * @return void
     */
    public function setSource($name)
    {
        $this->connection = $name;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>$this->connection</code> に <code>$name</code> を代入しています。<code>setConnection()</code> メソッドは以上です。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>usingConnection()</code> の続きです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
return tap($callback(), function () use ($previousConnection) {
    $this->setConnection($previousConnection);
});
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>tap()</code> メソッドをコールしています。これは Laravelのヘルパ関数です。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    /**
     * Call the given Closure with the given value then return the value.
     *
     * @param  mixed  $value
     * @param  callable|null  $callback
     * @return mixed
     */
    function tap($value, $callback = null)
    {
        if (is_null($callback)) {
            return new HigherOrderTapProxy($value);
        }

        $callback($value);

        return $value;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
第二引数として渡されたコールバック関数に第一引数を渡してコールしています。戻り値は第一引数です。<br />
今回はコールパック関数として <code>setConnection()</code> するクロージャーが入っています。クロージャーの引数として渡されるのは <code>usingConnection()</code> の第二引数として渡されたコールバック関数です。これをコールした戻り値を <code>setConnection()</code> する流れですね。</p>
<div class="su-spacer" style="height:30px"></div>
<p>では <code>Migrations\MigrateCommand()</code> の続きに戻りましょう。<br />
<code>usingConnection()</code> の第二引数にクロージャーが渡されています。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\MigrateCommand::handle() 抜粋</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
function () {
    $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]);
    }
}
</pre>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\MigrateCommand::prepareDatabase()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Prepare the migration database for running.
     *
     * @return void
     */
    protected function prepareDatabase()
    {
        if (! $this->migrator->repositoryExists()) {
            $this->call('migrate:install', array_filter([
                '--database' => $this->option('database'),
            ]));
        }
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\Migrator::repositoryExists()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Determine if the migration repository exists.
     *
     * @return bool
     */
    public function repositoryExists()
    {
        return $this->repository->repositoryExists();
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\DatabaseMigrationRepository::repositoryExists()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Determine if the migration repository exists.
     *
     * @return bool
     */
    public function repositoryExists()
    {
        $schema = $this->getConnection()->getSchemaBuilder();

        return $schema->hasTable($this->table);
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\DatabaseMigrationRepository::getConnections()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Resolve the database connection instance.
     *
     * @return \Illuminate\Database\Connection
     */
    public function getConnection()
    {
        return $this->resolver->connection($this->connection);
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\DatabaseManager::connection()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get a database connection instance.
     *
     * @param  string|null  $name
     * @return \Illuminate\Database\Connection
     */
    public function connection($name = null)
    {
        [$database, $type] = $this->parseConnectionName($name);

        $name = $name ?: $database;

        // If we haven't created this connection, we'll create it based on the config
        // provided in the application. Once we've created the connections we will
        // set the "fetch mode" for PDO which determines the query return types.
        if (! isset($this->connections[$name])) {
            $this->connections[$name] = $this->configure(
                $this->makeConnection($database), $type
            );
        }

        return $this->connections[$name];
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\MySqlConnection::getSchemaBuilder()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get a schema builder instance for the connection.
     *
     * @return \Illuminate\Database\Schema\MySqlBuilder
     */
    public function getSchemaBuilder()
    {
        if (is_null($this->schemaGrammar)) {
            $this->useDefaultSchemaGrammar();
        }

        return new MySqlBuilder($this);
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Schema\Builder::getSchemaBuilder()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * 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();
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->prepareDatabase()</code> メソッドがコールされています。<br />
<code>$this->prepareDatabase()</code> メソッドでは <code>$this->migrator->repositoryExists()</code> の戻り値を判定しています。<br />
<code>$this->migrator->repositoryExists()</code> メソッドは <code>$this->repository->repositoryExists()</code> の戻り値を戻しています。<code>$this->repository->repositoryExists()</code> は <code>$this->getConnection()->getSchemaBuilder()</code> の戻り値を変数 <code>$schema</code> に代入しています。</p>
<p><code>getConnection()</code> は <code>resolver</code> つまり <code>DatabaseManager</code> の <code>connection()</code> に接続名を渡し <code>Connection</code> インスタンスを受け取ります。<br />
今回はデータベースが MySQL ですので、<code>MySqlConnection::getSchemaBuilder()</code> をコールします。<br />
<code>getSchemaBuilder()</code> は <code>$this->schemaGrammar</code> が <code>null</code> ならば、<code>useDefaultSchemaGrammar()</code> メソッドでスキーマグラマーをデフォルトにセットします。<br />
<code>SchemaBuilder</code> インスタンスを自身を引数に渡し生成したものを返します。<br />
<code>SchemaBuilder</code> インスタンスは生成時にコンストラクタで接続とグラマーを変数に代入ます。</p>
<div class="su-spacer" style="height:20px"></div>
<p>随分とたらい回しをしました。スキーマが生成されたのを <code>DatabaseMigrationRepository::repositoryExists()</code> が変数に代入し、<code>hasTable()</code> メソッドを <code>$this->table</code> を引数に渡しコールします。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->table</code> とは何でしょうか。この<code>$this</code> は <code>DatabaseMigrationRepository</code> インスタンスです。生成されたタイミングは <code>Illuminate\Database\MigrationServiceProvider::registerRepository()</code> です。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\MigrationServiceProvider::registerRepository()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Register the migration repository service.
     *
     * @return void
     */
    protected function registerRepository()
    {
        $this->app->singleton('migration.repository', function ($app) {
            $table = $app['config']['database.migrations'];

            return new DatabaseMigrationRepository($app['db'], $table);
        });
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Migrations\DatabaseMigrationRepository::__construct()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new database migration repository instance.
     *
     * @param  \Illuminate\Database\ConnectionResolverInterface  $resolver
     * @param  string  $table
     * @return void
     */
    public function __construct(Resolver $resolver, $table)
    {
        $this->table = $table;
        $this->resolver = $resolver;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>DatabaseMigrationRepository</code> 生成時にコンストラクタに第二引数として <code>$app['config']['database.migrations']</code> を渡しています。<br />
<code>$app['config']['database.migrations']</code> は <code>PROJECT_ROOT/config/database.app</code> に記述されている配列のキー「<code>migrations</code>」の値です<br />
つまり、<code>$this->table</code> はデフォルトでは「<code>migrations</code>」 です。</p>
<div class="su-spacer" style="height:30px"></div>
<p>次は以下が実行されます。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
return $schema->hasTable($this->table);
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>Builder</code> インスタンスの <code>hasTable()</code> がコールされています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\MySqlBuilder::hasTable()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Determine if the given table exists.
     *
     * @param  string  $table
     * @return bool
     */
    public function hasTable($table)
    {
        $table = $this->connection->getTablePrefix().$table;

        return count($this->connection->select(
            $this->grammar->compileTableExists(), [$this->connection->getDatabaseName(), $table]
        )) > 0;
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Schema\MySqlBuilder::hasTable()</h6>
<p>第一引数はストリングでテーブル名です。<br />
戻り値はブーリアンです。
</p></div>
<p>テーブルプレフィックスを取得して対象テーブル名に連結し <code>$table</code> に代入します。<br />
テーブルプレフィックスはおそらくこれを切り替えることで環境を簡単に変更できるというものでしょう。</p>
<p><code>select()</code> メソッドをコールしています。引数を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$this->grammar->compileTableExists()
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this</code> は <code>ConnectionFactory::createConnection()</code> で <code>$driver</code> を <code>switch</code> で検証して生成された <code>MySqlConnection</code> でした。<br />
ということで、  <code>MySqlConnection::compileTableExists()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\MySqlConnection::getDefaultSchemaGrammar() | 関連事項</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
use Illuminate\Database\Schema\Grammars\MySqlGrammar as SchemaGrammar;
class MySqlConnection extends Connection
{
    /**
     * Get the default schema grammar instance.
     *
     * @return \Illuminate\Database\Schema\Grammars\MySqlGrammar
     */
    protected function getDefaultSchemaGrammar()
    {
        return $this->withTablePrefix(new SchemaGrammar);
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Connection::withTablePrefix()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Set the table prefix and return the grammar.
     *
     * @param  \Illuminate\Database\Grammar  $grammar
     * @return \Illuminate\Database\Grammar
     */
    public function withTablePrefix(Grammar $grammar)
    {
        $grammar->setTablePrefix($this->tablePrefix);

        return $grammar;
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Grammarb::setTablePrefix()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Set the grammar's table prefix.
     *
     * @param  string  $prefix
     * @return $this
     */
    public function setTablePrefix($prefix)
    {
        $this->tablePrefix = $prefix;

        return $this;
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Schema\Grammars\MySqlGrammar::compileTableExists()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Compile the query to determine the list of tables.
     *
     * @return string
     */
    public function compileTableExists()
    {
        return "select * from information_schema.tables where table_schema = ? and table_name = ? and table_type = 'BASE TABLE'";
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>MySqlConnection</code> クラスは名前空間のエイリアス設定で <code>SchemaGrammar</code> を <code>Illuminate\Database\Schema\Grammars\MySqlGrammar</code> としています。<br />
<code>getDefaultSchemaGrammar()</code> メソッドで <code>MySqlGrammar</code> を生成し、<code>setTablePrefix()</code> でテーブルプレフィックスを設定しています。</p>
<p>先程取得しようとしていたクエリは、 <code>MySqlGrammar::compileTableExists()</code> の戻り値となります。<br />
以下がそのクエリです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
select * from information_schema.tables where table_schema = ? and table_name = ? and table_type = 'BASE TABLE'
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>やっとSQLが見れましたね。<br />
このSQLを第一引数、データベース名、テーブル名を配列にしたものを第二引数として渡して <code>select()</code> メソッドとコールします。</p>
<div class="su-spacer" style="height:30px"></div>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Connection::select()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run a select statement against the database.
     *
     * @param  string  $query
     * @param  array  $bindings
     * @param  bool  $useReadPdo
     * @return array
     */
    public function select($query, $bindings = [], $useReadPdo = true)
    {
        return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
            if ($this->pretending()) {
                return [];
            }

            // For select statements, we'll simply execute the query and return an array
            // of the database result set. Each element in the array will be a single
            // row from the database table, and will either be an array or objects.
            $statement = $this->prepared($this->getPdoForSelect($useReadPdo)
                              ->prepare($query));

            $this->bindValues($statement, $this->prepareBindings($bindings));

            $statement->execute();

            return $statement->fetchAll();
        });
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Connection::selectFromWriteConnection()</h6>
<p>第一引数はストリング型でクエリです。<br />
第二引数は配列型でバインド対象です。<br />
戻り値は配列です。
</p></div>
<p><code>run()</code> メソッドをクエリとバインド配列とクロージャーを渡してコールしています。<br />
クロージャーを読んでみましょう。<br />
引数にクエリとバインド配列を受け取ります。<br />
<code>pretending()</code> メソッドをコールしています。これは単に <code>$this->pretending</code> を見ているだけです。このパラメーターはドライランの時に <code>true</code> にセットすることでクエリ実行をブロックする仕様のようです。<br />
<code>pretending()</code> メソッドの戻り値が <code>true</code> だった場合、空の配列を戻します。</p>
<p>続きを読みます。<br />
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$statement = $this->prepared($this->getPdoForSelect($useReadPdo)
        ->prepare($query));
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>prepared()</code> してますね。中から見ましょう。<code>getPdoForSelect()</code> メソッドをコールしています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\getPdoForSelect | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    /**
     * Get the PDO connection to use for a select query.
     *
     * @param  bool  $useReadPdo
     * @return \PDO
     */
    protected function getPdoForSelect($useReadPdo = true)
    {
        return $useReadPdo ? $this->getReadPdo() : $this->getPdo();
    }
    /**
     * Get the current PDO connection.
     *
     * @return \PDO
     */
    public function getPdo()
    {
        if ($this->pdo instanceof Closure) {
            return $this->pdo = call_user_func($this->pdo);
        }

        return $this->pdo;
    }
    /**
     * Get the current PDO connection used for reading.
     *
     * @return \PDO
     */
    public function getReadPdo()
    {
        if ($this->transactions > 0) {
            return $this->getPdo();
        }

        if ($this->recordsModified && $this->getConfig('sticky')) {
            return $this->getPdo();
        }

        if ($this->readPdo instanceof Closure) {
            return $this->readPdo = call_user_func($this->readPdo);
        }

        return $this->readPdo ?: $this->getPdo();
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>getPdoForSelect()</code> メソッドが受け取る引数は読み取り専用接続を使うかどうかのブーリアンのようです。以前データベース接続のコードを読んだ際に、通常の接続 <code>$this->pdo</code> と 読み取り専用接続 <code>$this->readPdo</code> に分けるロジックがありましたね。それの切り替えを行っています。検証した適切な <code>PDO</code> インスタンスが戻されます。</p>
<div class="su-spacer" style="height:30px"></div>
<p>戻された <code>PDO</code> インスタンスに <code>prepare()</code> メソッドがコールされ <code>PDOStatement</code> が戻されます。<br />
戻された <code>PDOStatement</code> を引数にして <code>prepared()</code> メソッドをコールします。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Connection::prepared()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Configure the PDO prepared statement.
     *
     * @param  \PDOStatement  $statement
     * @return \PDOStatement
     */
    protected function prepared(PDOStatement $statement)
    {
        $statement->setFetchMode($this->fetchMode);

        $this->event(new StatementPrepared(
            $this, $statement
        ));

        return $statement;
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Events\StatementPrepared 抜粋</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
namespace Illuminate\Database\Events;
class StatementPrepared
{
    public $connection;
    public $statement;
    public function __construct($connection, $statement)
    {
        $this->statement = $statement;
        $this->connection = $connection;
    }
}

</pre>
<div class="description">
<h6>Illuminate\Database\Connection::prepared()</h6>
<p>第一引数は <code>PDOStatement</code> です。<br />
戻り値は <code>PDOStatement</code> です。
</div>
<p><code>setFetchMode()</code> でフェッチモードをセットします。<br />
<code>StatementPrepared</code> インスタンスを <code>Connection</code> と <code>PDOStatement</code> を引数に渡し生成します。 <code>StatementPrepared</code> は <code>Connection</code> と <code>PDOStatement</code> を記憶しておくだけのシンプルなクラスです。<br />
生成した <code>StatementPrepared</code> をイベントにディスパッチします。<br />
<code>PDOStatement</code> を戻します。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>select()</code> の続きです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$this->bindValues($statement, $this->prepareBindings($bindings));
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>prepareBindings()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Connection::prepareBindings()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Prepare the query bindings for execution.
     *
     * @param  array  $bindings
     * @return array
     */
    public function prepareBindings(array $bindings)
    {
        $grammar = $this->getQueryGrammar();

        foreach ($bindings as $key => $value) {
            // We need to transform all instances of DateTimeInterface into the actual
            // date string. Each query grammar maintains its own date string format
            // so we'll just ask the grammar for the format to get from the date.
            if ($value instanceof DateTimeInterface) {
                $bindings[$key] = $value->format($grammar->getDateFormat());
            } elseif (is_bool($value)) {
                $bindings[$key] = (int) $value;
            }
        }

        return $bindings;
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Connection::prepareBindings()</h6>
<p>第一引数は配列でバインドする値です。<br />
戻り値は配列です。
</p></div>
<p>バインドする値が <code>DateTimeInterface</code> インスタンスである場合、データベースの種類に合わせたフォーマットに変更します。<br />
バインドする値がブーリアンの場合は整数型に変換します。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>bindValues()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Connection::bindValues()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Bind values to their parameters in the given statement.
     *
     * @param  \PDOStatement  $statement
     * @param  array  $bindings
     * @return void
     */
    public function bindValues($statement, $bindings)
    {
        foreach ($bindings as $key => $value) {
            $statement->bindValue(
                is_string($key) ? $key : $key + 1, $value,
                is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR
            );
        }
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Connection::bindValues()</h6>
<p>第一引数は <code>PDOStatement</code> インスタンスです。<br />
第二引数は配列でバインドする値です。<br />
戻り値はありません。
</div>
<p><code>PDOStatement</code> とバインドする配列を受け取り、 <code>foreach</code> でバインドする配列を回してバインド処理していきます。ストリング型と整数型で処理を変えていますね。<br />
<code>PDOStatement</code> をもう一度見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
select * from information_schema.tables where table_schema = ? and table_name = ? and table_type = 'BASE TABLE'
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>このクエリに配列 [データベース名, テーブル名] がバインドされます。完成形のSQLが見れましたね。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>slect()</code> の続きです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$statement->execute();
return $statement->fetchAll();
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>execut()</code> が実行され <code>fetchAll()</code> が戻されました。<br />
とうとうクエリが叩かれましたね。<code>hasTable</code> に戻ります。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
return count($this->connection->select(
            $this->grammar->compileTableExists(), [$this->connection->getDatabaseName(), $table]
        )) > 0;
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>select()</code> の戻り値が 0 より大きいかを検証した結果を戻しています。<br />
<code>migrations</code> テーブルの存在確認ですね。<br />
<code>prepareDatabase()</code> メソッドに戻ります。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    protected function prepareDatabase()
    {
        if (! $this->migrator->repositoryExists()) {
            $this->call('migrate:install', array_filter([
                '--database' => $this->option('database'),
            ]));
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>「&#8217;migarate&#8217;」 テーブルが存在しなかった場合、<code>call()</code> メソッドがコールされます。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Console\Concerns\CallsCommands::call()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Call another console command.
     *
     * @param  \Symfony\Component\Console\Command\Command|string  $command
     * @param  array  $arguments
     * @return int
     */
    public function call($command, array $arguments = [])
    {
        return $this->runCommand($command, $arguments, $this->output);
    }
</pre>
<h4 class="codeTitle">Illuminate\Console\Concerns\CallsCommands::runCommand()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run the given the console command.
     *
     * @param  \Symfony\Component\Console\Command\Command|string  $command
     * @param  array  $arguments
     * @param  \Symfony\Component\Console\Output\OutputInterface  $output
     * @return int
     */
    protected function runCommand($command, array $arguments, OutputInterface $output)
    {
        $arguments['command'] = $command;

        return $this->resolveCommand($command)->run(
            $this->createInputFromArguments($arguments), $output
        );
    }
</pre>
<div class="description">
<h6>Illuminate\Console\Concerns\CallsCommands::call()</h6>
<p>第一引数は <code>Command</code> インスタンス若しくはストリング型で コマンドです。<br />
第二引数は配列で引数です。<br />
戻り値は です。
</div>
<p><code>call()</code> メソッドに受け取ったコマンドとオプションにデータベースキーの情報があればそれを、なければ空の配列を引数に渡します。<br />
<code>call()</code> メソッドは受け取った引数に出力を足して <code>runCommand()</code> メソッドに渡します。<br />
<code>runCommand()</code> メソッドは <code>resolveCommand()</code> メソッドを引数にコマンドを渡しコールし、その戻り値の<code>run()</code> メソッドをコールします。<br />
<code>resolveCommand()</code> メソッドを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Console\Concerns\CallsCommands:: resolveCommand()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Resolve the console command instance for the given command.
     *
     * @param  \Symfony\Component\Console\Command\Command|string  $command
     * @return \Symfony\Component\Console\Command\Command
     */
    protected function resolveCommand($command)
    {
        if (! class_exists($command)) {
            return $this->getApplication()->find($command);
        }

        $command = $this->laravel->make($command);

        if ($command instanceof SymfonyCommand) {
            $command->setApplication($this->getApplication());
        }

        if ($command instanceof self) {
            $command->setLaravel($this->getLaravel());
        }

        return $command;
    }
</pre>
<h4 class="codeTitle">Symfony\Component\Console\Command::getApplication()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Gets the application instance for this command.
     *
     * @return Application|null An Application instance
     */
    public function getApplication()
    {
        return $this->application;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>resolveCommand()</code> は受け取った引数を <code>class_exists()</code> で判定してクラス定義されていなければ <code>getApplication()->find()</code> を戻します。<br />
<code>getApplication()</code> はアプリケーションインスタンスを戻します。返されたインスタンスの <code>find()</code> メソッドをコールします。以前、<code>find()</code> メソッドは読みましたね。渡されるコマンドは 「<code>migrate:install</code>」です。<code>find()</code> メソッドは、<code>Illuminate\Database\MigrationServiceProvider::$commands</code> から対象のコマンドがあるか検索します。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\MigrationServiceProvider::$commands | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    protected $commands = [
        'Migrate' => 'command.migrate',
        'MigrateFresh' => 'command.migrate.fresh',
        'MigrateInstall' => 'command.migrate.install',
        'MigrateRefresh' => 'command.migrate.refresh',
        'MigrateReset' => 'command.migrate.reset',
        'MigrateRollback' => 'command.migrate.rollback',
        'MigrateStatus' => 'command.migrate.status',
        'MigrateMake' => 'command.migrate.make',
    ];
    /**
     * Register the migration repository service.
     *
     * @return void
     */
    protected function registerRepository()
    {
        $this->app->singleton('migration.repository', function ($app) {
            $table = $app['config']['database.migrations'];

            return new DatabaseMigrationRepository($app['db'], $table);
        });
    }
    /**
     * Register the command.
     *
     * @return void
     */
    protected function registerMigrateInstallCommand()
    {
        $this->app->singleton('command.migrate.install', function ($app) {
            return new InstallCommand($app['migration.repository']);
        });
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>ありましたね。<code>InstallCommand</code> インスタンスが返ってくるようです。<br />
<code>InstallCommand::run()</code> は先程読みました。ぐるぐるまわって自身の <code>execute()</code> メソッドがコールされ、コマンドアプリケーションから自身の <code>handle()</code> をコールする手続きでした。<br />
<code>InstallCommand::handle()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\InstallCommand::handle() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * 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.');
    }
    /**
     * 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;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>MigrationServiceProvider::registerMigrateInstallCommand()</code> でコマンドをアプリケーションコンテナに結合する際に、 <code>InstallCommand</code> インスタンスに引数 <code>$app['migration.repository']</code> が渡されていました。<br />
<code>InstallCommand</code> クラスではコンストラクタで <code>$repository</code> に引数を入れています。<br />
つまり、<code>handle()</code> メソッド内の <code>$this->repository</code> は <code>Illuminate\Database\Migrations\DatabaseMigrationRepository</code> インスタンスです。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Migrations\DatabaseMigrationRepository::setSource() | createRepository ()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Set the information source to gather data.
     *
     * @param  string  $name
     * @return void
     */
    public function setSource($name)
    {
        $this->connection = $name;
    }
    /**
     * 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');
        });
    }
    /**
     * Create a new database migration repository instance.
     *
     * @param  \Illuminate\Database\ConnectionResolverInterface  $resolver
     * @param  string  $table
     * @return void
     */
    public function __construct(Resolver $resolver, $table)
    {
        $this->table = $table;
        $this->resolver = $resolver;
    }
    /**
     * Resolve the database connection instance.
     *
     * @return \Illuminate\Database\Connection
     */
    public function getConnection()
    {
        return $this->resolver->connection($this->connection);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>setSource()</code> に <code>$this->input->getOption('database')</code> を引数として渡しています。接続名ですね。<br />
次に <code>repository->createRepository()</code> をコールしています。<br />
<code>createRepository()</code> では、スキーマビルダーに <code>create()</code> メソッドをコールしています。<br />
引数は <code>$this->table</code> とクロージャーです。<br />
<code>$this->table</code> は <code>MigrationServiceProvider::registerRepository()</code> で <code>DatabaseMigrationRepository</code> を生成する際に第二引数として <code>$app['config']['database.migrations']</code> を渡しています。<code>DatabaseMigrationRepository</code> のコンストラクタで渡された引数を <code>$this->table</code> に代入しています。つまり、デフォルトでは 「<code>migrations</code>」 です。<br />
同様に <code>this->resolver</code> は <code>$app['db']</code> つまり、<code>DatabaseManager</code> ですね。<br />
<code>DatabaseManager::connection()</code> メソッドに接続名を渡し接続を受け取ります。受け取った接続の <code> getSchemaBuilder()</code> をコールします。接続は <code>MySqlConnection</code> ですから戻ってくるのは <code>MySqlBuilder</code> ですね。だいぶ読めるようになってきましたね。<br />
では、<code>Builder::create()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:30px"></div>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Builder::create() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new table on the schema.
     *
     * @param  string  $table
     * @param  \Closure  $callback
     * @return void
     */
    public function create($table, Closure $callback)
    {
        $this->build(tap($this->createBlueprint($table), function ($blueprint) use ($callback) {
            $blueprint->create();

            $callback($blueprint);
        }));
    }
    /**
     * Execute the blueprint to build / modify the table.
     *
     * @param  \Illuminate\Database\Schema\Blueprint  $blueprint
     * @return void
     */
    protected function build(Blueprint $blueprint)
    {
        $blueprint->build($this->connection, $this->grammar);
    }
    /**
     * Create a new command set with a Closure.
     *
     * @param  string  $table
     * @param  \Closure|null  $callback
     * @return \Illuminate\Database\Schema\Blueprint
     */
    protected function createBlueprint($table, Closure $callback = null)
    {
        $prefix = $this->connection->getConfig('prefix_indexes')
                    ? $this->connection->getConfig('prefix')
                    : '';

        if (isset($this->resolver)) {
            return call_user_func($this->resolver, $table, $callback, $prefix);
        }

        return new Blueprint($table, $callback, $prefix);
    }

</pre>
<div class="description">
<h6>Illuminate\Database\Schema\Builder::create()</h6>
<p>第一引数はストリング型でテーブル名です。<br />
第二引数はクロージャーでコールバックです<br />
戻り値はありません。
</p></div>
<p><code>tap()</code> メソッドに <code>createBlueprint()</code> メソッドをコールしたものとクロージャーを渡した戻り値を <code>build()</code> メソッドに渡しています。</p>
<div class="su-spacer" style="height:20px"></div>
<div class="description">
<h6>Illuminate\Database\Schema\Builder::create()</h6>
<p>第一引数はストリング型でテーブル名です。<br />
第二引数はクロージャーでコールバックです<br />
戻り値は <code>Blueprint</code> インスタンスです。
</div>
<p>接続設定の <code>prefix_indexes</code> が <code>true</code> なら 接続設定の <code>prefix</code> を <code>false</code> なら空文字を <code>$prefix</code> に代入します。<br />
接続設定は <code>PROJECT_ROOT/config/database.php</code> の内容です。<br />
<code>$this->resolver</code> が <code>null</code> でなければ、<code>call_user_func()</code> にコールバックとして <code>$this->resolver</code> にテーブル名、コールバック関数、プレフィックスを渡しコールし戻ってきたものを戻します。<br />
セットされていなければ、<code>Blueprint</code> インスタンスをテーブル名、コールバック関数、プレフィックスを渡して生成したものを返します。</p>
<p><code>Builder::resolver</code> の更新インターフェースは <code>Builder::blueprintResolver()</code> メソッドのみですので今回は <code>null</code> ですね。<br />
<code>Blueprint</code> インスタンスが生成されます。<code>Blueprint</code> クラスのコンストラクタを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::__construct()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new schema blueprint.
     *
     * @param  string  $table
     * @param  \Closure|null  $callback
     * @param  string  $prefix
     * @return void
     */
    public function __construct($table, Closure $callback = null, $prefix = '')
    {
        $this->table = $table;
        $this->prefix = $prefix;

        if (! is_null($callback)) {
            $callback($this);
        }
    }
</pre>
<div class="description">
<h6>Illuminate\Database\Schema\Blueprint::__construct()</h6>
<p>第一引数はストリング型でテーブル名です。<br />
第二引数はクロージャーか <code>null</code> です。<br />
第三引数はストリングでプレフィックスです。<br />
戻り値はありません。
</div>
<p>引数として受け取ったテーブル名とプレフィックスを変数に代入します。引数にクロージャーが渡されていた場合は自身を引数にコールします。<br />
今回はクロージャーは渡されていません。</p>
<div class="su-spacer" style="height:20px"></div>
<p>ここで生成されたクロージャーが <code>Builder::create()</code> メソッドの <code>tap()</code> ヘルパ関数に渡されます。第二引数のクロージャーを第一引数を引数として渡してコールするんでしたね。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
        $this->build(tap($this->createBlueprint($table), function ($blueprint) use ($callback) {
            $blueprint->create();
            $callback($blueprint);
        }));
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>このクロージャーに渡されている <code>$callback</code> は <code>DatabaseMigrationRepository::createRepository()</code> で渡されている以下です。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
        $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');
        });

</pre>
<div class="su-spacer" style="height:20px"></div>
<p>まず、渡された <code>Blueprint</code> インスタンスの <code>create()</code> メソッドをコールしています。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::create() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Indicate that the table needs to be created.
     *
     * @return \Illuminate\Support\Fluent
     */
    public function create()
    {
        return $this->addCommand('create');
    }
    /**
     * Add a new command to the blueprint.
     *
     * @param  string  $name
     * @param  array  $parameters
     * @return \Illuminate\Support\Fluent
     */
    protected function addCommand($name, array $parameters = [])
    {
        $this->commands[] = $command = $this->createCommand($name, $parameters);

        return $command;
    }
    /**
     * Create a new Fluent command.
     *
     * @param  string  $name
     * @param  array  $parameters
     * @return \Illuminate\Support\Fluent
     */
    protected function createCommand($name, array $parameters = [])
    {
        return new Fluent(array_merge(compact('name'), $parameters));
    }

</pre>
<div class="su-spacer" style="height:20px"></div>
<code>addCommand()</code> メソッドに 「<code>create</code>」という文字列を引数として渡しています。<br />
<code>addCommand()</code> メソッドは <code>$this->commands[]</code> に <code>createCommand()</code> メソッドの戻り値を代入しそれを戻しています。<br />
<code>createCommand()</code> は引数にコマンド名と引数配列を受け取り、<code>compact()</code> で「<code>name</code> 」というキーにコマンド名「<code>create</code> 」を代入した配列と引数配列を <code>array_merge()</code> したものを引数として <code>Fluent</code> インスタンスを生成して戻しています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Support\Fluent::__construct | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
class Fluent implements Arrayable, ArrayAccess, Jsonable, JsonSerializable
{
    /**
     * All of the attributes set on the fluent instance.
     *
     * @var array
     */
    protected $attributes = [];

    /**
     * Create a new fluent instance.
     *
     * @param  array|object  $attributes
     * @return void
     */
    public function __construct($attributes = [])
    {
        foreach ($attributes as $key => $value) {
            $this->attributes[$key] = $value;
        }
    }

    /**
     * Get an attribute from the fluent instance.
     *
     * @param  string  $key
     * @param  mixed  $default
     * @return mixed
     */
    public function get($key, $default = null)
    {
        if (array_key_exists($key, $this->attributes)) {
            return $this->attributes[$key];
        }

        return value($default);
    }

</pre>
<div class="description">
<h6>Illuminate\Support\Fluent::__construct</h6>
<p>第一引数は配列で格納したいパラメーターです。<br />
戻り値はありません。
</p></div>
<p><code>Fluent</code> クラスはコードをざっと見た感じ、コンストラクタや <code>_set()</code> で設定した値を以下の形で取得できるクラスのようです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$fluent = new Fluent(['key' => 'value']);
var_dump($fluent->key); // string(5) "value"
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>ArrayAccess</code> も可能で格納する情報は <code>JSON</code> 型でもいいみたいですね。</p>
<div class="su-spacer" style="height:20px"></div>
<p>続きを見ましょう。</p>
<p><code>$this->table</code> にテーブル名「<code>migrations</code> 」を代入し、<code>$this->commands[]</code> に「<code>'name' => 'create'</code> 」というコマンドが登録された <code>blueprint</code> インスタンスを引数にコールバックをコールします。<br />
コールバックの内容を追いましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Schema\Blueprint::increments() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new auto-incrementing integer (4-byte) column on the table.
     *
     * @param  string  $column
     * @return \Illuminate\Database\Schema\ColumnDefinition
     */
    public function increments($column)
    {
        return $this->unsignedInteger($column, true);
    }
    /**
     * Create a new unsigned integer (4-byte) column on the table.
     *
     * @param  string  $column
     * @param  bool  $autoIncrement
     * @return \Illuminate\Database\Schema\ColumnDefinition
     */
    public function unsignedInteger($column, $autoIncrement = false)
    {
        return $this->integer($column, $autoIncrement, true);
    }
    /**
     * Create a new integer (4-byte) column on the table.
     *
     * @param  string  $column
     * @param  bool  $autoIncrement
     * @param  bool  $unsigned
     * @return \Illuminate\Database\Schema\ColumnDefinition
     */
    public function integer($column, $autoIncrement = false, $unsigned = false)
    {
        return $this->addColumn('integer', $column, compact('autoIncrement', 'unsigned'));
    }
    /**
     * Add a new column to the blueprint.
     *
     * @param  string  $type
     * @param  string  $name
     * @param  array  $parameters
     * @return \Illuminate\Database\Schema\ColumnDefinition
     */
    public function addColumn($type, $name, array $parameters = [])
    {
        $this->columns[] = $column = new ColumnDefinition(
            array_merge(compact('type', 'name'), $parameters)
        );

        return $column;
    }

</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>increments()</code> > <code>unsignedInteger()</code> > <code>integer()</code> とカラム名に加えてパラメーターをつなぎ合わせて <code>addColumn()</code> メソッドをコールしています。<br />
<code>addColumn()</code> メソッドは受け取った引数を渡して <code>ColumnDefinition</code> インスタンスを生成しています。</p>
<p><code>ColumnDefinition</code> インスタンスを生成する時に渡される引数は以下のようなイメージです。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
array(4) {
  ["type"]=> string(7) "integer"
  ["name"]=> string(2) "id"
  ["autoIncrement"]=> bool(true)
  ["unsigned"]=> bool(true)
}

</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>ColumnDefinition</code> は先程の <code>Fluent</code> クラスを継承するクラスです。テーブル情報を管理するためのインスタンスでしょう。生成された <code>ColumnDefinition</code> インスタンスを <code>columns[]</code> に代入し、そのまま戻しています。<br />
<code>string()</code> メソッド、<code>integer()</code> メソッド共に同様の流れです。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>tap()</code> ヘルパ関数の戻り値はクロージャーに引数で渡した値でした。つまり、いろいろな手続きを踏んだ <code>$blueprint</code> が戻ってきます。これを引数に <code>build()</code> する流れとなります。</p>
<p>その１４に続く。</p>

	<div class="st-post-tags">
	TAGS: <a href="https://xi.ayane.co.jp/archives/tag/php" title="php" rel="tag">php</a> / <a href="https://xi.ayane.co.jp/archives/tag/laravel" title="laravel" rel="tag">laravel</a> / <a href="https://xi.ayane.co.jp/archives/tag/%e3%83%95%e3%83%ac%e3%83%bc%e3%83%a0%e3%83%af%e3%83%bc%e3%82%af%e3%82%92%e8%aa%ad%e3%82%80" title="フレームワークを読む" rel="tag">フレームワークを読む</a><br /></div>

<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1573">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１３：MigrateCommand::handle()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1573</post-id>	</item>
		<item>
		<title>【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１２：Console\Command::execute()</title>
		<link>https://xi.ayane.co.jp/archives/1537</link>
		
		<dc:creator><![CDATA[xi]]></dc:creator>
		<pubDate>Fri, 03 Apr 2020 12:54:45 +0000</pubDate>
				<category><![CDATA[フレームワーク]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[laravel]]></category>
		<category><![CDATA[フレームワークを読む]]></category>
		<guid isPermaLink="false">https://xi.ayane.co.jp/?p=1537</guid>

					<description><![CDATA[<p>Symfony\Component\Console\Command\Command::run() から Illuminate/Console/Command::execute() がコールされました。見てみましょう。 I [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1537">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１２：Console\Command::execute()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p> <code>Symfony\Component\Console\Command\Command::run()</code> から <code>Illuminate/Console/Command::execute()</code> がコールされました。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate/Console/Command::execute()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    /**
     * Execute the console command.
     *
     * @param  \Symfony\Component\Console\Input\InputInterface  $input
     * @param  \Symfony\Component\Console\Output\OutputInterface  $output
     * @return int
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        return (int) $this->laravel->call([$this, 'handle']);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<div class="description">
第二引数は <code>Symfony\Component\Console\Input\InputInterface</code> インスタンスで入力です。<br />
第三引数は <code>Symfony\Component\Console\Output\OutputInterface</code> インスタンスで出力です。<br />
戻り値は整数です。
</div>
<p>コマンドアプリケーションコンテナの <code>call()</code> を引数に自身と 「<code>handle</code>」を配列にして渡しコールしています。しばらく Symfony だったので Laravel側は久しぶりですね。見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Container\Container::call()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Call the given Closure / class@method and inject its dependencies.
     *
     * @param  callable|string  $callback
     * @param  array  $parameters
     * @param  string|null  $defaultMethod
     * @return mixed
     */
    public function call($callback, array $parameters = [], $defaultMethod = null)
    {
        return BoundMethod::call($this, $callback, $parameters, $defaultMethod);
    }
</pre>
<h4 class="codeTitle">Illuminate\Container\BoundMethod::call()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Call the given Closure / class@method and inject its dependencies.
     *
     * @param  \Illuminate\Container\Container  $container
     * @param  callable|string  $callback
     * @param  array  $parameters
     * @param  string|null  $defaultMethod
     * @return mixed
     *
     * @throws \ReflectionException
     * @throws \InvalidArgumentException
     */
    public static function call($container, $callback, array $parameters = [], $defaultMethod = null)
    {
        if (static::isCallableWithAtSign($callback) || $defaultMethod) {
            return static::callClass($container, $callback, $parameters, $defaultMethod);
        }

        return static::callBoundMethod($container, $callback, function () use ($container, $callback, $parameters) {
            return call_user_func_array(
                $callback, static::getMethodDependencies($container, $callback, $parameters)
            );
        });
    }
    /**
     * Determine if the given string is in Class@method syntax.
     *
     * @param  mixed  $callback
     * @return bool
     */
    protected static function isCallableWithAtSign($callback)
    {
        return is_string($callback) && strpos($callback, '@') !== false;
    }
    /**
     * Call a string reference to a class using Class@method syntax.
     *
     * @param  \Illuminate\Container\Container  $container
     * @param  string  $target
     * @param  array  $parameters
     * @param  string|null  $defaultMethod
     * @return mixed
     *
     * @throws \InvalidArgumentException
     */
    protected static function callClass($container, $target, array $parameters = [], $defaultMethod = null)
    {
        $segments = explode('@', $target);

        // We will assume an @ sign is used to delimit the class name from the method
        // name. We will split on this @ sign and then build a callable array that
        // we can pass right back into the "call" method for dependency binding.
        $method = count($segments) === 2
                        ? $segments[1] : $defaultMethod;

        if (is_null($method)) {
            throw new InvalidArgumentException('Method not provided.');
        }

        return static::call(
            $container, [$container->make($segments[0]), $method], $parameters
        );
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<h6>Illuminate\Container\Container::call()</h6>
<div class="description">
第一引数はコールバック関数若しくはストリング型です。<br />
第二引数は配列型でコールバック関数に渡す引数です。<br />
第三引数はストリング若しくは <code>null</code> でデフォルトのメソッドです。<br />
戻り値は <code>mix</code> です。
</div>
<p>自身と受け取った引数をそのまま <code>BoundMethod::call()</code> メソッドにスタティックコールして戻り値をそのまま戻しています。</p>
<div class="su-spacer" style="height:20px"></div>
<h6>Illuminate\Container\BoundMethod::call()</h6>
<div class="description">
第一引数はアプリケーションコンテナです。<br />
第二引数はコールバック関数若しくはストリング型です。<br />
第三引数は配列型でコールバック関数に渡す引数です。<br />
第四引数はストリング若しくは <code>null</code> でデフォルトのメソッドです。<br />
戻り値は <code>mix</code> です。
</div>
<p><code>isCallableWithAtSign()</code> メソッドを引数にコールバック関数を渡した戻り値が <code>true</code> 若しくはデフォルトメソッドが引数として渡されている場合は <code>callClass</code> メソッドを引数そのまま渡してコールします。<br />
<code>isCallableWithAtSign()</code> メソッドは コールバック関数として渡された第一引数がストリング型で且つ「@」が含まれている場合 <code>true</code> を戻します。これは、「クラス名@メソッド名」という形でコールバックする関数を指定する事ができるようにしています。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>callClass</code> メソッドは、コールバック関数として受け取ったストリングを 「@」で <code>explode()</code> し、<code>$segments</code> に代入します。<code>$segments</code> の配列の数が２ならば一番目、そうでなければデフォルトメソッドを <code>$method</code> に代入します。<br />
<code>$method</code> が <code>null</code> ならば「Method not provided.」とメッセージを添えて例外 <code> InvalidArgumentException</code> をスローします。<br />
コマンドアプリケーションコンテナに <code>$segments[0]</code> と <code>$method</code> を使い <code>make</code> し、それをコールバック関数として <code>call()</code> メソッドをコールします。</p>
<p><code>isCallableWithAtSign()</code> の戻り値が <code>false</code> だった場合、<code>callBoundMethod()</code> メソッドをコマンドアプリケーションとコールバック関数とクロージャーを引数に渡しコールします。<br />
渡されているクロージャーを見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
function () use ($container, $callback, $parameters) {
    return call_user_func_array(
        $callback, static::getMethodDependencies($container, $callback, $parameters)
    );
}
</pre>
<h4 class="codeTitle">Illuminate\Container\BoundMethod::getMethodDependencies() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /*
     * Get all dependencies for a given method.
     *
     * @param  \Illuminate\Container\Container  $container
     * @param  callable|string  $callback
     * @param  array  $parameters
     * @return array
     *
     * @throws \ReflectionException
     */
    protected static function getMethodDependencies($container, $callback, array $parameters = [])
    {
        $dependencies = [];

        foreach (static::getCallReflector($callback)->getParameters() as $parameter) {
            static::addDependencyForCallParameter($container, $parameter, $parameters, $dependencies);
        }

        return array_merge($dependencies, $parameters);
    }
    /**
     * Get the proper reflection instance for the given callback.
     *
     * @param  callable|string  $callback
     * @return \ReflectionFunctionAbstract
     *
     * @throws \ReflectionException
     */
    protected static function getCallReflector($callback)
    {
        if (is_string($callback) && strpos($callback, '::') !== false) {
            $callback = explode('::', $callback);
        } elseif (is_object($callback) && ! $callback instanceof Closure) {
            $callback = [$callback, '__invoke'];
        }

        return is_array($callback)
                        ? new ReflectionMethod($callback[0], $callback[1])
                        : new ReflectionFunction($callback);
    }
    /**
     * Get the dependency for the given call parameter.
     *
     * @param  \Illuminate\Container\Container  $container
     * @param  \ReflectionParameter  $parameter
     * @param  array  $parameters
     * @param  array  $dependencies
     * @return void
     */
    protected static function addDependencyForCallParameter($container, $parameter,
                                                            array &$parameters, &$dependencies)
    {
        if (array_key_exists($parameter->name, $parameters)) {
            $dependencies[] = $parameters[$parameter->name];

            unset($parameters[$parameter->name]);
        } elseif ($parameter->getClass() && array_key_exists($parameter->getClass()->name, $parameters)) {
            $dependencies[] = $parameters[$parameter->getClass()->name];

            unset($parameters[$parameter->getClass()->name]);
        } elseif ($parameter->getClass()) {
            $dependencies[] = $container->make($parameter->getClass()->name);
        } elseif ($parameter->isDefaultValueAvailable()) {
            $dependencies[] = $parameter->getDefaultValue();
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>call_user_func_array()</code> してます。引数はコールバック関数と <code>getMethodDependencies()</code> です。<br />
まずは <code>getMethodDependencies()</code> メソッドを見てみましょう。</p>
<p><code>getCallReflector()</code> メソッドの戻り値の <code>getParameters()</code> を  <code>foreach</code> してます。<br />
<code>getCallReflector()</code>メソッドを見てみましょう。</p>
<p>コールバックがストリング型で「::」を含む場合は、「::」 で <code>explode()</code> した結果の配列を <code>$callback</code> に代入します。<br />
コールバックがクロージャーの場合は、それを０番目、１番目に「<code>__invoke</code>」を入れた配列を <code>$callback</code> に代入します。<br />
コールバックが配列だった場合は、<code>ReflectionMethod</code> インスタンスを引数にコールバック配列の０番目と１番目を渡して生成して返します。<br />
そうでなかった場合は <code>ReflectionFunction</code> インスタンスを引数にコールバックを渡して生成したものを戻します。<br />
今回は「[$this, &#8216;handle&#8217;]」が入っていますので、 <code>ReflectionMethod</code> が生成されます。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>ReflectionMethod</code> インスタンス及び <code>ReflectionFunction</code> インスタンスの <code> getParameters()</code> メソッドは共にパラメータを <code>ReflectionParameter</code> オブジェクトの配列で返します。</p>
<p>つまり、<code>ReflectionParameter</code> オブジェクトの配列を <code>foreach</code> で回すわけですね。<br />
<code>foreach</code> の中で、<code>addDependencyForCallParameter()</code> メソッドを コマンドアプリケーションコンテナ、<code>ReflectionParameter</code> オブジェクトの配列の中身、参照渡しでコマンドの引数、そして参照渡しで依存関係配列を引数として渡しコールします。</p>
<div class="su-spacer" style="height:20px"></div>
<p><code>addDependencyForCallParameter()</code> メソッドを見てみましょう。<br />
<code>$parameter->name</code> は <code>ReflectionParameter</code> クラスで <code>$name</code> は読込専用でパラメータ名が格納されています。つまり、コマンド引数のパラメーターの中にコールバック関数のパラメータ名がキーとなる値が存在していた場合、依存関係配列にパラメーター名をキーとした配列の値を追加し、同じくパラメーター名をキーとしたコマンド引数の配列を <code>unset</code> で削除します。</p>
<p>そうでない場合、<br />
<code>ReflectionParameter::getClass()</code> メソッドはタイプヒント付きのクラスを取得します。取得したクラスは <code>ReflectionClass</code> インスタンスで <code>$name</code> はクラス名です。<br />
依存関係にコマンド引数のクラス名をキーとした値を追加し、同じくクラス名をキーとしたコマンド引数の配列を <code>unset</code> で削除します。</p>
<p>そうでない場合、<br />
<code>$parameter->getClass()</code> でクラス名が取得できた場合は、コマンドアプリケーションコンテナで <code>$parameter->getClass()->name</code> で取得したクラス名で <code>make()</code> したものを依存関係配列に追加します。</p>
<p>そうでない場合、<code>$parameter->isDefaultValueAvailable()</code> でデフォルト値が存在するか調べています。<br />
もし、存在する場合は依存関係に <code>$parameter->getDefaultValue()</code> でデフォルト値を取得したものを追加します。</p>
<p><code>ReflectionParameter</code> 等の詳細は <a href="http://php.net" rel="noopener noreferrer" target="_blank">php.net</a> を参照してください。</p>
<hr>
<p>依存関係の解決の処理のようです。ここらへんは複雑になってきましたので、追いきれていない感じがします。<br />
別の機会を作ってもう少し整理したいと思います。<br />
続きを読みましょう。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>addDependencyForCallParameter()</code> メソッドで依存関係を調べて戻ってきた配列とコマンド引数配列を <code>array_merge</code> で結合して戻したものが <code>call_user_func_array()</code> で実行されます。</p>
<p>もう一度流れを見てみましょう。</p>
<div class="su-spacer" style="height:30px"></div>
<h4 class="codeTitle">Illuminate/Console/Command::execute()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    /**
     * Execute the console command.
     *
     * @param  \Symfony\Component\Console\Input\InputInterface  $input
     * @param  \Symfony\Component\Console\Output\OutputInterface  $output
     * @return int
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        return (int) $this->laravel->call([$this, 'handle']);
    }
</pre>
<h4 class="codeTitle">Illuminate\Container\Container::call()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Call the given Closure / class@method and inject its dependencies.
     *
     * @param  callable|string  $callback
     * @param  array  $parameters
     * @param  string|null  $defaultMethod
     * @return mixed
     */
    public function call($callback, array $parameters = [], $defaultMethod = null)
    {
        return BoundMethod::call($this, $callback, $parameters, $defaultMethod);
    }
</pre>
<h4 class="codeTitle">Illuminate\Container\BoundMethod::call() | 抜粋</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
function () use ($container, $callback, $parameters) {
    return call_user_func_array(
        $callback, static::getMethodDependencies($container, $callback, $parameters)
    );
}
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>call_user_func_array()</code> の <code>use</code> に渡される変数は、 <code>$container</code> がコマンドアプリケーションコンテナ、<code>$callback</code> が <code>[$this, 'handle']</code>、<code>$parameters</code> が <code>[]</code> ですね。<br />
<code>[$this, 'handle']</code> に対して <code>static::getMethodDependencies($container, $callback, $parameters)</code> を引数に渡してコールします。<br />
<code>getMethodDependencies()</code> は <code>getCallReflector()</code> でコールバックを使い、<code>new ReflectionMethod($callback[0], $callback[1])</code> をで <code>ReflectionMethod</code> を生成しています。つまり、<code>MigrateCommand::handle()</code> がコールされる結果となります。</p>
<p>いよいよ <code>MigrateCommand::handle()</code> を読むろころまで来ました。<br />
その１３に続きます。</p>

	<div class="st-post-tags">
	TAGS: <a href="https://xi.ayane.co.jp/archives/tag/php" title="php" rel="tag">php</a> / <a href="https://xi.ayane.co.jp/archives/tag/laravel" title="laravel" rel="tag">laravel</a> / <a href="https://xi.ayane.co.jp/archives/tag/%e3%83%95%e3%83%ac%e3%83%bc%e3%83%a0%e3%83%af%e3%83%bc%e3%82%af%e3%82%92%e8%aa%ad%e3%82%80" title="フレームワークを読む" rel="tag">フレームワークを読む</a><br /></div>

<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1537">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１２：Console\Command::execute()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1537</post-id>	</item>
		<item>
		<title>【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１１：Console\Application:: doRunCommand()</title>
		<link>https://xi.ayane.co.jp/archives/1472</link>
		
		<dc:creator><![CDATA[xi]]></dc:creator>
		<pubDate>Thu, 02 Apr 2020 11:32:32 +0000</pubDate>
				<category><![CDATA[フレームワーク]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[laravel]]></category>
		<category><![CDATA[フレームワークを読む]]></category>
		<guid isPermaLink="false">https://xi.ayane.co.jp/?p=1472</guid>

					<description><![CDATA[<p>Symfony\Component\Console\Application::find() メソッドからコマンド名が返ってきました。 TRY でコールしていましたので、例外が出た場合のロジックも書いてあります。 「定義さ [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1472">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１１：Console\Application:: doRunCommand()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><code>Symfony\Component\Console\Application::find()</code> メソッドからコマンド名が返ってきました。<br />
TRY でコールしていましたので、例外が出た場合のロジックも書いてあります。<br />
「定義されていません」「もしかしてこれのこと？」のような感じのメッセージをセットしています。<br />
ざっと斜めよみしたら次に行きましょう。<br />
<code>$this->runningCommand</code> に実行コマンドを代入しています。状態管理でしょうか。<br />
次に <code>doRunCommand()</code> がコールされています。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$exitCode = $this->doRunCommand($command, $input, $output);
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>コマンドと入出力が引数として渡されてます。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Symfony\Component\Console\Application::doRunCommand()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Runs the current command.
     *
     * If an event dispatcher has been attached to the application,
     * events are also dispatched during the life-cycle of the command.
     *
     * @return int 0 if everything went fine, or an error code
     */
    protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
    {
        foreach ($command->getHelperSet() as $helper) {
            if ($helper instanceof InputAwareInterface) {
                $helper->setInput($input);
            }
        }

        if (null === $this->dispatcher) {
            return $command->run($input, $output);
        }

        // bind before the console.command event, so the listeners have access to input options/arguments
        try {
            $command->mergeApplicationDefinition();
            $input->bind($command->getDefinition());
        } catch (ExceptionInterface $e) {
            // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition
        }

        $event = new ConsoleCommandEvent($command, $input, $output);
        $e = null;

        try {
            $this->dispatcher->dispatch($event, ConsoleEvents::COMMAND);

            if ($event->commandShouldRun()) {
                $exitCode = $command->run($input, $output);
            } else {
                $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
            }
        } catch (\Throwable $e) {
            $event = new ConsoleErrorEvent($input, $output, $e, $command);
            $this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
            $e = $event->getError();

            if (0 === $exitCode = $event->getExitCode()) {
                $e = null;
            }
        }

        $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
        $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE);

        if (null !== $e) {
            throw $e;
        }

        return $event->getExitCode();
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<h6>Symfony\Component\Console\Application::doRunCommand()</h6>
<div class="description">
第一引数は <code>Command</code> インスタンスです。<br />
第二引数は <code>InputInterface</code> インスタンスで入力です。<br />
第三引数は <code>OutputInterface</code> インスタンスで出力です。<br />
戻り値は コマンドが全て問題なく処理できた場合は 0 例外が起きた場合はエラーコードです。
</div>
<p><code>$command->getHelperSet()</code> を  <code>foreach</code> で回します。おそらく今回はフォーマッタ、デバグフォーマッタ、プロセス、クエスチョンのヘルパが入っていると思います。回したインスタンスが <code>InputAwareInterface</code> だった場合、 <code>setInput()</code> に入力を渡しています。おそらく入力対応インターフェースを持ったヘルパで使うのでしょう。</p>
<p><code>$this->dispatcher</code> を検証し <code>null</code> ならば <code>$command->run()</code> します。<br />
<code>$this->dispatcher</code> は <code>setDispatcher()</code> で設定され、 <code>EventDispatcherInterface</code> インスタンスが代入されます。<br />
イベント登録されている場合の処理が後に続きます。<br />
今回はイベント登録されていませんのでここは通りません。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>$command</code> に入っているのは以下です。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
function ($app) {
    return new MigrateCommand($app['migrator']);
}
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\MigrateCommand</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new migration command instance.
     *
     * @param  \Illuminate\Database\Migrations\Migrator  $migrator
     * @return void
     */
    public function __construct(Migrator $migrator)
    {
        parent::__construct();

        $this->migrator = $migrator;
    }
</pre>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\BaseCommand</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
class BaseCommand extends Command
{
}
</pre>
<h4 class="codeTitle">Illuminate\Console\Command</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new console command instance.
     *
     * @return void
     */
    public function __construct()
    {
        // We will go ahead and set the name, description, and parameters on console
        // commands just to make things a little easier on the developer. This is
        // so they don't have to all be manually specified in the constructors.
        if (isset($this->signature)) {
            $this->configureUsingFluentDefinition();
        } else {
            parent::__construct($this->name);
        }

        // Once we have constructed the command, we'll set the description and other
        // related properties of the command. If a signature wasn't used to build
        // the command we'll set the arguments and the options on this command.
        $this->setDescription((string) $this->description);

        $this->setHelp((string) $this->help);

        $this->setHidden($this->isHidden());

        if (! isset($this->signature)) {
            $this->specifyParameters();
        }
    }
    /**
     * Run the console command.
     *
     * @param  \Symfony\Component\Console\Input\InputInterface  $input
     * @param  \Symfony\Component\Console\Output\OutputInterface  $output
     * @return int
     */
    public function run(InputInterface $input, OutputInterface $output)
    {
        $this->output = $this->laravel->make(
            OutputStyle::class, ['input' => $input, 'output' => $output]
        );

        return parent::run(
            $this->input = $input, $this->output
        );
    }
</pre>
<h4 class="codeTitle">Symfony\Component\Console\Command\Command::__construct() | run() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * @param string|null $name The name of the command; passing null means it must be set in configure()
     *
     * @throws LogicException When the command name is empty
     */
    public function __construct(string $name = null)
    {
        $this->definition = new InputDefinition();

        if (null !== $name || null !== $name = static::getDefaultName()) {
            $this->setName($name);
        }

        $this->configure();
    }
    /**
     * Configures the current command.
     */
    protected function configure()
    {
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<h6>Symfony\Component\Console\Command\Command::run()</h6>
<div class="description">
第二引数は <code>InputInterface</code> インスタンスで入力です。<br />
第三引数は <code>OutputInterface</code> インスタンスで出力です。<br />
戻り値は整数でステータスコードです。
</div>
<p><code>MigrateCommand</code> インスタンスは生成される時に渡される引数 <code>$app['migrator']</code> を変数 <code>$this->migrator</code> に代入します。 スーパークラスのインストラクタをコールします。<br />
<code>MigrateCommand</code> クラスは <code>BaseCommand</code> を継承しています。しかし、 <code>BaseCommand</code> にはコンストラクタがありません。<code>BaseCommand</code> は <code>Command</code> クラスを継承していますのでこのクラスのコンストラクタがコールされます。<br />
まず、<code>$this->signature</code> を検証しています。この部分は前回のその１０で読みましたね。署名に設定されたストリングからコマンド名とオプションの解説をパースして登録します。そして、スーパークラスの <code>Symfony\Component\Console\Command\Command</code> のコンストラクタがコールされ、説明文とヘルプと非表示フラグをセットします。<br />
<code>Symfony\Component\Console\Command\Command</code> はクラス変数 <code>$definition</code> に <code>InputDefinition</code> インスタンスを生成し代入します。自身の <code>configure()</code> メソッドをコールしますが、中身は空です。</p>
<div class="su-spacer" style="height:30px"></div>
<p><code>$command</code> の中には <code>MigrateCommand</code> が生成されて入っているのはわかりました。</p>
<p><code>MigrateCommand::run()</code> メソッドがコールされます。</p>
<p><code>MigrateCommand</code> クラス自体に <code>run()</code> メソッドはありません。継承している <code>BaseCommand</code> クラスにもありません。さらに継承している <code>Command</code> に定義してあります。内容は以下です。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Console\Command::run()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Run the console command.
     *
     * @param  \Symfony\Component\Console\Input\InputInterface  $input
     * @param  \Symfony\Component\Console\Output\OutputInterface  $output
     * @return int
     */
    public function run(InputInterface $input, OutputInterface $output)
    {
        $this->output = $this->laravel->make(
            OutputStyle::class, ['input' => $input, 'output' => $output]
        );

        return parent::run(
            $this->input = $input, $this->output
        );
    }
</pre>
<h4 class="codeTitle">Illuminate\Console\OutputStyle::__construct()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new Console OutputStyle instance.
     *
     * @param  \Symfony\Component\Console\Input\InputInterface  $input
     * @param  \Symfony\Component\Console\Output\OutputInterface  $output
     * @return void
     */
    public function __construct(InputInterface $input, OutputInterface $output)
    {
        $this->output = $output;

        parent::__construct($input, $output);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<h6>Illuminate\Console\Command::run()</h6>
<div class="description">
第一引数は <code>Symfony\Component\Console\Input\InputInterface</code> で入力です。<br />
第二引数は <code>Symfony\Component\Console\Output\OutputInterface</code> で出力です。<br />
戻り値はありません。
</div>
<p><code>OutputStyle</code> としてコマンドアプリケーションに入出力を配列で登録し、<code>$this->output</code> に代入します。<br />
<code>OutputStyle</code> は <code>Symfony\Component\Console\Style\SymfonyStyle</code> を継承するクラスで、出力装飾に関するクラスのようです。</p>
<div class="su-spacer" style="height:20px"></div>
<p>スーパークラスの <code>run()</code> メソッドを入出力を渡してコールします。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Symfony\Component\Console\Command\Command::run()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Runs the command.
     *
     * The code to execute is either defined directly with the
     * setCode() method or by overriding the execute() method
     * in a sub-class.
     *
     * @return int The command exit code
     *
     * @throws \Exception When binding input fails. Bypass this by calling {@link ignoreValidationErrors()}.
     *
     * @see setCode()
     * @see execute()
     */
    public function run(InputInterface $input, OutputInterface $output)
    {
        // force the creation of the synopsis before the merge with the app definition
        $this->getSynopsis(true);
        $this->getSynopsis(false);

        // add the application arguments and options
        $this->mergeApplicationDefinition();

        // bind the input against the command specific arguments/options
        try {
            $input->bind($this->definition);
        } catch (ExceptionInterface $e) {
            if (!$this->ignoreValidationErrors) {
                throw $e;
            }
        }

        $this->initialize($input, $output);

        if (null !== $this->processTitle) {
            if (\function_exists('cli_set_process_title')) {
                if (!@cli_set_process_title($this->processTitle)) {
                    if ('Darwin' === PHP_OS) {
                        $output->writeln('<comment>Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.</comment>', OutputInterface::VERBOSITY_VERY_VERBOSE);
                    } else {
                        cli_set_process_title($this->processTitle);
                    }
                }
            } elseif (\function_exists('setproctitle')) {
                setproctitle($this->processTitle);
            } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
                $output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
            }
        }

        if ($input->isInteractive()) {
            $this->interact($input, $output);
        }

        // The command name argument is often omitted when a command is executed directly with its run() method.
        // It would fail the validation if we didn't make sure the command argument is present,
        // since it's required by the application.
        if ($input->hasArgument('command') && null === $input->getArgument('command')) {
            $input->setArgument('command', $this->getName());
        }

        $input->validate();

        if ($this->code) {
            $statusCode = ($this->code)($input, $output);
        } else {
            $statusCode = $this->execute($input, $output);

            if (!\is_int($statusCode)) {
                throw new \TypeError(sprintf('Return value of "%s::execute()" must be of the type int, %s returned.', static::class, \gettype($statusCode)));
            }
        }

        return is_numeric($statusCode) ? (int) $statusCode : 0;
    }
    /**
     * Returns the synopsis for the command.
     *
     * @param bool $short Whether to show the short version of the synopsis (with options folded) or not
     *
     * @return string The synopsis
     */
    public function getSynopsis(bool $short = false)
    {
        $key = $short ? 'short' : 'long';

        if (!isset($this->synopsis[$key])) {
            $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
        }

        return $this->synopsis[$key];
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<h6>Symfony\Component\Console\Command\Command::run()</h6>
<div class="description">
第一引数は <code>Symfony\Component\Console\Input\InputInterface</code> で入力です。<br />
第二引数は <code>Symfony\Component\Console\Output\OutputInterface</code> で出力です。<br />
戻り値は整数です。
</div>
<p><code>getSynopsis()</code> を引数に <code>true</code> を入れてコールします。<br />
<code>getSynopsis()</code> を引数に <code>false</code> を入れてコールします。<br />
<code>true</code> と <code>false</code> を１回ずつ叩くことで概要の略と詳細を確実に生成しておきます。<br />
<code>mergeApplicationDefinition()</code> メソッドが叩かれています。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Symfony\Component\Console\Command\Command::mergeApplicationDefinition()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Merges the application definition with the command definition.
     *
     * This method is not part of public API and should not be used directly.
     *
     * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
     */
    public function mergeApplicationDefinition(bool $mergeArgs = true)
    {
        if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) {
            return;
        }

        $this->definition->addOptions($this->application->getDefinition()->getOptions());

        $this->applicationDefinitionMerged = true;

        if ($mergeArgs) {
            $currentArguments = $this->definition->getArguments();
            $this->definition->setArguments($this->application->getDefinition()->getArguments());
            $this->definition->addArguments($currentArguments);

            $this->applicationDefinitionMergedWithArgs = true;
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<h6>Symfony\Component\Console\Command\Command::mergeApplicationDefinition()</h6>
<div class="description">
第一引数はブーリアンでアプリケーション定義とコマンド定義をマージするかどうかの指定が入ります。<br />
戻り値はありません。
</div>
<p>コマンドアプリケーションが <code>null</code> 若しくはマージ済み、若しくはマージするかどうかの引数が <code>false</code> の場合、そのまま戻します。<br />
そうでない場合はコマンド定義にアプリケーション定義と引数をマージし、マージ済みフラグを <code>true</code> に設定します。</p>
<div class="su-spacer" style="height:30px"></div>
<p>次は以下が実行されます。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$input->bind($this->definition);
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Symfony\Component\Console\Input\bind()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Binds the current Input instance with the given arguments and options.
     *
     * @throws RuntimeException
     */
    public function bind(InputDefinition $definition)
    {
        $this->arguments = [];
        $this->options = [];
        $this->definition = $definition;

        $this->parse();
    }
</pre>
<h4 class="codeTitle">Symfony\Component\Console\ArgvInput\parse()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Processes command line arguments.
     */
    protected function parse()
    {
        $parseOptions = true;
        $this->parsed = $this->tokens;
        while (null !== $token = array_shift($this->parsed)) {
            if ($parseOptions && '' == $token) {
                $this->parseArgument($token);
            } elseif ($parseOptions && '--' == $token) {
                $parseOptions = false;
            } elseif ($parseOptions && 0 === strpos($token, '--')) {
                $this->parseLongOption($token);
            } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
                $this->parseShortOption($token);
            } else {
                $this->parseArgument($token);
            }
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<h6>Symfony\Component\Console\ArgvInput\parse()</h6>
<div class="description">
第一引数は <code>InputDefinition</code> インスタンスです。<br />
戻り値はありません。
</div>
<p>引数とオプションを空の配列で初期化して、 <code>$this->definition</code> に引数として渡された定義を代入します。<br />
今回の場合は <code>$command->getDefinition()</code> で取得したコマンド定義が入ります。<br />
その後 <code>parse()</code> で解析し引数、オプションを登録していきます。</p>
<div class="su-spacer" style="height:30px"></div>
<p>次に以下が実行されています。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$this->initialize($input, $output);
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>Symfony\Component\Console\Command\Command::initialize()</code> メソッドは中身が空になっています。オーバーライドされてもいないようです。初期化が必要なコマンドを作成する場合はここを拡張して利用するのでしょう。</p>
<div class="su-spacer" style="height:30px"></div>
<p>次に <code>$this->processTitle</code> が <code>null</code> かどうか検証しています。<br />
 <code>$this->processTitle</code> を設定できるインターフェイスは <code>setProcessTitle()</code> でそのコードドキュメントには 「この機能は、長いプロセスコマンドを作成する場合にのみ使用してください。」とあります。デーモンのようなコマンドを作成する時にプロセス名をセットするためのもののようです。今回は <code>null</code> なのでここは通りません。ざっとみるとプロセス名を設定出来なかった場合の例外処理やメッセージなどのロジックのようです。次を見ましょう。<code>isInteractive()</code> メソッドがコールされています。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    if ($input->isInteractive()) {
        $this->interact($input, $output);
    }
</pre>
<h4 class="codeTitle">Symfony\Component\Console\Input\Input::interact()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
    /**
     * Interacts with the user.
     *
     * This method is executed before the InputDefinition is validated.
     * This means that this is the only place where the command can
     * interactively ask for values of missing required arguments.
     */
    protected function interact(InputInterface $input, OutputInterface $output)
    {
    }
</pre>
<h4 class="codeTitle">Symfony\Component\Console\Input\Input::isInteractive()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    protected $interactive = true;
    /**
     * Is this input means interactive?
     *
     * @return bool
     */
    public function isInteractive()
    {
        return $this->interactive;
    }
    /**
     * {@inheritdoc}
     */
    public function setInteractive(bool $interactive)
    {
        $this->interactive = $interactive;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>isInteractive()</code> メソッドは <code>$this->interactive</code> をそのまま戻しています。 <code>$this->interactive</code> はインスタンス生成時に <code>true</code> に設定されています。変更するインターフェースは <code> setInteractive()</code> メソッドです。このメソッドをコールしているところは特に見当たりません。<code>Input::interact()</code> メソッドのコードドキュメントに書いてある内容から推測すると、実行される前に会話式のインターフェースで実行内容を変更するタイプのコマンドを作成したい時に <code>interact()</code> メソッドをオーバーライドして使うのかもしれません。</p>
<div class="su-spacer" style="height:30px"></div>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
        // The command name argument is often omitted when a command is executed directly with its run() method.
        // It would fail the validation if we didn't make sure the command argument is present,
        // since it's required by the application.
        if ($input->hasArgument('command') && null === $input->getArgument('command')) {
            $input->setArgument('command', $this->getName());
        }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>コマンド定義に引数「&#8217;command&#8217;」があり、コマンドラインから受け取った引数「&#8217;command&#8217;」がなかった場合、コマンド名を設定しています。<br />
コードドキュメントによると、コマンドがrun（）メソッドで直接実行される場合、コマンド名の引数が省略されることがあるため、だそうです。<br />
今回はここは通らなそうです。</p>
<div class="su-spacer" style="height:30px"></div>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$input->validate();
</pre>
<h4 class="codeTitle">Symfony\Component\Console\Input\Input::validate()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Validates the input.
     *
     * @throws RuntimeException When not enough arguments are given
     */
    public function validate()
    {
        $definition = $this->definition;
        $givenArguments = $this->arguments;

        $missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) {
            return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
        });

        if (\count($missingArguments) > 0) {
            throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments)));
        }
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
コマンドラインから受け取った引数とコマンド定義を照らし合わせ足りない引数があった場合は 「<code>sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))</code>」 とメッセージを添えて例外 <code> RuntimeException</code> を投げています。引数チェックですね。</p>
<p>続きです。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Symfony\Component\Console\Command\Command::run() 抜粋</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
        if ($this->code) {
            $statusCode = ($this->code)($input, $output);
        } else {
            $statusCode = $this->execute($input, $output);
            if (!\is_int($statusCode)) {
                throw new \TypeError(sprintf('Return value of "%s::execute()" must be of the type int, %s returned.', static::class, \gettype($statusCode)));
            }
        }
        return is_numeric($statusCode) ? (int) $statusCode : 0;
</pre>
<h4 class="codeTitle">Symfony\Component\Console\Command\Command::execute() | setCode()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Executes the current command.
     *
     * This method is not abstract because you can use this class
     * as a concrete class. In this case, instead of defining the
     * execute() method, you set the code to execute by passing
     * a Closure to the setCode() method.
     *
     * @return int 0 if everything went fine, or an exit code
     *
     * @throws LogicException When this abstract method is not implemented
     *
     * @see setCode()
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        throw new LogicException('You must override the execute() method in the concrete command class.');
    }
    /**
     * Sets the code to execute when running this command.
     *
     * If this method is used, it overrides the code defined
     * in the execute() method.
     *
     * @param callable $code A callable(InputInterface $input, OutputInterface $output)
     *
     * @return $this
     *
     * @throws InvalidArgumentException
     *
     * @see execute()
     */
    public function setCode(callable $code)
    {
        if ($code instanceof \Closure) {
            $r = new \ReflectionFunction($code);
            if (null === $r->getClosureThis()) {
                $code = \Closure::bind($code, $this);
            }
        }

        $this->code = $code;

        return $this;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->code</code> が設定されている場合は、それを、されていない場合は、<code>$this->execute()</code> をコールし、戻り値を <code>$statusCode</code> に代入します。 <code>$statusCode</code> が整数型でなかった場合、「<code>sprintf('Return value of "%s::execute()" must be of the type int, %s returned.', static::class, \gettype($statusCode))</code>」のメッセージを添えて例外 <code>TypeError</code> をスローします。<br />
<code>$this->code</code> は <code>setCode()</code> で設定できます。<br />
<code>Symfony\Component\Console\Command\Command</code> を継承し、<code>execute()</code> メソッドをオーバーライドしてコマンド処理を記述する方法の他に、<code>Symfony\Component\Console\Command\Command</code> 自体に <code>setCode()</code> にクロージャーを渡す方法もあるそうです。今回は <code>Illuminate/Console/Command</code> で <code>execute()</code> をオーバーライドしています。<br />
<code>Illuminate/Console/Command::execute()</code> は次の回で読みます。<br />
<code>$statusCode</code> が整数ならばそれを、そうでなければ 0 を戻します。</p>
<div class="su-spacer" style="height:30px"></div>
<p>その１２に続きます。</p>

	<div class="st-post-tags">
	TAGS: <a href="https://xi.ayane.co.jp/archives/tag/php" title="php" rel="tag">php</a> / <a href="https://xi.ayane.co.jp/archives/tag/laravel" title="laravel" rel="tag">laravel</a> / <a href="https://xi.ayane.co.jp/archives/tag/%e3%83%95%e3%83%ac%e3%83%bc%e3%83%a0%e3%83%af%e3%83%bc%e3%82%af%e3%82%92%e8%aa%ad%e3%82%80" title="フレームワークを読む" rel="tag">フレームワークを読む</a><br /></div>

<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1472">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１１：Console\Application:: doRunCommand()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1472</post-id>	</item>
		<item>
		<title>【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１０：Console\Application::find()</title>
		<link>https://xi.ayane.co.jp/archives/1440</link>
		
		<dc:creator><![CDATA[xi]]></dc:creator>
		<pubDate>Thu, 02 Apr 2020 04:53:54 +0000</pubDate>
				<category><![CDATA[フレームワーク]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[laravel]]></category>
		<category><![CDATA[フレームワークを読む]]></category>
		<guid isPermaLink="false">https://xi.ayane.co.jp/?p=1440</guid>

					<description><![CDATA[<p>Console\Application::doRun() の処理の流れで find() メソッドがコールされているところまで読みました。では、find() を見てみましょう。 Symfony\Component\Cons [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1440">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１０：Console\Application::find()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><code>Console\Application::doRun()</code> の処理の流れで <code>find()</code> メソッドがコールされているところまで読みました。では、<code>find()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Symfony\Component\Console\Application::find() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Finds a command by name or alias.
     *
     * Contrary to get, this command tries to find the best
     * match if you give it an abbreviation of a name or alias.
     *
     * @return Command A Command instance
     *
     * @throws CommandNotFoundException When command name is incorrect or ambiguous
     */
    public function find(string $name)
    {
        $this->init();

        $aliases = [];

        foreach ($this->commands as $command) {
            foreach ($command->getAliases() as $alias) {
                if (!$this->has($alias)) {
                    $this->commands[$alias] = $command;
                }
            }
        }

        if ($this->has($name)) {
            return $this->get($name);
        }

        $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands);
        $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
        $commands = preg_grep('{^'.$expr.'}', $allCommands);

        if (empty($commands)) {
            $commands = preg_grep('{^'.$expr.'}i', $allCommands);
        }

        // if no commands matched or we just matched namespaces
        if (empty($commands) || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) {
            if (false !== $pos = strrpos($name, ':')) {
                // check if a namespace exists and contains commands
                $this->findNamespace(substr($name, 0, $pos));
            }

            $message = sprintf('Command "%s" is not defined.', $name);

            if ($alternatives = $this->findAlternatives($name, $allCommands)) {
                // remove hidden commands
                $alternatives = array_filter($alternatives, function ($name) {
                    return !$this->get($name)->isHidden();
                });

                if (1 == \count($alternatives)) {
                    $message .= "\n\nDid you mean this?\n    ";
                } else {
                    $message .= "\n\nDid you mean one of these?\n    ";
                }
                $message .= implode("\n    ", $alternatives);
            }

            throw new CommandNotFoundException($message, array_values($alternatives));
        }

        // filter out aliases for commands which are already on the list
        if (\count($commands) > 1) {
            $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands;
            $commands = array_unique(array_filter($commands, function ($nameOrAlias) use (&$commandList, $commands, &$aliases) {
                if (!$commandList[$nameOrAlias] instanceof Command) {
                    $commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias);
                }

                $commandName = $commandList[$nameOrAlias]->getName();

                $aliases[$nameOrAlias] = $commandName;

                return $commandName === $nameOrAlias || !\in_array($commandName, $commands);
            }));
        }

        if (\count($commands) > 1) {
            $usableWidth = $this->terminal->getWidth() - 10;
            $abbrevs = array_values($commands);
            $maxLen = 0;
            foreach ($abbrevs as $abbrev) {
                $maxLen = max(Helper::strlen($abbrev), $maxLen);
            }
            $abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen, &$commands) {
                if ($commandList[$cmd]->isHidden()) {
                    unset($commands[array_search($cmd, $commands)]);

                    return false;
                }

                $abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription();

                return Helper::strlen($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev;
            }, array_values($commands));

            if (\count($commands) > 1) {
                $suggestions = $this->getAbbreviationSuggestions(array_filter($abbrevs));

                throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $name, $suggestions), array_values($commands));
            }
        }

        $command = $this->get(reset($commands));

        if ($command->isHidden()) {
            throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
        }

        return $command;
    }
    private function init()
    {
        if ($this->initialized) {
            return;
        }
        $this->initialized = true;

        foreach ($this->getDefaultCommands() as $command) {
            $this->add($command);
        }
    }
    /**
     * Adds a command object.
     *
     * If a command with the same name already exists, it will be overridden.
     * If the command is not enabled it will not be added.
     *
     * @return Command|null The registered command if enabled or null
     */
    public function add(Command $command)
    {
        $this->init();

        $command->setApplication($this);

        if (!$command->isEnabled()) {
            $command->setApplication(null);

            return null;
        }

        // Will throw if the command is not correctly initialized.
        $command->getDefinition();

        if (!$command->getName()) {
            throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', \get_class($command)));
        }

        $this->commands[$command->getName()] = $command;

        foreach ($command->getAliases() as $alias) {
            $this->commands[$alias] = $command;
        }

        return $command;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<h6>Symfony\Component\Console\Application::find()</h6>
<div class="description">
第一引数は<code>InputInterface</code> で入力です。<br />
第二引数は<code>OutputInterface</code> で出力です<br />
戻り値はコマンドが全て正常に実行された場合は 0 そうでない場合はエラーコードです。
</div>
<p>まず、<code>init()</code> メソッドがコールされます。<br />
<code>init()</code> メソッドでは、<code>$this->initialized</code> が検証され <code>true</code> であればそのまま戻されます。<br />
これまでの流れで <code>$this->initialized</code> に<code>true</code> を代入した箇所はありませんでした。そのまま飛ばします。<br />
次に <code>$this->initialized</code> に <code>true</code> を代入します。初期化フラグですね。<br />
<code>foreach</code> で <code>$this->getDefaultCommands()</code> を回し、中身を <code>add()</code> メソッドに渡しています。<br />
<code>$this->getDefaultCommands()</code> の戻り値は以下です。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
return [new HelpCommand(), new ListCommand()];
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>つまり、ヘルプと一覧コマンドが登録されるわけですね。<code>add()</code> メソッドの中でも <code>$this->init()</code> メソッドがコールされています。一度 <code>init()</code> メソッドがコールされれば <code>$this->initialized</code> は <code>true</code> になるのでそのまま戻されるのですが、少し冗長な印象です。どこからコールされてもヘルプと一覧だけは登録したいということなのでしょう。もう少し違う設計もありそうに思いますが、Symfonyの思想的なものなのでしょうか。</p>
<div class="su-spacer" style="height:30px"></div>
<p>次に <code>$command->setApplication()</code> を引数に自身を渡してコールしています。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Symfony\Component\Console\Command\Command::setApplication()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    public function setApplication(Application $application = null)
    {
        $this->application = $application;
        if ($application) {
            $this->setHelperSet($application->getHelperSet());
        } else {
            $this->helperSet = null;
        }
    }

    public function setHelperSet(HelperSet $helperSet)
    {
        $this->helperSet = $helperSet;
    }

</pre>
<h4 class="codeTitle">Symfony\Component\Console\Application::getHelperSet() | 関連メソッド</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Get the helper set associated with the command.
     *
     * @return HelperSet The HelperSet instance associated with this command
     */
    public function getHelperSet()
    {
        if (!$this->helperSet) {
            $this->helperSet = $this->getDefaultHelperSet();
        }

        return $this->helperSet;
    }
    /**
     * Gets the default helper set with the helpers that should always be available.
     *
     * @return HelperSet A HelperSet instance
     */
    protected function getDefaultHelperSet()
    {
        return new HelperSet([
            new FormatterHelper(),
            new DebugFormatterHelper(),
            new ProcessHelper(),
            new QuestionHelper(),
        ]);
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>Command</code> インスタンスの変数にアプリケーションコンテナを代入し、<code>setHelperSet()</code> メソッドをコールしています。引数として <code>$application->getHelperSet()</code> メソッドをコールした戻り値を渡しています。 <code>setHelperSet()</code> は受け取った引数をそのまま <code>$this->helperSet</code> に代入するだけです。<br />
<code>getHelperSet()</code> は <code>$this->helperSet</code> つまりコマンドアプリケーションコンテナの <code>helperSet</code> がセットされていなければ、 <code>getDefaultHelperSet()</code> をコールし戻り値をそれに代入してさらにその値を戻します。つまり <code>Command</code> インスタンスにも同じものが共有されます。内容は、フォーマッタ、デバグフォーマッタ、プロセス、クエスチョンのヘルパです。<br />
ちなみに、ヘルパは <code>setHelperSet()</code> で変更することができます。</p>
<div class="su-spacer" style="height:30px"></div>
<p>次に <code>$command->getDefinition()</code> メソッドをコールしています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Symfony\Component\Console\Command\Command::getDefinition()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Gets the InputDefinition attached to this Command.
     *
     * @return InputDefinition An InputDefinition instance
     */
    public function getDefinition()
    {
        if (null === $this->definition) {
            throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class));
        }

        return $this->definition;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>$this->definition</code> が <code>null</code> だった場合「Command class &#8220;%s&#8221; is not correctly initialized. You probably forgot to call the parent constructor.」というメッセージを添えて例外 <code>LogicException</code> をスローします。<code>$this->definition</code> は <code>Command</code> インスタンスが生成される時にコンストラクタで <code>InputDefinition</code> インスタンスを生成したものが代入されるはずですので、ここで例外がスローされるケースがちょっとわかりません。思想でしょうか。問題なければそのまま <code>this->definition</code> を戻します。コール元は引数を受け取っていません。つまり初期化が済んでいなかったら例外をスローするという処理です。</p>
<p>次に <code>$command->getName()</code> を検証しています。これは 各コマンドが <code>Command</code> クラスを継承して <code>configure()</code> メソッドで設定を構築する時に <code>setName()</code> メソッドで設定するコマンド名です。ない場合は「The command defined in &#8220;%s&#8221; cannot have an empty name.」というメッセージを添えて例外 <code>LogicException</code> をスローします。<br />
コマンド名があれば <code>$this->commands[]</code> 配列にコマンド名をキーに <code>Command</code> インスタンスを追加します。<br />
コマンドにコマンドエイリアスが登録されていた場合、コマンドアプリケーションにもエイリアス登録します。<br />
そのまま <code>Command</code> インスタンスを戻します。<br />
コール元の <code>init()</code> の <code>foreach</code> では戻り値を受け取っていません。 <code>init</code> の処理完了です。</p>
<div class="su-spacer" style="height:30px"></div>
<hr>
<p>その８で、<code>Symfony\Component\Console\Application::add()</code> までたどり着いたところで一旦引き返しました。<br />
その時コールされた <code>add()</code> の挙動は先程理解しました。<br />
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$this->commands[$alias] = $command;
</pre>
<div class="su-spacer" style="height:20px"></div>
の処理でコマンド群が登録されていますので、ここからコマンドラインから指示されたコマンドを検索して実行すると推測されます。<br />
その８で <code>ArtisanServiceProvider</code> インスタンスの <code>$commands</code> と <code>$devCommands</code> を <code>array_merge</code> したものを登録していました。ただ、この中には <code>migrate</code> コマンドらしきものはありません。あてが外れたようです。<code>Illuminate\Foundation\Providers\ConsoleSupportServiceProvider</code> の <code>$providers</code> には以下の記述がされていました。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
protected $providers = [
    ArtisanServiceProvider::class,
    MigrationServiceProvider::class,
    ComposerServiceProvider::class,
];
</pre>
<div class="su-spacer" style="height:20px"></div>
そうです。<code>ArtisanServiceProvider</code> ではなく、 <code>MigrationServiceProvider</code> を読むべきでした。<br />
読んでみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\MigrationServiceProvider 抜粋</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * The commands to be registered.
     *
     * @var array
     */
    protected $commands = [
        'Migrate' => 'command.migrate',
        'MigrateFresh' => 'command.migrate.fresh',
        'MigrateInstall' => 'command.migrate.install',
        'MigrateRefresh' => 'command.migrate.refresh',
        'MigrateReset' => 'command.migrate.reset',
        'MigrateRollback' => 'command.migrate.rollback',
        'MigrateStatus' => 'command.migrate.status',
        'MigrateMake' => 'command.migrate.make',
    ];

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerRepository();

        $this->registerMigrator();

        $this->registerCreator();

        $this->registerCommands($this->commands);
    }

    /**
     * Register the migration repository service.
     *
     * @return void
     */
    protected function registerRepository()
    {
        $this->app->singleton('migration.repository', function ($app) {
            $table = $app['config']['database.migrations'];

            return new DatabaseMigrationRepository($app['db'], $table);
        });
    }

    /**
     * Register the migrator service.
     *
     * @return void
     */
    protected function registerMigrator()
    {
        // The migrator is responsible for actually running and rollback the migration
        // files in the application. We'll pass in our database connection resolver
        // so the migrator can resolve any of these connections when it needs to.
        $this->app->singleton('migrator', function ($app) {
            $repository = $app['migration.repository'];

            return new Migrator($repository, $app['db'], $app['files'], $app['events']);
        });
    }

    /**
     * Register the migration creator.
     *
     * @return void
     */
    protected function registerCreator()
    {
        $this->app->singleton('migration.creator', function ($app) {
            return new MigrationCreator($app['files'], $app->basePath('stubs'));
        });
    }

    /**
     * Register the given commands.
     *
     * @param  array  $commands
     * @return void
     */
    protected function registerCommands(array $commands)
    {
        foreach (array_keys($commands) as $command) {
            call_user_func_array([$this, "register{$command}Command"], []);
        }

        $this->commands(array_values($commands));
    }
    /**
     * Register the command.
     *
     * @return void
     */
    protected function registerMigrateCommand()
    {
        $this->app->singleton('command.migrate', function ($app) {
            return new MigrateCommand($app['migrator']);
        });
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>やはり、そのものズバリの <code>'Migrate' => 'command.migrate'</code> がありました。<br />
構造は以前読んだ <code>ArtisanServiceProvider</code> とほぼ同じですね。違う点は <code>migrator</code> コマンドを登録する際に <code>database.migrations</code> を <code>migration.repository</code> として <code>Migrator</code> インスタンスに渡しているところ、さらに <code>migration.creator</code> として <code>MigrationCreator</code> インスタンスをアプリケーションコンテナに登録したファイルシステムを引数に渡して生成しています。おそらくコマンドラインからマイグレーションファイルを生成する時にファイルストリームを使うのでしょう。</p>
<p>これで、<code>$this->commands['migrate']</code> に <code>MigrageCommand</code> を生成するクロージャーが入っているのが推測されます。</p>
<div class="su-spacer" style="height:20px"></div>
<p>一つ疑問があります。<code>Symfony\Component\Console\Application::add()</code> の中で以下の処理によって <code>$this->commands[]</code> にコマンドを登録していると思います。</p>
<div class="su-spacer" style="height:20px"></div>
<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-linenumbers="false">
$this->commands[$command->getName()] = $command;
</pre>
<div class="su-spacer" style="height:20px"></div>
<p>この <code>$command->getName()</code> で取得するコマンド名が <code>migrate</code> の場合どこで定義しているのでしょうか。<br />
<code>Illuminate\Database\Console\Migrations\MigrateCommand</code> クラスの定義を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Database\Console\Migrations\MigrateCommand 抜粋</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    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;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>registerMigrateCommand()</code> メソッドで <code>MigrateCommand</code> インスタンスを生成しています。<code>MigrateCommand</code> クラスは <code>$signature</code> 変数にコマンド名と署名が定義してあります。<br />
<code>MigrateCommand</code> クラスのスーパークラスのスーパークラス、<code>Illuminate\Console\Command</code> のコンストラクタに以下のようなロジックが入っています。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Console\Command::__construct()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Create a new console command instance.
     *
     * @return void
     */
    public function __construct()
    {
        // We will go ahead and set the name, description, and parameters on console
        // commands just to make things a little easier on the developer. This is
        // so they don't have to all be manually specified in the constructors.
        if (isset($this->signature)) {
            $this->configureUsingFluentDefinition();
        } else {
            parent::__construct($this->name);
        }

        // Once we have constructed the command, we'll set the description and other
        // related properties of the command. If a signature wasn't used to build
        // the command we'll set the arguments and the options on this command.
        $this->setDescription((string) $this->description);

        $this->setHelp((string) $this->help);

        $this->setHidden($this->isHidden());

        if (! isset($this->signature)) {
            $this->specifyParameters();
        }
    }

    /**
     * Configure the console command using a fluent definition.
     *
     * @return void
     */
    protected function configureUsingFluentDefinition()
    {
        [$name, $arguments, $options] = Parser::parse($this->signature);

        parent::__construct($this->name = $name);

        // After parsing the signature we will spin through the arguments and options
        // and set them on this command. These will already be changed into proper
        // instances of these "InputArgument" and "InputOption" Symfony classes.
        $this->getDefinition()->addArguments($arguments);
        $this->getDefinition()->addOptions($options);
    }

</pre>
<div class="su-spacer" style="height:20px"></div>
<p><code>$this->signature</code> を検証し設定されていた場合、<code>configureUsingFluentDefinition()</code> メソッドがコールされます。<code>configureUsingFluentDefinition()</code> メソッドは <code>Parser::parse($this->signature)</code> の静的メソッドをコールして戻り値を <code>$this->name</code> に代入しています。<br />
<code>Parser::parse()</code> を見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Illuminate\Console\Parser::parse()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Parse the given console command definition into an array.
     *
     * @param  string  $expression
     * @return array
     *
     * @throws \InvalidArgumentException
     */
    public static function parse($expression)
    {
        $name = static::name($expression);

        if (preg_match_all('/\{\s*(.*?)\s*\}/', $expression, $matches)) {
            if (count($matches[1])) {
                return array_merge([$name], static::parameters($matches[1]));
            }
        }

        return [$name, [], []];
    }
    /**
     * Extract the name of the command from the expression.
     *
     * @param  string  $expression
     * @return string
     *
     * @throws \InvalidArgumentException
     */
    protected static function name($expression)
    {
        if (! preg_match('/[^\s]+/', $expression, $matches)) {
            throw new InvalidArgumentException('Unable to determine command name from signature.');
        }

        return $matches[0];
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<h6>Illuminate\Console\Parser::parse()</h6>
<div class="description">
引数はストリング型でコマンド説明文です。<br />
戻り値は配列型でコマンド名、パラメーターです。
</div>
<p>受け取った引数のはじめからスペースまでの文字列をコマンド名に、パラメーターの説明として「{}」で囲まれた文字列を全て取得して配列型として戻されます。</p>
<div class="su-spacer" style="height:20px"></div>
<p>これで <code>$this->commands['migrate']</code> に <command>MigrageCommand</code> を生成するクロージャーが入っていることがはっきり確認できました。スッキリしましたね。<br />
続きを読みましょう。</p>
<div class="su-spacer" style="height:30px"></div>
<hr>
<p><code>find()</code> の続きにもどります。</p>
<p><code>$this->commands[]</code> に登録されているコマンドに登録されているエイリアスで、<code>$this->commands[]</code> に登録されていないものを登録します。</p>
<p><code>has()</code> メソッドで実行したいコマンドが登録されているか確認します。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Symfony\Component\Console\Application::has()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Returns true if the command exists, false otherwise.
     *
     * @return bool true if the command exists, false otherwise
     */
    public function has(string $name)
    {
        $this->init();

        return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name) && $this->add($this->commandLoader->get($name)));
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<h6>Symfony\Component\Console\Application::has()</h6>
<div class="description">
第一引数はストリングでコマンド名です。<br />
戻り値はブーリアンでコマンド名の登録確認の結果です。
</div>
<p><code>init()</code> メソッドの後に <code>$this->commands[$name]</code> がセットされているか確認しています。今回はもちろん <code>true</code> が戻されます。<br />
その後に書いてある <code>commandLoader</code> はなんでしょうか。コマンドアプリケーションの <code>$commandLoader</code> に代入されている <code>CommandLoaderInterface</code> インターフェースを実装したクラスのようです。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Symfony\Component\Console\CommandLoader\CommandLoaderInterface</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
interface CommandLoaderInterface
{
    /**
     * Loads a command.
     *
     * @return Command
     *
     * @throws CommandNotFoundException
     */
    public function get(string $name);

    /**
     * Checks if a command exists.
     *
     * @return bool
     */
    public function has(string $name);

    /**
     * @return string[] All registered command names
     */
    public function getNames();
}
</pre>
<div class="su-spacer" style="height:20px"></div>
<code>get()</code> と <code>has()</code> と <code>getNames()</code> のメソッドが定義されているのみです。コードドキュメントも特にありません。推測ですが、この作りだとコマンドの遅延ロードをしたい時に使うのかもしれません。本筋に戻りましょう。</p>
<hr>
<p><code>$this->has()</code> の返り値は <code>true</code> ですので、 <code>$this->get()</code> がコールされます。<br />
見てみましょう。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">Symfony\Component\Console\Application::get()</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
    /**
     * Returns a registered command by name or alias.
     *
     * @return Command A Command object
     *
     * @throws CommandNotFoundException When given command name does not exist
     */
    public function get(string $name)
    {
        $this->init();

        if (!$this->has($name)) {
            throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
        }

        $command = $this->commands[$name];

        if ($this->wantHelps) {
            $this->wantHelps = false;

            $helpCommand = $this->get('help');
            $helpCommand->setCommand($command);

            return $helpCommand;
        }

        return $command;
    }
</pre>
<div class="su-spacer" style="height:20px"></div>
<h6>Symfony\Component\Console\Application::get()</h6>
<div class="description">
第一引数はストリングでコマンド名です。<br />
戻り値は <code>Command</code> インスタンスです。
</div>
<p>まず初期化をします。そこら中に書いてありますね。そういう思想なのでしょう。<br />
もし、渡されたコマンド名が登録されていなかった場合、「<code>sprintf('The command "%s" does not exist.', $name)</code>」とメッセージを添えて例外 <code>CommandNotFoundException</code> をスローします。<br />
登録されていた場合、そのコマンドを取得して戻します。<br />
ヘルプ表示フラグが立っていた場合はヘルプコマンドを優先して返します。</p>
<p>コマンドを受け取った <code>find()</code> メソッドはそれをそのまま返します。<br />
今回のケースでは <code>find()</code> メソッドはここまで読めば良いのですが、<br />
そのあとを少し覗いてみましょう。<br />
いろいろ判定していますが、<br />
「そんなコマンドはないけどもしかしてこれの事かい？」というようなメッセージをセットしていたりします。<br />
コマンドが見つからなかった時の救済処置や例外などを色々書いているのでしょう。今回はこれ以上深追いしないことにします。</p>
<hr>
<p><code>find()</code> メソッドの中身はほぼ理解しました。続きを読みましょう。</p>
<p>その１１に続く。</p>

	<div class="st-post-tags">
	TAGS: <a href="https://xi.ayane.co.jp/archives/tag/php" title="php" rel="tag">php</a> / <a href="https://xi.ayane.co.jp/archives/tag/laravel" title="laravel" rel="tag">laravel</a> / <a href="https://xi.ayane.co.jp/archives/tag/%e3%83%95%e3%83%ac%e3%83%bc%e3%83%a0%e3%83%af%e3%83%bc%e3%82%af%e3%82%92%e8%aa%ad%e3%82%80" title="フレームワークを読む" rel="tag">フレームワークを読む</a><br /></div>

<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1440">【フレームワークを読む】Laravelのマイグレーションファイルは何をしているのか？ ：その１０：Console\Application::find()</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1440</post-id>	</item>
		<item>
		<title>PHP ストリングに対する[] でのアクセス</title>
		<link>https://xi.ayane.co.jp/archives/1389</link>
		
		<dc:creator><![CDATA[xi]]></dc:creator>
		<pubDate>Wed, 01 Apr 2020 07:53:59 +0000</pubDate>
				<category><![CDATA[言語]]></category>
		<category><![CDATA[php]]></category>
		<guid isPermaLink="false">https://xi.ayane.co.jp/?p=1389</guid>

					<description><![CDATA[<p>PHP 5.5 以降では、文字列リテラル内の文字に対して [] や {} でアクセスできるそうです。 例 $xion = 'xionxionxion'; echo($xion[0].PHP_EOL); echo($xio [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1389">PHP ストリングに対する[] でのアクセス</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>PHP 5.5 以降では、文字列リテラル内の文字に対して [] や {} でアクセスできるそうです。</p>
<div class="su-spacer" style="height:20px"></div>
<h4 class="codeTitle">例</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
$xion = 'xionxionxion';
echo($xion[0].PHP_EOL);
echo($xion[1].PHP_EOL);
echo($xion[2].PHP_EOL);
echo($xion[3].PHP_EOL);
</pre>
<h4 class="codeTitle">出力結果</h4>
<pre class="EnlighterJSRAW" data-enlighter-language="php">
x
i
o
n
</pre>
<div class="su-spacer" style="height:20px"></div>

	<div class="st-post-tags">
	TAGS: <a href="https://xi.ayane.co.jp/archives/tag/php" title="php" rel="tag">php</a><br /></div>

<p>The post <a rel="nofollow" href="https://xi.ayane.co.jp/archives/1389">PHP ストリングに対する[] でのアクセス</a> appeared first on <a rel="nofollow" href="https://xi.ayane.co.jp">XIONCC</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1389</post-id>	</item>
	</channel>
</rss>
