LoginSignup
43
39

More than 3 years have passed since last update.

CodeIgniter チュートリアルをやってみた。

Last updated at Posted at 2019-09-30

見習いPHPプログラマを始めてようやく2か月が経とうとしていたとき、ついに初フレームワークを使う機会が来ました。
初フレームワークは CodeIgniter 。
見渡しが良いとか、インドで人気らしいとか、その CodeIgniter です。

フレームワーク自体が初めてで、MVCってなんだ?というレベルだったので、チュートリアルから始めてみました。
CodeIgniter に関する日本語の情報自体がそれほど多くないようなので、何かしら参考になれば幸いです。

環境
Windows 10
XAMPP for Windows 7.3.7
CodeIgniter v3.1.1-dev
Google Chrome 77.0

環境のインストール

XAMPP for Windows インストール

XAMPP のインストール方法は語られているので必要に応じてそちらをご覧ください。
ここではデフォルト通り、Cドライブ直下にインストールすることにします。

CodeIgniter ダウンロード

CodeIgniterのダウンロードページで「CodeIgniter v3.1.1-dev」をクリック。
クリックすると zip ファイルのダウンロードが始まります。

準備

XAMPP 側

文字コード設定

念のため、データベースの文字コードをUTF-8に統一しておきます。
MySQL の設定ファイルをこの記事のように修正します。

\xampp\mysql\bin\my.ini
## UTF 8 Settings
init-connect=SET NAMES utf8
collation_server=utf8_unicode_ci
character_set_server=utf8
skip-character-set-client-handshake
character_sets-dir="C:/xampp/mysql/share/charsets"

[client]
default-character-set = utf8

[mysqldump]
default-character-set = utf8

[mysql]
default-character-set = utf8

Apache と MariaDB の起動

XAMPP Control Panel から Apache と MySQL を Start させます。
XAMPP Control Panel の操作も XAMPP のインストール方法の記事等に一緒に載っていることが多いので必要に応じてご覧いただければ幸いです。

CodeIgniter 側

CodeIgniter の配置

ダウンロードした zip ファイルを解凍し、ドキュメントルート C:\xampp\htdocs にフォルダごと移動し、リネームします。
今回は test_app にしてみます。
http://localhost/test_app/ にアクセスして以下のように表示されればちゃんと配置できています。
welcome.jpg

ちなみにこのページに書いてあることは、

このページのビューは
application/views/welcome_message.php
コントローラは
application/controllers/Welcome.php
だよ

ってことです(意訳)。
静的なページのチュートリアルを終えてから見てみると良いと思います。

ベースURL

設定ファイルにベース URL を記入します。
これを設定しないと、Web サイトのベース URL を CodeIgniter は自動検知しようとするみたいです。
セキュリティに影響したり、バージョンによって挙動が変わったりしてしまうらしいので、設定しておきましょう。

application/config/config.php
$config['base_url'] = 'http://localhost/test_app/';

データベース情報

データベース情報を設定しておきます。
この後のチュートリアルでは「ニュース」について扱うので、とりあえず 'dbname' は news_db としておきました。
'dbdriver' は今回は PDO を使うことにします。勿論 MySQLi 等も使えます。
'username' や 'password' は設定したい場合は設定してください。ここでは簡単のため、root、パス無しにしています。

application/config/database.php
$db['default'] = array(
    'dsn'   => 'mysql:host=localhost;dbname=news_db;charset=utf8',
    'hostname' => 'localhost', // 今回はPDOを使用するので設定しなくても良い
    'username' => 'root',
    'password' => '',
    'database' => 'news_db', // 今回はPDOを使用するので設定しなくても良い
    'dbdriver' => 'pdo',
///////////(中略)///////////
);

チュートリアル

静的なページ

コントローラ

静的なページを表示する Pages クラスを実装したコントローラ Pages.php を作ります。
作成するクラスは、system/core/Controller.php の CI_Controller クラスを継承させます。
引数にページの名前を取り、そのページをロードするメソッド show() を実装します。

application/controllers/Pages.php
class Pages extends CI_Controller {

        public function show($page = 'home')
        {
        }

}

この段階ではまだ何も表示しません。
この状態でここアクセスしても何も表示されません。

ビュー

ヘッダ、フッタ

application/views に templates フォルダを作り、ヘッダとフッタを表示するテンプレートを作ります。
内容はお好きにどうぞ。
後ほど使用するので $title だけはどこかに入れておいてください。

application/views/templates/header.php
<html>
    <head>
        <title>CodeIgniter3 チュートリアル</title>
    </head>

    <body>
        <h1><?php echo $title; ?></h1>
application/views/templates/footer.php
        <em>&copy; 2019 desho.</em>
    </body>
</html>

ページテンプレート

application/views に pages フォルダを作ります。
home.php と about.php を作り、表示したい内容を入力します(開始タグは付けないでください)。

application/views/pages/home.php
Going home
application/views/pages/about.php
It's about time to go home.

コントローラ・再

先ほど書きかけたコントローラ Pages.php でビューを表示できるように実装します。

application/controllers/Pages.php
class Pages extends CI_Controller {
    public function show($page = 'home')
    {
        if ( ! file_exists(APPPATH.'views/pages/'.$page.'.php'))    
        {
            // application/views/errors/html/error_404.php を表示する
            show_404();
        }

        $data['title'] = ucfirst($page);

        $this->load->view('templates/header', $data);
        $this->load->view('pages/'.$page, $data);
        $this->load->view('templates/footer', $data);
    }
}

予約語

ここで、 APPPATH という予約語を使っています。
デフォルトでこれは application フォルダを表します。
C:\xampp\htdocs\test_app\index.php で設定できます。

ローダクラス

ここで load->view() と、ビューを読み込んでいます。
ローダについてはこのページの下部にまとめました。

ここまで確認

http://localhost/test_app/index.php/pages/show にアクセスすると以下のように表示されます。
http://localhost/test_app/index.php/pages/show/home でも同じ。
home.jpg

引数としてaboutを渡したいときは以下にアクセス。
http://localhost/test_app/index.php/pages/show/about
about.jpg

引数を間違えてあげれば404エラーが表示されます。
http://localhost/test_app/index.php/pages/show/abou
404.jpg

ルーティング

ルーティングファイル application/config/routes.php を編集することで、
以下の規則に従わなくてもアクセスできるようになります。
http://example.com/[コントローラクラス]/[コントローラメソッド]/[引数]

application/config/routes.php
$route['default_controller'] = 'pages/show';
// http://localhost/test_app/index.php/ で home にアクセスできる
// ちなみに http://localhost/test_app/ でもアクセスできる

上記のように \$route 配列で独自のルーティングルールを指定できます。
'default_controller' はルート URL を読み込んだときに呼び出される予約済みルートです。
ここではルート URL を読み込むと pages クラスの show メソッドが呼び出されるという設定です。

application/config/routes.php
$route['(:any)'] = 'pages/view/$1';
// http://localhost/test_app/index.php/about でaboutにアクセスできる
// ちなみに http://localhost/test_app/about は Object not found になる

このようにワイルドカード (:any) を使うことで、任意の文字に対するルーティングを設定できます。
\$route 配列の値に \$1 と指定することで、読み込まれた URL を受け取ることができるようです(以下の例参照)。

example
$route['products/([a-z]+)/(\d+)'] = '$1/id_$2';
/*
上記の設定を行うと、
products/shirts/123 の URI は、
shirts コントローラクラスと id_123 メソッドを呼び出します。
*/

ニュースセクション

今度は動的コンテンツを作成するためにデータベースを使ってみます。

準備

データベースとテーブルを作成します。

MySQLモニタ
CREATE DATABASE news_db;
USE news_db

CREATE TABLE news (
        id int(11) NOT NULL AUTO_INCREMENT,
        title varchar(128) NOT NULL,
        slug varchar(128) NOT NULL,
        text text NOT NULL,
        PRIMARY KEY (id),
        KEY slug (slug)
);

-- 表示したいニュースのタイトル、スラッグ、内容を入力します。
-- いくつ入力しても良いです。ここでは以下の2つを入力します。
INSERT INTO news(title, slug, text) VALUES(
    "Welcome to CodeIgniter",
    "Welcome",
    "CodeIgniter is an Application Development Framework - a toolkit - for people who build web sites using PHP."
);
INSERT INTO news(title, slug, text) VALUES(
    "Tutorial",
    "Tutorial",
    "This tutorial is intended to introduce you to the CodeIgniter framework and the basic principles of MVC architecture."
);

ちなみに「slug」とは

モデル

データベースの操作は、クエリを後で再利用しやすくするためにコントローラではなくモデルに記述するのが良いです。
モデルとは、データベース等のデータ格納場所からデータを抽出したり、追加したり、更新したりする場所です。

application/models/ ディレクトリを開き、News_model.php としてモデルを作成します。
コントローラーのときのように CI_Model クラスを継承して実装します。
Database ライブラリをロードすることで、後々 Database クラスを $this->db オブジェクトを通して利用できるようになります。

application/models/News_model.php
class News_model extends CI_Model {

        public function __construct()
        {
                $this->load->database();
        }
}

ここにデータベースからレコードを取得するためのメソッドを追加していきます。

application/models/News_model.php
class News_model extends CI_Model {

        public function __construct()
        {
                $this->load->database();
        }

        public function get_news($slug = FALSE)
        // $slugのエスケープ処理は Query Builder がしてくれる。
        {
            if ($slug === FALSE)
            {
                // SELECT * FROM news
                $query = $this->db->get('news');

                // 結果を配列で取得する。
                return $query->result_array();
            }

            // SELECT * FROM news WHERE 'slug' = $slug
            $query = $this->db->get_where('news', array('slug' => $slug));

            // 結果を1行、配列で取得する。
            return $query->row_array();
        }
}

ここではクエリビルダクラスを使用しています。詳細はこの記事の下部でまとめています。

コントローラ

モデルの準備ができたら、モデル経由でデータベースからデータを取得するコントローラを定義します。

application/controllers/News.php
class News extends CI_Controller {

        public function __construct()
        {
                parent::__construct();

                // モデルのロード。 news_model というオブジェクト名で使用できる。
                // このコントローラの他のメソッドで使う。
                $this->load->model('news_model');

                // system/helpers の URL ヘルパー関数をロード。ビューで使う。
                $this->load->helper('url_helper');
        }

        public function index()
        {
                // 引数を指定せずに全ニュースをモデル経由で連想配列として取得する。
                $data['news'] = $this->news_model->get_news();
        }

        public function view($slug = NULL)
        {
                // 引数を指定して WHERE 'slug' = $slug のニュースをモデル経由で連想配列として取得する。
                $data['news_item'] = $this->news_model->get_news($slug);
        }
}

これでモデル経由でデータを取得できます。
取得したデータをビューに渡せば表示できます。
ビューの扱いは静的なページのとき同様にローダを使って以下のようにします。

application/controllers/News.php
class News extends CI_Controller {

        public function __construct()
        {
                parent::__construct();

                // モデルのロード。 news_model というオブジェクト名で使用できる。
                // このコントローラの他のメソッドで使う。
                $this->load->model('news_model');

                // system/helpers の URL ヘルパー関数をロード。ビューで使う。
                $this->load->helper('url_helper');
        }

        // 概要ページ
        public function index()
        {
                // 引数を指定せずに全ニュースをモデル経由で連想配列として取得する。
                $data['news'] = $this->news_model->get_news();

                $data['title'] = 'News archive';

                $this->load->view('templates/header', $data);
                $this->load->view('news/index', $data);
                $this->load->view('templates/footer');

        }

        // 個々のニュースページ
        public function view($slug = NULL)
        {
                // 引数を指定して WHERE 'slug' = $slug のニュースをモデル経由で連想配列として取得する。
                $data['news_item'] = $this->news_model->get_news($slug);

                if (empty($data['news_item']))
                {
                        show_404();
                }

                $data['title'] = $data['news_item']['title'];

                $this->load->view('templates/header', $data);
                $this->load->view('news/view', $data);
                $this->load->view('templates/footer');
        }


}

ビュー

あとは実際にページを描画するビューを作成すれば完了です。
概要ページは index.php として以下のように作成します。

application/views/news/index.php
<h2><?php echo $title; ?></h2>

<?php foreach ($news as $news_item): ?>

        <h3><?php echo $news_item['title']; ?></h3>
        <div class="main">
                <?php echo $news_item['text']; ?>
        </div>
        <p><a href="<?php echo site_url('news/'.$news_item['slug']); ?>">View article</a></p>

<?php endforeach;

ここで URL ヘルパー の site_url() を使っています。URL ヘルパーについてもこのページの下部にまとめました。
個々のニュースページは view.php として以下のように作成します。

application/views/news/view.php
echo '<h2>'.$news_item['title'].'</h2>';
echo $news_item['text'];

ルーティング

これでデータベースから取得した内容を表示する準備は整いました。
ですが、静的なページのチュートリアルでルーティング設定をしてしまっているので、このままだとせっかく作成したページが表示できません。
まずはルーティングをデフォルトの設定に戻してみましょう。

application/config/routes.php
 $route['default_controller'] = 'welcome';
 $route['404_override'] = '';
 $route['translate_uri_dashes'] = FALSE;

このように設定することで、以下のいずれも概要ページが表示されます。
http://localhost/test_app/index.php/news
http://localhost/test_app/index.php/news/index
ただ、ルーティングする前提でリンクを張っているので View article のリンクは飛べません。

以下にアクセスすれば個別のニュースページが表示されます。
http://localhost/test_app/index.php/news/view/welcome
http://localhost/test_app/index.php/news/view/tutorial

理解を深めたところでルーティングを設定しましょう。

application/config/routes.php
/* 1 */$route['news/(:any)'] = 'news/view/$1';
/* 2 */$route['news'] = 'news';
/* 3 */$route['(:any)'] = 'pages/show/$1';
/* 4 */$route['default_controller'] = 'pages/show';

ルートは定義順で実行され、上のルートは常に下のものよりも優先されるというルールがあるので、
2 を 3 よりも上で定義することによって news ページが表示できます。
2 と 3 が上下逆に定義されていると、以下のページは 404 Page Not Found になります。
http://localhost/test_app/index.php/news/

確認

それぞれにアクセスして確認してみましょう。
概要ページ
概要ページ.jpg
ニュース1
ニュース2

ニュース記事を作成

最後にデータベースに情報を書き込んでみます。

ビュー

データを入力し送信するためのフォームと、
データの送信に成功した際に表示するページを作ります。

フォーム

データベースにデータを入力するためのフォームを作成します。
ここでは title と text をフォームで入力し、モデル内で title を基に slug を作成します。
以下のようにビューを新しく作成します。

application/views/news/create.php
<h2><?php echo $title; ?></h2>

<?php 
 // フォームバリデーションを行い、戻されたすべてのエラーメッセージを返します。
 // メッセージがない場合、空の文字列を返します。
 echo validation_errors();
?>

<?php
 // form の開始タグを作成し、action 先を http://localhost/test_app/index.php/news/create に設定する。
 // フォームヘルパーは自動的に CSRF のための隠しフィールドを挿入する。
 // リクエストメソッドはデフォルトでは POST になるよう。
 echo form_open('news/create');
?>

    <label for="title">Title</label>
    <input type="text" name="title" /><br />

    <label for="text">Text</label>
    <textarea name="text"></textarea><br />

    <input type="submit" name="submit" value="Create news item" />

</form>

ここではフォームヘルパーを使用しています。
フォームヘルパーのロードはこの後コントローラを修正して行います。
フォームヘルパーについてもこの記事の下部でまとめています。

データ送信成功ページ

何でも良いです。表示したい内容を入力してください。

application/view/news/success.php
<html>
<head>
    <title>Success</title>
</head>

<body>

    <h3>Your form was successfully submitted.</h3>

    <!-- URL ヘルパーの anchor() メソッドでリンクを作成 -->
    <p><?php echo anchor('news/create', '入力を続ける'); ?></p>

</body>
</html>

コントローラ

先ほどニュースセクションで作成した news コントローラに create() メソッドを追加します。

application/controllers/News.php
public function create()
{
        // フォームヘルパーとフォームバリデーションライブラリをロードする。コンストラクタでやってもよい。
        $this->load->helper('form');
        $this->load->library('form_validation');

        $data['title'] = 'Create a news item';

        // title と text を必須入力 required に設定する。
        $this->form_validation->set_rules('title', 'Title', 'required');
        $this->form_validation->set_rules('text', 'Text', 'required');

        if ($this->form_validation->run() === FALSE)
        {
                // submit 前や、不正な入力のときはフォームを表示する。
                $this->load->view('templates/header', $data);
                $this->load->view('news/create');
                $this->load->view('templates/footer');
        }
        else
        {
                // 正しく入力されたときは成功ページを表示する。
                $this->news_model->set_news();
                $this->load->view('news/success');
        }
}        

モデル

コントローラ同様、ニュースセクションで作成した News_model.php に set_news() メソッドを追加します。

application/models/News_model.php
public function set_news()
{
    // URL ヘルパーをロード。コンストラクタでやってもよいし、
    // ニュースセクションのときにコントローラのコンストラクタでロードする設定にしているので、
    // ここで明示的にロードしなくてもよい。
    $this->load->helper('url');

    // URL ヘルパーの url_title() メソッドを使い、スペースを - に、大文字を小文字に置換する。
    // input ライブラリの post() メソッドを使い、データを取得する。
    // 組み込み関数 urlencode() を使うことで、日本語等マルチバイト文字の入力にも対応する。
    $slug = urlencode(url_title($this->input->post('title'), '-', TRUE));

    $data = array(
        'title' => $this->input->post('title'),
        'slug' => $slug,
        'text' => $this->input->post('text')
    );

    return $this->db->insert('news', $data);

ここでは input ライブラリを使用しています。これについてもページ下部にまとめました。

urlencode() を使用する理由

試しにチュートリアルと同様に urlencode() せずに最後までやってみてください。
おそらく title に日本語を入力したニュースの個別ページが開けないと思います。
CodeIgniter 標準ルーターで、function の引数として日本語を扱うと文字として扱われません。
この記事を読み恐らく問題は違いますが、これと似た考え方で対処しています。
ちなみに最初に環境のところで Chrome を使用していると記載しましたが、
Microsoft edge でも Internet Explorer でも同じ現象を確認しました。

私が考えたアクセスできない原因は以下です。
正しいかはわかりませんが、以下に対処しようとして urlencode() を使って改善できたので正しいと信じています。
1. $slug には UTF-8 エンコードされた日本語が格納される。
2. 日本語を含んだ URL にブラウザでアクセスすると URLエンコーディングされる。
3. 1. と 2. とがかみ合わず、404 Page Not Found になる。

なお、このチュートリアルでは使用しませんが、
もし $slug を表示させたいとき等は urldecode() 関数を使用してデコードしてあげれば大丈夫です。

また、urlencode() することで文字数が増えます。長いタイトルを入力すると slug をデータベースに格納できない可能性があるので、長いタイトルを入力したい方は以下のようなコマンドを実行しておいてください。

MySQLモニタ
ALTER TABLE news MODIFY slug VARCHAR(500);

ルーティング

application/config/routes.php
$route['news/create'] = 'news/create';
$route['news/(:any)'] = 'news/view/$1';
$route['news'] = 'news';
$route['(:any)'] = 'pages/show/$1';
$route['default_controller'] = 'pages/show';

ここでも順序が重要な意味を持つので注意してください。

確認

以下にアクセスし、入力してみる。
http://localhost/test_app/index.php/news/create
create.jpg

入力した内容が表示されれば完成です。

使用したクラス等のまとめ

クエリビルダクラス

データベースごとに専用のクラスを必要としないため、データベースから独立したアプリケーションを作成できる。
システムにより自動的に値のエスケープ処理が行われる。

get()

「SELECT * FROM」クエリを実行し、結果を返す。

第一引数

取得したいテーブル名を文字列で指定する。

第二引数(オプション)

limit 句を数値で指定する。

第三引数(オプション)

offset 句を数値で指定する。

get_where()

where 句を指定して「SELECT * FROM」クエリを実行し、結果を返す。

第一引数

取得したいテーブル名を文字列で指定する。

第二引数

where 句を配列で指定する。

result_array()

結果を純粋な配列として返す。結果が生成されなかった場合は空の配列を返す。

row_array()

結果の単一行を配列で返す。

第一引数

取得したい行番号を数値で指定できる。

insert()

INSERT 文を生成し実行する。値は自動的にエスケープされる。

第一引数

INSERT を実行したいテーブル名を文字列で指定する。

第二引数

INSERT したいデータを連想配列またはオブジェクトで指定する。

URL ヘルパー

site_url()

設定ファイルで指定されている URL を文字列で返す。

第一引数(オプション)

URI 文字列(配列)を指定することで URL を生成して返す。
これを用いてサイト内 URL を生成しておくことで移植性が高まる。

第二引数(オプション)

プロトコルを文字列で指定できる。

anchor()

設定ファイルで指定されているベース URL から情報を取得し、
サイト URL に基づいて HTML のアンカータグを生成し、文字列として返す。

第一引数

URI 文字列(配列)を指定することで URL にセグメントを追加できる。

第二引数

リンクに指定するテキスト。空にすると URL が使用される。

第三引数

追加したい属性を文字列(配列)で指定する。

url_title()

文字列を URL 文字列に変換し文字列として返す。

第一引数

変換したい文字列。

第二引数

単語を何で区切るかを指定する。
'-' または '_' を指定できる。

第三引数

小文字に変換して出力するかどうかを bool で指定する。
デフォルトでは変換されない。

フォームヘルパー

form_open()

設定ファイルに基づいて構築したベース URL を元にフォームの開始タグを作成し、返す。
設定ファイルの文字コードの値に基づいた accept-charset 属性が常に付与される。

第一引数(オプション)

URI 文字列を指定することでフォームの action 先を指定できる。

第二引数(オプション)

HTML属性を配列(文字列)で指定できる。

第三引数(オプション)

隠しフィールドを定義した配列を指定できる。

validation_errors()

フォームバリデーションを行い、返ったエラーメッセージを全て返す。

第一引数(オプション)

エラーメッセージの文字列の前に置きたいHTMLタグ

第二引数(オプション)

エラーメッセージの文字列の後ろに置きたいHTMLタグ

フォームバリデーション

set_rules()

検証ルールを設定できる。

第一引数

name 属性で指定したフォームフィールド名を文字列で指定する。

第二引数

フォームフィールド名の代わりにエラーメッセージに表示される名称を文字列で指定する。

第三引数

検証ルールを文字列または配列で指定する。
また、 trim や htmlspecialchars 等引数を1つとるネイティブ関数を指定することでデータの整形も行える。

第四引数(オプション)

任意のルールに任意のエラーメッセージを配列で設定できる。
指定しない場合はデフォルトのエラーメッセージを使用する。

run()

ルールを適用し、バリデーションが成功した場合にTRUE、1つでも失敗した場合にはFALSEを返す。

第一引数(オプション)

特定のルールグループを文字列で指定して呼び出せる。
application/config/form_validation.php としてバリデーションルールファイルを保存しているとき、
そのファイルで設定しているルールのセットを呼び出せる。

入力クラス

入力クラスは自動で初期化されるため、手動で初期化する必要はない。
入力データの前処理も行う。

post()

post()、get()、cookie()、server() を使用することでデータを取得できる。
このメソッドを使うことで自動的に値が存在するかチェックし、存在しない場合は NULL を返す。

感想

チュートリアルをやることでフォルダ構成等も理解でき、
業務が捗っただけでなく、自分で何かしら Web サービス作れそうだと妄想を膨らませられて楽しかったです。

チュートリアルをコピペだけでこなせないところが逆に初心者には良いのかもしれません。
たとえば urlencode() のところとか。

CodeIgniter は Qiita でも情報が少なめですが、
せっかくの初フレームワークなので、いろいろ使ったり、(することがあれば)情報発信したりしていきたいです。

43
39
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
39