[Laravel] Macroableを使ってクラスを拡張する

LaravelのMacroableトレイトを使ったクラスの拡張について。

Illuminate\Support\Traits\MacroableをuseするとClass::macro(method, callback)として既存のクラスにメソッドを拡張することができます。

公式では実装済みのCollectionクラスにでの使い方が記されており、独自のメソッドを追加できるようになっています。

Collections - Laravel - The PHP Framework For Web Artisans

use Illuminate\Support\Str;

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

$collection = collect(['first', 'second']);

$upper = $collection->toUpper();

// ['FIRST', 'SECOND']

また、コントローラーでリクエストされた値に対しての評価をする方法として下記があります。

/**
 * Store a new blog post.
 *
 * @param  Request  $request
 * @return Response
 */
public function store(Request $request)
{
    $validatedData = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);

    // The blog post is valid...
}

このvalidate()メソッドは下記のようにMacroableトレイトを使って定義されています。

  1. Illuminate\Http\RequestIlluminate\Support\Traits\Macroableをuseしてメソッドの拡張を可能に
  2. Illuminate\Foundation\Providers\FoundationServiceProviderでmacro で登録
// laravel/framework/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php

/**
 * Register the "validate" macro on the request.
 *
 * @return void
 */
public function registerRequestValidate()
{
    Request::macro('validate', function (array $rules, ...$params) {
        validator()->validate($this->all(), $rules, ...$params);

        return $this->only(collect($rules)->keys()->map(function ($rule) {
            return str_contains($rule, '.') ? explode('.', $rule)[0] : $rule;
        })->unique()->toArray());
    });
}

Macroableトレイトのデメリットとしては、ServiceProvider等を使わずに拡張しすぎると見通しが悪くなってしまう点と、barryvdh/laravel-ide-helperで補完されないようです。

Validate method not found in Illuminate\Http\Request · Issue #608 · barryvdh/laravel-ide-helper · GitHub

© Xzxzyzyz