Collection等のIlluminateパッケージのクラスやファサードにメソッドを追加する方法を紹介します。
要約
- Collectionクラスのように、`Illuminate\Support\Traits\Macroable`をuseしているクラスの場合、macro()を使ってメソッドを追加できる
- メソッドを追加するには、サービスプロバイダーを作成して、boot()内で`Collection::macro(‘メソッド名’, クロージャー)`を定義すればOK
説明
Laravelを使っていると、Illuminate\Support\CollectionやIlluminate\Http\Request等のIlluminate系のクラスを使う場面がよくあります。
Illuminateパッケージのクラスはメソッドを豊富に揃えていて便利ですが、実際のプロダクトで使用している汎用的な処理をメソッドとして追加したくなることがあります。
例えばCollectionの各要素に対して処理を加えたい場合は、foreachやmap()でループさせて処理をすると思います。
foreach($collection as $value) {
// 処理
}
$collection->map(function ($value) {
// 処理
});
こうした処理を自作のクラスで関数化するのもありだと思いますが、LaravelではMacroを使うことでCollectionクラスのメソッドとして追加することができます。
例えばCollectionにtoUpper()というメソッドを追加し、下記のように実行することが可能です。
$collection->toUpper();
メソッドの実装内容によってはメソッドチェーンに組み込むこともできたりと、柔軟な使い方が可能なので、頻繁に使う処理はmacro()を使用して、Illuminateパッケージのクラスやファサードへのメソッド追加を 検討する価値は十分にあると思います。
Macroの定義
それでは最初のサンプルとして、上記のtoUpper()の実装方法を紹介します。
Macro用のサービスプロバイダーを作成する
php artisan make:provider MacroServiceProvider
メソッドの定義(引数なしVer)
boot()内でCollection::macro()を使用して追加したいメソッドの処理を実装します。
class MacroServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
Collection::macro('toUpper', function () {
return $this->map(function ($value) {
return Str::upper($value);
});
});
}
/**
* Register services.
*
* @return void
*/
public function register()
{
//
}
}
macro()で定義しているクロージャー内で$this
を使用していますが、この$this
にはCollectionクラスのインスタンスがbindされています。
そのため、Collectionクラスに定義されているメソッドを呼び出すことが可能です。
config/app.phpにサービスプロバイダーを登録する
'providers' => [
// 中略
App\Providers\MacroServiceProvider::class,
];
使い方
$collection = collect(['foo', 'bar', 'foobar'])
$collection->toUpper()
// [ "FOO", "BAR", "FOOBAR" ]
引数を渡したい場合はどうすれば良いか?
上記の例は引数を取らないメソッドだったので、引数を取る場合の例を紹介します。
ドメインが引数の値を含むかどうか判定する、というメソッドをIlluminate\Http\Request
に追加します。
メソッドの定義(引数ありVer)
先程作成したサービスプロバイダーに、新たにmacroを追加します。
class MacroServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
Collection::macro('toUpper', function () {
return $this->map(function ($value) {
return Str::upper($value);
});
});
Request::macro('rootContains', function ($value) {
return Str::contains($this->root(), $value);
});
}
/**
* Register services.
*
* @return void
*/
public function register()
{
//
}
}
macro()の第2引数に渡しているクロージャが$value
を受け取るようになっています。
rootContains()に引数を渡した際に$value
にその値が設定され、クロージャー内で使われます。
使い方
$request->rootContains('admin')
// true or false
どのクラスにメソッドを追加できるのか?
Illuminateパッケージの全てのクラスや、ファサードにmacro()を使ってメソッドを追加できるわけではありません。
クラスを拡張してメソッドが追加するためには、対象のクラスがIlluminate\Support\Traits\Macroable
をuseしている必要があります。
Laravel5.6でMacroableをuseしているファサードとクラスの一覧を記載するので、参考にして下さい。
ファサード
Auth
Cache
File
Lang
Mail
Redirect
Request
Response
Route
URL
Illuminateクラス
Illuminate\Auth\RequestGuard
Illuminate\Auth\SessionGuard
Illuminate\Cache\Repository
Illuminate\Console\Command
Illuminate\Console\Scheduling\Event
Illuminate\Database\Grammar
Illuminate\Database\Eloquent\FactoryBuilder
Illuminate\Database\Eloquent\Relations\Relation
Illuminate\Database\Query\Builder
Illuminate\Database\Schema\Blueprint
Illuminate\Filesystem\Filesystem
Illuminate\Foundation\Testing\TestResponse
Illuminate\Http\JsonResponse
Illuminate\Http\RedirectResponse
Illuminate\Http\Request
Illuminate\Http\Response
Illuminate\Http\UploadedFile
Illuminate\Mail\Mailer
Illuminate\Routing\Redirector
Illuminate\Routing\ResponseFactory
Illuminate\Routing\Route
Illuminate\Routing\Router
Illuminate\Routing\UrlGenerator
Illuminate\Support\Arr
Illuminate\Support\Carbon
Illuminate\Support\Collection
Illuminate\Support\Optional
Illuminate\Support\Str
Illuminate\Translation\Translator
Illuminate\Validation\Rule
Illuminate\View\View