[Laravel]macroについて知る

## 背景

- Laravelプロジェクトアサイン当初、VSCodeの拡張機能`PHP Intelephense`により、知らないメソッドはよくジャンプしてvender/laravel配下を読みに行くことが多かった
- そんな中、以下コードを見た

```php
$query->whereLike('code', $this->input)
```

- この`whereLike`というメソッドを見て

> 「LaravelはLIKE検索もできるwhere句もあるんだ～」

とのんきに`PHP Intelephense`でジャンプしようと思ったらできない。

> おや？

ジャンプできない理由は謎だったが、ドキュメントを確認すると、`whereLike`なんてメソッドはない…

https://readouble.com/laravel/8.x/ja/queries.html

- よくよく調べるとプロジェクト内にこんなソースコードを発見

```php
Builder::macro('whereLike',function...
```

> 「なんじゃこりゃ…？」

- `macro`というワードとそれ以降に書いてある（特に第2引数）コードの意味がよく分からんかったので、深く理解しようというのが今回の背景

## 目的

- macroを理解する
 - ※きっかけとなったwhereLikeをわざわざ作った背景等はもう一つ上の次元なので、一旦割愛
- 気になる方は概要理解
[https://qiita.com/take_3/items/1154ecbd8033a9a3beaf](https://qiita.com/take_3/items/1154ecbd8033a9a3beaf)

## 環境
* Laravel 8.x

## 内容

### 前提

- 今回はmacroを知ったきっかけであるBuilderで理解進める

https://readouble.com/laravel/8.x/ja/scout.html#builder-macros

- Collectionにもmacroがあるらしい。
 - こっちも利用頻度高いと思うので、取り上げる

https://readouble.com/laravel/8.x/ja/collections.html#extending-collections


### 概要

- まずは1次情報からということで公式ドキュメントで

> 8.x macro

で検索すると、8件のヒット（21/11現在）

- そもそも「マクロとは？」の答えは以下

> 関連する複数の操作や手順、命令などを一つにまとめ、必要に応じて呼び出すことができるようにする機能

※参考↓
[https://e-words.jp/w/マクロ.html](https://e-words.jp/w/%E3%83%9E%E3%82%AF%E3%83%AD.html)

- なるほど、意味を知ってしまえばそんな難しくない
 - 「一つにまとめて、必要に応じて使う」といいう点からサービスコンテナの延長の概念だなと感じた

### 使い方－Builder

公式ドキュメントに沿って行く

### ソースコード

### マクロの登録

```ExampleServiceProbiver.php
use Illuminate\\Support\\Facades\\Response;
use Illuminate\\Support\\ServiceProvider;
use Laravel\\Scout\\Builder;

/**
 * 全アプリケーションサービスの初期起動処理
 *
 * @return void
 */
public function boot()
{
    Builder::macro('count', function () {
        return $this->engine->getTotalCount(
            $this->engine()->search($this)
        );
    });
}
```

- 記述するのは場所はどこでもいいと思うが、一般的には公式と同じようにサービスプロバイダ―に記述することが多そう（推測）
    - そうすれば、いつでも使いたい時に作ったメソッドを使用できるから
- Builderの場合は`Laravel\\Scout\\Builder`内にあるmacroメソッドを使う。引数は
    - 第2引数：メソッド名
    - 第3引数：メソッドによる処理内容

### マクロの使用

```ExampleController.php
use App\\Models\\Order;

Order::search('Star Trek')->count();

```

- あとはどこでも使うだけ。
 - すごく楽。

### 使い方－Collection

こちらも公式ドキュメントに沿って行く

### ソースコード

### マクロの登録

```ExampleServiceProbiver.php
use Illuminate\\Support\\Collection;
use Illuminate\\Support\\Str;

Collection::macro('toUpper', function () {
    return $this->map(function ($value) {
        return Str::upper($value);
    });
});
```

- 95%くらいbuilderと同じ
    - Biulderと異なるのは`macro`メソッド本体の設定場所くらい
- 記述するのは場所はどこでもよさそう。（理由は上述の通り）
- Collectionの場合は`Illuminate\\Support\\Collection`内にあるmacroメソッドを使う。引数はBuilderと同じ以下の通り
    - 第2引数：メソッド名
    - 第3引数：メソッドによる処理内容
- 今回は`toUpper`というメソッドをCollectionに対して使うことで、Collection内の文字列を大文字にするマクロを作った模様。

### マクロの使用

- 使用もBuilderと同じ。

```ExampleController.php
$collection = collect(['first', 'second']);

$upper = $collection->toUpper();

dump($upper)

// ↓出力結果
// ['FIRST', 'SECOND']
```

- 無事、小文字→大文字に変換されております。

## 補足

- 他にはレスポンスに対してマクロを登録したり、

https://readouble.com/laravel/8.x/ja/responses.html?#response-macros

- そのレスポンスをサービスプロバイダで依存注入（DI）してマクロ登録したり

https://readouble.com/laravel/8.x/ja/providers.html#boot-method-dependency-injection

結構いろんなところで使える。

- Laravelのみならず、macroと類似した機能は他の言語にも存在する
  - C言語ではdefineという関数らしい
  - [https://www.sejuku.net/blog/25927](https://www.sejuku.net/blog/25927)

## 所見

### ただの感想

- サービスプロバイダに記述するあたりなどみて

> 独自の処理をメソッドにギュッと凝縮していつでも使える感じにした「お手軽サービスコンテナ」だな
> 

という直感的な感想です笑

- そもそも現状でもかゆいところに手が届くくらい、組み込み関数は充実している感があるが、

> 「使いたいやつないなら、自分で作って」
> 

と言わんばかりにサービス精神あふれるLaravelはしゅごい。

### 引っかかる点

- ただ、Builderについては`スコープ`、Collectionについては`アクセサ`がある
- 個人的主観としては、以下の使い分けのイメージ
  - 汎用度が相対的に高い（＝スコープが相対的に広い）：`マクロ`
     - 扱うリソースが多岐にわたる（User, Order, Post…etc）
  - 汎用度が相対的に低い（＝スコープが相対的に狭い）：`スコープ`、`アクセサ`
     - 扱うリソースが限られている（User内だけとか）
- ただ、そうなると、

> マクロとサービスコンテナとの使い分けは？

とかの疑問がわくので、ここら辺は実務で使っていく中で言語化していこうと思います。