PHP アプリケーション URL ルーティング
-
02-07-2019 - |
質問
そこで、私は取り組んでいるいくつかのアプリのベースとなるフレームワークを作成しています (フレームワークがあるので、作業するための環境と、たとえば 1 つのアプリを使用できるシステムがあります)入社する)
このフレームワークとそれに含まれるアプリをリソース指向アーキテクチャを使用して作りたいと考えています。
さて、APP 作成者 (おそらく CMS アプリ ユーザーも可能ですが、それはかなり先のことです) が拡張可能な URL ルーティング クラスを作成したいと考えており、次のようなことを調べて、それを行うための最良の方法を見つけようとしています。他のアプリがどうやってやるか。
解決
私は独自の形式を作成するよりも reg ex を使用することを好みます。これは一般的な知識であるためです。私は、これらの正規ルーティング テーブルをネストできるようにする小さなクラスを作成しました。以前は継承によって実装された同様のものを使用していましたが、継承が必要なかったので書き直しました。
キーに対して正規表現を実行し、独自の制御文字列にマップします。以下の例を見てみましょう。訪問します /api/related/joe
私のルータークラスは新しいオブジェクトを作成します ApiController
そしてそのメソッドを呼び出します relatedDocuments(array('tags' => 'joe'));
// the 12 strips the subdirectory my app is running in
$index = urldecode(substr($_SERVER["REQUEST_URI"], 12));
Route::process($index, array(
"#^api/related/(.*)$#Di" => "ApiController/relatedDocuments/tags",
"#^thread/(.*)/post$#Di" => "ThreadController/post/title",
"#^thread/(.*)/reply$#Di" => "ThreadController/reply/title",
"#^thread/(.*)$#Di" => "ThreadController/thread/title",
"#^ajax/tag/(.*)/(.*)$#Di" => "TagController/add/id/tags",
"#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id",
"#^ajax/reply/(.*)$#Di" => "ArticleController/newReply/id",
"#^ajax/toggle/(.*)$#Di" => "ApiController/toggle/toggle",
"#^$#Di" => "HomeController",
));
エラーを抑えて簡素化するために、テーブルを細分化できます。このようにして、ルーティング テーブルを制御するクラスに配置できます。上の例では、3 つのスレッド呼び出しを 1 つのスレッド呼び出しに結合できます。
Route::process($index, array(
"#^api/related/(.*)$#Di" => "ApiController/relatedDocuments/tags",
"#^thread/(.*)$#Di" => "ThreadController/route/uri",
"#^ajax/tag/(.*)/(.*)$#Di" => "TagController/add/id/tags",
"#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id",
"#^ajax/reply/(.*)$#Di" => "ArticleController/newReply/id",
"#^ajax/toggle/(.*)$#Di" => "ApiController/toggle/toggle",
"#^$#Di" => "HomeController",
));
次に、ThreadController::route を次のように定義します。
function route($args) {
Route::process($args['uri'], array(
"#^(.*)/post$#Di" => "ThreadController/post/title",
"#^(.*)/reply$#Di" => "ThreadController/reply/title",
"#^(.*)$#Di" => "ThreadController/thread/title",
));
}
また、右側でルーティング文字列に必要なデフォルトを定義することもできます。文書化することを忘れないでください。そうしないと、人々が混乱することになります。右側に関数名を含めない場合は、現在indexを呼び出しています。 ここ 私の現在のコードです。エラーを好みの方法で処理したり、デフォルトのアクションを処理したりできるように変更することもできます。
他のヒント
さらに別のフレームワーク? - ともかく...
ルーティングのコツは、すべてをルーティング コントローラーに渡すことです。
おそらく、私がここで文書化したものと同様のものを使用したいと思うでしょう。
http://www.hm2k.com/posts/friend-urls
2 番目のソリューションでは、Zend Framework と同様の URL を使用できます。
正規表現のリストを使用して、使用すべきオブジェクトと一致させる
例えば
^/users/[\w-]+/bookmarks/(.+)/$
^/users/[\w-]+/bookmarks/$
^/users/[\w-]+/$
長所:素晴らしくてシンプルで、ルートを直接定義してみましょう。注文する必要があるため、新しいものを追加するのが簡単ではありません(非常にエラーが発生しやすい)
知る限り、これが Django のやり方です
多くのフレームワークは Apache の mod_rewrite とフロント コントローラーを組み合わせて使用していると思います。mod_rewrite を使用すると、次のように URL を変換できます。/people/get/3 をこれに入力します:Index.php?controller=people&method=get&id=3。Index.php は、指定されたパラメーターに基づいてページリクエストをルーティングするフロントコントローラーを実装します。
ご想像のとおり、方法はたくさんあります。
たとえば、 スリムなフレームワーク ルーティング エンジンの例は次のとおりです (パターンに基づく) ${OBJECT}->${REQUEST METHOD}(${PATTERM}, ${CALLBACK})
):
$app->get("/Home", function() {
print('Welcome to the home page');
}
$app->get('/Profile/:memberName', function($memberName) {
print( 'I\'m viewing ' . $memberName . '\'s profile.' );
}
$app->post('/ContactUs', function() {
print( 'This action will be fired only if a POST request will occure');
}
したがって、初期化されたインスタンス ($app
)リクエストメソッドごとにメソッドを取得します(例:get、post、put、delete など)、最初のパラメータとしてルートを取得し、2 番目のパラメータとしてコールバックを取得します。
ルートはトークンを取得できます。トークンは、何らかのデータ (メンバー名、記事 ID、組織の場所名など、すべてのルーティング コントローラーと同様) に基づいて実行時に変更される「変数」です。
個人的にはこの方法が気に入っていますが、高度なフレームワークには十分な柔軟性がないと思います。
私は現在 ZF と Yii を使用しているため、勤務している会社のフレームワークの一部として作成したルーターの例があります。
ルート エンジンは (@gradbot のものと同様の) 正規表現に基づいていますが、双方向の会話が行われるため、クライアントが (Apache で) mod_rewrite を実行できない場合、またはサーバーに書き換えルールを追加できない場合、クライアントはクエリ文字列を含む従来の URL を引き続き使用できます。
ファイルには配列が含まれており、その各項目は次の例のようになります。
$_FURLTEMPLATES['login'] = array(
'i' => array( // Input - how the router parse an incomming path into query string params
'pattern' => '@Members/Login/?@i',
'matches' => array( 'Application' => 'Members', 'Module' => 'Login' ),
),
'o' => array( // Output - how the router parse a query string into a route
'@Application=Members(&|&)Module=Login/?@' => 'Members/Login/'
)
);
次のような、より複雑な組み合わせを使用することもできます。
$_FURLTEMPLATES['article'] = array(
'i' => array(
'pattern' => '@CMS/Articles/([\d]+)/?@i',
'matches' => array( 'Application' => "CMS",
'Module' => 'Articles',
'Sector' => 'showArticle',
'ArticleID' => '$1' ),
),
'o' => array(
'@Application=CMS(&|&)Module=Articles(&|&)Sector=showArticle(&|&)ArticleID=([\d]+)@' => 'CMS/Articles/$4'
)
);
私が思うに、肝心なのは、可能性は無限であり、フレームワークをどれだけ複雑にしたいか、そしてそれを使って何をしたいかによって決まります。
たとえば、Web サービスまたは単純な Web サイト ラッパーを目的としている場合は、Slim フレームワークの記述スタイルを使用するだけで、非常に簡単で見栄えの良いコードになります。
ただし、正規表現を使用して複雑なサイトを開発したい場合は、正規表現が解決策だと思います。
幸運を!:)
Puxをチェックしてみてください https://github.com/c9s/Pux
あらすじはこちら
<?php
require 'vendor/autoload.php'; // use PCRE patterns you need Pux\PatternCompiler class.
use Pux\Executor;
class ProductController {
public function listAction() {
return 'product list';
}
public function itemAction($id) {
return "product $id";
}
}
$mux = new Pux\Mux;
$mux->any('/product', ['ProductController','listAction']);
$mux->get('/product/:id', ['ProductController','itemAction'] , [
'require' => [ 'id' => '\d+', ],
'default' => [ 'id' => '1', ]
]);
$mux->post('/product/:id', ['ProductController','updateAction'] , [
'require' => [ 'id' => '\d+', ],
'default' => [ 'id' => '1', ]
]);
$mux->delete('/product/:id', ['ProductController','deleteAction'] , [
'require' => [ 'id' => '\d+', ],
'default' => [ 'id' => '1', ]
]);
$route = $mux->dispatch('/product/1');
Executor::execute($route);
Zend の MVC フレームワークはデフォルトで次のような構造を使用します。
/router/controller/action/key1/value1/key2/value2
どこ router
ルーター ファイル ( mod_rewrite
, controller
から派生したクラスによって定義されたコントローラー アクション ハンドラーからのものです。 Zend_Controller_Action
そして action
コントローラ内のメソッドを参照します。 actionAction
. 。キーと値のペアは任意の順序で指定でき、アクション メソッドで連想配列として使用できます。
私は過去に自分のコードで同様のものを使用したことがありますが、これまでのところかなりうまく機能しています。
見てみてください MVC パターン。
たとえば、Zend Framework だけでなく、CakePHP、CodeIgniter などでも使用されます。
私は個人的に MVC モデルが好きではありませんが、ほとんどの場合、「Web 用ビュー」コンポーネントとして実装されます。
決定はかなり好みに依存します...