Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convention-based router #398

Open
koriym opened this issue Mar 27, 2022 · 5 comments
Open

Convention-based router #398

koriym opened this issue Mar 27, 2022 · 5 comments
Labels

Comments

@koriym
Copy link
Member

koriym commented Mar 27, 2022

規約ベースのルーター

現在、BEAR.Sundayにルーターが2つあります。

  • 規約ベースのWebRouter
  • 正規表現ベースのAura.Router

WebRouterは高速でルーティング設定ファイルも不要ですが、パスの中にパラメーターを埋め込むことはできません。Aura.Routerは万能ですがルーティングファイルのメンテナンスと、実行コストが必要でDRY原則にも違反しています。(ほとんどのフレームワークが採用している方式です)

新しい規約ベースのルーターが両者のメリットを融合しないかと考えました。

BEAR.AutoRouter

pmjones氏のAutoRouteを用いたRouterInterfaceを実装します。

AutoRoute概要

AutoRouteはメンテナンスが少なくて済みます。認識された名前空間と認識されたアクションメソッド名で、クラスをソースコードに追加するだけで、自動的にルートとして利用できるようになります。アクションクラスと同期させるために、ルーティングファイルを管理する必要はもうありません。

AutoRouteは高速です。実際、FastRouteがキャッシュされたルート定義を使用している場合でも、一般的なケースではFastRouteの約2倍の速度があります。

注意事項

代替品を比較する場合、AutoRouteはAltoRouter、FastRoute、Kleinなどと同じカテゴリーであり、Aura、Laminas、Laravel、Symfonyなどとは別物であるとお考え下さい。

AutoRouteモチベーション

正規表現(regex)ルーターは一般に、代わりにリフレクションで見つけることができる重要な情報を重複しています。ルートが対象とするアクションメソッドのパラメータを変更する場合、ルートの正規表現自体も変更する必要があります。そのため、正規表現ルータの使用は DRY 原則に反していると考えられます。ルートが数本しかないシステムでは、重複した情報としてルートファイルを管理することはそれほど面倒なことではありません。しかし、ルートが100以上あるシステムでは、ルートと対象のアクションクラスやメソッドを同期させておくことは負担になることがあります。

同様に、アノテーションベースのルーターはルーティングの指示をコメント内に配置するため、明示的なメソッドのシグネチャにすでに存在する動的パラメータと重複することがよくあります。

正規表現とアノテーションに基づくルーターの代替として、このルーター実装では、HTTPアクションクラス階層をHTTPメソッドの動詞とURLパスに自動的にマッピングし、URLの動的部分を決定するためにタイプヒントされたアクションメソッドパラメーターを反映することによってルート定義の必要性を排除しています。アクションクラス名は明確に定義された規約に準拠し,アクションメソッドパラメーターはURLの動的な部分を示すと仮定しています.これにより、柔軟かつ比較的メンテナンスフリーな実装が可能となります。

機能概略

GET /photo/1/edit

規約により(ルーターファイルなしで)、上記リクエストで下記ResourceObjectのメソッドがコールされます。

namespace MyVendor\MyProject\Resource\Page\Photo;

class Edit
{
    public function onGet(int $photoId)

上記Editクラスが存在しない場合には下記クラスが呼ばれます

namespace MyVendor\MyProject\Resource\Page;

class Photo
{
    public function onGet(int $photoId)

または

namespace MyVendor\MyProject\Resource\Page;

class Photo
{
    public function onGet(int $photoId, string $action)
    {
        // $action = 'edit'

Editクラスが有効になるにはPhotoクラスの存在が必要です。なければ探索されません。

実装ヒント

以下のAutoRouteで以下の値が得られます。

$autoRoute = new AutoRoute(
    'MyVendor\MyPackage\Resource',
    'path/to/Resource/'
);
$router = $autoRoute->getRouter();
$route = $router->route('GET', '/foo-item/1/edit');
var_dump($route);
object(AutoRoute\Route)#17 (7) {
  ["class":protected]=>
  string(42) "MyVendor\MyPackage\FooItem\Edit\GetFooItemEdit"
  ["method":protected]=>
  string(8) "__invoke"
  ["arguments":protected]=>
  array(1) {
    [0]=>
    int(1)
  }

BEAR.Resourceのリクエストに

このclassmethodをBEAR.Resourceにルートできるように更新するRouterInterfaceを実装します。

prop from (AutoRoute) to (BEAR)
class MyVendor\MyPackage\FooItem\Edit\GetFooItemEdit MyVendor\MyPackage\FooItem\Edit
method __invoke onGet
@koriym koriym added the feature label Mar 27, 2022
@koriym
Copy link
Member Author

koriym commented Mar 27, 2022

問題:

AutoRoute内部でclass_exsitsしてGetFooItemEditが存在しているかを確認しているから規約の違うBEAR.Sundayではこの確認部分を変更する必要がある。

@koriym
Copy link
Member Author

koriym commented Mar 27, 2022

上記問題を解決するテストスクリプト
koriym/AutoRoute@1cfb3be

これで解決できているようですが、このスクリプトではGET /photo/1/editのルートを行う場合

対象のMyVendor\MyProject\Photo\Editへのルーティングを有効にするために、MyVendor\MyProject\Photoクラスが存在が必要なようです。

@koriym
Copy link
Member Author

koriym commented Mar 27, 2022

MyVendor\MyProject\Photoクラスが存在するときのログ

image

MyVendor\MyProject\Photoクラスが存在せずルーティング失敗のときのログ

image

GET /photo/1/editのルート探索で/photo/1が解決できないとそれ以上の探索を打ち切り、失敗と見做してるようです。

@koriym
Copy link
Member Author

koriym commented Mar 27, 2022

🎉 評価用のearly-birdバージョンをリリースしました。
https://github.com/bearsunday/BEAR.AutoRouter

@koriym
Copy link
Member Author

koriym commented Mar 28, 2022

🎉 評価用のデモサイトを用意しました
https://github.com/koriym/MyVendor.AutoRoute

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant