【Laravel】Model::where()のような書き方ができる理由
よくmodel::whereのようなコードを見かけるが、Modelのドキュメントにはwhereは載っていないです。
ではなぜ正常に実行することができるのか。
laravel8のコードリーディングを軽くしていきたいと思います。
まず
でwhereメソッドの定義がされていないことを確認する。
定義されていないクラスメソッドを実行しようとすると呼び出される。
modelのインスタンスを生成して、whereを実行しようとしていますが、whereは定義されていないので、
__callに処理が移ります。
調べた結果whereは以下の分岐を通ることになります。
Traitsに定義されているforwardCallToを読み進めていきます。
まず引数に渡しているのが、
$this->newQueryとメソッド名とそのパラメータ。
newQueryメソッドは
\Illuminate\Database\Eloquent\Builderを生成してくれています。
生成した\Illuminate\Database\Eloquent\Builderインスタンスに対して
whereを実行します。
の
にwhereが定義されていました!>where('id','=','1')
を指定して、elseを通るとします。
queryプロパティには
\Illuminate\Database\Query\Builder がインスタンス生成時に代入されています。
つまり最終的に
\Illuminate\Database\Query\Builder のwhereメソッドが実行されます。
またfirst等でクエリ結果を取得する際のことを考えます。
\Illuminate\Database\Eloquent\Builderで以下のtraitがuseされています。
Illuminate\Database\Concerns\BuildsQueries (trait)でのfirstの定義
\Illuminate\Database\Eloquent\Builderでtakeは定義されていないので、
__callマジックメソッドが実行される。
\Illuminate\Database\Query\Builderのtakeが実行され、
\Illuminate\Database\Eloquent\Builderの\Illuminate\Database\Query\Builderインスタンスが更新される。ここで取得したデータのレコード数が1になる。
\Illuminate\Database\Eloquent\Builderでgetは定義されており、
1つのModalが入った\Illuminate\Database\Eloquent\Collectionインスタンスを返してくれる。
\Illuminate\Database\Eloquent\CollectionはIlluminate\Support\Collectionを拡張して作られているため、fisrtを使用してコレクションの最初の要素を取得しています。
上記のようなことから
例えば
Player::where('id','=','1')->first()
のような簡単なコードでも、上記につらつら書いたような動作をlaravelが内部でやってくれています。
このコードリーディングを通して得た理解としては、以下のような感じ。
Illuminate/Database/Eloquent/Model
↓
Illuminate/Database/Eloquent/Builder
(\Illuminate\Database\Query\Builder をラッピングしている、プロパティとして持っている)
Modelには
のように明示的にIlluminate/Database/Eloquent/Builderを生成してくれるクラスメソッドがあるので 、個人的には
Player::where('id','=','1')->first()
と書くよりも、
Player::query()->where('id','=','1')->first()
と書く方が好きです。
Discussion