【改訂版】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/usersadmin/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.phpapp/Config/Feature.php の 2箇所で設定する必要があります。

app/Config/Routes.php

$routes->setAutoRoute(true);

app/Config/Feature.php

    public bool $autoRoutesImproved = true;

$autoRoutesImprovedfalse だと、自動ルーティング(レガシー)になります。

自動ルーティング(改善)の基本

自動ルーティング(改善)は、基本的に以下の規約で自動的にルーティングします。

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       |
+--------+------------------+------------------------------------------+----------------+---------------+

/ へのルートは MethodGET のルート(1行目)と auto のルート(3行目)の 2つあります。リクエストが GET / の場合、GET のルートが上にあるため、auto のルートは実際には使われません。しかし、リクエストが POST / の場合は auto のルートがマッチします。

Methodauto のルートは自動ルーティング(レガシー)であることを示し、 あらゆるHTTPメソッドでアクセス可能です。

この記事は CodeIgniter Advent Calendar 2022 - Qiita の5日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

関連

参考

Date: 2022/12/05

Tags: codeigniter, codeigniter4