Edited at
LaravelDay 1

Undocumented Laravel (1) Routing 編

More than 1 year has passed since last update.


この記事について

Laravel Advent Calendar 2017 の 12/1 のエントリーです。

Laravel 公式サイトに載ってない機能について紹介します。

今回は、ルーティング編です。

公式ドキュメントに載ってないということは、後方互換を考慮せずに変更されることもありうるので、使用にあたっては熟慮されることをオススメします。

役に立つか立たないか、ご自身の目でお確かめください。


環境


  • PHP 7.1.4

  • Laravel 5.5.14


目次


  • ルーティングファイルを分離する

  • FormRequest にてルートパラメータを取得する

  • フォールバックルート

  • RouteMatched イベント


ルーティングファイルを分離する

わりと知られたところでは、routes 以下のファイル(web.php, api.php) を分割する方法です。

app/Providers/RouteServiceProvider.php にメソッドを追加して、すでにある mapWebRoutes と同様に読み込むファイルを指定すればOKです。

以下の例では、app/Http/Controllers/Admin というディレクトリをつくって、サイト管理者のみアクセスできるルートを別ファイルに分離する場合の記述例です。


RouteServiceProvider.php

class RouteServiceProvider extends ServiceProvider

{
public function map()
{
$this->mapApiRoutes();

$this->mapWebRoutes();
// ↓↓↓追加↓↓↓
$this->mapAdminRoutes();
}

protected function mapAdminRoutes()
{
Route::middleware('web')
->namespace($this->namespace . '\Admin')
->prefix('admin')
->as('admin.')
->group(base_path('routes/admin.php'));
}
}



おまけ:Route::as メソッド

5.4 からルートグループの設定が、上記のようにメソッドチェーンでできるようになりました。

5.4 未満

Route::group(['middleware' => 'web', 'namespace' => 'Admin', 'prefix' => 'admin', 'as' => 'admin.'], function () {});

5.4 以上

Route::middleware('web')

->namespace('Admin')
->prefix('admin')
->as('admin.')
->group(function () {});

これらのメソッドについては以下に記載があるんですが、 as だけ載っていません。

https://laravel.com/docs/5.5/routing#route-groups

当然使えるだろうと思ってソースコードを読んでみても、メソッド定義はされておらず(Route ファサードの実体は Router クラスです)、毎度おなじみ __call メソッドによって RouteRegistrar クラスへ委譲されていました。


FormRequest にてルートパラメータを取得する

POST 系の処理で使用する、FormRequest を継承したクラスにおいて、ルートパラメータを取得する方法をご紹介します。

ルートパラメータの取得は、色んな方法がありますが、Controller においては、引数に DI して使うのがいちばんいいと思います。

たとえば、Route::get('/users/{id}', ...) というルートでは、

class UserController extends Controller

{
public function show(int $id)
{
}
}

のようにすればユーザーIDが取得できますし、モデルバインディングを使って、Route::get('/users/{user}', ...) とすれば、

class UserController extends Controller

{
public function show(User $user)
{
}
}

のように User クラスのインスタンスが取得できます。

では、FormRequest クラスでは、どうやってルートパラメータを取得すればいいでしょう?

Route::put('/users/{id}', ...) というユーザーモデルを更新するルートに対して、UserUpdateRequest というクラスでバリデーションするケースを考えます。

何らかの理由で、Request クラスにメソッドをつくる必要があって、その中でルートパラメータにアクセスするには、以下の二通りが考えられます。


UserUpdateRequest.php

class UserUpdateRequest extends FormRequest

{
public function users()
{
$userIds = [];
$userIds[] = $this->route('id');
$userIds[] = Route::input('id');

return $userIds; // ["1", "1"]
}
}


ちなみに、モデルバインディングを使っていれば、モデルのインスタンスをこれで取ることができます。


フォールバックルート

どのルートにもマッチしない URL にアクセスされた場合、通常は NotFoundHttpException がスローされ、404 ページが表示されます。

Route::fallback() メソッドを使えば、どのルートにもマッチしない URL でアクセスされた場合に呼ばれるコールバック関数を作成し、何らかの処理を挟むことができます。


web.php

Route::fallback(function ($route) {

// ここで何らかの処理をする
throw new NotFoundHttpException($route . ' not found');
});

もしくは、


web.php

Route::fallback(function ($route) {

if (Str::startsWith($route, 'some/url')) {
return view('special_view');
}
throw new NotFoundHttpException($route . ' not found');
});

みたいに、特定の URL のときだけ、特別なビューを返す、とかできそうです。

まぁ、あまりいい使い途が思い浮かびませんね(笑)


RouteMatched イベント

いずれかのルートにマッチした場合に呼ばれるコールバック関数を設定することができます。


RouteServiceProvider.php

class RouteServiceProvider extends ServiceProvider

{
public function boot()
{
parent::boot();

Route::matched(function (RouteMatched $event) {
Log::debug('route matched', ['event' => $event]);
});
}
}


RouteMatched イベントは、公開プロパティとして、Route および Request のインスタンスを保持しており、それらの情報をデータベースなどに保存したりするために使えそうです。


まとめ

Undocumented Laravel と題して、公式ドキュメントに載っていない機能をいくつかご紹介しました。

Laravel には他にもさまざまな隠し(?)機能がありますので、いずれ他のカテゴリーについても、記事にしていきたいと思います。

他にも公式ドキュメントに載っていないルーティング周りの便利な機能があれば、ぜひ、コメント欄にてご紹介ください :bow: