17
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Laravelのソースコードリーディング第1回

Last updated at Posted at 2022-11-12

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の中身を見てみましょう!

Model.php
public static function __callStatic($method, $parameters)
{
    return (new static)->$method(...$parameters);
}

このように定義されています。
これはつまりこういうことです!

// この2つのコードは同じ意味
(new static)->$method(...$parameters);
(new User)->find(1);

このようにして動的なfindメソッドを呼び出しています。
ただこのクラスには動的なfindメソッドもありませんでした。
動的なメソッドが存在しないので呼び出されるのが__callです。

Model.php
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メソッドがありました!

Builder.php
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動画

今後も何かしらのメソッドの内部コード解説記事を書けたらなと思います!

17
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?