CodeIgniterのモデルを使いやすくしよう
CodeIgniterのモデルは、自由度が高いですね。他のフレームワークでは、テーブルとモデルが密な関係になっていいて、ちょっと扱いづらいところがあるんですが、CodeIgniterは自由すぎるぐらい自由です。CodeIgniterのソースコードを読むと、その途方も無い自由さは分かります。ただ、自由すぎるがゆえに不便なところもあります。これを使いやすくしましょうというのが、ここでテーマです。
不便その1:いちいちテーブル名を指定すること
たとえば、users_modelというモデルがあるとしましょう。そこから usersテーブルのレコードを引っ張ってくる時は、
$query = $this->db->get_where('users', array('id' => $id));
$result = $query->result_array();
こんな感じで取得します。自由だけど少し面倒ですね。わざわざテーブル名を指定しています。自由度が高いゆえなんですけどね。ぼくとしては、usersモデルの中からusersテーブルのレコードを取得する時は、こう書きたいです。
$result = $this->get(array('id' -> $id));
不便その2:よくjoinするので、結局SQLを直に書いてしまう
さて、他のテーブルとjoin(結合)する時のことを考えてみましょう。たとえば、payments(支払情報)テーブルのレコードを取得する時に、users(会員情報)テーブルから常に会員名を結合して取得したい・・・ということはよくありますね。そんな時、ふつうはこんな感じで書くと思います。
$this->db->select('payments.*,users.name');
$this->db->from('payments');
$this->db->join('users', 'payments.user_id = users.id');
$this->db->where('payments.user_id', $id);
$query = $this->db->get();
$result = $quert->result_array();
面倒ですよね。こんなことならSQLを直に書いちゃいますよ。
でも、フレームワークを使うからにはSQLはあんまり書きたくないんですよね。(といいつつガリゴリ書いていますが)
こんなときでも、よくばりな私は
$result = $this->get(array('payments.user_id' -> $id));
(paymentsモデルから呼び出すことを想定しています)
1行で済ませたいのです。これで$resultに会員名も含まれていたら素敵。
(どっかでアソシエーション設定はしておくのですが、毎回結合条件を書かなくて済みます)
不便その3:getメソッドぐらい用意して欲しい
たとえば、コントローラから特定ユーザーの情報を取得したいって状況があるとします。usersモデルにgetメソッドなるものを書いて中継させると思います。だけど、テーブルから単純にレコードを取得するなんてよくある処理じゃないですか。すべてのモデルにgetメソッドをいそいそと実装するのもバカバカしい気がします。せめてget/insert/update/deleteメソッドがモデルにあったらなぁ。
解決策
私はこれらの問題を解決するために、コアのモデルを拡張することで、これらを実現しています。かれこれ3年以上愛用しています。コアモデルを拡張するには、core/ の中に MY_Model.php という名前でファイルを作成します。
私の拡張モデルは以下の様になっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class MY_Model extends CI_Model { // モデルと関連付けるテーブル名(各モデルで必要な場合は定義) var $table; // SELECT句で取得する列 var $cols; // JOINするテーブル var $join; function __construct() { parent::__construct(); } /** * 条件に合致するレコード数を取得 * @param mixed $where WHERE句(連想配列 or 文字列) * @return int レコード数 */ function get_rows($where, $include_deleted_rec = FALSE) { $this->set_params($this->table, 'COUNT(*) as count', $this->join, $where, NULL, NULL, NULL, $include_deleted_rec); $query = $this->db->get(); $result = $query->result_array(); return $result[0]['count']; } /** * 結果を1件取得する * @param mixed $wehre where句(連想配列 or 文字列) * @return array 結果を連想配列で返す */ function get_one($where, $include_deleted_rec = FALSE) { $result = $this->get($where, NULL, NULL, NULL, $include_deleted_rec); if (is_array($result)) { return @$result[0]; } return $result; } /** * テーブルからデータを取得 * @param mixed $where WHERE句(連想配列 or 文字列) * @param string $order ORDER BY 句(文字列) * @param int $limit limit数 * @param int $offset offset数 * @return array 結果配列 */ function get($where = NULL, $order = NULL, $limit = NULL, $offset = NULL, $include_deleted_rec = FALSE) { $this->set_params($this->table, $this->cols, $this->join, $where, $order, $limit, $offset, $include_deleted_rec); $query = $this->db->get(); return $query->result_array(); } /** * レコードの挿入 * @access public * @param mixed $data ... 挿入するデータの連想配列 * @return int 挿入したデータID */ function insert($data) { // 登録データ配列をセット $this->db->set($data); // register_datetime等の共通フィールドを初期値で埋める $this->db->set(set_common_fields($this->table), '', FALSE); // INSERT処理 $this->db->insert($this->table); return $this->db->insert_id(); } /** * レコードを更新する * @param mixed $where WHERE句を配列もしくは文字列で * @param array $data 更新するデータの連想配列 * @return void */ function update($where = NULL, $data) { if ( is_null($where) || ! is_array($data)) { return FALSE; } $data = set_update_datetime($this->table, $data); $this->set_where($where, "update"); $this->db->update($this->table, $data); } /** * レコードを物理削除する * @param mixed $where WHERE句を配列もしくは文字列で * @return void */ function real_delete($where = NULL) { if ( is_null($where)) { return FALSE; } $this->set_where($where, "delete"); $this->db->delete($this->table); } /** * レコードを削除する。delete_datetimeのあるテーブルはそこに現在時刻を埋め込む。(論理削除) * @param mixed $where WHERE句を配列もしくは文字列で * @return void */ function delete($where = NULL) { if ( is_null($where)) { return FALSE; } if ( ! $this->db->field_exists('delete_datetime', $this->table)) { $this->real_delete($where); } else { $this->update($where, array('delete_datetime' => @date('Y-m-d H:i:s'))); } return TRUE; } /** * パラメータ設定(外部から呼び出すことはあまりない) * * @access public * @param mixed $table テーブル名 * @param mixed $cols (default: NULL) SELECT句で指定するカラム * @param mixed $join (default: NULL) JOINするテーブル情報を配列で [0] テーブル名 [1] 結合条件 [2] 結合方法 left/inner * @param mixed $where (default: NULL) WHERE句を連想配列もしくは文字列 * @param mixed $order (default: NULL) order句を文字列で * @param mixed $limit (default: NULL) 取得する件数 * @param mixed $offset (default: NULL) 取得開始位置 * @param mixed $include_deleted_rec (default: FALSE) 論理削除したレコードも含める * @return void */ function set_params($table, $cols = NULL, $join = NULL, $where = NULL, $order = NULL, $limit = NULL, $offset = NULL, $include_deleted_rec = FALSE) { if ( ! $table) { return FALSE; } $this->db->from($table); if (is_array($cols)) { $this->db->select(implode(", ", $cols), FALSE); } else if ($cols) { $this->db->select($cols, FALSE); } if (is_array($join)) { foreach ($join as $one) { $this->db->join($one[0], $one[1], $one[2]); } } $this->set_where($where, $include_deleted_rec); if ( ! is_null($order)) { $this->db->order_by($order); } if ( ! is_null($limit) && ! is_null($offset)) { $this->db->limit($limit, $offset); } else if ( ! is_null($limit) ) { $this->db->limit($limit); } return TRUE; } /** * where句のセット(外部からは使わない) * * @access public * @param mixed $where * @param string $mode (default: "select") * @param mixed $include_deleted_rec (default: FALSE) * @return void */ function set_where($where, $mode = "select", $include_deleted_rec = FALSE) { // WHERE句の処理 if ( ! is_null($where)) { if (is_array($where)) { foreach ($where as $key => $value) { if ($key == "SQL") { if (is_array($value)) { $value = implode(" AND ", $value); } $this->db->where($value); continue; } // 配列の場合はLIKE if (is_array($value)) { if ($value[1] == 'OR') { $this->db->or_where($key, $value[0]); } elseif ($value[1] == 'OR_LIKE') { $this->db->or_like($key, $value[0], $value[2]); } else { $this->db->like($key, $value[0], $value[1]); } } else { $this->db->where($key, $value); } } } else { $this->db->where($where); } } // delete_datetimeは削除済みレコードなので除外する if ($mode != "delete" && $include_deleted_rec === FALSE && $this->db->field_exists('delete_datetime', $this->table)) { $this->db->where($this->table . '.delete_datetime IS null'); } } } |
そして、原則テーブルごとにモデルを作成しています。(モデルを作らないテーブルもあります)
usersテーブルにはusersモデルを作成します。ここではusersモデルを例にしましょう。
models/ディレクトリに 以下の内容で、Users_model.phpというファイルを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Users_model extends MY_Model { // 関連付けるテーブル var $table = "users"; // SELECT句で取得する列 var $cols = array( ); // JOINするテーブル(JOIN対象テーブル、ONの内容、結合方法) var $join = array( ); function __construct() { parent::__construct(); } } ?> |
このようなファイルをテーブルごとに作るんです。usersという箇所を該当テーブル名に差し替えればいいだけ。
これで準備完了です。
非常にDBへのアクセスが楽になります。
たとえばusersモデルからusersテーブルにアクセスする時は
$result = $this->get(array('users.id' -> $id));
これだけ。
コントローラからusersテーブルにアクセスするときは、
$this->load->model('users_model');
$users = $this->users_model->get();
簡単ですよね。
このモデル拡張は他にも機能をもたせています。簡単にjoinさせることもできますし、すこし複雑なクエリもSQLを書かずして構築できます。それは次回以降にご紹介しましょう。
この続きは現在作成中です。