PHP
Laravel
laravel5.6

【Laravel】Route Model Binding のカスタムロジックをクラスに切り出す


前提

Laravel ではルーティングパラメータから Model を bind させることができる。


RouteServiceProvider

public function boot()

{
parent::boot();

// ルーティングで {user} を指定すると下記コールバックの $userName に渡され
// その戻り値を Controller の引数に渡すことができる
Route::bind('user', function ($userName) {
return resolve(MyApp\Domain\Repositories\UserRepository::class)->findByName($userName) ?? abort(404);
});
}


詳しくは 公式ドキュメント(Routing # Explicit Binding) の "Customizing The Resolution Logic" を参照。

しかし、 bind のためのロジックは ServiceProvier の関心事ではなく、そのコードがここに出てくるのはあまりよろしくない。


Route::bind() の中身を見てみると

実は Route::bind() の第2引数には callback だけでなく文字列を渡すことができる。

過程は省略するがRoute::bind()の第2引数に文字列を渡した場合、以下のメソッドの引数 $binding となる。

https://github.com/laravel/framework/blob/5.6/src/Illuminate/Routing/RouteBinding.php#L34


Illuminate\Routing\RouteBinding

    protected static function createClassBinding($container, $binding)

{
return function ($value, $route) use ($container, $binding) {
// If the binding has an @ sign, we will assume it's being used to delimit
// the class name from the bind method name. This allows for bindings
// to run multiple bind methods in a single class for convenience.
list($class, $method) = Str::parseCallback($binding, 'bind');

$callable = [$container->make($class), $method];

return call_user_func($callable, $value, $route);
};
}


bind() というメソッドを持ったクラス名、あるいはソース中のコメントにある通りクラス名@メソッド名を指定すれば、bind のためのロジックを指定することができる。


bind のためのロジックを別のクラスに切り出す

上記より、次のようなクラスを作成すれば、


MyApp\Application\Routing\Binders\User

namespace MyApp\Application\Routing\Binders;

use MyApp\Domain\Repositories\UserRepository;

class User
{
protected $repository;

public function __construct(UserRepository $repository)
{
$this->repository = $repository;
}

public function bind($userName)
{
return $this->repository->findByName($userName) ?? abort(404);
}
}


RouteServiceProvider 側では次のようにして使うことができる。


RouteServiceProvider

public function boot()

{
parent::boot();

Route::bind('user', MyApp\Application\Routing\Binders\User::class);
}


これで、ServiceProvider の関心事の外である「bind のためのロジック」をカプセル化することができる。