ルーティングとURL生成
Webサービスのフレームワーク初心者の方に「フレームワークの何が便利か」を説明してみたいと思います。最初はルーティングとURLの生成についてです。
ルーティングは何が便利?
説明によるとURIもURLも同じものを指すそうですが、フレームワーク界隈では一般的にベースのURLへファイル構造に当たる部分をつけたものをURL、ファイル構造に当たる部分のみをURIと呼びます。ベースのURIは通常はドメイン(例:http://example.com)のことですが、ドキュメントルートのサブディレクトリーにシステムを準備する場合はそのサブディレクトリー名を含みます。(例:http://example.com/sub-directory)まず、この2つの用語を覚えておきましょう。
何も工夫がないファイル構造
PHPを忘れ、HTMLファイルのみでサイトを構成するとしましょう。ドキュメントルートディレクトリーへ次のようにファイルを設置します。
index.html about.html post/index.html post/1.html post/2.html
Webサーバーの設定にもよりますが、たいていの場合index.htmlがサイトURLのみ、もしくはディレクトリーを指定し、ファイル名を省略した場合のアクセスファイルとして設定されます。たとえばexample.comのドメインを使っているのであれば、http://example.comへアクセスすれば、index.htmlの内容が表示されます。
http://example.com/postへアクセスすれば、post/index.htmlの内容が表示され、http://example.com/post/1.htmlであれば、post/1.htmlファイルの内容です。基本的な内容の復習です。
ここで、htmlファイルではなくphpを利用する場合も同様になります。
index.php about.php post/index.php post/1.php post/2.php common.php
htmlがphpに変わるだけで、アクセス方法は同じです。設置場所がURIになり、そのままドメインの後に付きます。
利点と欠点
この方法はとにかくシンプルなのが利点です。直感的に理解できます。
では、欠点はどんなものでしょう。
先ず思いつくのが、シンプルにリンクで関係つけている場合、ファイル名を変更したりディレクトリー構造を変えたりすると、リンクのファイル位置を毎回変更する必要が起きることです。
また、commmon.phpが他のPHPファイルから共通にrequireされるファイルで、http://example.com/common.phpとして呼び出されてはまずい場合、何らかの回避策を取る必要が起きます。CMSなどではPHPコードの最初でチェックし、直接呼び出されている場合はdieするか、適切なページヘリダイレクトしているものが多いようです。
開発するサイトの規模が大きくなると、ドキュメントルートにはPHPファイルがどんどん増えます。カオス化していきます。リンク箇所も読み込むファイルも増えていくでしょう。修正し間違えによるバグも増えていくでしょう。
リンクの指定方法にも注目してみましょう。たとえば、post/index.phpがつぎのHTMLリンクを含んでいたとします。
<a href="../index.php">ホーム</a>
この場合、post/index.phpとindex.phpの相対位置が変更になれば、手を入れなくてはなりません。頭のよいあなたは、きっと次のように変えるでしょう。
<a href="/index.php">ホーム</a>
完璧です。このシステムがサブディレクトリーに設置されなければですけど。もし、このシステム全体がドキュメントルートのblogサブディレクトリーへ設置される場合、/index.phpはドキュメントルートディレクトリー直下のindex.phpへリンクされます。意図としてはblog/index.phpへリンクしたいのにです。
<a href="http://example.com/blog/index.php">ホーム</a>
これで間違ありません!ただ、融通は効かなくなりました。example.comのドメインのドキュメントルートにあるblogサブディレクトリー以外では動きません。ふぅ…。
ルーティング
そこでルーティングです。URL(URI)ルーティングとも呼ばれますが、今回は短くルーティングと呼びます。
Webサービスを作成するフレームワークではほとんど用意されている機能です。
概念は簡単です。呼び出されるURI(ベースのURL以降のこと)と起動するPHPコードを対応付けることです。Laravelの場合を見てください。
Route::get('/', function() { return "トップページです。" }); Route::get('/post', 'PostController@displayPostList');
各フレームワークにより、指定方法は様々です。Laravelの場合は、上記のように指定します。
最初の定義は、PHPのクロージャーを利用し、起動するコードを直接記述しています。他の場所にあるPHPコードを呼び出す場合には、2つ目の記法が使えます。URIがpostの場合、PostControllerクラスのdisplayPostListメソッドが呼びだされます。
定義しているメソッド名がリクエストメソッド(HTTP動詞)を表します。getはGETメソッドです。フォーム送信に一般的に使用されるPOSTメソッドであればpostです。
このシステムがexample.comのドキュメントルートに設置されようが、別のドメインであろうが、フレームワークが面倒を見ますので、URIのみ指定します。大抵のフレームワークでは、サブディレクトリーに設置した場合も動作します。(大抵です。全部ではありません。フレームワークによりサブディレクトリーに設置する考慮をしていないものもあります。)
フロントコントローラー
こうした仕組みはPHPだけでは実現できません。ApacheやNginxなどのWebサーバーの機能を利用しています。
Webサーバーの機能を利用し、どんなURLに対するアクセスであろうと、同じPHPファイルへのアクセスとして処理してもらいます。通常はドキュメントルート直下のindex.phpファイルです。この「どんなアクセスでも、ひとまず受け止める」ファイルをフロントコントローラーと呼びます。(コンピューター業界のご多分にもれず、いくらかの方言は存在しますが、たいていフロントコントローラーが使用されます。さらに何でもかんでもプログラムパターンにしたがる人もいますので、フロントコントローラーパターンと呼ぶ人もいます。まあ、「パターン」ブームの前から、フロントコントローラーとは呼ばれていましたけれど。)
Laravelの場合、ドキュメントルートはpublicディレクトリーです。フレームワークにより異なり、webなどの名前が使われるものもあります。
publicディレクトリーの中に存在するPHPファイルは、フロントコントローラーのindex.phpのみです。他のファイルはWebサーバーから直接アクセスできない別のディレクトリーへ設置しています。そのため各PHPファイルで、直接のアクセスを防ぐために余計なコードを先頭に付けるなどの手間は必要ありません。
フロントコントローラーがどのような動作を行うかは、フレームワークによります。
いずれにせよ、Webサーバーの設定により、フロントコントローラーへ動作がわたらない場合などではフレームワークは正しく動作しません。
たとえばApacheで起きがちなのは:
- URLを書き換えるために使用する、apacheの拡張であるmod-rewriteがインストールされていない、有効にされていない。
- mod-rewriteの定義を含んでいる.htaccessファイルが動作しないように設定されている。
- サブディレクトリーへの設置時に.htaccessの内容が適合していない。
などが、「あー、あるある」状況です。
わざわざ、この説明をしたのは、サーバー設定の設定ができていないのに、それをフレームワークのせいにする初心者が多いからです。一応たいてのフレームワークでは.htaccessファイルを用意したり、サーバー設定をドキュメントで説明していますが、実際サーバーの設定は様々で、そうした状況全部に対応できないからです。サーバー設定はフレームワーク開発者の仕事ではなく、サイトを立ち上げようとしている側の責務です。
ベースURLとURI
いったん正しくフロントコントローラーが起動できれば、PHPはアクセスされたURLやらドメインやらをプログラム中から参照できますので、そうした取得可能なデーターから、ベースのURLとURIを分けられます。
前述の通り、ドメインのドキュメントルートへ直接フレームワークをインストールする場合、ベースURLはドメインへのURLになるでしょう。サブディレクトリにインストールした場合、前述のblogディレクトリーなら、ドメイン+そのディレクトリー名を含めた名前がベースURLになります。
もちろん、フレームワークがよしなにベースURLを求めてくれます。一度ベースURLが決まれば、ベースURLとクエリー文字列(存在している場合)までの間がURIとなります。
WebサーバーがPHPを起動する方法にもいろいろありますが、その際の設定がうまく行っておらず、PHPへ情報が正しく伝わっていないと、ベースURLとURIの切り分けがうまく働きません。するとルーティングも正しく動かず、頭を悩ますことになります。フレームワークに限らず、CMSでも同様で、はまりがちなポイントです。
URL生成
さて、ルーティングがうまく動作すれば、URIにより動作させるコードを柔軟に指定できます。
しかし、PHPが生成するHTMLの中のリンクが固定のままでは、「利点と欠点」のセクションで説明した問題が解決されません。
そこでフレームワークは最低限、ベースURLを提供してくれますので、これを利用しましょう。たとえばベースURLを返してくれる関数がbase_urlと言う名前であれば、次のように利用します。
<a href="<?php echo base_url; ?>">ホーム</a> <a href="<?php echo base_url.'/post'; ?>">ポスト</a>
example.comのドキュメントルートへフレームワークがインストールされているのであれば、最初のリンクのURLはhttp://example.comになり、2つ目はhttp://example.com/postになります。
もしもドキュメントルートではなく、blogサブディレクトリーにフレームワークを設置している場合、最初のリンクのURLはhttp://example.com/blogになり、2つ目はhttp://example.com/blog/postになります。
名前付きルート
大抵のメジャーなフレームワークであれば、ルートに名前を付けることができ、その名前を利用しURLを生成できるように用意されています。この仕組みを使えば、ルートの処理対象のURLを変更しても、URLを生成する部分を全く変更する必要がなくなります。
前に利用したLaravelのサンプルルートに名前を付けてみましょう。
Route::get('/', [ 'as' => 'site-root', function() { return "トップページです。" }, ]); Route::get('/post', [ 'as' => 'post-list', 'uses' => 'PostController@displayPostList', ]);
細かい点はさておき、Laravelでは'as'キーを使用し、ルートへ名前を指定します。この名前を利用してURLを生成してみましょう。
<a href="<?php echo route('site-root'); ?>">ホーム</a> <a href="<?php echo route('post-list'); ?>">ポスト</a>
routeヘルパー関数により、URLが生成されます。もちろん、ルート定義の内容が変更になれば、それに合わせて生成されるURLも変わります。たとえば、ルートの定義で/postを代わりに/articleへ変更すれば、2つ目のリンクのURLはhttp://example.com/article(フレームワークをドキュメントルートへインストールした場合)を生成してくれます。
ルート定義を変えるだけで、あとはフレームワークがよしなに仕事をしてくれます。