【Laravel】Modelクラスに存在しないfindメソッドが実行できる仕組み(マジックメソッドとBuilderへの動的委譲)
1. 概要 / 結論
- LaravelのEloquentにおいて、
Modelクラス(およびその継承クラス)でfind()メソッドが呼び出せる内部メカニズムに関する備忘録。 -
find()はModel.phpに直接定義されておらず、PHPのマジックメソッド(__callStatic/__call)経由でBuilder.php(Illuminate\Database\Eloquent\Builder)へ動的に処理が委譲される。
2. 課題・背景(なぜ調べたか)
- Eloquentのソースコード(
Model.php)を確認した際、頻繁に利用するfind()メソッドの定義が存在せず、なぜUser->find(1)などの記述で正常に動作するのか疑問に思ったため。 - 親子関係(クラス継承)ではない
ModelとBuilderがどのように連携してクエリを発行しているのか、構造を把握したかったため。
3. 実装・解決方法 / 使い方
find() 呼び出し時の動的委譲フロー
User::find(1) や $user->find(1) を実行すると、以下のステップで Builder.php の find() メソッドへと到達する。
// 1. 静的呼び出し(User::find(1))の場合
// Model.php に静的 find() が存在しないため __callStatic が発動
public static function __callStatic($method,$parameters)
{
// 遅延静的結合 (new static) により、呼び出し元(Userクラス)のインスタンスを生成して __call へ接続
return (new static)->$method(...$parameters);
}
// 2. インスタンス呼び出し((new User)->find(1))に変換された場合
// Model.php にインスタンスメソッドの find() も存在しないため __call が発動
public function __call($method,$parameters)
{
// newQuery() で Eloquent Builder インスタンスを生成し、処理を Builder::find() へ横流しする
return $this->forwardCallTo($this->newQuery(), $method,$parameters);
}
// 3. 委譲先:Illuminate/Database/Eloquent/Builder.php
public function find($id,$columns = ['*'])
{
// 単一IDか配列かに応じてクエリを組み立てて実行
if (is_array($id) \vert{}\vert{}$id instanceof Arrayable) {
return $this->findMany($id,$columns);
}
return $this->where($this->model->getKeyName(), $id)->first($columns);
}
なぜこのような設計なのか
Model と Builder で役割を明確に分担させる設計思想(関心の分離)に基づいている。
- Model.php(データの表現): 属性(カラム)の保持、リレーション定義、イベントハンドリングなどを担当。
- Builder.php(クエリの構築・実行): where、orderBy、limit、find などのSQL生成と実行を担当。
マジックメソッドを用いて Model から Builder へ委譲することで、Model.php の巨大化(Fat Model)を防ぎつつ、直感的でシンプルな記述法を実現している。
4. はまりポイント・注意点
-
new static()(遅延静的結合)の動き:
__callStatic 内で記述されている (new static()) は、PHP標準の遅延静的結合(Late Static Bindings)を利用している。親クラスの Model ではなく、呼び出された子クラス(例: User や Post)のインスタンスを動的に生成する。 -
ローカルスコープ(scopeXxx)との混同:
User::active() などのローカルスコープも __callStatic / __call 経由で呼び出されるが、find() の委譲ロジックはスコープ機能ではなく、PHPの標準機能(static 構文)とマジックメソッドによる動的委譲そのものである。
5. 参考リンク
- Laravel Official Documentation - Eloquent ORM
- GitHub - laravel/framework (Illuminate/Database/Eloquent/Model.php)
- GitHub - laravel/framework (Illuminate/Database/Eloquent/Builder.php)