🔥

CodeIgniter 4.3 最速マスター

2023/01/20に公開

(最終更新:2023/01/27)

インストール&設定

インストール

📕インストール

Composerでインストールすると簡単です。

$ composer create-project codeigniter4/appstarter {フォルダ名}

フォルダ構成

CodeIgniter 4.3 のフォルダ構成は以下のようになっています。

ci4app/
├── app/ ... アプリケーション
│   ├── Common.php
│   ├── Cells/       ... (ビューセル)
│   ├── Commands/    ... (コマンド)
│   ├── Config/      ... 設定
│   ├── Controllers/ ... コントローラ
│   ├── Database/    ... データベース
│   ├── Entities/    ... (エンティティ)
│   ├── Filters/     ... コントローラフィルタ
│   ├── Helpers/     ... ヘルパー
│   ├── Language/    ... 言語ファイル
│   ├── Libraries/   ... ライブラリ 
│   ├── Models/      ... モデル
│   ├── ThirdParty/  ... サードパーティライブラリ用
│   ├── Validation/  ... (バリデーションルール)
│   └── Views/       ... ビュー
├── builds*       ... buildsコマンド
├── composer.json
├── composer.lock
├── env           ... 環境変数設定ファイルのサンプル
├── phpunit.xml.dist
├── public/ ... Web公開領域(ドキュメントルート)
│   ├── favicon.ico
│   ├── index.php
│   └── robots.txt
├── spark* ... sparkコマンド
├── tests/ ... テストファイル
│   ├── _support/
│   ├── database/
│   ├── session/
│   └── unit/
├── vendor/   ... Composer管理
└── writable/ ... 書き込み用フォルダ
    ├── cache/    ... キャッシュファイル
    ├── debugbar/ ... デバッグバー用
    ├── logs/     ... ログファイル
    ├── session/  ... セッションファイル
    └── uploads/  ... アップロードファイル

システムメッセージの翻訳のインストール

Composerで最新の開発版の翻訳をインストールします。

$ composer require codeigniter4/translations:dev-develop

設定

📕設定

設定ファイル

app/Config/ フォルダに設定ファイルがあります。設定ファイルはクラスです。
本番環境と共通の設定項目は、設定ファイルを変更します。

特に設定を変更しなくてもCodeIgniterは動作しますが、app/Config/App.php の以下の項目は多くの場合、変更することになるでしょう。

  • $indexPage ... URLにindex.phpを付けない場合は、この設定を空文字にする
  • $defaultLocale ... デフォルトのロケール('ja' に変更すると日本語のシステムメッセージが使用されます)
  • $supportedLocales ... サポートするロケール(優先順位が高い順に記載する)
  • $appTimezone ... タイムゾーン

開発環境固有の設定項目(データベースのパスワードなど)は、env ファイルを .env にコピーして、そこに設定します。

設定クラスのプロパティに対応する環境変数があると、設定ファイルのインスタンス化時に環境変数の値が自動的に設定されます。

複数の環境

📕複数の環境の処理

デフォルトでは、CodeIgniterは本番環境(production)用に動作します。
エラーが発生してもページには表示されません。

開発環境では development を指定します。これで開発モードになり、詳細なエラーやデバッグツールバーが表示されます。
.env で設定できます。

CI_ENVIRONMENT = development

なお、testing はPHPUnitでのテストのための特別な環境です。通常の開発やステージング環境には使えません。

データベース設定

📕データベース設定

app/Config/Database.php に設定クラスがあります。

設定クラスの値を変更したい開発環境固有の設定項目は、以下のように .env に設定します。

.env

database.default.hostname = localhost
database.default.database = ci4app
database.default.username = dbuser
database.default.password = dbpassword

サーバの起動

spark コマンドでPHPビルトインサーバーが起動します。

$ php spark serve

http://localhost:8080/ にアクセスしてください。

ルーティング

📕URIルーティング

CodeIgniter v4.2.0から、セキュリティ上の理由で自動ルーティングはデフォルトでオフになりました。

セキュリティ上の理由から、自動ルーティング(レガシー)は推奨しません。
できる限り、自動ルーティング(レガシー)は使用せず、「手動ルーティングのみ」または「自動ルーティング(改善)」を使いましょう。

手動ルーティング

HTTPメソッドでのルーティング

app/Config/Routes.php にルートを設定します。

GETメソッドの場合は、$routes->get() を使用します。

use App\Controllers\News;

$routes->get('news', [News::class, 'index']);

上記は、http://localhost:8080/news にブラウザからアクセスすると、News コントローラの index() メソッドが呼び出されます。

POSTメソッドの場合は、$routes->post() を使用します。

$routes->post('news/create', [News::class, 'create']);

上記は、http://localhost:8080/news/create にPOSTすると、News コントローラの create() メソッドが呼び出されます。

URLの一部をキャプチャ

(:segment)(:any) などのプレースホルダーを使います。
(:segment) は1つのURIセグメント、(:any) は任意の文字列にマッチします。

$routes->get('news/(:segment)', [News::class, 'view']);

上記は、/news/foo の場合に、News コントローラの view() メソッドに foo を渡します。

なお、上記は、以下のように書くこともできます。

$routes->get('news/(:segment)', '\App\Controllers\News::view/$1');

グループ化

ルートをグループ化することもできます。

$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 のルートを設定しています。

ルートの確認

spark routes コマンドでルートを確認できます。上に表示されたルートが優先されます。

$ php spark routes

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

📕自動ルーティング(改善)

CodeIgniter v4.1.x までデフォルトだった自動ルーティングは、あまりにも簡単に脆弱性を作り込んでしまうため、
v4.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://localhost:8080/{コントローラ名}/{メソッド名(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://localhost:8080/news/create にGETメソッドでリクエストした場合、NewsコントローラのgetCreate()メソッドが実行される
  • メソッド名が省略されたURLの場合、デフォルトメソッド(デフォルトではindex)があれば実行される
    • http://localhost:8080/news にGETメソッドでリクエストした場合、NewsコントローラのgetIndex()メソッドが実行される
  • トップページへのリクエストの場合、デフォルトコントローラ(デフォルトでは Home)が実行される
    • http://localhost:8080/ にGETメソッドでリクエストした場合、HomeコントローラのgetIndex()メソッドが実行される
  • デフォルトコントローラとデフォルトメソッドはURLの中で必ず省略する必要がある
    • http://localhost:8080/ にはアクセス可能だが http://localhost:8080/home には 404 が返る
    • http://localhost:8080/news にはアクセス可能だが http://localhost:8080/news/index には 404 が返る
  • メソッドのパラメータ数がチェックされる
    • パラメータ数が一致しない場合は 404 が返る
  • 手動ルーティングで定義されているコントローラには一切アクセスできない
    • 手動ルーティングするコントローラと自動ルーティングするコントローラは厳密に区別される
    • News::index() へのルートが手動ルーティングで設定されている場合、News::getCreate() メソッドを追加してnews/create にアクセスしても 404 が返る
  • _remap() メソッドはサポートされない

コントローラ

📕コントローラー

コントローラの作成

sparkコマンドで作成できます。

$ php spark make:controller {コントローラクラス名}

以下のようなファイルが作成されます。

app/Controllers/Blog.php

<?php

namespace App\Controllers;

use App\Controllers\BaseController;

class Blog extends BaseController
{
    public function index()
    {
        //
    }
}

コントローラは、BaseController を継承します。
$this->request(Requestオブジェクト) と $this->response(Responseオブジェクト)が使えます。

リクエストパラメータの取得

📕入力の取得

$this->request->getPost()$this->request->getGet()$this->request->getJsonVar() を使います。

$this->request->getPost('title')

上記は、POSTされた title の値を取得します。

バリデーション

📕$this->validateData()

$this->validateData() を使います。

$data = [
    'title' => $this->request->getPost('title'),
    'body'  => $this->request->getPost('body'),
];

$rules = [
    'title' => ['label' => 'タイトル', 'rules' => ['required', 'max_length[100]']],
    'body'  => ['label' => '本文', 'rules' => 'required'],
];

if ($this->request->is('post') && $this->validateData($data, $rules)) {
    // 検証パス
} else {
    // 検証エラー
}

CodeIgniter v4.3.0から、$this->request->is() メソッドでHTTPメソッドを判定できます。

エラーメッセージの上書き

$this->validateData() の第3引数にエラーメッセージを指定します。

if ($this->request->is('post') && $this->validateData(
    $data,
    $rules,
    [
        'title' => [
            'required' => 'タイトルは必須です。',
        ],
    ]
)) {
    // 検証パス
}

独自の検証ルールの作成

📕カスタムルールの作成

app/Config/Validation.php$ruleSets に検証ルールを実装したクラス名を追加します。

public $ruleSets = [
    Rules::class,
    FormatRules::class,
    FileRules::class,
    CreditCardRules::class,
    \App\Libraries\Validation\MyRules::class,
];

検証ルールを実装したクラスは以下のようになり、バリデーションルール even が使えるようになります。

namespace App\Libraries\Validation;

class MyRules
{
    public function even($str, string &$error = null): bool
    {
        if ((int) $str % 2 !== 0) {
            $error = 'エラーメッセージ。';
    
            return false;
        }
    
        return true;
    }
}

クロージャルールの作成

CodeIgniter v4.3.0から、一箇所でしか使わないルールはクロージャで定義することもできます。

$rules = [
    'foo' => [
        'required',
        static function ($value, $data, &$error, $field) {
            if ((int) $value % 2 === 0) {
                return true;
            }
    
            $error = 'エラーメッセージ。';
    
            return false;
        },
    ],
];

if ($this->request->is('post') && $this->validateData($data, $rules)) {
    // 検証パス
}

モデルの呼び出し

📕model() 関数を使います。

$model = model(UserModel::class);

ビューのレンダリング

📕view() 関数を使います。

return view('pages/view_filename', $data);

第1引数はビューファイル名、第2引数はビューに渡すデータの配列です。
ビューファイル名、app/Views/ 以下のファイルパスを .php を除いて指定します。

ログの出力

📕log_message() 関数を使います。

log_message('debug', 'デバッグ用のログメッセージ。');

ログファイルは、writable/logs/ に日付別に作成されます。

リダイレクト

📕redirect() 関数を使います。

return redirect()->to('home/index');

リダイレクトさせるには、RedirectResponse オブジェクトを返す必要があります。

API

📕RESTfulなリソース処理

ルーティング

app/Config/Routes.php

$routes->resource('photos');

上記を設定すると、以下のルートが設定されたことになります。

$routes->get('photos/new',             'Photos::new');
$routes->post('photos',                'Photos::create');
$routes->get('photos',                 'Photos::index');
$routes->get('photos/(:segment)',      'Photos::show/$1');
$routes->get('photos/(:segment)/edit', 'Photos::edit/$1');
$routes->put('photos/(:segment)',      'Photos::update/$1');
$routes->patch('photos/(:segment)',    'Photos::update/$1');
$routes->delete('photos/(:segment)',   'Photos::delete/$1');

不要なメソッドがあれば、除外指定します。

$routes->resource('photos', ['except' => 'new,edit']);

コントローラ

ResourceController を継承します。

<?php

namespace App\Controllers;

use App\Models\PhotoModel;
use CodeIgniter\RESTful\ResourceController;

class Photos extends ResourceController
{
    protected $modelName = PhotoModel::class;
    protected $format    = 'json';

    public function index()
    {
        return $this->respond($this->model->findAll());
    }

    // ...
}

ResponseTrait に実装されている $this->respond() などのメソッドが使えるようになります。
上記のコードでは、モデルを検索した結果がJSONで返されます。

ビュー

📕ビュー

デフォルトでは、ビューファイルは普通のPHPファイルです。

個人的には、出力時に自動でエスケープ処理してくれるTwigなどのテンプレートエンジンかView Parserの使用を推奨します。

デフォルトのView

コントローラから渡された配列が、 キーを変数名とした変数に設定されます。

変数を出力する場合は、忘れずに esc() 関数でエスケープします。

View Parser

📕ビューパーサー

テンプレートエンジンです。変数の値の自動HTMLエスケープフィルター機能も提供します。

コントローラから渡された配列の値が {blog_title} のような疑似変数に表示されます。
値は自動的にHTMLエスケープされます。

{ } はJavaScriptと相性が悪いので、設定で {{ }} に変更することをお薦めします。

モデル

CodeIgniter\Model が標準で提供されていますが、使わなくても構いません。
データベース操作が必要な場合、 最小限のモデルクラスは以下のようになります。

app/Models/UserModel.php

<?php

namespace App\Models;

use CodeIgniter\Database\ConnectionInterface;

class UserModel
{
    protected $db;

    public function __construct(ConnectionInterface $db)
    {
        $this->db = $db;
    }
}

$db はインスタンス化する時に自分で注入してください。

モデルの作成

以下のようなファイルを作成します。

app/Models/UserModel.php

<?php

namespace App\Models;

use CodeIgniter\Model;

class UserModel extends Model
{
    protected $table = 'users';
}

CodeIgniter\Model を継承すると、いろいろ便利な機能が使えます。

クエリビルダー

📕クエリビルダークラス

ビルダーの生成

db_connect() 関数で「データベース接続」オブジェクトを取得できます。

$db = db_connect();

データベース接続オブジェクトの table() メソッドにテーブル名を指定すると、新しいクエリビルダーを取得できます。

$builder = $db->table('users');

SELECT

$query = $builder->where('name !=', $name)
    ->where('id <', $id)
    ->orderBy('name', 'ASC')
    ->get();

foreach ($query->getResult() as $row) {
    echo $row->title;
}

CodeIgniter3との違いは、テーブル名をビルダー生成時に指定することとと、メソッド名がcamelCaseに変わった程度です。

INSERT

insert() メソッドを使います。

$data = [
    'title' => 'My title',
    'name'  => 'My Name',
    'date'  => 'My date',
];
$builder->insert($data);

UPDATE

update() メソッドを使います。

$data = [
    'title' => $title,
    'name'  => $name,
    'date'  => $date,
];
$builder->where('id', $id);
$builder->update($data);

DELETE

delete() メソッドを使います。

$builder->delete(['id' => $id]);

where() メソッドでレコードを指定することもできます。

$builder->where('id', $id);
$builder->delete();

CodeIgniter\Model

📕CodeIgniterのモデルの使用

以下のような機能があります。

  • 自動データベース接続
  • 基本的なCRUDメソッド
  • 自動ページネーション
  • 論理削除

データの検索

📕データの検索

find()

プライマリーキーでレコードを検索します。

$user  = $userModel->find($id);
$users = $userModel->find([1, 2, 3]);

findAll()

レコードを検索します。

$users = $userModel->findAll();
$users = $userModel->where('active', 1)->findAll();
$users = $userModel->findAll($limit, $offset);

データの保存

📕データの保存

insert()

レコードを挿入します。

$data = [
    'username' => 'darth',
    'email'    => 'd.vader@example.com',
];
$userModel->insert($data);

update()

プライマリーキーを指定してレコードを更新します。

$data = [
    'username' => 'darth',
    'email'    => 'd.vader@example.com',
];
$userModel->update($id, $data);
$data = [
    'active' => 1,
];
$userModel->update([1, 2, 3], $data);

WHERE句を指定してレコードを更新します。

$userModel->whereIn('id', [1, 2, 3])->set(['active' => 1])->update();

save()

プライマリーキーがない場合は挿入します。

$data = [
    'username' => 'darth',
    'email'    => 'd.vader@example.com',
];
$userModel->save($data);

プライマリーキーがある場合は更新します。

$data = [
    'id'       => 3,
    'username' => 'darth',
    'email'    => 'd.vader@example.com',
];
$userModel->save($data);

データの削除

📕データの削除

delete()

プライマリーキーを指定してレコードを削除します。

$userModel->delete(12);
$userModel->delete([1, 2, 3]);

WHERE句を指定してレコードを削除します。

$userModel->where('id', 12)->delete();

Entityクラス

📕エンティティクラスの使用

テーブルのレコードを表すクラスです。POPOではありません。使わなくても構いません。

app/Entities/User.php

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    // ...
}

CodeIgniter\Model では、$returnType プロパティに指定すると、検索結果がこのクラスのインスタンスになります。

    protected $returnType = \App\Entities\User::class;

クエリビルダーでも getResult() メソッドで指定すれば使えます。

$query->getResult(\App\Entities\User::class);

認証

ユーザガイドの 📕認証 に推奨事項が記載されています。

執筆時点で上記を満たす推奨できるパッケージは codeigniter4/shieldmyth/auth 以外ありません。基本的に公式パッケージである codeigniter4/shield を使うことを推奨します。

セッション

📕セッションライブラリ

以下でセッションオブジェクトが取得できます。

$session = session();

セッションの読み書き

書き込み

1アイテムを書き込む場合。

$session->set('some_name', 'some_value');

まとめて書き込む場合。

$newdata = [
    'username'  => 'johndoe',
    'email'     => 'johndoe@example.com',
    'logged_in' => true,
];
$session->set($newdata);

読み込み

$session->get('some_name');

削除

1アイテムを削除する場合。

$session->remove('some_name');

まとめて削除する場合。

$items = ['username', 'email'];
$session->remove($items);

参考

Discussion