laravel クエリビルダ whereDateメソッドの定義場所と内容 間違えてたらすみません

# 目的

- laravelのクエリビルダのメソッドである`whereDate()`がどこで定義されているのか気になり調べてみた

# 情報

- クエリビルダのメソッドとしてのバインドは下記で定義されていた。
    - `アプリ名ディレクトリ/todos/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php`
- 下記の様に定義されていた。関係するメソッドのみ抜粋して記載する。（どちらかというとSQLを組み立てている部分がどうなっているか知りたいので下記はサラッと記載する。）

    ```アプリ名ディレクトリ/todos/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php
    /**
     * Prepare the value and operator for a where clause.
     *
     * @param  string  $value
     * @param  string  $operator
     * @param  bool  $useDefault
     * @return array
     *
     * @throws \InvalidArgumentException
     */
    public function prepareValueAndOperator($value, $operator, $useDefault = false)
    {
        if ($useDefault) {
            return [$operator, '='];
        } elseif ($this->invalidOperatorAndValue($operator, $value)) {
            throw new InvalidArgumentException('Illegal operator and value combination.');
        }

        return [$value, $operator];
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~中略~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

    /**
     * Add a "where date" statement to the query.
     *
     * @param  string  $column
     * @param  string  $operator
     * @param  \DateTimeInterface|string|null  $value
     * @param  string  $boolean
     * @return $this
     */
    public function whereDate($column, $operator, $value = null, $boolean = 'and')
    {
        [$value, $operator] = $this->prepareValueAndOperator(
            $value, $operator, func_num_args() === 2
        );

        $value = $this->flattenValue($value);

        if ($value instanceof DateTimeInterface) {
            $value = $value->format('Y-m-d');
        }

        return $this->addDateBasedWhere('Date', $column, $operator, $value, $boolean);
    }
    
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~中略~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

    /**
     * Add a date based (year, month, day, time) statement to the query.
     *
     * @param  string  $type
     * @param  string  $column
     * @param  string  $operator
     * @param  mixed  $value
     * @param  string  $boolean
     * @return $this
     */
    protected function addDateBasedWhere($type, $column, $operator, $value, $boolean = 'and')
    {
        $this->wheres[] = compact('column', 'type', 'boolean', 'operator', 'value');

        if (! $value instanceof Expression) {
            $this->addBinding($value, 'where');
        }

        return $this;
    }

    /**
     * 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;
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~中略~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
    ```

- MySQLの文法としてwhereDate句をコンパイルしている箇所は下記だった。
    - `アプリ名ディレクトリ/todos/vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php`
- 関係のあるメソッドのみ抜き出して下記に記載する。

    ```アプリ名ディレクトリ/todos/vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php
    /**
     * Compile a "where date" clause.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  array  $where
     * @return string
     */
    protected function whereDate(Builder $query, $where)
    {
        $value = $this->parameter($where['value']);

        return 'cast('.$this->wrap($where['column']).' as date) '.$where['operator'].' '.$value;
    }
    ```

- `'cast('.$this->wrap($where['column']).' as date) '.$where['operator'].' '.$value;`のSQLを組み立てている部分を区切って見ていく。例として`whereDate('created_at', '<=', '2021-07-07')`を実行したものとする。
    - `'cast('.$this->wrap($where['column']).' as date) '`
        - これはSQLのcast関数を組み立てている部分である。
        - SQLのcast関数は下記のように振る舞う。
            - cast(テキスト as データ型)とすることで「テキスト」を指定の「データ型」に変換する
        - `$this->wrap($where['column'])`の部分は評価対象のカラム（今回だとcreated_at）の値である。
        - なので`'cast('.$this->wrap($where['column']).' as date) '`は`'cast('.created_atの値.' as date) '`と表す事ができる。
    - `.$where['operator'].`
        - これは演算子（オペレーター）を表している（今回だと<=の部分）
        - なので`.$where['operator'].`は`.<=.`と表すことができる。
    - `'.$value;`
        - これはwhereDate条件の絞り込みを行う値である。（今回だと2021-07-07の部分）
        - なので`'.$value;`は`'.2021-07-07`と表すことができる。
- 区切ったものをまとめてみる。（`whereDate('created_at', '<=', '2021-07-07')`を実行した場合）
    - `'cast('.$this->wrap($where['column']).' as date) '`は`'cast('.created_atの値.' as date) '`と表す。
    - `.$where['operator'].`は`.<=.`と表す。
    - `'.$value;`は`'.2021-07-07`と表す。
    - 区切ったものを一行で表すと下記の様になる。
        - `'cast('.created_atの値.' as date) '.<=.' '.2021-07-07;`
    - なのでSqlServerGrammarクラスのwhereDate()メソッドが返している文字列は下記となる。
        - `cast(created_atの値 as date) <= 2021-07-07`
    - 余談だが、SQLのくみたてということもあり`.`を使った文字列連結が多用されている。パット見めっちゃ複雑に見えたけど大したことをやってなかった。

# メモ

- operator → 演算子

# 参考文献

- [https://www.php.net/manual/ja/function.func-num-args.php](https://www.php.net/manual/ja/function.func-num-args.php)
- [https://www.php.net/manual/ja/language.operators.type.php](https://www.php.net/manual/ja/language.operators.type.php)
- [https://dev.mysql.com/doc/refman/8.0/ja/cast-functions.html](https://dev.mysql.com/doc/refman/8.0/ja/cast-functions.html)