URI ルーティング

通常、URL 文字列と対応するコントローラクラス・メソッドは一対一の関係にあります。URI のセグメントは通常つぎのパターンに従います:

example.com/class/function/id/

しかしいくつかの場合ではこの関係を変更したいことでしょう。そのため、URLに対応するものとは異なるクラス・メソッドを呼び出せるようにもなっています。

例として、つぎの形式を持つ URL があるとします:

example.com/product/1/
example.com/product/2/
example.com/product/3/
example.com/product/4/

通常、URLの第2セグメントはメソッド名として予約されていますが、上のこの例では代わりに製品IDとなっています。これを実現するため、CodeIgniter は URL ハンドラを変更することができます。

独自のルーティングルールの設定

ルーティングルールは application/config/Routes.php ファイルに定義されています。これの中で、RouteCollection クラスのインスタンスが作られ、独自のルーティング基準を設定を可能としていることがわかるでしょう。 ルーティングはプレースホルダまたは正規表現で設定できます。

ルーティングは単に左側に URL、右側に対応するコントローラとメソッドを取ります。合わせて、あらゆるパラメータはコントローラに渡される必要があります。コントローラとメソッドは静的メソッドを使うのと同じ方法で列挙する必要があります。Users::list のように、完全な名前空間のクラス名とメソッド名をダブルコロンを使います。もしそのメソッドがパラメータを要求するなら、スラッシュ区切りでメソッド名の後に続けます:

// Calls the $Users->list()
Users::list
// Calls $Users->list(1, 23)
Users::list/1/23

プレースホルダ

典型的なルーティングはつぎのような形を取るでしょう:

$routes->add('product/:num', 'App\Catalog::productLookup');

ルーティングでは、第1引数に一致する URL を、第2引数に再ルーティング先を取ります。上の例では、URL の第1セグメントにリテラル文字列 "product" が、第2セグメントに数値が見つかれば、"AppCatalog" クラスの "productLookup" メソッドが代わりに使用されます。

プレースホルダは正規表現パターンを単純に文字列で替えたものです。ルーティングプロセス中、プレースホルダは正規表現の値に置換されます。これは主に読みやすさのためのものです。

以下のプレースホルダがルーティングで使用できます:

  • (:any) は URL のその位置から最後までのすべての文字に一致します。これは複数の URI セグメントを持ちえます。
  • (:segment) はスラッシュ (/) を除くすべての文字に一致し、結果としてひとつのセグメントに限定します。
  • (:num) はすべての数字に一致します。
  • (:alpha) はすべての英字に一致します。
  • (:alphanum) はすべての英数字に一致します。
  • (:hash):segment と同じですが、ハッシュ ID であることを見分けやすくするために使います(モデル ドキュメントをご覧ください)。

注釈

{locale} はプレースホルダおよびルーティングのその他の部品としては使えません。これは ローカライゼーション のために予約されています。

すこしだけ基本的なルーティングの例をここに示します:

$routes->add('journals', 'App\Blogs');

文字列 "journals" を第1セグメントに持つ URL は "AppBlogs" クラスに再マッピングされます。メソッドはデフォルトのままで、たいていは index() です:

$routes->add('blog/joe', 'Blogs::users/34');

セグメント "blog/joe" を持つ URL は“Blogs”クラスの“users”メソッドに再マッピングされます。 ID には“34”が設定されます:

$routes->add('product/(:any)', 'Catalog::productLookup');

URL の第1セグメントが“product”で第2セグメントが何かしら指定されていれば、“Catalog”クラスの“productLookup”メソッドに再マッピングされます:

$routes->add('product/(:num)', 'Catalog::productLookupByID/$1';

URL の第1セグメントが“product”で第2セグメントが数字なら、“Catalog”クラスの“productLookupByID”メソッドに再マッピングされ、一致した変数がメソッドに渡されます。

重要

add() メソッドはお手軽ですけども、このあと解説する HTTP 動詞をベースとしたルーティングを利用することが常に推奨されます。そのほうがよりセキュアです。また、わずかながらパフォーマンスが向上します。リクエストメソッドに一致するルーティングのみが使用され、結果として、マッチするかを調べるルーティングの数が少なくなります。

カスタムプレースホルダ

独自のプレースホルダを作成することができます。ルーティングファイル内で利用可能で、読みやすさと使用感を完全にカスタマイズできます。

新しいプレースホルダは addPlaceholder メソッドで追加します。第1引数はプレースホルダとして使う文字列です。第2引数は置換先の正規表現パターンです。 これはルーティングを追加する前に呼び出さなければなりません:

$routes->addPlaceholder('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}');
$routes->add('users/(:uuid)', 'Users::show/$1');

正規表現

もしお好みなら、正規表現をルーティングルールに使用することができます。あらゆる正しい正規表現を利用でき、後方参照することができます。

上の例では、products/shirts/123 のような URL は“Shirts”コントローラクラスの“id_123”メソッドを呼び出します。

正規表現を使うとセグメントは、たいてい区切り文字として使われるスラッシュ (‘/’) も取り込んでしまうことができます。

例えば、webアプリケーションでユーザがパスワードで保護されたエリアにアクセスしたとして、ログイン操作後に元のページに戻すリダイレクトを実現したいなら、つぎの例が役に立つでしょう:

$routes->add('login/(.+)', 'Auth::login/$1');

正規表現をよく知らず、これから学びたいのであれば、 regular-expressions.info が良い手始めになるでしょう。

重要

Note: 正規表現にはワイルドカードを混ぜることもできます。

クロージャ

無名関数、つまりクロージャをルーティングのマッピング先に使用することができます。この関数はユーザがその URL にアクセスすると実行されます。これは単純なビューを見せるだけのような小さいタスクの実行に便利です:

$routes->add('feed', function()
        {
                $rss = new RSSFeeder();
                return $rss->feed('general');
        {
);

ルーティングをまとめてマッピング

add() メソッドは簡単ですが、一度に複数ルーティングを登録するなら map() メソッドを使うとより便利なことが多いです。ひとつひとつ add() メソッドを呼び出す代わりに、ルーティングの配列を定義し、 map() メソッドの第1引数に渡します:

$routes = [];
$routes['product/(:num)']      = 'Catalog::productLookupById';
$routes['product/(:alphanum)'] = 'Catalog::productLookupByName';

$collection->map($routes);

リダイレクトするルーティング

長生きなサイトはどれでも、ページを移動させてしまっているものです。addRedirect() メソッドを使えば、他のルーティングにリダイレクトする必要のあるルーティングを設定することができます。第1引数は古いルーティングの URL パターンです。第2引数はリダイレクト先の新しい URI、または名前付きルーティングの名前です。第3引数は HTTP ステータスコードで、リダイレクトに合わせて送信する必要のあるものです。デフォルト値は 302 で、一時的なリダイレクトを示します。多くの場合に推奨されます:

$routes->add('users/profile', 'Users::profile', ['as' => 'profile']);

// 名前付きルーティングへのリダイレクト
$routes->addRedirect('users/about', 'profile');
// URI へのリダイレクト
$routes->addRedirect('users/about', 'users/profile');

ページの読込中にリダイレクトルーティングにマッチしたなら、ユーザはすぐにリダイレクトされます。コントローラが読み込まれる前です。

ルーティングのグルーピング

group() メソッドを使うと、ルーティングを共通の名前でグルーピングすることができます。グループ名は、グループ内で定義しているルーティングのセグメントの前に現れます。これは、開始文字列がまったく共通のルーティングでそれが多いとき、タイピングの手間を減らしてくれます。例えば管理画面を作るときなどです:

$routes->group('admin', function($routes)
{
        $routes->add('users', 'Admin\Users::index');
        $routes->add('blog',  'Admin\Blog::index');
});

これは 'users' と 'blog' の URI に "admin" のプレフィックスがつきます。操作する URL は /admin/users/admin/blog のようなものです。 必要であれば、より細かい整理のためにグループをネストすることができます:

$routes->group('admin', function($routes)
{
        $routes->group('users', function($routes)
        {
                $routes->add('list', 'Admin\Users::list');
        });

});

これは URL admin/users/list を取ります。

環境による制限

特定の環境下でしか使えなくなるルーティングのセットを作ることができます。これによって、開発者のローカルマシンでしか使えず、テスト用または本番環境では使えないツールを作ることができるようになります。 これには environment() メソッドを使います。第1引数は環境名です。クロージャ内で定義されたあらゆるルーティングはその環境でしかアクセスできません:

$routes->environment('development', function($routes)
{
        $routes->add('builder', 'Tools\Builder::index');
});

ルーティングの逆引き

ルーティングの逆引きは、コントローラとメソッドの定義とパラメータからリンクを作ることができます。現在のルーティングから引いてきます。これはアプリケーションの修正なしにルーティング定義を変更することを可能にします。これは典型的にはビューの中でリンクを作るのに使用されます。

例えば、フォトギャラリーにリンクしたいとすると、 route_to() ヘルパ関数で貼るべきリンク先を取得できます。第1引数は要件を満たしたコントローラ・メソッド名です。ダブルコロン (::) 区切りで、ちょうどルーティング初期化で書いたようにします。ルーティングに渡すほかの引数は次のように渡します:

// ルーティングはこのように定義されています:
$routes->add('users/(:id)/gallery(:any)', 'App\Controllers\Galleries::showUserGallery/$1/$2');

// ユーザ ID が 15、ギャラリー 12 の相対 URL を生成します
// 生成結果: /users/15/gallery/12
<a href="<?= route_to('App\Controllers\Galleries::showUserGallery', 15, 12) ?>">ギャラリーを見る</a>

名前付きルーティングの利用

ルーティングには名前をつけることができ、アプリケーションの崩れやすさを抑えられます。ルーティングに名前をつけることで後からその名前で呼び出せるようにします。それにより例えルーティング定義が変わったとしても、アプリケーションの route_to で作ったすべてのリンクは修正なしに動作し続けられます。ルーティングは as オプションで名前をつけられます:

// ルーティングは次のように定義されています:
$routes->add('users/(:id)/gallery(:any)', 'Galleries::showUserGallery/$1/$2', ['as' => 'user_gallery');

    // ユーザ ID が 15、ギャラリー 12 の相対 URL を生成します
    // 生成結果: /users/15/gallery/12
    <a href="<?= route_to('user_gallery', 15, 12) ?>">ギャラリーを見る</a>

また、これで読みやすくもなります。

HTTP 動詞のルーティングでの利用

HTTP 動詞(リクエストメソッド)をルーティングルール定義に使用することができます。これは特に RESTful アプリケーションを作る際に有用です。標準の HTTP 動詞(GET、POST、PUT、DELETEなど)はどれでも使えます。 それぞれの動詞にはそれぞれにメソッドがあります:

$routes->get('products', 'Product::feature');
$routes->post('products', 'Product::feature');
$routes->put('products/(:num)', 'Product::feature');
$routes->delete('products/(:num)', 'Product::feature');

ルーティングが一致する必要のある複数の動詞を配列にして match メソッドに渡すことができます:

$routes->match(['get', 'put'], 'products', 'Product::feature');

コマンドラインだけのルーティング

webブラウザからはアクセスできず、コマンドラインからのみ動作するルーティングを作ることができます。cli() メソッドを使います。これは cron ジョブまたは CLI だけのツールを作るのにすごく良いです。HTTP 動詞ベースのあらゆるルーティングは CLI からのアクセスも制限しますが、any() メソッドで作られたルーティングはコマンドラインからのアクセスも可能です:

$routes->cli('migrate', 'App\Database::migrate');

リソースのルーティング

ひとつのリソースに対する最低限の RESTful ルーティング は resource() メソッドで手早く生成できます。これは、リソースの完全な CRUD に必要な、とても一般的な5つのルーティングを作ります: 新しいリソースの作成、存在するリソースの更新、リソースすべての一覧、単一のリソースの表示、単一のリソースの削除、です。第1引数はリソース名です:

$routes->resource('photos');

// つぎの内容と等価です:
$routes->get('photos',               'Photos::listAll');
$routes->get('photos/(:segment)',    'Photos::show/$1');
$routes->post('photos',              'Photos::create');
$routes->put('photos/(:segment)',    'Photos::update/$1');
$routes->delete('photos/(:segment)', 'Photos::delete/$1');

第2引数には生成ルーティングを編集するオプション配列を受け取ります。これらのルーティングは API の用途には機能しますが、多くのメソッドを許可しているところでの話です。'websafe' オプションを使うと、update および delete メソッドを HTML フォームでも動くようにルーティングを生成します:

$routes->resource('photos', ['websafe' => 1]);

// つぎのものと同等のルーティングを作ります:
$routes->post('photos/(:segment)',        'Photos::update/$1');
$routes->post('photos/(:segment)/delete', 'Photos::delete/$1');

使用するコントローラの変更

controller オプションで使用されるコントローラを指定することができます:

$routes->resources('photos', ['controller' =>'App\Gallery']);

// 次のようなルーティングを生成します:
$routes->get('photos', 'App\Gallery::listAll');

使用するプレースホルダの変更

デフォルトでは segment プレースホルダがリソース ID に使われます。placeholder オプションでこれを変更できます:

$routes->resources('photos', ['placeholder' => '(:id)']);

// 次のようなルーティングを生成します:
$routes->get('photos/(:id)', 'Photos::show/$1');

生成するルーティングの限定

only オプションで生成されるルーティングを制限できます。生成すべきメソッド名を配列にします。それらのどれかに一致するルーティングのみが生成されます。残りは無視されます:

$routes->resources('photos', ['only' => ['listAll', 'show']]);

使えるメソッドは: listAll、show、create、update、deleteです。

広範囲のオプション

ルーティングを作るすべてのメソッド(add、get、post、resourcesなど)はオプション配列を渡せます。生成するルーティングを編集、もしくは制限できます。$options 配列は常に引数の最後です:

$routes->add('from', 'to', $options);
$routes->get('from', 'to', $options);
$routes->post('from', 'to', $options);
$routes->put('from', 'to', $options);
$routes->head('from', 'to', $options);
$routes->options('from', 'to', $options);
$routes->delete('from', 'to', $options);
$routes->patch('from', 'to', $options);
$routes->match(['get', 'put'], 'from', 'to', $options);
$routes->resources('photos', $options);
$routes->map($array, $options);
$routes->group('name', $options, function());

名前空間の割り当て

デフォルトの名前空間がコントローラの前につけられますが(後述参照)、異なる名前空間を namespace オプションで指定することもできます。変更したい名前空間を値とします:

// \Admin\Users::index() へのルーティング
$routes->add('admin/users', 'Users::index', ['namespace' => 'Admin']);

get、postなどのような単一のルーティングを作るメソッドすべてについて、新しい名前空間は呼び出したものにだけ適用されます。 複数のルーティングを作るメソッドについて、新しい名前空間はそのメソッドで作るすべての生成ルーティングに対して適用されます。group() についてはクロージャで作成する全てに適用されます。

ホスト名の限定

ルーティングのグループを特定のドメインまたはサブドメインでのみ機能するように制限することができます。"hostname" オプションに許可が必要なドメインを渡し、オプション配列の一部としてください:

$collection->get('from', 'to', ['hostname' => 'accounts.example.com']);

この例では "accounts.example.com" に正確に一致するホストでのみ動作します。 "example.com" では動きません。

サブドメインの限定

subdomain オプションが渡されたら、そのサブドメインでのみルーティングが有効になります。ルーティングはそのサブドメイン経由で見ている場合だけマッチするようになります:

// media.example.com に限定する
$routes->add('from', 'to', ['subdomain' => 'media']);

値にアスタリスク (*) を使うと、あらゆるサブドメインに制限することができます。サブドメインのついていない URL で見ようとしても、これはマッチしません:

// サブドメインに限定する
$routes->add('from', 'to', ['subdomain' => '*']);

重要

この仕組みは不完全であり、本番環境で使う前にそのドメインでのテストをする必要があります。 ほとんどのドメインでは動作しますが、いくつかの極端なケースでは動きません。とりわけ、ドメインそれ自体にピリオドを含む場合(属性型ドメインやwwwを使っていない場合)は誤った判定をする可能性があります。

一致パラメータの位置ずらし

ルーティング内で一致したパラメータのオフセットを offset オプションに数値を渡すことで変更できます。値にはオフセットするセグメント数を使います。

これは API の URL 第1セグメントにバージョン番号を使いたいときに効力を発揮します。第1パラメータに言語の文字列を指定する場合にも使えます:

$routes->get('users/(:num)', 'users/show/$1', ['offset' => 1]);

// Creates:
$routes['users/(:num)'] = 'users/show/$2';

ルーティングの設定オプション

RoutesCollection クラスにはルーティング全体に影響するいくつかのオプションがあり、アプリケーションの必要に合わせて変更することができます。それらのオプションは /application/Config/Routes.php の上部にあります。

デフォルト名前空間

ルーティングにコントローラをマッチさせるとき、ルータはデフォルトの名前空間を指定のコントローラの前にくっつけます。デフォルトではこの値は空であり、それぞれのルーティングを完全な名前空間で修飾されたコントローラ名にします:

$routes->setDefaultNamespace('');

// コントローラは \Users
    $routes->add('users', 'Users::index');

    // コントローラは \Admin\Users
    $routes->add('users', 'Admin\Users::index');

コントローラに明示的な名前空間をつけていないなら、変更する必要はありません。コントローラに名前空間をつけているなら、この値を変えればタイピングの手間が省けるでしょう:

$routes->setDefaultNamespace('App');

// コントローラは \App\Users
$routes->add('users', 'Users::index');

// コントローラは \App\Admin\Users
$routes->add('users', 'Admin\Users::index');

デフォルトコントローラ

サイトのルートを表示した場合(例: example.com)、明示的なルーティングが存在しない限り、setDefaultController() メソッドの値で使用するコントローラが決まります。これのデフォルト値は Home で、/application/Controllers/Home.php に一致します:

// example.com のルートを application/Controllers/Welcome.php にします
$routes->setDefaultController('Welcome');

また、デフォルトコントローラはルーティングにマッチしなかった場合にも使用されます。URI はコントローラディレクトリ内のディレクトリを指し示します。例えば example.com/admin を表示したとして、/application/Controllers/admin/Home.php があればそれを使います。

デフォルトメソッド

これはデフォルトコントローラの設定と同じように動きますが、デフォルトメソッドを決定するのに使用します。URI に一致したコントローラが見つかり、しかしメソッドを決めるセグメントがない場合に使用されます。デフォルト値は index です:

$routes->setDefaultMethod('listAll');

この例では、ユーザが example.com/products を表示して、Products コントローラがあった場合、Products::listAll() メソッドが実行されます。

URI ダッシュの変換

このオプションは URI セグメントのうちコントローラとメソッドのダッシュ (‘-‘) を自動的にアンダースコアに変換します。そのためそれが必要な場合、ルーティングの追加を省くことができます。これは必要なことです。なぜならダッシュはクラス名またはメソッド名として不正であり、使おうとすれば fatal error の原因になります:

$routes->setTranslateURIDashes(true);

定義したルーティングのみの使用

URI が定義済みのルーティングにマッチしなかった場合、上述したようにシステムは URI に一致するコントローラとメソッドがないかを試します。この自動マッチングは無効化できます。setAutoRoute() オプションを false にすることで、定義されたルーティングのみに制限できます:

$routes->setAutoRoute(false);

404のオーバーライド

現在の URI に一致するページが見つからなかった場合、システムは共通の 404 ページを表示します。set404Override() オプションにアクションを指定することで、その挙動を変更することができます。この値は、他のルーティングで指定しているような有効なクラス・メソッドの組み合わせ、またはクロージャのどちらかです:

// App\Errors クラスの show404 メソッドが実行されます
$routes->set404Override('App\Errors::show404');

// 独自のビューが表示されます
$routes->set404Override(function(){
    echo view('my_errors/not_found.html');
});