Modelクラス
Modelクラスの内部のソースを読んでみたいと思います!
こんなコードがあったとします
$user = User::find(1);
これはユーザーテーブルからid(primary key)が1のデータを取得するというコードです。
ちなみにfindメソッドは引数に配列を渡して複数取得するとこも可能です。最近知りました。
ここのfindメソッドは静的メソッドとして呼び出されています。ということはModelクラスでstaticで定義されているはずです。
Model.phpのファイルの中を探してみます!
見当たらない、、、
Modelクラスには静的なfindメソッドどころか動的なfindメソッドも定義されていないのです。
これを知った私は、じゃあなんでエラーにならないのか意味が分かりませんでした。
色々調べているうちに__callStatic
と__call
というマジックメソッドの存在を知りました。
この2つのマジックメソッドを簡単に説明すると、定義されていない静的メソッドを呼び出した時に通るのが__callStatic
。
定義されていない動的メソッドを呼び出した時に通るのが__call
です。
今回の場合だとまず__callStatic
を通るということです。
では__callStatic
の中身を見てみましょう!
public static function __callStatic($method, $parameters)
{
return (new static)->$method(...$parameters);
}
このように定義されています。
これはつまりこういうことです!
// この2つのコードは同じ意味
(new static)->$method(...$parameters);
(new User)->find(1);
このようにして動的なfindメソッドを呼び出しています。
ただこのクラスには動的なfindメソッドもありませんでした。
動的なメソッドが存在しないので呼び出されるのが__call
です。
public function __call($method, $parameters)
{
if (in_array($method, ['increment', 'decrement'])) {
return $this->$method(...$parameters);
}
if ($resolver = (static::$relationResolvers[get_class($this)][$method] ?? null)) {
return $resolver($this);
}
return $this->forwardCallTo($this->newQuery(), $method, $parameters);
}
今回は一番下のreturn文が実行されます。
次はforwardCallTo
メソッドを見てます。
このメソッドはこのように定義されています。
protected function forwardCallTo($object, $method, $parameters)
{
try {
return $object->{$method}(...$parameters);
} catch (Error|BadMethodCallException $e) {
$pattern = '~^Call to undefined method (?P<class>[^:]+)::(?P<method>[^\(]+)\(\)$~';
if (! preg_match($pattern, $e->getMessage(), $matches)) {
throw $e;
}
if ($matches['class'] != get_class($object) ||
$matches['method'] != $method) {
throw $e;
}
static::throwBadMethodCallException($method);
}
}
return $object->{$method}
これが実行されます。
この$object
はEloquentのBuilderです。
なのでEloquentのBuilderクラスのfindメソッドが呼び出されることになります。
EloquentのBuilderクラスにはちゃんとfindメソッドがありました!
public function find($id, $columns = ['*'])
{
if (is_array($id) || $id instanceof Arrayable) {
return $this->findMany($id, $columns);
}
return $this->whereKey($id)->first($columns);
}
findメソッドの中身を少し追って見てみましたが、内部ではwhereメソッドが呼び出されてるんですね!
この先が気になる人は続きのコードを読んでみてください!
$user = User::find(1);
こんな単純なコードですが結構寄り道してるんだなと思って面白かったです。
英語ですが解説してくれてる動画があるので載せておきます!YouTube動画
今後も何かしらのメソッドの内部コード解説記事を書けたらなと思います!