コントローラ
コントローラは、HTTPリクエストをどのように処理するかを決定するため、アプリケーションの中核となります。
コントローラとは?
コントローラは、HTTPリクエストを処理する単なるクラスファイルです。URIルーティング は、URIをコントローラに関連付けます。ビュー文字列またはResponse
オブジェクトを返します。
作成するすべてのコントローラは、BaseController
クラスを拡張する必要があります。このクラスは、すべてのコントローラで利用できるいくつかの機能を提供します。
コンストラクタ
CodeIgniterのコントローラには、特別なコンストラクタinitController()
があります。これは、PHPのコンストラクタ__construct()
の実行後にフレームワークによって呼び出されます。
initController()
をオーバーライドする場合は、メソッドにparent::initController($request, $response, $logger);
を追加することを忘れないでください
<?php
namespace App\Controllers;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
class Product extends BaseController
{
public function initController(
RequestInterface $request,
ResponseInterface $response,
LoggerInterface $logger
) {
parent::initController($request, $response, $logger);
// Add your code here.
}
// ...
}
重要
コンストラクタではreturn
を使用できません。したがって、return redirect()->to('route');
は機能しません。
initController()
メソッドは、次の3つのプロパティを設定します。
含まれるプロパティ
CodeIgniterのコントローラは、これらのプロパティを提供します。
Requestオブジェクト
アプリケーションのメインのRequestインスタンスは、常にクラスプロパティ$this->request
として利用できます。
Responseオブジェクト
アプリケーションのメインのResponseインスタンスは、常にクラスプロパティ$this->response
として利用できます。
Loggerオブジェクト
Loggerクラスのインスタンスは、クラスプロパティ$this->logger
として利用できます。
ヘルパー
ヘルパーファイルの配列をクラスプロパティとして定義できます。コントローラがロードされるたびに、これらのヘルパーファイルは自動的にメモリにロードされるため、コントローラ内のどこでもそのメソッドを使用できます。
<?php
namespace App\Controllers;
class MyController extends BaseController
{
protected $helpers = ['url', 'form'];
}
forceHTTPS
コントローラ内では、メソッドへのアクセスをHTTPS経由で強制するための便利なメソッドが利用可能です
<?php
if (! $this->request->isSecure()) {
$this->forceHTTPS();
}
デフォルトでは、およびHTTP Strict Transport Securityヘッダーをサポートする最新のブラウザでは、この呼び出しはブラウザに、非HTTPS呼び出しを1年間HTTPS呼び出しに変換するように強制する必要があります。最初のパラメータとして期間(秒単位)を渡すことで、これを変更できます。
<?php
if (! $this->request->isSecure()) {
$this->forceHTTPS(31536000); // one year
}
注
時間ベースの定数は、YEAR
、MONTH
などを含めて、いつでも使用できます。
データの検証
$this->validateData()
バージョン4.2.0で新しく追加されました。
データチェックを簡素化するために、コントローラには便利なメソッドvalidateData()
も用意されています。
このメソッドは、(1)検証するデータの配列、(2)ルール配列、(3)項目が無効な場合に表示するカスタムエラーメッセージのオプション配列、(4)使用するオプションのデータベースグループを受け入れます。
検証ライブラリドキュメントには、ルールとメッセージの配列形式、および利用可能なルールに関する詳細が記載されています
<?php
namespace App\Controllers;
class StoreController extends BaseController
{
public function product(int $id)
{
$data = [
'id' => $id,
'name' => $this->request->getPost('name'),
];
$rule = [
'id' => 'integer',
'name' => 'required|max_length[255]',
];
if (! $this->validateData($data, $rule)) {
return view('store/product', [
'errors' => $this->validator->getErrors(),
]);
}
// ...
}
}
$this->validate()
重要
このメソッドは、下位互換性のためにのみ存在します。新しいプロジェクトでは使用しないでください。すでに使用している場合でも、代わりにvalidateData()
メソッドを使用することをお勧めします。
コントローラには、便利なメソッドvalidate()
も用意されています。
警告
validate()
の代わりに、POSTデータのみを検証するには、validateData()
を使用してください。validate()
は、$request->getVar()
を使用します。これは、php.iniのrequest-orderに応じて、$_GET
、$_POST
、または$_COOKIE
データをこの順序で返します。新しい値は古い値を上書きします。POST値は、同じ名前のクッキーによって上書きされる場合があります。
このメソッドは、最初のパラメータにルールの配列を受け取り、オプションの2番目のパラメータに、項目が無効な場合に表示するカスタムエラーメッセージの配列を受け取ります。
内部的には、これはコントローラの$this->request
インスタンスを使用して、検証するデータを取得します。
検証ライブラリドキュメントには、ルールとメッセージの配列形式、および利用可能なルールに関する詳細が記載されています
<?php
namespace App\Controllers;
class UserController extends BaseController
{
public function updateUser(int $userID)
{
if (! $this->validate([
'email' => "required|is_unique[users.email,id,{$userID}]",
'name' => 'required|alpha_numeric_spaces',
])) {
// The validation failed.
return view('users/update', [
'errors' => $this->validator->getErrors(),
]);
}
// The validation was successful.
// Get the validated data.
$validData = $this->validator->getValidated();
// ...
}
}
警告
validate()
メソッドを使用する場合は、検証済みのデータを取得するためにgetValidated()メソッドを使用する必要があります。validate()
メソッドは、内部的にValidation::withRequest()メソッドを使用し、$request->getJSON()または$request->getRawInput()または$request->getVar()からデータを検証するため、攻撃者が検証されるデータを変更する可能性があります。
注
v4.4.0以降では、$this->validator->getValidated() メソッドを使用できます。
設定ファイルにルールを保持する方が簡単な場合は、app/Config/Validation.php で定義されているグループの名前で、$rules
配列を置き換えることができます。
<?php
namespace App\Controllers;
class UserController extends BaseController
{
public function updateUser(int $userID)
{
if (! $this->validate('userRules')) {
// The validation failed.
return view('users/update', [
'errors' => $this->validator->getErrors(),
]);
}
// The validation was successful.
// Get the validated data.
$validData = $this->validator->getValidated();
// ...
}
}
注
バリデーションはモデルで自動的に処理することもできますが、コントローラーで行う方が簡単な場合もあります。どこで行うかはあなた次第です。
メソッドの保護
場合によっては、特定のメソッドをパブリックアクセスから隠したい場合があります。これを実現するには、メソッドを private
または protected
として宣言するだけです。これにより、URLリクエストで処理されるのを防ぐことができます。
たとえば、Helloworld
コントローラーに対して次のようなメソッドを定義した場合
<?php
namespace App\Controllers;
class Helloworld extends BaseController
{
protected function utility()
{
// some code
}
}
そして、そのメソッドに対してルート (helloworld/utitilty
) を定義した場合。次のURLを使用してアクセスしようとしても機能しません。
example.com/index.php/helloworld/utility
自動ルーティングも機能しません。
自動ルーティング (改善版)
バージョン4.2.0で新しく追加されました。
v4.2.0以降、新しい、より安全な自動ルーティングが導入されました。
注
CodeIgniter 3から4.1.xまでデフォルトで有効になっていた自動ルーティングに慣れている場合は、ChangeLog v4.2.0 で違いを確認できます。
このセクションでは、新しい自動ルーティングの機能について説明します。これは、ルート定義なしでHTTPリクエストを自動的にルーティングし、対応するコントローラーメソッドを実行します。
v4.2.0以降、自動ルーティングはデフォルトで無効になっています。使用するには、自動ルーティングの有効化 を参照してください。
次のURIを考えてみましょう。
example.com/index.php/helloworld/
上記の例では、自動ルーティングが有効になっている場合、CodeIgniterは App\Controllers\Helloworld
という名前のコントローラーを見つけてロードしようとします。
注
コントローラーの短縮名がURIの最初のセグメントと一致すると、そのコントローラーがロードされます。
試してみましょう: Hello World!
動作を確認できるように、簡単なコントローラーを作成してみましょう。テキストエディタを使用して、Helloworld.php というファイルを作成し、次のコードを記述します。 Helloworld
コントローラーは BaseController
を拡張していることに気づくでしょう。BaseController の機能が必要ない場合は、CodeIgniter\Controller
を拡張することもできます。
BaseController は、すべてのコントローラーで必要なコンポーネントをロードしたり、機能を実行したりするための便利な場所を提供します。このクラスは、新しいコントローラーで拡張できます。
<?php
namespace App\Controllers;
class Helloworld extends BaseController
{
public function getIndex()
{
return 'Hello World!';
}
}
次に、ファイルを app/Controllers ディレクトリに保存します。
重要
ファイルの名前は Helloworld.php で、先頭の H
は大文字にする必要があります。自動ルーティングを使用する場合、コントローラーのクラス名は必ず大文字で始まり、最初の一文字のみが大文字である必要があります。
重要
自動ルーティング(改善版)によって実行されるコントローラーメソッドには、getIndex()
, postCreate()
のように、HTTP動詞 (get
, post
, put
など) の接頭辞が必要です。
次に、次のようなURLを使用してサイトにアクセスします。
example.com/index.php/helloworld
正しく行われた場合は、次のように表示されるはずです。
Hello World!
これは有効です。
<?php
namespace App\Controllers;
class Helloworld extends BaseController
{
// ...
}
これは無効です。
<?php
namespace App\Controllers;
class helloworld extends BaseController
{
// ...
}
これは無効です。
<?php
namespace App\Controllers;
class HelloWorld extends BaseController
{
// ...
}
また、常にコントローラーが親コントローラークラスを拡張していることを確認して、そのすべてのメソッドを継承できるようにしてください。
注
システムは、定義されたルートに対して一致が見つからなかった場合、app/Controllers のディレクトリ/ファイルと各セグメントを照合して、コントローラーに対してURIを照合しようとします。そのため、ディレクトリ/ファイルは大文字で始まり、残りは小文字でなければなりません。
別の命名規則が必要な場合は、定義済みルートルーティング を使用して手動で定義する必要があります。次に、PSR-4オートローダーに基づく例を示します。
<?php
/*
* Folder and file structure:
* \<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
*/
$routes->get('helloworld', '\App\Controllers\HelloWorld::index');
メソッド
メソッドの可視性
HTTPリクエストを介して実行可能なメソッドを定義する場合は、メソッドを public
として宣言する必要があります。
警告
セキュリティ上の理由から、新しいユーティリティメソッドは必ず protected
または private
として宣言してください。
デフォルトメソッド
上記の例では、メソッド名は getIndex()
です。このメソッド(HTTP動詞 + Index()
)は、デフォルトメソッドと呼ばれ、URIの2番目のセグメントが空の場合にロードされます。
通常のメソッド
URIの2番目のセグメントによって、コントローラー内のどのメソッドが呼び出されるかが決まります。
試してみましょう。コントローラーに新しいメソッドを追加します。
<?php
namespace App\Controllers;
class Helloworld extends BaseController
{
public function getIndex()
{
return 'Hello World!';
}
public function getComment()
{
return 'I am not flat!';
}
}
次に、次のURLをロードして、getComment()
メソッドを表示します。
example.com/index.php/helloworld/comment/
新しいメッセージが表示されるはずです。
メソッドへのURIセグメントの受け渡し
URIに3つ以上のセグメントが含まれている場合は、パラメーターとしてメソッドに渡されます。
たとえば、次のようなURIがあるとします。
example.com/index.php/products/shoes/sandals/123
メソッドには、URIセグメント3と4 ('sandals'
と '123'
) が渡されます。
<?php
namespace App\Controllers;
class Products extends BaseController
{
public function getShoes($sandals, $id)
{
return $sandals . $id;
}
}
デフォルトコントローラー
デフォルトコントローラーは、URIがディレクトリ名で終わる場合、またはURIが存在しない場合に使用される特別なコントローラーです。これは、サイトのルートURLのみが要求された場合に発生します。
デフォルトコントローラーの定義
Helloworld
コントローラーで試してみましょう。
デフォルトコントローラーを指定するには、app/Config/Routing.php ファイルを開き、次のプロパティを設定します。
public string $defaultController = 'Helloworld';
ここで、Helloworld
は、使用するコントローラークラスの名前です。
そして、app/Config/Routes.php の行をコメントアウトします。
$routes->get('/', 'Home::index');
これで、URIセグメントを指定せずにサイトを参照すると、「Hello World」メッセージが表示されます。
重要
自動ルーティング(改善版)を使用する場合は、$routes->get('/', 'Home::index');
の行を削除する必要があります。定義されたルートは自動ルーティングよりも優先され、定義されたルートで定義されたコントローラーは、セキュリティ上の理由から自動ルーティング(改善版)によるアクセスが拒否されるためです。
詳細については、構成オプション のドキュメントを参照してください。
デフォルトメソッドのフォールバック
バージョン 4.4.0 で新しく追加されました。
メソッド名のURIセグメントに対応するコントローラーメソッドが存在せず、デフォルトメソッドが定義されている場合、残りのURIセグメントは実行のためにデフォルトメソッドに渡されます。
<?php
namespace App\Controllers;
class Product extends BaseController
{
public function getIndex($id = null, $action = '')
{
// ...
}
}
次のURLをロードします。
example.com/index.php/product/15/edit
メソッドには、URIセグメント2と3 ('15'
と 'edit'
) が渡されます。
重要
URIにメソッドパラメーターよりも多くのパラメーターがある場合、自動ルーティング(改善版)はメソッドを実行せず、404 Not Foundになります。
デフォルトコントローラーへのフォールバック
コントローラー名のURIセグメントに対応するコントローラーが存在せず、デフォルトコントローラー(デフォルトでは Home
)がディレクトリに存在する場合、残りのURIセグメントはデフォルトコントローラーのデフォルトメソッドに渡されます。
たとえば、app/Controllers/News ディレクトリに次のデフォルトコントローラー Home
がある場合
<?php
namespace App\Controllers\News;
use App\Controllers\BaseController;
class Home extends BaseController
{
public function getIndex($id = null)
{
// ...
}
}
次のURLをロードします。
example.com/index.php/news/101
News\Home
コントローラーとデフォルトの getIndex()
メソッドが見つかります。したがって、デフォルトメソッドにはURIセグメント2 ('101'
) が渡されます。
注
App\Controllers\News
コントローラーがある場合は、それが優先されます。URIセグメントは順番に検索され、最初に見つかったコントローラーが使用されます。
注
URIにメソッドパラメーターよりも多くのパラメーターがある場合、自動ルーティング(改善版)はメソッドを実行せず、404 Not Foundになります。
コントローラーをサブディレクトリに整理する
大規模なアプリケーションを構築する場合、コントローラーをサブディレクトリに階層的に整理したり、構造化したりする場合があります。CodeIgniterでは、これを行うことができます。
メインの app/Controllers の下にサブディレクトリを作成し、その中にコントローラークラスを配置するだけです。
重要
ディレクトリ名は必ず大文字で始まり、最初の一文字のみが大文字である必要があります。
この機能を使用する場合、URIの最初のセグメントはディレクトリを指定する必要があります。たとえば、ここにコントローラーがあるとします。
app/Controllers/Products/Shoes.php
上記のコントローラーを呼び出すには、URIは次のようになります。
example.com/index.php/products/shoes/show/123
注
app/Controllers と public に同じ名前のディレクトリを含めることはできません。これは、ディレクトリが存在する場合、Webサーバーがそれを検索し、CodeIgniterにルーティングされないためです。
各サブディレクトリには、URLにサブディレクトリのみが含まれている場合に呼び出されるデフォルトコントローラーを含めることができます。app/Config/Routing.php ファイルで指定されているデフォルトコントローラーの名前に一致するコントローラーをそこに入れるだけです。
CodeIgniterでは、定義済みのルートルーティング を使用してURIをマッピングすることもできます。
自動ルーティング(レガシー)
重要
この機能は後方互換性のためだけに存在します。新しいプロジェクトでは使用しないでください。すでに使用している場合でも、代わりに自動ルーティング(改良版)を使用することをお勧めします。
このセクションでは、CodeIgniter 3のルーティングシステムである自動ルーティング(レガシー)の機能について説明します。これは、ルート定義なしでHTTPリクエストを自動的にルーティングし、対応するコントローラーメソッドを実行します。自動ルーティングはデフォルトで無効になっています。
警告
誤った設定やコーディングを防ぐため、自動ルーティング(レガシー)を使用しないことをお勧めします。コントローラーフィルターやCSRF保護がバイパスされる脆弱なアプリを簡単に作成できます。
重要
自動ルーティング(レガシー)は、あらゆるHTTPメソッドによるHTTPリクエストをコントローラーメソッドにルーティングします。
次のURIを考えてみましょう。
example.com/index.php/helloworld/
上記の例では、CodeIgniterはHelloworld.phpという名前のコントローラーを見つけてロードしようとします。
注
コントローラーの短縮名がURIの最初のセグメントと一致すると、そのコントローラーがロードされます。
試してみましょう: Hello World! (レガシー)
動作を確認できるように、簡単なコントローラーを作成してみましょう。テキストエディタを使用して、Helloworld.php というファイルを作成し、次のコードを記述します。 Helloworld
コントローラーは BaseController
を拡張していることに気づくでしょう。BaseController の機能が必要ない場合は、CodeIgniter\Controller
を拡張することもできます。
BaseController は、すべてのコントローラーで必要なコンポーネントをロードしたり、機能を実行したりするための便利な場所を提供します。このクラスは、新しいコントローラーで拡張できます。
セキュリティ上の理由から、新しいユーティリティメソッドは必ずprotected
またはprivate
として宣言してください。
<?php
namespace App\Controllers;
class Helloworld extends BaseController
{
public function index()
{
return 'Hello World!';
}
}
次に、ファイルを app/Controllers ディレクトリに保存します。
重要
ファイルの名前は Helloworld.php で、先頭の H
は大文字にする必要があります。自動ルーティングを使用する場合、コントローラーのクラス名は必ず大文字で始まり、最初の一文字のみが大文字である必要があります。
次に、次のようなURLを使用してサイトにアクセスします。
example.com/index.php/helloworld
正しく行われた場合は、次のように表示されるはずです。
Hello World!
これは有効です。
<?php
namespace App\Controllers;
class Helloworld extends BaseController
{
// ...
}
これは無効です。
<?php
namespace App\Controllers;
class helloworld extends BaseController
{
// ...
}
これは無効です。
<?php
namespace App\Controllers;
class HelloWorld extends BaseController
{
// ...
}
また、常にコントローラーが親コントローラークラスを拡張していることを確認して、そのすべてのメソッドを継承できるようにしてください。
注
システムは、定義されたルートに対して一致が見つからなかった場合、app/Controllers のディレクトリ/ファイルと各セグメントを照合して、コントローラーに対してURIを照合しようとします。そのため、ディレクトリ/ファイルは大文字で始まり、残りは小文字でなければなりません。
別の命名規則が必要な場合は、定義済みルートルーティング を使用して手動で定義する必要があります。次に、PSR-4オートローダーに基づく例を示します。
<?php
/*
* Folder and file structure:
* \<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
*/
$routes->get('helloworld', '\App\Controllers\HelloWorld::index');
メソッド (レガシー)
上記の例では、メソッド名はindex()
です。URIの2番目のセグメントが空の場合、index()
メソッドは常にデフォルトでロードされます。別の「Hello World」メッセージを表示する方法は次のとおりです。
example.com/index.php/helloworld/index/
URIの2番目のセグメントによって、コントローラー内のどのメソッドが呼び出されるかが決まります。
試してみましょう。コントローラーに新しいメソッドを追加します。
<?php
namespace App\Controllers;
class Helloworld extends BaseController
{
public function index()
{
return 'Hello World!';
}
public function comment()
{
return 'I am not flat!';
}
}
次のURLをロードして、commentメソッドを確認してください。
example.com/index.php/helloworld/comment/
新しいメッセージが表示されるはずです。
メソッドへのURIセグメントの渡し方(レガシー)
URIに3つ以上のセグメントが含まれている場合は、パラメーターとしてメソッドに渡されます。
たとえば、次のようなURIがあるとします。
example.com/index.php/products/shoes/sandals/123
メソッドには、URIセグメント3と4 ('sandals'
と '123'
) が渡されます。
<?php
namespace App\Controllers;
class Products extends BaseController
{
public function shoes($sandals, $id)
{
return $sandals . $id;
}
}
デフォルトコントローラー(レガシー)
デフォルトコントローラーは、URIがディレクトリ名で終わる場合や、サイトのルートURLのみがリクエストされた場合のように、URIが存在しない場合に使用される特別なコントローラーです。
デフォルトコントローラーの定義(レガシー)
Helloworld
コントローラーで試してみましょう。
デフォルトコントローラーを指定するには、app/Config/Routing.php ファイルを開き、次のプロパティを設定します。
public string $defaultController = 'Helloworld';
ここで、Helloworld
は、使用するコントローラークラスの名前です。
そして、app/Config/Routes.php の行をコメントアウトします。
$routes->get('/', 'Home::index');
これで、URIセグメントを指定せずにサイトを参照すると、「Hello World」メッセージが表示されます。
注
$routes->get('/', 'Home::index');
という行は、「現実世界」のアプリで使用したい最適化です。ただし、デモンストレーションのため、その機能は使用しないことにします。$routes->get()
については、URIルーティングで説明しています。
詳細については、設定オプション (レガシー)のドキュメントを参照してください。
コントローラーをサブディレクトリに整理する(レガシー)
大規模なアプリケーションを構築する場合、コントローラーをサブディレクトリに階層的に整理したり、構造化したりする場合があります。CodeIgniterでは、これを行うことができます。
メインの app/Controllers の下にサブディレクトリを作成し、その中にコントローラークラスを配置するだけです。
重要
ディレクトリ名は必ず大文字で始まり、最初の一文字のみが大文字である必要があります。
この機能を使用する場合、URIの最初のセグメントはディレクトリを指定する必要があります。たとえば、ここにコントローラーがあるとします。
app/Controllers/Products/Shoes.php
上記のコントローラーを呼び出すには、URIは次のようになります。
example.com/index.php/products/shoes/show/123
注
app/Controllersとpublic/に同じ名前のディレクトリを持つことはできません。これは、ディレクトリが存在する場合、Webサーバーがそれを検索し、CodeIgniterにルーティングされないためです。
各サブディレクトリには、URLにサブディレクトリのみが含まれている場合に呼び出されるデフォルトコントローラーを含めることができます。app/Config/Routing.php ファイルで指定されているデフォルトコントローラーの名前に一致するコントローラーをそこに入れるだけです。
CodeIgniterでは、定義済みのルートルーティング を使用してURIをマッピングすることもできます。
メソッド呼び出しの再マッピング
注
自動ルーティング(改良版)では、この機能は意図的にサポートされていません。
上記のように、通常、URIの2番目のセグメントによって、コントローラー内のどのメソッドが呼び出されるかが決まります。CodeIgniterでは、_remap()
メソッドを使用することで、この動作をオーバーライドできます。
<?php
namespace App\Controllers;
class Products extends BaseController
{
public function _remap()
{
// Some code here...
}
}
重要
コントローラーに_remap()
という名前のメソッドが含まれている場合、URIの内容に関係なく、常に呼び出されます。これにより、URIによってどのメソッドが呼び出されるかが決定されるという通常の動作がオーバーライドされ、独自のメソッドルーティングルールを定義できます。
オーバーライドされたメソッド呼び出し(通常はURIの2番目のセグメント)は、_remap()
メソッドへのパラメーターとして渡されます。
<?php
namespace App\Controllers;
class Products extends BaseController
{
public function _remap($method)
{
if ($method === 'some_method') {
return $this->{$method}();
}
return $this->default_method();
}
}
メソッド名の後の追加のセグメントは、_remap()
に渡されます。これらのパラメーターをメソッドに渡して、CodeIgniterのデフォルトの動作をエミュレートできます。
例
<?php
namespace App\Controllers;
class Products extends BaseController
{
public function _remap($method, ...$params)
{
$method = 'process_' . $method;
if (method_exists($this, $method)) {
return $this->{$method}(...$params);
}
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
}
コントローラーの拡張
コントローラーを拡張する場合は、コントローラーの拡張を参照してください。
以上です!
要するに、これがコントローラーについて知っておくべきことのすべてです。