ルーティング

デフォルトのルーティングルール

webmanのデフォルトのルーティングルールは http://127.0.0.1:8787/{controller}/{action}です。

デフォルトのコントローラは app\controller\IndexController で、デフォルトのアクションは index です。

例えば:

  • http://127.0.0.1:8787app\controller\IndexController クラスの index メソッドにデフォルトでアクセスします。
  • http://127.0.0.1:8787/fooapp\controller\FooController クラスの index メソッドにデフォルトでアクセスします。
  • http://127.0.0.1:8787/foo/testapp\controller\FooController クラスの test メソッドにデフォルトでアクセスします。
  • http://127.0.0.1:8787/admin/foo/testapp\admin\controller\FooController クラスの test メソッドにデフォルトでアクセスします(複数アプリを参照)。

また、webmanは1.4からより複雑なデフォルトのルーティングをサポートしています。例えば

app
├── admin
│   └── v1
│       └── v2
│           └── v3
│               └── controller
│                   └── IndexController.php
└── controller
    ├── v1
    │   └── IndexController.php
    └── v2
        └── v3
            └── IndexController.php

特定のリクエストのルーティングを変更したい場合は、config/route.php の設定ファイルを変更してください。

デフォルトのルーティングを無効にしたい場合は、config/route.php ファイルの最後の行に以下の設定を追加してください:

Route::disableDefaultRoute();

クロージャルーティング

config/route.php に以下のようなルーティングコードを追加してください。

use support\Request;
Route::any('/test', function (Request $request) {
    return response('test');
});

注意
クロージャ関数はどのコントローラにも属さないため、$request->app $request->controller $request->action はすべて空の文字列です。

http://127.0.0.1:8787/test にアクセスすると、test の文字列が返されます。

注意
ルーティングパスは / で始まる必要があります。例:

use support\Request;
// 間違った使用法
Route::any('test', function (Request $request) {
    return response('test');
});

// 正しい使用法
Route::any('/test', function (Request $request) {
    return response('test');
});

クラスルーティング

config/route.php に以下のようなルーティングコードを追加してください。

Route::any('/testclass', [app\controller\IndexController::class, 'test']);

http://127.0.0.1:8787/testclass にアクセスすると、app\controller\IndexController クラスの test メソッドの返り値が返されます。

アノテーションルーティング

コントローラメソッドにアノテーションでルートを定義し、config/route.php での設定は不要です。

注意
この機能には webman-framework >= v2.2.0 が必要です

基本用法

namespace app\controller;
use support\annotation\route\DisableDefaultRoute;
use support\annotation\route\Get;
use support\annotation\route\Post;
use support\annotation\route\Route;

#[DisableDefaultRoute]
class UserController
{
    #[Get('/user/{id}')]
    public function show($id)
    {
        return "user $id";
    }

    #[Post('/user')]
    public function store()
    {
        return 'created';
    }

    #[Route('/user/form', ['GET', 'POST'], 'user.form')]
    public function form()
    {
        return 'form';
    }
}

使用可能なアノテーション:#[Get] #[Post] #[Put] #[Delete] #[Patch] #[Head] #[Options] #[Any](任意のメソッド)。パスは / で始める必要があります。第2パラメータでルート名を指定でき、route() でURL生成に使用します。

#[DisableDefaultRoute] はそのコントローラのデフォルトルートを無効にします(任意)。アノテーションで定義したルートのみアクセス可能になります。

#[Route] 完全構文#[Route(path, methods, name)]

  • path:ルートパス、/ で始める;null の場合はデフォルトルートのHTTPメソッドのみ制限し、新ルートは登録しない
  • methods:HTTPメソッド、文字列または文字列配列、例:'GET' または ['GET','POST']
  • nameroute('name') でURL生成に使用するルート名、省略可

パスなしアノテーション:デフォルトルートのHTTPメソッド制限

パスを指定しない場合、そのアクションで許可するHTTPメソッドのみを制限し、デフォルトルートパスはそのまま使用されます:

#[Post]
public function create() { ... }  // POSTのみ、パスは引き続き /user/create

#[Get]
public function index() { ... }   // GETのみ

複数のアノテーションを組み合わせて、複数のメソッドを許可できます:

#[Get]
#[Post]
public function form() { ... }  // GET と POST を許可

宣言されていないHTTPメソッドは405を返します。

複数のパスアノテーションは別々のルートとして登録されます:#[Get('/a')] #[Post('/b')] は GET /a と POST /b の両方を作成します。

ルートグループプレフィックス

クラスに #[RouteGroup] を使用して、全てのメソッドルートにプレフィックスを追加します:

use support\annotation\route\RouteGroup;
use support\annotation\route\Get;

#[RouteGroup('/api/v1')]
class UserController
{
    #[Get('/user/{id}')]  // 実際のパス: /api/v1/user/{id}
    public function show($id) { ... }
}

カスタムHTTPメソッドとルート名

#[Route] は複数HTTPメソッドとルート名を指定できます:

use support\annotation\route\Route;

#[Route('/user', ['GET', 'POST'], 'user.form')]
public function form() { ... }

// パスとメソッドのみ、ルート名なし
#[Route('/user/update', 'PUT')]
public function update() { ... }

パスパラメータと正規表現

アノテーションルートのパスは config/route.php と同じパラメータ構文をサポートし、正規表現とオプショナルパラメータを含みます:

#[Get('/user/{id:\d+}')]           // 数字のidのみ
public function show($id) { ... }

#[Get('/user[/{name}]')]           // nameはオプション、/user または /user/xxx
public function list($name = null) { ... }

#[Any('/user/[{path:.+}]')]        // /user および /user/ に任意のサフィックスでマッチ
public function catchAll($path = null) { ... }

詳細は本文書の「ルーティングパラメータ」の章を参照してください。

ミドルウェア

コントローラやメソッドの #[Middleware] はアノテーションルートに適用され、support\annotation\Middleware と同じ用法です:

use support\annotation\Middleware;

#[Middleware(\app\middleware\AuthMiddleware::class)]
class UserController { ... }

#[Get('/user/{id}')]
#[Middleware(\app\middleware\RateLimitMiddleware::class)]
public function show($id) { ... }

route() での URL 生成

アノテーションでルート名を指定した場合、route('name', $params) でURLを生成できます:

#[Get('/user/{id}', 'user.show')]
public function show($id) { ... }

// 使用例:route('user.show', ['id' => 123])  =>  /user/123

ルーティングパラメータ

ルーティングにパラメータが含まれている場合、 {key} を使用してマッチングし、マッチング結果は対応するコントローラーメソッドパラメータに渡されます(2番目のパラメータから順に渡されます)。例:

// /user/123 および /user/abc にマッチ
Route::any('/user/{id}', [app\controller\UserController::class, 'get']);
namespace app\controller;
use support\Request;

class UserController
{
    public function get(Request $request, $id)
    {
        return response('パラメータを受け取りました'.$id);
    }
}

さらに例:

use support\Request;
// /user/123 にマッチ、/user/abc にはマッチしない
Route::any('/user/{id:\d+}', function (Request $request, $id) {
    return response($id);
});

// /user/foobar にマッチ、/user/foo/bar にはマッチしない
Route::any('/user/{name}', function (Request $request, $name) {
   return response($name);
});

// /user、/user/123、/user/abc にマッチ   [] はオプションを表す
Route::any('/user[/{name}]', function (Request $request, $name = null) {
   return response($name ?? 'tom');
});

// /user/ をプレフィックスとするすべてのリクエストにマッチ
Route::any('/user/[{path:.+}]', function (Request $request) {
    return $request->path();
});

// すべての options リクエストにマッチ   : の後に正規表現を指定し、名前付きパラメータのパターンを表す
Route::options('[{path:.+}]', function () {
    return response('');
});

応用まとめ

Webman ルーティングの [] 構文は、主にオプションのパス部分や動的ルートのマッチングを処理し、より複雑なパス構造とマッチングルールの定義を可能にします

: は正規表現の指定に使用する

ルーティンググループ

時にはルーティングには多くの共通の接頭辞が含まれていることがあります。このような場合は、ルーティンググループを使用して定義を簡素化できます。例:

Route::group('/blog', function () {
   Route::any('/create', function (Request $request) {return response('create');});
   Route::any('/edit', function (Request $request) {return response('edit');});
   Route::any('/view/{id}', function (Request $request, $id) {return response("view $id");});
});

次と同等です:

Route::any('/blog/create', function (Request $request) {return response('create');});
Route::any('/blog/edit', function (Request $request) {return response('edit');});
Route::any('/blog/view/{id}', function (Request $request, $id) {return response("view $id");});

グループのネスト使用

Route::group('/blog', function () {
   Route::group('/v1', function () {
      Route::any('/create', function (Request $request) {return response('create');});
      Route::any('/edit', function (Request $request) {return response('edit');});
      Route::any('/view/{id}', function (Request $request, $id) {return response("view $id");});
   });  
});

ルーティングミドルウェア

特定の1つまたはグループのルーティングにミドルウェアを設定することができます。
例えば:

Route::any('/admin', [app\admin\controller\IndexController::class, 'index'])->middleware([
    app\middleware\MiddlewareA::class,
    app\middleware\MiddlewareB::class,
]);

Route::group('/blog', function () {
   Route::any('/create', function () {return response('create');});
   Route::any('/edit', function () {return response('edit');});
   Route::any('/view/{id}', function ($request, $id) {return response("view $id");});
})->middleware([
    app\middleware\MiddlewareA::class,
    app\middleware\MiddlewareB::class,
]);
# 間違った使用例 (webman-framework >= 1.5.7 からこの方法が有効)
Route::group('/blog', function () {
   Route::group('/v1', function () {
      Route::any('/create', function (Request $request) {return response('create');});
      Route::any('/edit', function (Request $request) {return response('edit');});
      Route::any('/view/{id}', function (Request $request, $id) {return response("view $id");});
   });  
})->middleware([
    app\middleware\MiddlewareA::class,
    app\middleware\MiddlewareB::class,
]);
# 正しい使用例
Route::group('/blog', function () {
   Route::group('/v1', function () {
      Route::any('/create', function ($request) {return response('create');});
      Route::any('/edit', function ($request) {return response('edit');});
      Route::any('/view/{id}', function ($request, $id) {return response("view $id");});
   })->middleware([
        app\middleware\MiddlewareA::class,
        app\middleware\MiddlewareB::class,
    ]);  
});

リソースルート

Route::resource('/test', app\controller\IndexController::class);

// リソースルートの指定
Route::resource('/test', app\controller\IndexController::class, ['index','create']);

// 未定義のリソースルート
// たとえば、notifyにアクセスすると、any型のルート /test/notifyまたは/test/notify/{id} のいずれかを使用できます。routeNameはtest.notifyです。
Route::resource('/test', app\controller\IndexController::class, ['index','create','notify']);
動詞 URI アクション ルート名
GET /test index test.index
GET /test/create create test.create
POST /test store test.store
GET /test/{id} show test.show
GET /test/{id}/edit edit test.edit
PUT /test/{id} update test.update
DELETE /test/{id} destroy test.destroy
PUT /test/{id}/recovery recovery test.recovery

URL生成

注意
現時点では、グループにネストされたルートのURL生成はサポートされていません。

たとえば、次のようなルートがあるとします。

Route::any('/blog/{id}', [app\controller\BlogController::class, 'view'])->name('blog.view');

このルートのURLを生成するには、次のようにします。

route('blog.view', ['id' => 100]); // 結果は /blog/100 となります

ビューでルートのURLを使用する場合、この方法を使用すると、ルート規則が変わってもURLが自動的に生成されるため、多くのビューファイルを変更する必要がありません。

ルート情報の取得

$request->routeオブジェクトを使用すると、現在のリクエストのルート情報を取得できます。たとえば、

$route = $request->route; // $route = request()->route;と同等です。
if ($route) {
    var_export($route->getPath());
    var_export($route->getMethods());
    var_export($route->getName());
    var_export($route->getMiddleware());
    var_export($route->getCallback());
    var_export($route->param());
}

注意
現在のリクエストがconfig/route.phpに設定されたどのルートにも一致しない場合、$request->routeはnullになります。つまり、デフォルトのルートをたどる場合には、$request->routeはnullになります。

404エラーの処理

ルートが見つからない場合、デフォルトでは404のステータスコードが返され、404のコンテンツが出力されます。

開発者がルートが見つからない場合のビジネスプロセスに介入したい場合は、webmanが提供するフォールバックルートRoute::fallback($callback)メソッドを使用できます。たとえば、以下のコードのロジックは、ルートが見つからない場合にホームページにリダイレクトします。

Route::fallback(function(){
    return redirect('/');
});

また、ルートが存在しない場合にJSONデータを返すこともできます。これは、webmanがAPIエンドポイントとして使用される場合に非常に便利です。

Route::fallback(function(){
    return json(['code' => 404, 'msg' => '404 not found']);
});

404にミドルウェアを追加

デフォルトでは404リクエストはミドルウェアを通過しません。404リクエストにミドルウェアを追加する必要がある場合は、以下のコードを参照してください。

Route::fallback(function(){
    return json(['code' => 404, 'msg' => '404 not found']);
})->middleware([
    app\middleware\MiddlewareA::class,
    app\middleware\MiddlewareB::class,
]);

関連リンク カスタム404 500ページ

デフォルトルートの無効化

// メインプロジェクトのデフォルトルートを無効化、アプリケーションプラグインには影響しない
Route::disableDefaultRoute();
// メインプロジェクトのadminアプリのルートを無効化、アプリケーションプラグインには影響しない
Route::disableDefaultRoute('', 'admin');
// fooプラグインのデフォルトルートを無効化、メインプロジェクトには影響しない
Route::disableDefaultRoute('foo');
// fooプラグインのadminアプリのデフォルトルートを無効化、メインプロジェクトには影響しない
Route::disableDefaultRoute('foo', 'admin');
// コントローラ [\app\controller\IndexController::class, 'index'] のデフォルトルートを無効化
Route::disableDefaultRoute([\app\controller\IndexController::class, 'index']);

アノテーションでデフォルトルートを無効化

アノテーションでコントローラのデフォルトルートを無効化できます。例:

namespace app\controller;
use support\annotation\DisableDefaultRoute;

#[DisableDefaultRoute]
class IndexController
{
    public function index()
    {
        return 'index';
    }
}

同様に、アノテーションでコントローラの特定メソッドのデフォルトルートを無効化できます。例:

namespace app\controller;
use support\annotation\DisableDefaultRoute;

class IndexController
{
    #[DisableDefaultRoute]
    public function index()
    {
        return 'index';
    }
}

ルートインターフェイス

// $uriの任意のメソッドリクエストのルートを設定します。
Route::any($uri, $callback);
// $uriのgetリクエストのルートを設定します。
Route::get($uri, $callback);
// $uriのリクエストのルートを設定します。
Route::post($uri, $callback);
// $uriのputリクエストのルートを設定します。
Route::put($uri, $callback);
// $uriのpatchリクエストのルートを設定します。
Route::patch($uri, $callback);
// $uriのdeleteリクエストのルートを設定します。
Route::delete($uri, $callback);
// $uriのheadリクエストのルートを設定します。
Route::head($uri, $callback);
// $uriのoptionsリクエストのルートを設定します。
Route::options($uri, $callback);
// 複数のリクエスト種別のルートを同時に設定します。
Route::add(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'], $uri, $callback);
// グループルート
Route::group($path, $callback);
// リソースルート
Route::resource($path, $callback, [$options]);
// デフォルトルートを無効にする
Route::disableDefaultRoute($plugin = '');
// フォールバックルート、デフォルトのルートを設定
Route::fallback($callback, $plugin = '');
// すべてのルート情報を取得
Route::getRoutes();

もしuriに対応するルート(デフォルトのルートを含む)がなく、フォールバックルートも設定されていない場合、404が返されます。

複数のルート構成ファイル

ルートを複数の構成ファイルで管理したい場合は、たとえば複数のアプリの場合は、各アプリで独自のルート構成ファイルを持つようにしたいかもしれません。この場合は、外部のルート構成ファイルをrequireすることで読み込むことができます。
たとえば、config/route.phpでは、次のようになります。

<?php

// adminアプリのルート構成を読み込む
require_once app_path('admin/config/route.php');
// apiアプリのルート構成を読み込む
require_once app_path('api/config/route.php');