(jp) =
<!–
–>

最近、ルート属性について考えています。 そうすることで、注釈とも呼ばれる属性との関係が少しおかしいことに気づきました。 何年にもわたって、私はそれらを愛することから憎むこと、再び愛すること、そしてその中間のどこかに行きました. PHP の内部と外部の両方で、それらが乱用されているのを見てきました。また、支持者と反対者の両方が、それらに賛成または反対する説得力のある議論をしているのを聞いたことがあります。
最近、私はそれらの特定のユースケースについてかなり多くのことを考えてきました。 私はそれについて多くの人々と話し、可能な限り合理的に質問にアプローチしようとしました: 属性はルーティングに使用されますか? 良いアイデアですか、それとも悪いアイデアですか?
何ヶ月にもわたる考えと議論の後、私は (可能な限り客観的に考えると) 結論に達しました。 この投稿では、私の思考プロセスを共有し、過去数年間にルート属性に対して聞いたすべての反論に対処します。
始めましょう。
同じページにいることを確認するために、最も基本的な形式のルート属性は次のようになります。
class PostAdminController
public function index()
public function show(Post $post)
public function store(Post $post)
がある 多くの このように簡単な例で問題を説明しているので、1 つずつ見ていきましょう。
# 複製
まず、重複の問題があります。 この例では問題のようには見えないかもしれませんが、ほとんどのプロジェクトには間違いなく「少数のルート」以上のものがあります。 私が取り組んでいる 2 つの大きなプロジェクトでそれらを数えました: それぞれ 470 と 815 ルートです。
Symfony と Laravel の両方に、この種のスケーリングの問題に対処するための「ルート グループ」と呼ばれる概念があります。 ルート属性を使用する場合も、同じ考え方を適用できます。
このような属性ルート グループをモデル化するための非常に多くの異なるアプローチを考え出すことができると確信しています。 堅牢で定性的なソリューションと思われる 2 つを紹介しますが、これは決して決定的なリストではありません。
コントローラーレベルで、プレフィックスやミドルウェアなどの「共有ルート構成」を管理できます。
class PostController
public function index()
public function show(Post $post)
または、さらに一歩進んでください。 ジェネリックを持つ Route 次のように使用できる属性:
class PostController
public function index()
public function show(Post $post)
ただし、拡張可能にすることもできます。
class AdminRoute extends Route
public function __construct(
string $prefix,
array $middleware,
)
parent::__construct(
prefix: "/admin/$prefix",
middleware: [
AdminMiddleware::class,
...$middleware
],
)
次のように使用します。
class PostController
public function index()
public function show(Post $post)
この最後のアプローチは間違いなく私のお気に入りですが、その意見に異議を唱えることは自由です。 ここでの主なポイントは次のとおりです。 過度の重複は、ルート属性の問題である必要はありません.
# 見つけやすさ
ルート属性に対する 2 番目に大きな議論は、何百ものコントローラー ファイルにルートを分散させるよりも、ルートを簡単に検索できるように、ルートを 1 つのファイルに保持することを好むという人々からのものです。
実際の例を見てみましょう。 ここに連絡先コントローラがあります edit 方法:
class ContactsController
public function edit(Contact $contact)
;
「ルートを管理するための中心的な場所」を主張する人々、つまり: に対して ルート属性; は、ルート ファイルを一元化することで、探しているものを簡単に見つけることができると言っています。 それでは、routes ファイルをクリックしてみましょう (私の場合、Laravel IDEA プラグインを使用すると、 edit メソッドを開き、ルート定義に直接移動します)、そこにあるものを見てください:
Route::get('contact', [ContactsController::class, 'edit']);
では、このページにアクセスするための URI は何でしょうか? それは…ですか /contactId? もちろん、このルートはルート グループの一部です。
Route::prefix('people')->group(function (): void
Route::get('contact', [ContactsController::class, 'edit']);
);
っていうことは /people/contactId? いいえ、このグループは別のグループの一部であるため:
Route::prefix('crm')
->group(function (): void
Route::prefix('people')->group(function (): void
Route::get('contact', [ContactsController::class, 'edit']);
);
これは別のグループの一部です:
Route::middleware('can:admin,' . Tenant::class)
->group(function (): void {
Route::prefix('crm')
->group(function (): void
Route::prefix('people')->group(function (): void
Route::get('contact', [ContactsController::class, 'edit']);
);
これは、Laravel のルート サービス プロバイダーで定義された別のグループの一部です。
Route::middleware(['web', 'auth', ])
->prefix('admin/currentTenant')
->group(base_path('routes/admin_tenant.php'));
したがって、実際には、このコントローラーへの完全な URI は次のとおりです。 /admin/tenantId/crm/people/edit/contactId. ここで、ルート ファイルには、ここで共有したスニペットだけでなく、実際には 700 ~ 1500 行のコードが含まれていることを思い出してください。
次のような専用ルート属性を使用すると主張します CrmRoute 拡張する AdminRoute だろう 多くの グループ構成を手動で調べることなく、コントローラーから始めて、毎回 1 レベルずつクリックするだけでよいので、作業が簡単です。
さらに、 追加 このような大きなルート ファイル内の適切な場所へのルートは、同じ問題を引き起こします。適切なグループに分類されるようにするには、正確にどの行でルートを定義する必要がありますか? 同じプロセスを逆に繰り返すつもりはありません。私が指摘している問題がわかるはずです。
最後に、これらの問題を部分的に防ぐためにルートファイルを別々のものに分割することに言及する人もいます。 そして、私は彼らに同意します。それはまさに、ルート属性がコントローラーベースのレベルでできることです。
要するに、 専用ルートファイルは発見可能性を改善せず、ルート属性は間違いなく状況を悪化させません.
# 一貫性
2つの主な引数で に対して ルート属性が反論されたので、分離されたルート ファイルと比較して追加の利点があるかどうかを考えてみましょう。
ネタバレ:そうです。
ほとんどの場合、私自身の経験と他の人の証言に基づいて、コントローラーのメソッドと URI はほとんど いつも 1 対 1 で一緒にマップします。 では、なぜそれらを一緒に保管してはならないのでしょうか。
新しいコントローラー メソッドを作成するとき、コントローラー メソッドを作成するときに最後に悩まされたくないことは、「わかりました。正しいミドルウェア グループが設定されているどのルート ファイルに移動する必要があるか」について考えなければならないことです。 、そしてそのファイルのどこにこの特定のメソッドを登録する必要がありますか」. 別々のルート ファイルが原因で、非常に多くの不必要な認知的過負荷が導入されています。これらのファイルは、密接に関連する 2 つの概念を引き離すためです。
より重要なことに集中できるように、それらをまとめておきましょう。
さらに、その価値のあるフレームワークは、コントローラーメソッドに基づいて URI を生成するために必要なツールを提供します。
action([PostController::class, 'show'], $post);
プロジェクトの URI スキームへの「エントリ ポイント」としてコントローラー メソッドを既に使用している場合は、関連するメタデータも適切に保持してみませんか?
あ、はい、 ルート属性は、ルート ファイルと比較して付加価値があります。プログラミング中の認知的負荷を軽減します。.
# ルート衝突
私が同意するルート属性に対する唯一の議論の 1 つは、衝突を処理する方法です。 おそらく、2 つのルート定義が互いに衝突する次のような状況に対処したことがあるでしょう。
Route::get('/contacts/id', …);
Route::get('/contacts/list', …);
ここに古典的な衝突があります: /contacts/list、ルーターはそれを一致として検出できます /contacts/idとなり、そのルートに対して間違ったアクションを実行します。
このような問題はめったに発生しませんが、奇妙な機会に自分で対処しなければなりませんでした。 単一のルート ファイルを使用する場合の解決策は、単純に順序を入れ替えることです。
Route::get('/contacts/list', …);
Route::get('/contacts/id', …);
これにより、 /contacts/list は最初のヒットであり、ルートの衝突を防ぎます。 ただし、属性はコントローラー メソッドに直接結合され、グループ化されていないため、属性を使用する場合、ルートの順序を制御することはできません。 それで何?
まず第一に、ルート ファイルまたは属性を使用して、ルートの衝突を回避する方法がいくつかあります。 ルートの順序に依存する必要がない:
- 潜在的な競合がないように、URI スキームを変更できます。
/contacts/show/idまた/contacts/id/show; また - 正規表現検証を使用して、数値 ID のみに一致させることができます。
/contacts/id:\d+.
ただし、衝突が避けられないエッジ ケースがいくつか存在する可能性があります。 それらをどのように処理しますか? 最も明白な解決策は、ルート属性にある種の「順序」キーを許可することです。これにより、順序を自分で慎重に制御できます。
public function index()
public function show(Contact $contact)
このアプローチが理想的ではないことに同意しますが、ルートの衝突を解決することは決して理想的ではないと思います。 その上に、 この種の衝突はめったに起こらないので、私はそれを非常に小さな問題としか考えていません。 できる 必要に応じて解決する.
# パフォーマンス
最後に短いものですが、言及する必要があります。一部の人々は、パフォーマンスの問題のために属性を恐れています。
まず第一に、PHP のリフレクションは非常に高速であり、主要なフレームワークはすべてリフレクションを広範囲に使用していますが、これらの部分がパフォーマンスのボトルネックになっていることに気付いていないに違いありません。
次に、属性の検出とルートの登録は、本番環境で非常に簡単にキャッシュできるものです。Laravel は、2 つの例を挙げると、イベント リスナーとブレード コンポーネントで既にこれを行っています。
実際、「ルート キャッシュ」の概念は Symfony と Laravel の両方に既に存在し、Symfony はすでにルート属性をサポートしています。
いいえ、 ルート属性を使用する場合、パフォーマンスは問題になりません.
それで、何が残っていますか? ここで取り上げなかった唯一の議論は、「人々は単に属性を好まない」ということです。
それに反対することはほとんどありません。 一般的に「人は変化を嫌う」という意味だと思います。 私自身、この態度に罪を犯してきました。 あなたがそのような状況にある場合、私ができる唯一のアドバイスは、それを試してみることです. 快適なゾーンから出てください。それは解放的なことです。
さて、あなたは私が間違っていると言いたいのか、それともこの問題についてあなた自身の考えを共有したいのかもしれません. 私の意見に異議を唱えたいので、お気軽にあなたの考えを共有してください ツイッター またはメールで!
//platform.twitter.com/widgets.js