【改訂版】CodeIgniter4のルーティング
この記事は CodeIgniter Advent Calendar 2022 - Qiita の5日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。
CodeIgniter4のルーティングについて解説します。
手動ルーティング
CodeIgniter 4.2.0 から、セキュリティ上の理由で、すべてのルートを設定ファイルに定義する「手動ルーティング」がデフォルトになりました。 詳細は、「【改訂版】本当は危ないCodeIgniter4の自動ルーティング」を参照してください。
コントローラがあるだけではルーティングされないため、ルートを定義しない限りコントローラにアクセスできません。定義されていないURLにアクセスしても 404 ページが表示されます。
HTTPメソッドでのルーティング
ルートは、app/Config/Routes.php
に設定します。
特定のHTTPメソッドを指定してルートを設定することができます。
GETメソッドの場合は、$routes->get()
を使用します。
$routes->get('news', [\App\Controllers\News::class, 'index']);
上記は、http://example.com/news
にアクセスすると、News
コントローラの index()
メソッドが呼び出されます。
POSTメソッドの場合は、$routes->post()
を使用します。
$routes->post('news/create', [\App\Controllers\News::class, 'create']);
上記は、http://example.com/news/create
にアクセスすると、News
コントローラの create()
メソッドが呼び出されます。
$routes->put()
、$routes->delete()
なども同様です。
URLの一部をキャプチャ
(:segment)
、(:num)
、(:any)
などのプレースホルダーを使います。
(:segment)
は1つのURIセグメント、(:num)
は数字だけの1つのセグメント、(:any)
は任意の文字列にマッチします。
$routes->get('news/(:segment)', [\App\Controllers\News::class, 'view']);
上記は、/news/foo
にアクセスすると、News
コントローラの view()
メソッドの第1引数に foo
を渡し、呼び出します。
ルートのグループ化
ルートをグループ化することもできます。
$routes->group('admin', function ($routes) {
$routes->get('users', [\App\Controllers\Admin\Users::class, 'index']);
$routes->get('blog', [\App\Controllers\Admin\Blog::class, 'index']);
});
上記は、admin/users
と admin/blog
のルートを設定しています。
コントローラフィルターの適用
ルートに対してコントローラフィルターを指定することもできます。
以下は、login
というフィルターをルートに適用するサンプルです。
$routes->post('news/create', [\App\Controllers\News::class, 'create'], ['filter' => 'login']);
$routes->group('admin', ['filter' => 'login'], function ($routes) {
$routes->get('users', [\App\Controllers\Admin\Users::class, 'index']);
$routes->get('blog', [\App\Controllers\Admin\Blog::class, 'index']);
});
名前付きルート
オプションでルートに名前を付けることができます。
$routes->get(
'users/(:num)/gallery/(:num)',
[\App\Controllers\Galleries::class, 'showUserGallery'],
['as' => 'user-gallery']
);
ビューで以下のように使えます。
<a href="<?= url_to('user-gallery', 15, 12) ?>">ギャラリーを表示する</a>
<!-- 結果: 'http://example.com//users/15/gallery/12' -->
名前付きルートを使うと、ビューを変更せずに簡単にルートを変更することができます。
ルート設定の確認
spark routes
コマンドで設定したルートを確認することができます。
$ php spark routes
CodeIgniter v4.2.10 Command Line Tool - Server Time: 2022-12-05 20:17:56 UTC-06:00
+--------+--------+------------------------------------------+----------------+---------------+
| Method | Route | Handler | Before Filters | After Filters |
+--------+--------+------------------------------------------+----------------+---------------+
| GET | / | \App\Controllers\Home::index | | toolbar |
| CLI | ci(.*) | \CodeIgniter\CLI\CommandRunner::index/$1 | | |
+--------+--------+------------------------------------------+----------------+---------------+
デフォルトでは上記のルートが設定されています。ルートは上から順に評価され、マッチすればそのメソッドが実行されます。
なお、CLI
はコマンドライン用の特殊なルートです。
自動ルーティング(改善)
CodeIgniter 4.1.x までデフォルトだった自動ルーティングは、あまりにも簡単に脆弱性を作り込んでしまうため、 4.2.0 からより安全な「自動ルーティング(改善)」が追加されました。
自動ルーティング(改善)を有効にする
自動ルーティング(改善)を有効にするには、app/Config/Routes.php
と app/Config/Feature.php
の 2箇所で設定する必要があります。
app/Config/Routes.php
$routes->setAutoRoute(true);
app/Config/Feature.php
public bool $autoRoutesImproved = true;
$autoRoutesImproved
が false
だと、自動ルーティング(レガシー)になります。
自動ルーティング(改善)の基本
自動ルーティング(改善)は、基本的に以下の規約で自動的にルーティングします。
http://example.com/{コントローラ名}/{メソッド名(HTTP動詞を除く)}/{引数1}/{引数2}/...
以下の News
コントローラを作成します。
<?php
namespace App\Controllers;
class News extends BaseController
{
public function getTest($p1 = 'x', $p2 = 'x')
{
return '$p1: ' . esc($p1) . ', $p2: ' . esc($p2);
}
}
すると、以下のルートが自動的に定義されます。
+-----------+---------------------+------------------------------------------+----------------+---------------+
| Method | Route | Handler | Before Filters | After Filters |
+-----------+---------------------+------------------------------------------+----------------+---------------+
| GET(auto) | news/test[/..][/..] | \App\Controllers\News::getTest | | toolbar |
+-----------+---------------------+------------------------------------------+----------------+---------------+
getTest()
メソッドの最初の get
はHTTPメソッド(HTTP動詞)です。この場合はGETメソッドでこのルートにアクセスできることになります。
例えば、POSTメソッドでアクセスさせたい場合は、postTest()
メソッドを定義する必要があります。
php spark serve
コマンドを実行し、
http://localhost:8080/news/test/a/b
にブラウザからアクセスすると、以下が表示されます。
$p1: a, $p2: b
http://localhost:8080/news/test/a
にアクセスすると、以下が表示されます。
$p1: a, $p2: x
http://localhost:8080/news/test
にアクセスすると、以下が表示されます。
$p1: x, $p2: x
http://localhost:8080/news/test/a/b/c
にアクセスすると、404 - File Not Found
が表示されます。
引数の数が合わないからです。
デフォルトメソッド
http://localhost:8080/news
でアクセス可能にするには、以下のように getIndex()
メソッドを追加します。
<?php
namespace App\Controllers;
class News extends BaseController
{
// ...
public function getIndex()
{
return __METHOD__;
}
}
これで http://localhost:8080/news
にアクセスすると、以下が表示されます。
App\Controllers\News::getIndex
このようにURLの {メソッド名(HTTP動詞を除く)}
の部分を省略したときに実行されるメソッドを「デフォルトメソッド」と言います。
デフォルトでは index
が設定されています。
デフォルトコントローラ
http://localhost:8080/
のように {コントローラ名}
がないURLにアクセス可能にするには、以下のように Home
コントローラを作成します。
<?php
namespace App\Controllers;
class Home extends BaseController
{
public function getIndex()
{
return view('welcome_message');
}
}
しかし、デフォルトでは app/Config/Routes.php
に /
へのルート(以下)が定義されています。
$routes->get('/', 'Home::index');
定義済みのルートは自動ルーティングより優先されるので、Home::index()
メソッドが存在しないため、404 - File Not Found
になります。
そこで、上記のルートの定義をコメントアウトしてください。
//$routes->get('/', 'Home::index');
これで、http://localhost:8080/
に自動ルーティング(改善)によりアクセス可能になります。
自動ルーティング(改善)の規約
規約をまとめておきます。
- URLは基本的にはすべて小文字を想定
- URLでの
{コントローラ名}
はすべて小文字、実際のクラス名は先頭のみ大文字- サブフォルダを使う場合は、サブフォルダ名も先頭のみ大文字
- メソッド名には
getIndex()
、postCreate()
のようにHTTP動詞のプリフィックスが必要http://example.com/news/create
にGETメソッドでリクエストした場合、News
コントローラのgetCreate()
メソッドが実行される
- メソッド名が省略されたURLの場合、デフォルトメソッド(デフォルトでは
index
)があれば実行されるhttp://example.com/news
にGETメソッドでリクエストした場合、News
コントローラのgetIndex()
メソッドが実行される
- トップページへのリクエストの場合、デフォルトコントローラ(デフォルトでは
Home
)が実行されるhttp://example.com/
にGETメソッドでリクエストした場合、Home
コントローラのgetIndex()
メソッドが実行される
- デフォルトコントローラとデフォルトメソッドはURLの中で必ず省略する必要がある
http://example.com/
にはアクセス可能だがhttp://example.com/home
には 404 が返るhttp://example.com/news
にはアクセス可能だがhttp://example.com/news/index
には 404 が返る
- メソッドのパラメータ数がチェックされる
- パラメータ数が一致しない場合は 404 が返る
- 手動ルーティングで定義されているコントローラには一切アクセスできない
- 手動ルーティングするコントローラと自動ルーティングするコントローラは厳密に区別される
News::index()
へのルートが手動ルーティングで設定されている場合、News::getCreate()
メソッドを追加してnews/create
にアクセスしても 404 が返る
_remap()
メソッドはサポートされない
自動ルーティング(レガシー)
CodeIgniter 4.1.x までは、デフォルトで以下の規約で自動的にルーティングされていました。 これは CodeIgniter3 の自動ルーティングと同じものです。
http://example.com/{コントローラ名}/{メソッド名}/{引数1}/{引数2}/...
この機能は現在では「自動ルーティング(レガシー)」と呼ばれています。
セキュリティ上の問題があるため、デフォルトでは無効になっています。
自動ルーティング(レガシー)を有効にする
自動ルーティング(レガシー)を有効にするには、
app/Config/Routes.php
で以下を設定します。
app/Config/Routes.php
$routes->setAutoRoute(true);
セキュリティ上の理由から、自動ルーティング(レガシー)は推奨しません。できる限りオフに設定してください。
自動ルーティング(レガシー)を有効にすると、デフォルトでは以下のルートがあることがわかります。
+--------+------------------+------------------------------------------+----------------+---------------+
| Method | Route | Handler | Before Filters | After Filters |
+--------+------------------+------------------------------------------+----------------+---------------+
| GET | / | \App\Controllers\Home::index | | toolbar |
| CLI | ci(.*) | \CodeIgniter\CLI\CommandRunner::index/$1 | | |
| auto | / | \App\Controllers\Home::index | | toolbar |
| auto | home | \App\Controllers\Home::index | | toolbar |
| auto | home/index[/...] | \App\Controllers\Home::index | | toolbar |
+--------+------------------+------------------------------------------+----------------+---------------+
/
へのルートは Method
が GET
のルート(1行目)と auto
のルート(3行目)の 2つあります。リクエストが GET /
の場合、GET
のルートが上にあるため、auto
のルートは実際には使われません。しかし、リクエストが POST /
の場合は auto
のルートがマッチします。
Method
が auto
のルートは自動ルーティング(レガシー)であることを示し、 あらゆるHTTPメソッドでアクセス可能です。
この記事は CodeIgniter Advent Calendar 2022 - Qiita の5日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。
関連
参考
Date: 2022/12/05