CodeIgniter 4 とは?最新バージョンの特徴と強み
軽量で高速なPHPフレームワークの代表格
CodeIgniter 4(以下、CI4)は、シンプルながらパワフルな機能を備えたPHPフレームワークとして知られています。特に以下の特徴が、多くの開発者から支持されている理由です:
- メモリ効率の高さ
- フレームワークコアが約2MB未満と軽量
- 最小限の依存パッケージで動作
- 効率的なメモリ管理システム
- 処理速度の優位性
// パフォーマンス計測例 use CodeIgniter\Debug\Timer; $timer = new Timer(); $timer->start('benchmark'); // アプリケーションコード $result = $model->find(); $timer->stop('benchmark'); echo $timer->getElapsedTime('benchmark'); // 一般的なフレームワークの1.5-2倍の速度
- シンプルなアーキテクチャ
- 直感的なMVCパターン
- 明確なディレクトリ構造
- 最小限の設定で開始可能
バージョン4で劇的に進化した新機能と改善点
- モダンPHPの完全採用
namespace App\Controllers; use CodeIgniter\Controller; use App\Models\UserModel; class Users extends Controller { protected $userModel; public function __construct() { $this->userModel = new UserModel(); } public function index() { return view('users/index', [ 'users' => $this->userModel->findAll() ]); } }
- 主要な機能強化
- PSR-4準拠の名前空間対応
- Composerによる依存管理
- 強化されたデータベース抽象化層
- 新しいセキュリティ機能の導入
- 開発効率を向上させる新機能
- 充実したCLIツール
- エンティティクラスのサポート
- 高度なルーティングシステム
- キャッシュシステムの刷新
他のPHPフレームワークと比較したメリット
- 開発生産性の比較
機能 | CodeIgniter 4 | Laravel | Symfony |
---|---|---|---|
初期学習時間 | 1-2週間 | 1-2ヶ月 | 2-3ヶ月 |
設定の複雑さ | 低 | 中 | 高 |
ドキュメント充実度 | ◎ | ◎ | ○ |
日本語資料 | ○ | ◎ | △ |
- パフォーマンス面での優位性
- 最小構成時のメモリ使用量:約4MB
- リクエスト処理時間:平均30%削減
- アプリケーション起動時間:Laravel比約50%減
- プロジェクトに最適な選択基準
- スピーディーな開発が求められるプロジェクト
- リソースに制約のある環境での開発
- APIサーバーの構築
- 中小規模のWebアプリケーション
- フレームワークの柔軟性
- 最小限の制約
- 容易なカスタマイズ
- サードパーティライブラリとの統合のしやすさ
以上の特徴から、CI4は特に「高速な開発」と「高いパフォーマンス」が求められるプロジェクトにおいて、最適な選択肢の一つとなっています。
CodeIgniter 4 の環境構築手順
必要な開発環境とシステム要件
CodeIgniter 4を快適に動作させるために、以下のシステム要件を満たす必要があります:
- PHP要件
- PHP バージョン 7.4以上(8.0以上推奨)
- 必須PHP拡張機能:
- intl
- json
- mbstring
- mysqlnd(MySQLを使用する場合)
- xml
- curl
- データベース要件(選択可能)
- MySQL (5.1+)
- PostgreSQL (7.4+)
- SQLite3
- Microsoft SQL Server (2005+)
- Webサーバー要件
- Apache 2.4+ with mod_rewrite
- Nginx 1.13+
# Nginxの基本設定例 location / { try_files $uri $uri/ /index.php$is_args$args; }
- 推奨開発ツール
- Composer(パッケージ管理)
- Git(バージョン管理)
- VS Code / PHPStorm(IDE)
Composer を使用したインストール方法
- プロジェクトの新規作成
# プロジェクト作成コマンド composer create-project codeigniter4/appstarter ci4-project # プロジェクトディレクトリへ移動 cd ci4-project # 開発サーバーの起動 php spark serve
- 既存プロジェクトへの導入
# Composerを使用してCodeIgniter 4をインストール composer require codeigniter4/framework # 設定ファイルとpublicディレクトリの準備 php spark install:config
- インストール後の確認
# バージョン確認 php spark --version # 環境チェック php spark env:check # ルートの確認 php spark routes
初期設定と基本的な環境設定のポイント
- 環境設定ファイルの準備
# .envファイルの作成 cp env .env # 環境設定の編集 # CI_ENVIRONMENT = development # app.baseURL = 'http://localhost:8080/'
- データベース設定
// app/Config/Database.php public $default = [ 'DSN' => '', 'hostname' => 'localhost', 'username' => 'your_username', 'password' => 'your_password', 'database' => 'your_database', 'DBDriver' => 'MySQLi', 'DBPrefix' => '', 'pConnect' => false, 'DBDebug' => true, 'charset' => 'utf8', 'DBCollat' => 'utf8_general_ci', 'swapPre' => '', 'encrypt' => false, 'compress' => false, 'strictOn' => false, 'failover' => [], 'port' => 3306, ];
- セキュリティ設定
// app/Config/Security.php public $tokenName = 'csrf_token_name'; public $headerName = 'X-CSRF-TOKEN'; public $cookieName = 'csrf_cookie_name'; public $expires = 7200;
- アプリケーション基本設定
// app/Config/App.php public $baseURL = 'http://localhost:8080/'; public $indexPage = ''; public $defaultLocale = 'ja'; public $negotiateLocale = true; public $supportedLocales = ['en', 'ja'];
- エラー報告とロギング設定
// app/Config/Logger.php public $threshold = 4; // 開発環境では4(全てのログを記録) // ログハンドラの設定 public $handlers = [ 'file' => [ 'class' => 'CodeIgniter\Log\Handlers\FileHandler', 'baseDir' => WRITEPATH . 'logs/', 'filePermission' => 0644, 'fileExtension' => 'log' ] ];
- 開発環境特有の設定
// 開発時の推奨設定 error_reporting(-1); ini_set('display_errors', '1');
以上の設定が完了すれば、CodeIgniter 4での開発を開始する準備が整います。環境構築時に特に注意が必要な点は以下の通りです:
- 本番環境では必ず
CI_ENVIRONMENT
をproduction
に設定 - セキュリティ関連の設定は本番環境で再確認
- データベース接続情報は環境ごとに適切に管理
- ログレベルは環境に応じて適切に設定
これらの設定を適切に行うことで、安全で効率的な開発環境を構築することができます。
CodeIgniter 4 の基本アーキテクチャ理解
MVCパターンの実装方法と特徴
CodeIgniter 4のMVCパターンは、シンプルながら強力な実装を提供しています。
- コントローラーの基本実装
namespace App\Controllers; use CodeIgniter\Controller; use App\Models\UserModel; class Users extends Controller { protected $userModel; public function __construct() { // モデルのインスタンス化 $this->userModel = new UserModel(); } public function index() { $data = [ 'users' => $this->userModel->findAll(), 'title' => 'ユーザー一覧' ]; // ビューの読み込みと表示 return view('users/index', $data); } public function show($id) { $user = $this->userModel->find($id); if ($user === null) { throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); } return view('users/show', ['user' => $user]); } }
- モデルの実装例
namespace App\Models; use CodeIgniter\Model; class UserModel extends Model { protected $table = 'users'; protected $primaryKey = 'id'; protected $returnType = 'array'; protected $allowedFields = ['name', 'email', 'password']; // バリデーションルール protected $validationRules = [ 'name' => 'required|min_length[3]', 'email' => 'required|valid_email|is_unique[users.email]', 'password' => 'required|min_length[8]' ]; // カスタムコールバック protected $beforeInsert = ['hashPassword']; protected function hashPassword(array $data) { if (isset($data['data']['password'])) { $data['data']['password'] = password_hash($data['data']['password'], PASSWORD_DEFAULT); } return $data; } }
- ビューの実装方法
<!-- app/Views/users/index.php --> <?= $this->extend('layouts/main') ?> <?= $this->section('content') ?> <h1><?= esc($title) ?></h1> <div class="user-list"> <?php foreach ($users as $user): ?> <div class="user-item"> <h3><?= esc($user['name']) ?></h3> <p><?= esc($user['email']) ?></p> <a href="/users/<?= $user['id'] ?>">詳細を見る</a> </div> <?php endforeach; ?> </div> <?= $this->endSection() ?>
ルーティングの設定と活用テクニック
- 基本的なルーティング設定
// app/Config/Routes.php $routes->get('/', 'Home::index'); $routes->get('users', 'Users::index'); $routes->get('users/(:num)', 'Users::show/$1'); // RESTfulリソースルート $routes->resource('api/users'); // グループ化したルート $routes->group('admin', ['filter' => 'auth'], function($routes) { $routes->get('users', 'Admin\Users::index'); $routes->get('users/new', 'Admin\Users::new'); $routes->post('users', 'Admin\Users::create'); });
- カスタムルートフィルターの実装
// app/Filters/AuthFilter.php namespace App\Filters; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\Filters\FilterInterface; class AuthFilter implements FilterInterface { public function before(RequestInterface $request, $arguments = null) { if (!session()->get('logged_in')) { return redirect()->to('/login'); } } public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) { // アクション後の処理 } }
依存性注入とサービスの活用方法
- サービスクラスの作成
namespace App\Services; class UserService { protected $userModel; protected $emailService; public function __construct(UserModel $userModel, EmailService $emailService) { $this->userModel = $userModel; $this->emailService = $emailService; } public function registerUser(array $userData) { // トランザクション処理 $db = \Config\Database::connect(); $db->transStart(); try { // ユーザー登録 $userId = $this->userModel->insert($userData); // ウェルカムメール送信 $this->emailService->sendWelcomeEmail($userData['email']); $db->transComplete(); return $userId; } catch (\Exception $e) { $db->transRollback(); throw $e; } } }
- Services.phpでのサービス登録
// app/Config/Services.php public static function userService($getShared = true) { if ($getShared) { return static::getSharedInstance('userService'); } return new \App\Services\UserService( model('UserModel'), service('email') ); }
- コントローラーでのサービス利用
class RegistrationController extends Controller { protected $userService; public function __construct() { $this->userService = service('userService'); } public function register() { try { $userData = $this->request->getPost(); $userId = $this->userService->registerUser($userData); return redirect()->to('/login') ->with('success', '登録が完了しました'); } catch (\Exception $e) { return redirect()->back() ->with('error', '登録に失敗しました') ->withInput(); } } }
これらのアーキテクチャパターンを適切に活用することで、保守性が高く、拡張性のあるアプリケーションを構築することが可能です。特に、依存性注入を活用することで、テスタビリティの向上やコンポーネント間の結合度の低減を実現できます。
データベースオペレーションとモデルの実践的な使い方
モデルクラスを使用したCRUDオペレーションの実装
- モデルの基本設定と実装
namespace App\Models; use CodeIgniter\Model; class ProductModel extends Model { protected $table = 'products'; protected $primaryKey = 'id'; protected $useAutoIncrement = true; protected $returnType = 'array'; protected $useSoftDeletes = true; protected $allowedFields = [ 'name', 'description', 'price', 'category_id', 'stock', 'status' ]; // 日付関連のフィールド protected $useTimestamps = true; protected $dateFormat = 'datetime'; protected $createdField = 'created_at'; protected $updatedField = 'updated_at'; protected $deletedField = 'deleted_at'; // バリデーションルール protected $validationRules = [ 'name' => 'required|min_length[3]|max_length[255]', 'price' => 'required|numeric|greater_than[0]', 'category_id' => 'required|integer|is_not_unique[categories.id]', 'stock' => 'required|integer|greater_than_equal_to[0]' ]; protected $validationMessages = [ 'name' => [ 'required' => '商品名は必須です', 'min_length' => '商品名は3文字以上で入力してください' ], 'price' => [ 'required' => '価格は必須です', 'numeric' => '価格は数値で入力してください', 'greater_than' => '価格は0より大きい値を入力してください' ] ]; }
- CRUD操作の実装例
class ProductController extends BaseController { protected $productModel; public function __construct() { $this->productModel = new ProductModel(); } // Create操作 public function create() { $data = [ 'name' => $this->request->getPost('name'), 'price' => $this->request->getPost('price'), 'category_id' => $this->request->getPost('category_id'), 'stock' => $this->request->getPost('stock') ]; try { $this->productModel->insert($data); return redirect()->to('/products') ->with('success', '商品が登録されました'); } catch (\Exception $e) { return redirect()->back() ->with('error', '商品の登録に失敗しました') ->withInput(); } } // Read操作(一覧取得) public function index() { $data['products'] = $this->productModel ->select('products.*, categories.name as category_name') ->join('categories', 'categories.id = products.category_id') ->paginate(10); $data['pager'] = $this->productModel->pager; return view('products/index', $data); } // Update操作 public function update($id) { $data = $this->request->getPost(); try { $this->productModel->update($id, $data); return redirect()->to('/products') ->with('success', '商品が更新されました'); } catch (\Exception $e) { return redirect()->back() ->with('error', '商品の更新に失敗しました'); } } // Delete操作(ソフトデリート) public function delete($id) { try { $this->productModel->delete($id); return redirect()->to('/products') ->with('success', '商品が削除されました'); } catch (\Exception $e) { return redirect()->back() ->with('error', '商品の削除に失敗しました'); } } }
クエリビルダの効果的な活用方法
- 高度な検索条件の実装
public function search() { $builder = $this->productModel->builder(); // 検索条件の構築 if ($category = $this->request->getGet('category')) { $builder->where('category_id', $category); } if ($minPrice = $this->request->getGet('min_price')) { $builder->where('price >=', $minPrice); } if ($maxPrice = $this->request->getGet('max_price')) { $builder->where('price <=', $maxPrice); } if ($keyword = $this->request->getGet('keyword')) { $builder->groupStart() ->like('name', $keyword) ->orLike('description', $keyword) ->groupEnd(); } // 在庫状況でのフィルタリング if ($this->request->getGet('in_stock') === 'true') { $builder->where('stock >', 0); } return $builder->select('products.*, categories.name as category_name') ->join('categories', 'categories.id = products.category_id') ->orderBy('products.created_at', 'DESC') ->paginate(10); }
- 集計クエリの実装
public function getProductStats() { $builder = $this->productModel->builder(); return $builder->select(' COUNT(*) as total_products, SUM(stock) as total_stock, AVG(price) as average_price, MIN(price) as min_price, MAX(price) as max_price ')->get()->getRow(); }
マイグレーションとシーディングの運用手法
- マイグレーションファイルの作成と実装
namespace App\Database\Migrations; use CodeIgniter\Database\Migration; class CreateProductsTable extends Migration { public function up() { $this->forge->addField([ 'id' => [ 'type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true ], 'name' => [ 'type' => 'VARCHAR', 'constraint' => 255 ], 'description' => [ 'type' => 'TEXT', 'null' => true ], 'price' => [ 'type' => 'DECIMAL', 'constraint' => '10,2' ], 'category_id' => [ 'type' => 'INT', 'constraint' => 11, 'unsigned' => true ], 'stock' => [ 'type' => 'INT', 'constraint' => 11, 'default' => 0 ], 'status' => [ 'type' => 'ENUM', 'constraint' => ['active', 'inactive'], 'default' => 'active' ], 'created_at' => [ 'type' => 'DATETIME', 'null' => true ], 'updated_at' => [ 'type' => 'DATETIME', 'null' => true ], 'deleted_at' => [ 'type' => 'DATETIME', 'null' => true ] ]); $this->forge->addKey('id', true); $this->forge->addForeignKey('category_id', 'categories', 'id', 'CASCADE', 'CASCADE'); $this->forge->createTable('products'); } public function down() { $this->forge->dropTable('products'); } }
- シーダーの実装
namespace App\Database\Seeds; use CodeIgniter\Database\Seeder; class ProductSeeder extends Seeder { public function run() { $data = [ [ 'name' => 'サンプル商品1', 'description' => '商品の説明文です。', 'price' => 1000, 'category_id' => 1, 'stock' => 100, 'status' => 'active', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], // 他のサンプルデータ... ]; $this->db->table('products')->insertBatch($data); } }
- マイグレーションとシーディングの実行コマンド
# マイグレーションの実行 php spark migrate # 特定のマイグレーションの実行 php spark migrate --group=products # マイグレーションのロールバック php spark migrate:rollback # シーディングの実行 php spark db:seed ProductSeeder
これらの実装パターンを活用することで、効率的なデータベース操作と保守性の高いアプリケーション開発が可能になります。特に、マイグレーションとシーディングを活用することで、開発環境とテスト環境の一貫性を保ちやすくなります。
セキュリティ対策と実務での注意点
CSRFプロテクションの実装方法
- 基本的なCSRF対策の設定
// app/Config/Security.php public $csrfProtection = 'cookie'; public $tokenName = 'csrf_token_name'; public $cookieName = 'csrf_cookie_name'; public $expires = 7200;
- フォームでのCSRFトークンの実装
<!-- ビューファイルでの実装 --> <form method="post" action="/users/create"> <?= csrf_field() ?> <input type="text" name="username"> <input type="password" name="password"> <button type="submit">登録</button> </form>
- APIでのCSRF対策
// app/Filters/APICSRFFilter.php namespace App\Filters; use CodeIgniter\Filters\FilterInterface; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; class APICSRFFilter implements FilterInterface { public function before(RequestInterface $request, $arguments = null) { // APIトークンの検証 $token = $request->getHeaderLine('X-CSRF-TOKEN'); if (!$token || !csrf_verify($token)) { return service('response') ->setStatusCode(403) ->setJSON(['error' => 'Invalid CSRF token']); } } public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) { } }
XSS対策とバリデーションの設定
- 入力バリデーションの実装
// app/Models/UserModel.php protected $validationRules = [ 'username' => [ 'rules' => 'required|min_length[4]|max_length[20]|alpha_numeric', 'errors' => [ 'required' => 'ユーザー名は必須です', 'min_length' => 'ユーザー名は4文字以上必要です', 'alpha_numeric' => '英数字のみ使用可能です' ] ], 'email' => [ 'rules' => 'required|valid_email|is_unique[users.email]', 'errors' => [ 'is_unique' => 'このメールアドレスは既に登録されています' ] ], 'password' => [ 'rules' => 'required|min_length[8]|containsSpecialCharacter[1]|containsNumber[1]', 'errors' => [ 'containsSpecialCharacter' => '特殊文字を1文字以上含める必要があります', 'containsNumber' => '数字を1文字以上含める必要があります' ] ] ]; // カスタムバリデーションルール public function containsSpecialCharacter(string $str, string $fields, array $data): bool { return preg_match('/[^a-zA-Z0-9]/', $str) > 0; }
- XSS対策の実装
// コントローラーでの出力エスケープ public function show($id) { $user = $this->userModel->find($id); return view('users/show', [ 'user' => [ 'name' => esc($user['name']), 'bio' => esc($user['bio'], 'html'), 'website' => esc($user['website'], 'url') ] ]); } // ビューでの安全な出力 <div class="user-profile"> <h1><?= esc($user['name']) ?></h1> <div class="bio"><?= esc($user['bio'], 'html') ?></div> <a href="<?= esc($user['website'], 'url') ?>">ウェブサイト</a> </div>
セッション管理とユーザー認証の実装
- セッション設定
// app/Config/Session.php public $driver = 'CodeIgniter\Session\Handlers\FileHandler'; public $cookieName = 'ci_session'; public $expiration = 7200; public $savePath = WRITEPATH . 'session'; public $matchIP = false; public $timeToUpdate = 300;
- 認証システムの実装
// app/Libraries/Auth.php namespace App\Libraries; class Auth { protected $session; protected $userModel; public function __construct() { $this->session = service('session'); $this->userModel = new \App\Models\UserModel(); } public function attempt(string $email, string $password): bool { $user = $this->userModel->where('email', $email)->first(); if (!$user) { return false; } if (!password_verify($password, $user['password'])) { // 失敗回数のカウントと制限 $this->incrementLoginAttempts($email); return false; } // セッションの再生成(セッション固定攻撃対策) $this->session->regenerate(); $this->session->set([ 'user_id' => $user['id'], 'user_email' => $user['email'], 'isLoggedIn' => true, 'last_activity' => time() ]); return true; } protected function incrementLoginAttempts(string $email): void { $attempts = cache()->get('login_attempts_' . md5($email)) ?? 0; cache()->save('login_attempts_' . md5($email), ++$attempts, 300); if ($attempts >= 5) { throw new \Exception('Too many login attempts. Please try again later.'); } } public function check(): bool { if (!$this->session->get('isLoggedIn')) { return false; } // セッションの有効期限チェック $lastActivity = $this->session->get('last_activity'); if (time() - $lastActivity > config('Session')->expiration) { $this->session->destroy(); return false; } // セッションの更新 $this->session->set('last_activity', time()); return true; } public function logout(): void { $this->session->destroy(); } }
- 認証フィルターの実装
// app/Filters/AuthFilter.php namespace App\Filters; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\Filters\FilterInterface; class AuthFilter implements FilterInterface { public function before(RequestInterface $request, $arguments = null) { $auth = service('auth'); if (!$auth->check()) { // APIリクエストの場合 if ($request->isAJAX()) { return service('response') ->setStatusCode(401) ->setJSON(['error' => 'Unauthorized']); } // 通常のリクエストの場合 return redirect() ->to('/login') ->with('error', 'ログインが必要です'); } } public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) { } }
これらのセキュリティ対策を実装することで、一般的な攻撃からアプリケーションを保護することができます。ただし、セキュリティは常に最新の脅威に対応する必要があるため、定期的な見直しと更新が重要です。
実践的なアプリケーション開発手法
RESTful APIの構築方法
- RESTful APIコントローラーの実装
namespace App\Controllers\API; use CodeIgniter\RESTful\ResourceController; use CodeIgniter\API\ResponseTrait; class Products extends ResourceController { use ResponseTrait; protected $modelName = 'App\Models\ProductModel'; protected $format = 'json'; // GET /api/products public function index() { $products = $this->model->findAll(); return $this->respond($products); } // GET /api/products/{id} public function show($id = null) { $product = $this->model->find($id); if ($product === null) { return $this->failNotFound('商品が見つかりません'); } return $this->respond($product); } // POST /api/products public function create() { $data = $this->request->getJSON(); if (!$this->validate($this->model->validationRules)) { return $this->failValidationErrors($this->validator->getErrors()); } $id = $this->model->insert($data); if ($id === false) { return $this->failServerError('商品の登録に失敗しました'); } $product = $this->model->find($id); return $this->respondCreated($product); } // PUT /api/products/{id} public function update($id = null) { $data = $this->request->getJSON(); if (!$this->model->find($id)) { return $this->failNotFound('更新対象の商品が見つかりません'); } if (!$this->validate($this->model->validationRules)) { return $this->failValidationErrors($this->validator->getErrors()); } if ($this->model->update($id, $data) === false) { return $this->failServerError('商品の更新に失敗しました'); } return $this->respond(['message' => '商品が更新されました']); } // DELETE /api/products/{id} public function delete($id = null) { if (!$this->model->find($id)) { return $this->failNotFound('削除対象の商品が見つかりません'); } if ($this->model->delete($id) === false) { return $this->failServerError('商品の削除に失敗しました'); } return $this->respondDeleted(['message' => '商品が削除されました']); } }
- APIフィルターの実装
namespace App\Filters; use CodeIgniter\Filters\FilterInterface; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; class APIAuthFilter implements FilterInterface { public function before(RequestInterface $request, $arguments = null) { $apiKey = $request->getHeaderLine('X-API-Key'); if (!$this->validateApiKey($apiKey)) { return service('response') ->setStatusCode(401) ->setJSON([ 'status' => 401, 'error' => '無効なAPIキーです' ]); } } protected function validateApiKey($apiKey) { // APIキーの検証ロジック return true; // 実際の実装では適切な検証を行う } public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) { } }
キャッシュ機能の効果的な活用
- キャッシュの基本設定
// app/Config/Cache.php public $handler = 'file'; public $backupHandler = 'dummy'; public $prefix = ''; public $ttl = 60; public $reservedCharacters = '{}()/\@:'; // Redisハンドラーの設定例 public $redis = [ 'host' => '127.0.0.1', 'password' => null, 'port' => 6379, 'timeout' => 0, 'database' => 0, ];
- キャッシュの実践的な活用例
class ProductController extends BaseController { public function index() { $cache = \Config\Services::cache(); $cacheKey = 'products_list_' . md5(json_encode($this->request->getGet())); // キャッシュの取得を試みる if ($products = $cache->get($cacheKey)) { return $this->response->setJSON($products); } // キャッシュがない場合はデータを取得 $products = $this->productModel ->select('products.*, categories.name as category_name') ->join('categories', 'categories.id = products.category_id') ->findAll(); // キャッシュに保存(5分間) $cache->save($cacheKey, $products, 300); return $this->response->setJSON($products); } // キャッシュの手動クリア protected function clearProductCache() { $cache = \Config\Services::cache(); $cache->deleteMatching('products_list_*'); } }
ユニットテストとデバッグの進め方
- テストクラスの実装
namespace App\Tests; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use CodeIgniter\Test\FeatureTestTrait; class ProductTest extends CIUnitTestCase { use DatabaseTestTrait; use FeatureTestTrait; protected $refresh = true; protected $seed = 'ProductSeeder'; public function testCreate() { $data = [ 'name' => 'テスト商品', 'price' => 1000, 'category_id' => 1, 'stock' => 100 ]; $result = $this->withHeaders([ 'X-API-Key' => 'your-api-key' ])->post('api/products', $data); $result->assertStatus(201) ->assertJSONFragment(['name' => 'テスト商品']); } public function testInvalidCreate() { $data = [ 'name' => '', // 空の名前(バリデーションエラー) 'price' => -100, // 負の価格 ]; $result = $this->withHeaders([ 'X-API-Key' => 'your-api-key' ])->post('api/products', $data); $result->assertStatus(400) ->assertJSONFragment(['errors']); } public function testUpdate() { $data = [ 'name' => '更新後の商品名', 'price' => 2000 ]; $result = $this->withHeaders([ 'X-API-Key' => 'your-api-key' ])->put('api/products/1', $data); $result->assertStatus(200) ->assertJSONFragment(['message' => '商品が更新されました']); } }
- デバッグツールの活用
// デバッグバーの設定 // app/Config/Toolbar.php public $collectors = [ \CodeIgniter\Debug\Toolbar\Collectors\Timers::class, \CodeIgniter\Debug\Toolbar\Collectors\Database::class, \CodeIgniter\Debug\Toolbar\Collectors\Logs::class, \CodeIgniter\Debug\Toolbar\Collectors\Views::class, \CodeIgniter\Debug\Toolbar\Collectors\Cache::class, \CodeIgniter\Debug\Toolbar\Collectors\Files::class, \CodeIgniter\Debug\Toolbar\Collectors\Routes::class, \CodeIgniter\Debug\Toolbar\Collectors\Events::class, ]; // コード内でのデバッグ public function debugExample() { // ログの出力 log_message('debug', 'デバッグメッセージ: {value}', ['value' => $someValue]); // 変数の内容確認 dd($variable); // die and dump // タイマーの使用 $benchmark = \Config\Services::timer(); $benchmark->start('my_timer'); // 処理 $benchmark->stop('my_timer'); echo $benchmark->getElapsedTime('my_timer'); }
これらの実践的な開発手法を適切に活用することで、保守性が高く、効率的なアプリケーション開発が可能になります。特に、テストとデバッグを重視することで、品質の高いアプリケーションを提供することができます。
CodeIgniter 4でのパフォーマンス最適化
キャッシュ戦略とその実装方法
- 階層的キャッシュの実装
class ProductService { protected $cache; protected $productModel; public function __construct() { $this->cache = \Config\Services::cache(); $this->productModel = new \App\Models\ProductModel(); } public function getProductDetails($productId) { // 第1層: メモリキャッシュ $cacheKey = "product_{$productId}"; $product = $this->cache->get($cacheKey); if ($product === null) { // 第2層: Redisキャッシュ $redis = new \Redis(); $redis->connect('127.0.0.1', 6379); $product = $redis->get($cacheKey); if ($product === null) { // 第3層: データベース $product = $this->productModel->find($productId); // キャッシュの更新 if ($product) { $redis->setex($cacheKey, 3600, serialize($product)); $this->cache->save($cacheKey, $product, 300); } } else { $product = unserialize($product); $this->cache->save($cacheKey, $product, 300); } } return $product; } }
- ビューキャッシュの活用
class CatalogController extends BaseController { public function categoryPage($categoryId) { $cacheKey = "category_page_{$categoryId}"; if (! $output = cache($cacheKey)) { $data = [ 'products' => $this->productModel ->where('category_id', $categoryId) ->findAll(), 'category' => $this->categoryModel->find($categoryId) ]; $output = view('catalog/category', $data); // ビューをキャッシュに保存(1時間) cache()->save($cacheKey, $output, 3600); } return $output; } }
データベースの最適化手法
- クエリの最適化
class OptimizedProductModel extends \CodeIgniter\Model { public function getProductsWithEfficiency($categoryId) { return $this->select('products.*, categories.name as category_name, COUNT(reviews.id) as review_count') ->join('categories', 'categories.id = products.category_id') ->join('reviews', 'reviews.product_id = products.id', 'left') ->where('products.category_id', $categoryId) ->groupBy('products.id') ->having('review_count >', 0) ->useIndex('idx_category') // インデックスの明示的な使用 ->findAll(); } // バルクインサートの実装 public function bulkInsertProducts($products, $batchSize = 100) { $chunks = array_chunk($products, $batchSize); $this->db->transStart(); try { foreach ($chunks as $chunk) { $this->insertBatch($chunk); } $this->db->transComplete(); return $this->db->transStatus(); } catch (\Exception $e) { $this->db->transRollback(); throw $e; } } }
- インデックス最適化
-- 必要なインデックスの作成 CREATE INDEX idx_category ON products(category_id); CREATE INDEX idx_price ON products(price); CREATE INDEX idx_created_at ON products(created_at); CREATE INDEX idx_status_category ON products(status, category_id); -- 複合インデックスの作成 CREATE INDEX idx_category_price ON products(category_id, price);
- データベースコネクションの最適化
// app/Config/Database.php public $default = [ 'hostname' => 'localhost', 'username' => 'your_username', 'password' => 'your_password', 'database' => 'your_database', 'DBDriver' => 'MySQLi', 'DBPrefix' => '', 'pConnect' => false, 'DBDebug' => true, 'charset' => 'utf8mb4', 'DBCollat' => 'utf8mb4_unicode_ci', 'swapPre' => '', 'encrypt' => false, 'compress' => false, 'strictOn' => false, 'failover' => [], 'port' => 3306, 'persistent' => true, // コネクションプーリングの設定 'pooling' => true, 'pool' => [ 'max_connections' => 100, 'idle_timeout' => 60, 'wait_timeout' => 30, ], ];
本番環境でのパフォーマンスチューニング
- アプリケーション設定の最適化
// app/Config/App.php public $baseURL = 'https://your-production-domain.com/'; // キャッシュの有効化 public $cache = 'file'; // エラー表示の無効化 public $displayErrors = false; // ログレベルの調整 public $logThreshold = 3; // Error以上のみ記録 // セッション設定の最適化 public $sessionDriver = 'redis'; public $sessionExpiration = 7200;
- パフォーマンスモニタリングの実装
class PerformanceMonitor { protected $timer; protected $logger; public function __construct() { $this->timer = \Config\Services::timer(); $this->logger = \Config\Services::logger(); } public function startMeasurement($point) { $this->timer->start($point); } public function endMeasurement($point) { $this->timer->stop($point); $elapsed = $this->timer->getElapsedTime($point); if ($elapsed > 1.0) { // 1秒以上かかった処理を記録 $this->logger->warning("Performance Alert: {$point} took {$elapsed} seconds"); } return $elapsed; } public function getMemoryUsage() { $memory = memory_get_usage(true); if ($memory > 64 * 1024 * 1024) { // 64MB以上使用している場合 $this->logger->warning("High Memory Usage: {$memory} bytes"); } return $memory; } } // 使用例 $monitor = new PerformanceMonitor(); $monitor->startMeasurement('database_query'); // データベースクエリの実行 $monitor->endMeasurement('database_query');
- 本番環境向けの.envファイル設定
# プロダクション環境設定 CI_ENVIRONMENT = production # データベース設定 database.default.hostname = your_production_db_host database.default.database = your_production_db database.default.username = your_production_user database.default.password = your_production_password database.default.DBDriver = MySQLi database.default.port = 3306 # キャッシュ設定 cache.handler = redis cache.redis.host = your_redis_host cache.redis.port = 6379 cache.redis.password = your_redis_password # セッション設定 session.handler = redis session.savePath = 'tcp://your_redis_host:6379' # メール設定 email.protocol = smtp email.SMTPHost = your_smtp_host email.SMTPUser = your_smtp_user email.SMTPPass = your_smtp_password email.SMTPPort = 587 email.SMTPCrypto = tls
これらのパフォーマンス最適化テクニックを適切に組み合わせることで、アプリケーションの応答性と安定性を大幅に向上させることができます。ただし、最適化は必ずモニタリングと計測に基づいて行い、実際のパフォーマンス改善を確認しながら進めることが重要です。