mcpField
C
codeigniter

codeigniter

github.com/yasserstudio開発9205 回インストール28 週間前に更新
リポジトリ

codeigniter について

CI3 または CI4 を扱う CodeIgniter 開発者向けの開発ガイドです。コーディング前にプロジェクトのバージョンを特定し、フレームワークのパターン、安全対策、基本機能、CI3 から CI4 への移行情報を扱います。

主な機能

  1. 01CI3 と CI4 のプロジェクトバージョン特定
  2. 02コントローラー、モデル、ルーティングのガイド
  3. 03クエリ、フォーム、セッションのパターン
  4. 04両バージョンのセキュリティチェックリスト
  5. 05CI3 から CI4 への移行対応表

活用シーン

  1. 01既存の CodeIgniter プロジェクトへの機能実装
  2. 02適用するバージョン別パターンの確認
  3. 03入力処理と出力エスケープの確認
  4. 04CI3 から CI4 への移行計画

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/yasserstudio/codeigniter-skills --skill codeigniter

インストール後、Claude Code で/codeigniterを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
ci3.md
ci4.md
deployment.md

詳細

CodeIgniter 開発

あなたは CodeIgniter のエキスパートです。CI3(3.1.x)と CI4(4.x、最新の安定版 4.7.x)の両方に対応し、それぞれの基本的なアーキテクチャの違いを理解しています。コードを書く前に、必ずプロジェクトで使用されているバージョンを特定してください。

バージョン固有の機能について詳しくは、次を参照してください。

  • CI3 の詳細解説 — ファイルアップロード、メール、キャッシュ、ページネーション、ライブラリ、HMVC、フック、ヘルパー、ログ
  • CI4 の詳細解説 — REST コントローラー、エンティティ、フィルター、マイグレーション、シーダー、アップロード、メール、キャッシュ、サービス、イベント、ビュー、セル、Spark CLI、テスト、Shield 認証、コンテンツネゴシエーション
  • デプロイと移行 — サーバー設定、本番環境のチェックリスト、CI3 から CI4 への完全な移行対応表

バージョンの検出

手がかりバージョン
system/core/CodeIgniter.php が存在するCI3
$this->load->model() / $this->load->view()CI3
application/config/config.phpCI3
app/Config/App.php が存在するCI4
コントローラー内に namespace App\Controllers; があるCI4
プロジェクトのルートに spark CLI があるCI4
composer.json が codeigniter4/framework を要求しているCI4

CI3 — 基本パターン

プロジェクト構成
project/
├── application/
│   ├── config/        # autoload.php, config.php, database.php, routes.php, hooks.php
│   ├── controllers/
│   ├── models/
│   ├── views/
│   ├── libraries/
│   ├── helpers/
│   ├── hooks/
│   ├── core/          # MY_Controller, MY_Model overrides
│   └── third_party/
├── system/            # framework core — never edit
└── index.php          # front controller
コントローラー
<?php
defined('BASEPATH') or exit('No direct script access allowed');

class Products extends CI_Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->load->model('product_model');
        $this->load->helper('url');
    }

    public function index()
    {
        $data['products'] = $this->product_model->get_all();
        $this->load->view('products/index', $data);
    }

    public function view($id)
    {
        $data['product'] = $this->product_model->get($id);
        if (empty($data['product'])) {
            show_404();
        }
        $this->load->view('products/view', $data);
    }
}

ルール: クラス名はファイル名と一致させる必要があります(先頭は大文字)。必ず parent::__construct() を呼び出してください。Linux ではファイル名の大文字と小文字が区別されます。

モデル
<?php
defined('BASEPATH') or exit('No direct script access allowed');

class Product_model extends CI_Model
{
    protected $table = 'products';

    public function get_all()
    {
        return $this->db->get($this->table)->result();
    }

    public function get($id)
    {
        return $this->db->get_where($this->table, ['id' => $id])->row();
    }

    public function insert($data)
    {
        $this->db->insert($this->table, $data);
        return $this->db->insert_id();
    }

    public function update($id, $data)
    {
        $this->db->where('id', $id);
        return $this->db->update($this->table, $data);
    }

    public function delete($id)
    {
        return $this->db->delete($this->table, ['id' => $id]);
    }
}
クエリビルダー
// SELECT with chaining
$results = $this->db
    ->select('p.*, c.name as category_name')
    ->from('products p')
    ->join('categories c', 'c.id = p.category_id', 'left')
    ->where('p.active', 1)
    ->where('p.price >', 10)
    ->order_by('p.name', 'ASC')
    ->limit(20, $offset)
    ->get()
    ->result();

// INSERT / UPDATE / DELETE
$this->db->insert('products', ['name' => $name, 'price' => $price]);
$this->db->where('id', $id)->update('products', ['price' => $new_price]);
$this->db->where('id', $id)->delete('products');

// Transactions
$this->db->trans_start();
$this->db->insert('orders', $order_data);
$this->db->insert('order_items', $item_data);
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) { /* failed */ }

// Raw query (always bind params)
$this->db->query('SELECT * FROM products WHERE slug = ?', [$slug]);
ルーティング
$route['default_controller'] = 'home';
$route['404_override'] = 'errors/page_missing';
$route['products'] = 'catalog/index';
$route['products/(:num)'] = 'catalog/view/$1';
$route['api/products/(:any)'] = 'api/products/$1';
入力とセキュリティ
// ALWAYS use $this->input — never raw $_POST/$_GET
$name = $this->input->post('name', TRUE);  // TRUE = XSS filter
$id   = $this->input->get('id');

// CSRF — enable in config.php
$config['csrf_protection'] = TRUE;

// Output escaping in views
<?php echo html_escape($user_input); ?>
フォーム検証
$this->load->library('form_validation');
$this->form_validation->set_rules('name', 'Name', 'required|min_length[3]|max_length[255]');
$this->form_validation->set_rules('email', 'Email', 'required|valid_email|is_unique[users.email]');

if ($this->form_validation->run() === FALSE) {
    $this->load->view('form', $data);
} else {
    $this->product_model->insert($this->input->post());
    redirect('products');
}
セッション
$this->session->set_userdata('user_id', $id);
$user_id = $this->session->userdata('user_id');
$this->session->set_flashdata('success', 'Product created.');

CI4 — 基本パターン

プロジェクト構成
project/
├── app/
│   ├── Config/        # App.php, Database.php, Routes.php, Filters.php, Services.php, Events.php
│   ├── Controllers/   # BaseController.php
│   ├── Models/
│   ├── Views/
│   ├── Filters/
│   ├── Entities/
│   ├── Cells/
│   ├── Commands/
│   ├── Database/      # Migrations/, Seeds/
│   └── Language/
├── public/            # web root — point server here
│   └── index.php
├── writable/          # cache, logs, session — must be writable by web server
├── tests/
├── .env
├── spark              # CLI tool
└── composer.json
コントローラー
<?php

namespace App\Controllers;

class Products extends BaseController
{
    public function index()
    {
        $model = model('ProductModel');
        return view('products/index', ['products' => $model->findAll()]);
    }

    public function show($id = null)
    {
        $product = model('ProductModel')->find($id);
        if ($product === null) {
            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
        }
        return view('products/show', ['product' => $product]);
    }

    public function create()
    {
        if (! $this->validate([
            'name'  => 'required|min_length[3]|max_length[255]',
            'price' => 'required|numeric|greater_than[0]',
        ])) {
            return view('products/create', ['validation' => $this->validator]);
        }
        model('ProductModel')->insert($this->request->getPost());
        return redirect()->to('/products')->with('success', 'Product created.');
    }
}
モデル
<?php

namespace App\Models;

use CodeIgniter\Model;

class ProductModel extends Model
{
    protected $table         = 'products';
    protected $primaryKey    = 'id';
    protected $returnType    = 'array';
    protected $useSoftDeletes = true;
    protected $useTimestamps  = true;

    protected $allowedFields = ['name', 'price', 'category_id', 'active'];

    protected $validationRules = [
        'name'  => 'required|min_length[3]|max_length[255]',
        'price' => 'required|numeric|greater_than[0]',
    ];
}

重要: $allowedFields は必須です。リストにないフィールドは、挿入/更新時に通知なく破棄されます。これにより一括代入を防止できます。

クエリビルダー
$db = db_connect();

// SELECT with joins + pagination
$model = model('ProductModel');
$data = [
    'products' => $model->where('active', 1)->paginate(20),
    'pager'    => $model->pager,
];

// Raw builder
$results = $db->table('products')
    ->select('products.*, categories.name as category')
    ->join('categories', 'categories.id = products.category_id', 'left')
    ->where('products.active', 1)
    ->orderBy('products.name', 'ASC')
    ->get()
    ->getResultArray();

// Transactions
$db->transStart();
$db->table('orders')->insert($order_data);
$db->table('order_items')->insert($item_data);
$db->transComplete();
if (! $db->transStatus()) { /* failed */ }
ルーティング
$routes->get('/', 'Home::index');
$routes->get('products', 'Products::index');
$routes->get('products/(:num)', 'Products::show/$1');
$routes->post('products', 'Products::create');
$routes->resource('products'); // generates all CRUD routes

$routes->group('api', ['namespace' => 'App\Controllers\Api', 'filter' => 'apiauth'], function ($routes) {
    $routes->resource('products');
});

$routes->get('admin/(:any)', 'Admin::$1', ['filter' => 'auth']);
入力とセキュリティ
// ALWAYS use $this->request — never raw $_POST/$_GET
$name = $this->request->getPost('name');
$id   = $this->request->getGet('id');
$data = $this->request->getJSON(true); // JSON API bodies → array

// Output escaping in views — context-aware
<?= esc($user_input) ?>
<?= esc($value, 'attr') ?>
<?= esc($value, 'js') ?>
セッション
$session = session();
$session->set('user_id', $id);
$userId = $session->get('user_id');
$session->setFlashdata('success', 'Done.');
$session->setTempdata('otp', $code, 300); // auto-expires in 5 minutes

セキュリティチェックリスト(両バージョン共通)

  • 生の $_POST/$_GET は使用せず、入力クラス(CI3)または $this->request(CI4)を使用してください
  • CSRF 保護を有効にし、ステートレスな API エンドポイントのみを例外にしてください
  • クエリビルダーまたはバインドパラメーターを使用し、SQL を連結しないでください
  • ビューでは出力をエスケープしてください。CI3 では html_escape()、CI4 では esc() を使用します
  • 本番環境では db_debug = FALSE(CI3)/CI_ENVIRONMENT = production(CI4)を設定してください
  • system/(CI3)またはプロジェクトのルート(CI4)を Web ルートの外に配置してください
  • 一括代入を防ぐため、CI4 のモデルで $allowedFields を使用してください
  • ファイルのアップロードを検証してください。拡張子だけでなく MIME タイプも確認します
  • 安全なセッション設定を使用してください。database ドライバーと httponly、secure フラグを設定します
  • HTML 属性には esc($var, 'attr')、JS コンテキストには esc($var, 'js') を使用してください
  • 本番環境では Content-Security-Policy ヘッダーを設定してください
  • すべての通信で HTTPS を使用してください。CI4 では app.forceGlobalSecureRequests = true を設定します
  • 本番環境でスタックトレースを公開しないでください

CI3 → CI4 クイックリファレンス

CI3CI4
$this->load->model('x')model('XModel') または DI
$this->load->view('x', $data)return view('x', $data)
$this->load->library('x')service('x') または new X()
$this->input->post('x')$this->request->getPost('x')
$this->db->get('table')$db->table('table')->get()
$query->result() / ->row()$query->getResult() / ->getRow()
$query->result_array()$query->getResultArray()
redirect('url')return redirect()->to('url')
show_404()throw PageNotFoundException::forPageNotFound()
$this->session->userdata('x')session()->get('x')
html_escape($x)esc($x)
$route['x'] = 'y'$routes->get('x', 'Y::method')
フック($hook[])フィルター(ミドルウェア)
CI_Controller / CI_ModelBaseController / CodeIgniter\Model
名前空間なしPSR-4 名前空間
CLI ジェネレーターなしphp spark make:*

30 項目以上のマッピングを含む移行対応表、サーバー設定、本番環境へのデプロイチェックリストの全容については、deployment.md を参照してください。

コメント