Web Developer's Struggle Memories

日々の業務から思ったこと、学んだことを書き連ねていきます。

Codeigniter(v3)を使って社内用のイベント管理システムを作ってみた感想

はじめに

CodeIgniter Advent Calendar 2015の18日目の記事になります! いつもAdvent Calendarは見る専門で参加するのは初めて。(なぜか緊張してます) 本投稿は「Codeigniterのコアな部分」には触れません。また、「Codeigniter入門」でもありませんので、ご了承ください。

初めてのCodeigniter

現在の職場で一番使われているPHPフレームワークCodeigniterなのだが、自分はこのフレームワークを使ったことがない。 先輩社員からは、「軽い・速い・CodeIgniter ユーザガイド 日本語版がとてもしっかりしていて分かりやすい*1・学習コストも低い、だからすぐに使えるようになるから勉強してみて!」と言われ、また 「何かわからなかったらググれ(ドヤァ」とのありがたいお言葉もいただいた。

というわけで、とにもかくにも使ってみないことには分からないので、これを使って一つ開発してみた。 開発中は、以下の「『Codeigniter徹底入門』サンプルアプリケーション」のソースコードを、めっさ参考にさせていただきました。かなり勉強になりました。 本当にありがとうございました。

github.com

作ったもの

実際に作ったものは、社内用のイベント参加者管理システム。もっぱら飲み会参加者が誰なのかを管理・把握するために利用する想定。 まぁ、ちょうど忘年会の時期でもあるので、あったら便利だと思い作った。(調整さんで良いじゃん、と自分でも思ったが、そこは勉強のため知らなかった体でww)機能仕様してはこんな感じ。

  • イベント作成(タイトル、作成者メールアドレス、日付)
  • 開催前の登録イベント一覧表示
  • 各イベントの出欠登録(名前、参加者メールアドレス、出欠、備考の入力)
  • 各出欠情報の編集
  • 終了イベントの非表示
  • 終了イベントの論理削除バッチ

とりあえず作ってみて社内では公開中。実際に社内で使うかは検討中。また機能仕様についても検討中。

実装記録

Codeigniterの書き方やMVCの分離などについては素晴らしい記事が既にたくさんあるので割愛。ここではCodeigniterでwebアプリを開発していく中で不可欠な設定や、私自身がいろいろ躓いたことについて備忘録的に一つずつ書いていく。

■開発環境

開発環境は以下。基本的には、yumでインストールできるもので開発したが、PHPだけバージョンを上げた。

■index.phpのリダイレクト

Codeigniter利用者なら必ず通るURLのindex.phpリダイレクト処理。.htaccessでやってもよかったのだが、 ここは書き換えることはそうそうないと思ったので、apacheの方で設定した。

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteCond %{REQUEST_URI} !\.(css|pdf|png|jpe?g|gif|js|swf|txt|ico|s?html?)$
    RewriteRule ^(.*)$ /index.php/$1 [L]
</IfModule>

■データベース接続設定

何はともあれデータベース(以下DB)の接続設定が必要。DBの作成は行っている前提で書く。*2  設定はapplication/config/database.phpにて設定する。

<?php
// 主に設定する項目のみ
'hostname' => 'localhost',
'username' => 'hogeuser',
'password' => 'hogepass',
'database' => 'hogedb',
'dbdriver' => 'mysqli',  // PHPのバージョンによって適宜mysqlに変更

■バリデーションの追加

Codeigniterに標準で実装されているバリデーションを確認(対象ファイルはsystem/libraries/Form_validation.php)したら、「日付」「時間」に関するものがなかったので追加した。※直接ネイティブライブラリのファイルを触るのではなく、ネイティブライブラリの拡張にてapplication/libraries/my_form_validation.phpを作成。今回はyyyy/mm/dd H:i形式で日時データがPOSTされるので、「日付」と「時間」で分離し、それぞれの正確性をチェックするようにした。

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class MY_Form_validation extends CI_Form_validation {
    public function __construct()
    {
        parent::__construct();
    }

   /**
    * Valid DateTime
    *
    * @param   string
    * @return  bool
    */
   public function valid_datetime($str)
   {
      // 日付と時間を分離
      $datetimes = explode(' ', trim($str));
      $dateData = $datetimes[0];
      $timeData = $datetimes[1];

      // 日付を年月日で分離
      $ymd = explode('/', $dateData);
      // 日付の正確性チェック
      if (!checkdate($ymd[1], $ymd[2], $ymd[0])) {
         return FALSE;
      }

      // 時間を時と分で分離
      $hourmin = explode(':', $timeData);
      // 時間の正確性チェック
      if (0 > $hourmin[0] || $hourmin[0] > 24 ||
          0 > $hourmin[1] || $hourmin[1] > 59) {
         return FALSE;
      }

      return TRUE;
    }
}

マイグレーション

ある意味で一番躓いたのがマイグレーションかもしれない。日本語のドキュメントが存在せず、以下の記事に大変助けていただきました。 細かいことは割愛するが、dbforgeクラスと組み合わせて利用する仕様となっている。

qiita.com

やったことを列挙すると、

<?php
class Migration_Add_hoge_table extends CI_Migration {

    public function __construct()
    {
        parent::__construct();
    }
    public function up()
    {
        $this->dbforge->add_field(array(
            'event_id' => array(
                'type'           => 'INT',
                'constraint'     => 8,
                'unsigned'       => TRUE,
                'auto_increment' => TRUE,
                'comment'        => 'イベントID'
            ),
            'event_title' => array(
                'type'       => 'VARCHAR',
                'constraint' => '255',
                'comment'    => 'イベントタイトル'
            ),
            'create_date' => array(
                'type'       => 'DATETIME',
                'null'       => TRUE,
                'comment'    => '作成日'
            ),
            'update_date' => array(
                'type'       => 'DATETIME',
                'comment'    => '更新日'
            ),
            'event_date' => array(
                'type'       => 'DATETIME',
                'comment'    => 'イベント日'
            ),
            'email' => array(
                'type'       => 'VARCHAR',
                'constraint' => '255',
                'comment'    => '作成者メールアドレス'
            ),
            'del_flg' => array(
                'type'       => 'TINYINT',
                'constraint' => '3',
                'default'    => 0,
                'unsigned'   => TRUE,
                'null'       => TRUE,
                'comment'    => '削除フラグ'
            ),
        ));
        $this->dbforge->add_key('event_id', true);
        $this->dbforge->create_table('hoge_table');
    }

    public function down()
    {
        $this->dbforge->drop_table('hoge_table');
    }
}
<?php
$config['migration_enabled'] = TRUE;
$config['migration_version'] = 1;
  • コマンドラインから実行するためapplications/controller/migrate.phpの作成 以下作成例
<?php
class Migrate extends CI_Controller
{
    function __construct()
    {
        parent::__construct();
        // コマンドラインから実行されていることを確認
        if(!$this->input->is_cli_request()) {
            log_message('error', 'Request from HTTP is not allowed.');
            return FALSE;
        }
        $this->load->library('migration');
    }

    function current() {
        if ($this->migration->current()) {
            log_message('debug', 'Migration Success.');
        } else {
            log_message('error', $this->migration->error_string());
        }
    }

    function rollback($version = 0)
    {
        $this->migration->version($version);
    }
}
# 実行
$ php index.php migrate current

# 戻す
$ php index.php migrate rollback

今回はデータベースの作成については先に行い、テーブルの作成・削除のマイグレーションのみ行った。本来の仕様だと画面から実行するそうだが、参考ページや以前使っていたYiiフレームワークでもコマンドラインから行っていたため、コマンドラインから実行できるように実装。また、テーブルにコメントを付けたかったが分からず…。どなたかご存知の方ご教示いただけますと幸いです。nanndemoiikaraさんにコメントにてやり方を教えていただきました!)

Smartyの導入

View側には、私が使い慣れているSmartyテンプレートエンジンを導入。導入の際にはこちらの記事を参考にさせていただき、またかなり助けていただきました。 結構いろんなことやらないといけないので、参考記事がなければと思うと…((( ;゚Д゚)))フガクブル

  • Github公式サイトから圧縮されたソースをダウンロード
  • 解凍、application/third_party/に配置
  • ディレクトリ名、ファイル名の変更
    • Smary-x.x.x ⇒ smarty
    • smarty/libs ⇒ smarty/libraries
    • smarty/libraries/Smarty.class.php ⇒ smarty/libraries/Smarty.php
  • テンプレートキャッシュディレクトapplication/cache/templates_c/の作成(※作成場所は任意)
  • application/config/autoload.phpにて自動読み込みの設定(※コントローラ毎に読み込む場合は不要)
<?php
$autoload['packages'] = array(APPPATH . 'third_party/smarty');
$autoload['libraries'] = array('smarty', 'database'); // ←その他追加するものがあれば適宜追記
  • My_Controller.phpの作成 ※smartyを利用するための設定を記述
<?php
/**
 * My_Controller
 * Smarty on CodeIgniter
 */
class My_Controller extends CI_Controller {
    protected $template;

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

        $this->smarty->template_dir = APPPATH . 'views/templates';
        $this->smarty->compile_dir  = APPPATH . 'cache/templates_c';
        $this->smarty->left_delimiter = '<!--{';
        $this->smarty->right_delimiter = '}-->';
        $this->template = null;
    }

    public function _view($template) {
        $this->template = $template;
    }

    public function _output($output) {
        if (strlen($output) > 0) {
            echo $output;
        } else {
            $this->smarty->display($this->template); 
        }
    }

    public function _assign($key, $value) {
        $this->smarty->assign($key, $value);
    }
}

ユニットテスト

これは調べたところ主に以下の3パターンの方法がある。以下参考にした記事。

 ⇒ 主に画面からテストを行うもののようで、結果は見やすいがあまりイケてない。。。ので却下!
 

  • 素のPHPUnitを使う(参考記事多数のため割愛)

 ⇒ これは悪くないが、Codeigniterに沿ったテストを書きたかったので、今回は見送り。
 

 ⇒ 今回はこちらの方法でテストを書いた。本投稿では環境用意までを書き残す。

# ci_phpunit_testのダウンロード
$ composer require kenjis/ci-phpunit-test --dev

# ci_phpunit_testのインストール
$ php vendor/kenjis/ci-phpunit-test/install.php

テストの書き方については、上記記事を参照。

■jQueryrプラグイン

Codeigniterとは関係ないが、今回開発したシステムにてイベント作成の際に日時を設定するため、日付と時間をGUIで選択できるjQueryプラグインdatetimepickerを利用した。これは特に躓きポイントはない。

  • 公式サイトよりソースをダウンロード、解凍
  • jquery.datetimepicker.full.min.jsjquery.datetimepicker.cssをアプリ内に配置
  • 利用する対象画面のテンプレートのheadタグ内に以下を追記 ※初期設定は適宜変更
<link rel="stylesheet" type="text/css" href="/css/jquery.datetimepicker.css">
<script src="/js/jquery.js"></script>
<script src="/js/jquery.datetimepicker.full.min.js"></script>
<script>
$(function() {
    $.datetimepicker.setLocale('ja');
    $( "#datetimepicker" ).datetimepicker({
        format: 'Y/m/d H:i',
        step: 30
    });
});
</script>
  • 同テンプレートのinputタグにid=datetimepickerを追記
<input type="text" name="eventDate" id="datetimepicker" placeholder="2015/12/18 10:30" />

今後の課題

まだ実装していない機能として、

  • 作成者のみイベントの削除ボタン表示
  • イベント前日、出席者へのアラートメール配信
  • 開催前のイベント削除時、出席者へのメール配信
  • 出欠表CSV出力

などがあるが、とりあえず社内で使うことが決まったら、別途仕様の相談をした上で実装仕様と思う。 また、開発したもののデモ環境を後ほど用意する(なにぶん会社用に作っているので、修正点がある)。何とか今年中に用意したいが…
※2015/01/06 Githubソースコードアップしました。デザイン周りは現在も対応中です。

使った感想

公式サイトでうたってあった、

  • 軽量
  • 高速
  • 拡張性
  • MVC分離性

については、納得できるものだった。また、学習コストもかなり低く、ユーザガイド眺めながらコーディングしていけば自然と身に付いた。 とにかく使いやすいので、迷ったらとりあえず使ってみよう、くらいの感覚で選択しても良いと思う。

しかし、軽量・拡張性の高さのゆえコーディングする人の書き方による部分も多く(素のPHPを書くことが多い)、コーディング規約を決めるなどしないと可読性は上がらない。 また、「テンプレートエンジンを必要としない」とあるが、個人的には必要だと思う。そもそも高速なフレームワークなのだから、多少パフォーマンスを犠牲にしたとしても、気にならない程度だろうし、 書く側としてはテンプレート使ったほうが簡単に書けるのだから、総合的には利用した方が利点はあるかと。

Yiiフレームワークとの比較

なぜYiiなのかと言うと、私が使ったことがあるPHPフレームワークYiiしかないため。速度に関する比較は2015年最新PHPフレームワーク(9つ)のベンチマークがとても参考になるので、ご参照ください。書きやすさについてはCodeigniterに軍配。ただ可読性はYiiの方が高い。まぁこの二つについてはトレードオフだな。総合的にCodeigniterの方が気に入ったし、おすすめ。
 
以上!(たぶん随時修正すると思う…)

*1:ただバージョンが2.0.3のものなので、最新のソースとは乖離がある

*2:マイグレーションで作成もできるが、マイグレーションはテーブル作成・削除のみにしたいため