FuelPHPサイド
FuelBeans 8 : ルーティング、more about
ルーティングに関してはドキュメントがNekogetさんの所で、既に翻訳されています。
http://press.nekoget.com/fuelphp_doc/general/routing.html
これを読めば、バッチリです。私も、基本的なことは、このドキュメントの内容程度しか知りません。
ルーティングに関しては、いやらしい部分もあります。
FuelPHPでは自主的に404ページを表示したい場合、routes.phpに指定した_404_のクラス/アクションを簡単に実行できるよう、例外を投げるだけで、行えるようになっています。
具体的には、throw new HttpNotFoundException;とするだけで、404のクラス/アクションが実行されます。でも、これは、内部で新しく_404_で定義したクラス/アクションのルートをあたかもURLで指定されたかのように実行するため、そのクラス/アクションがroutes.phpなどで定義したルーティングを通るということになります。(バグだとフォーラムで話したら、これは正常だ、ドキュメントから抜けている、issueで出せと言われました。ですから、新しいドキュメントのエラー処理のページには、これは正常だとわかりづらく書かれています。注釈になっていますが、あの文章を読んで、何のことやら分かる人はいないでしょう。)
言葉で説明するのは難しいのですが、例を見てもらえばわかりやすいでしょう。routes.phpの内容がこうなっていたとします。
<?php
return array(
'_404_' => 'error/404',
'buy/:syouhin/:kosu' => 'product/buy',
'show/:syouhin' => 'product/show',
'(:any)' => 'product/show/$1',
);
buyやshowのパターンでは、productクラスのアクションにいきます。(:any)はaaaにもaaa/bbbにもaaa/bbb/cccにも、どんなパターンにでも一致します。つまり、いくつでも商品名やコードを/で区切って指定してもらえば、その情報を表示してあげようという設計です。たぶん、productクラス中ではURLに指定された商品名や商品コードをチェックし、存在していなければ、404を表示したいでしょう。その場合、throw new HttpNotFoundException;が実行されます。
すると新たに、URLに_404_で指定されている、error/404がつけて呼び出されたものとして、フレームワークは取り扱います。そのため、ルーティングも行われます。直接error/404が実行されるわけでありません。
ルーティングでerror/404は(:any)のパターンに一致してしまいます。そのため、404は実行されません。(ループになるようですが、FuelPHPを作っている人が、内部でカウントしているから、ループにならないと言っていました。まだ実験していません。)
もちろんこの:anyがなければ、暗黙のルーティングルールに従い、errorクラスの404アクションが実行され、思惑通り実行されます。
throw new HttpNotFoundException;を実行した場合、ルーティングを通さず、直接404を実行したいときは、public/index.phpを修正してください。実際に一度見ておきましょう。エディターで開いてください。
38行目が通常の処理です。tryで囲まれています。この中で例外が投げられ、内部でcatchされないと、ここでcatchされるという仕組みです。404のルートが設定されているか調べ、設定されているようであれば、そのルートを指定し、最初と同じ処理をしているのがわかると思います。
404の再ルーティングを行わないためには、45行目を以下のようにコードを変更します。
$response = Request::forge($route,false)->execute()->response();
forgeするリクエストにfalseを指定してあげると、ルーティングを通らないとのこと。実際、私もこの方法でルーティングのトラブルが回避できました。
さて、ルーティング面倒ですよね。複雑なことしようとすると、どのパターンに引っかかるかわかりづらくなります。
そこで、ルーティングのセッテイングとシミュレーションを行う、コントローラークラスを書きました。ワンクラスにまとめてあります。
名前は各自が付けるものとバッティングしないよう、長めにroutingsetterとつけてあります。お好きに変更してください。実行はlocalhost/public/routingsetterもしくは、ご自身で変更した名前を指定してください。
このプログラムは、routes.phpを書き換えます。必要ならばバックアップは取っておきましょう。
ページの一番上部はルーティングをシミュレートする場合にURLを指定してください。このチュートリアルであれば、localhost/public/以降の部分を指定します。ここで指定したURLに一致するパターンの部分が緑色で表示されます。
行番号は増分を10にしています。これは、ルーティングは指定する順番が大切で、必要に応じて順番を入れ替えられるようにしてあります。
一番下の5行は、ルーティングを追加するときに指定してください。
routes.phpの最初のほうに、このクラスを実行するためのルーティングを指定しておくことをお勧めします。:anyや名前付きパラーメータ一つだけのパターンを指定してある場合は、このクラスが暗黙のルーティングで実行できなくなります。
運用段階では、当然、このような設定ツールに制御が渡らないよう、ルーティングで設定するか、認証するか、コードを削除するかしましょう。ユーザーに触らせない様に、しておきましょう。
<?php
class Controller_Routingsetter extends Controller
{
const NEW_ROUTE = 5;
public function action_index()
{
if (Input::method() == 'POST')
{
// 既存のルーティングの取得
$total_line = Input::post('total_line');
$route_array = array();
for ($i = 0; $i < $total_line; $i++)
{
if (!(Input::post('line'.$i, false) === false) and Input::post('pattern'.$i) and Input::post('class_action'.$i) and !Input::post('delete_radio'.$i))
{
$route_array[Input::post('line'.$i)] = array('pattern' => Input::post('pattern'.$i), 'class_action' => Input::post('class_action'.$i));
}
}
// 追加ルーティングの取得
for ($i = 0; $i < self::NEW_ROUTE; $i++)
{
if (Input::post('new_line'.$i) and Input::post('new_pattern'.$i) and Input::post('new_class_action'.$i))
{
$route_array[Input::post('new_line'.$i)] = array('pattern' => Input::post('new_pattern'.$i), 'class_action' => Input::post('new_class_action'.$i));
}
}
ksort($route_array);
// コンフィグのroutesを一度削除し、生成し直す
Config::delete('routes');
foreach ($route_array as $item)
{
Config::set('routes.'.$item['pattern'], $item['class_action']);
}
// コンフィグファイルに保存する
Config::save('routes', 'routes');
} // End of POST handing
// グループ名指定で、ルーティングの全設定取得
$routes = Config::get('routes', array());
$rt = array();
foreach ($routes as $key => $item)
{
// 正規表現に変換
$replace = str_replace(array(
':any',
':alnum',
':num',
':alpha',
':segment',
),
array(
'.+',
'[[:alnum:]]+',
'[[:digit:]]+',
'[[:alpha:]]+',
'[^/]*',
), '/'.$key.'/');
$replace = preg_replace('%:([[:alpha:]]+)/%', '(?P<$1>.+?)/', $replace);
$rt[] = array(
'exp' => trim($replace, '/'),
'pattern' => trim($key, '/'),
'class_action' => trim($item, '/')
);
}
// ルーティングのシミュレーション
$matched = false;
$p404 = false;
$test_uri = trim(Input::post('test_uri'), '/');
foreach ($rt as $key => $item)
{
if ($test_uri == '' and $item['exp'] == '_root_')
{
$matched = $key;
break;
}
elseif (preg_match('{^'.$item['exp'].'$}', $test_uri, $matches))
{
$matched = $key;
break;
}
elseif ($item['pattern'] == '_404_')
{
$p404 = $key;
}
}
if (!$matched)
$matched = $p404;
// 出力
// ビューやテンプレート使わない時の、文字化け対策にヘッダー出力
echo Html::doctype('html5');
echo '<head><meta charset="UTF-8"></head><body>';
echo Form::open(Uri::current());
echo 'ルーティングテストURI:'.Form::input('test_uri', Input::post('test_uri') ? : '',
array('size' => 60)).'<hr />';
$last_key = -1;
foreach ($rt as $key => $item)
{
$last_key = $key;
echo Form::input('line'.$key, $key * 10, array('size' => 3)).' : ';
echo Form::input('pattern'.$key, $item['pattern'], array('size' => 50)).' ';
echo Form::input('class_action'.$key, $item['class_action'],
array('size' => 50)).' ';
echo Form::label('削除', 'delete_radio'.$key);
echo Form::radio('delete_radio'.$key, 'de.ete_radio'.$key).'<br />';
if ($key == $matched)
{
echo '<div style="color:green">';
}
else
{
echo '<div>';
}
// 正規表現をそのまま表示すると正しく表示されないため、エスケープ
echo '比較される正規表現:'.Security::htmlentities($item['exp']).'<br />';
if ($key === $p404 and $key == $matched)
{
echo '404で_404_と一致';
}
elseif ($key == $matched)
{
if ($key == '_root_')
{
echo 'デフォルトとして_root_と一致';
}
else
{
ksort($matches, SORT_LOCALE_STRING);
foreach ($matches as $k => $m)
{
if (is_numeric($k))
{
echo '$'.$k.' => '.'"'.$m.'"<br />';
}
else
{
echo '$this->param("'.$k.'") => "'.$m.'"<br />';
}
}
}
}
echo '</div><hr />';
}
// 出力行数をhiddenで保存しておく
echo Form::hidden('total_line', $last_key + 1);
// ルーティング追加行表示
echo '<p>ルーティング追加</p>';
for ($i = 0; $i < self::NEW_ROUTE; $i++)
{
echo Form::input('new_line'.$i, ($last_key + $i + 1) * 10, array('size' => 3)).' : ';
echo Form::input('new_pattern'.$i, '', array('size' => 50)).' ';
echo Form::input('new_class_action'.$i, '', array('size' => 50)).'<br />';
}
echo '<hr />';
echo Form::submit('submit', '更新');
echo Form::close();
echo '</body>';
}
}
| FuelBeans 6 : ルーティングの基礎< 前 | 次 >FuelBeans 10 : ビューを利用する |
|---|
| < 前 | 次 > |
|---|