以前の記事でDBの使い方を簡単に解説してきました。
そして、世の中ではO/Rマッパーと呼ばれるものでDBを扱うプログラムを実装している事があるのでその紹介です。
O/Rマッパーとは
O/RマッパーはObject/Relationalマッパーの略で、オブジェクト指向のプログラムと、RDBをライブラリで橋渡ししようという考え方です。
賛否両論あるO/Rマッパーですが、小規模なツールであればメリットの方が大きいだろうと思います。
Idiorm & Paris
プログラム言語ごとや、DBごとにいろいろ存在しますが、今回はphp向けのO/Rマッパーの1つであるIdiorm & Parisを紹介します。
上記ページからIdiormとParisのコードをそれぞれダウンロードします。ダウンロードは各githubのページの「Release」からzipが落とせます。
解凍したら使いたいphpのところにコピーします。
これで使う準備が整いました。
サンプル
新たにこれを試しに使ってみるサンプルphpを用意します。
<?php // idiormとparisを使えるように設定 require_once dirname(__FILE__) . '/idiorm/idiorm.php'; require_once dirname(__FILE__) . '/paris/paris.php'; // データベースの接続設定 ORM::configure('mysql:dbname=health;host=localhost:3306;charset=utf8'); ORM::configure('username', 'root'); ORM::configure('password', ''); /** blood_pressuresテーブルを表すクラスを定義 */ class BloodPressures extends Model // Modelを継承しているとテーブルと紐づく { /** 対応させるテーブル名 */ public static $_table = 'blood_pressures'; /** 行を特定する為に利用するカラムの設定 */ public static $_id_column = 'id'; } /** usersテーブルを表すクラスを定義 */ class Users extends Model // Modelを継承しているとテーブルと紐づく { /** 対応させるテーブル名 */ public static $_table = 'users'; /** 行を特定する為に利用するカラムの設定 */ public static $_id_column = 'id'; /** このユーザーのblood_pressuresを取得する */ public function blood_pressures() { // BloodPressuresのuser_idカラムで紐づけする return $this->has_many('BloodPressures', 'user_id'); } } // idが1のユーザーを取得 $user = Model::factory('Users')->find_one(1); // 名前を表示 (usersテーブルのnameカラム) echo $user->user_name . '<br />'; // 血圧を取得(BloodPressuresの配列) $bloodPressures = $user->blood_pressures()->find_many(); // 取得した血圧をすべて表示 foreach ($bloodPressures as $data) { echo $data->timing; echo '/'; echo $data->high; echo '/'; echo $data->low; echo '/'; echo $data->pulse; echo '<br />'; } ?>
ファイル名はormapper.phpにしました。ユーザーIDが1の人の名前と血圧情報を一覧します。
で確認できます。動かない場合は解凍がちゃんと出来ているかや、フォルダ構成があっているかを確認してください。
(フォルダがidiorm\idiorm\ソース達、みたいになっていると動きません。idorm\ソース達、になるようにしてください。)
自分 2016-05-28 09:00:00/115/90/80 2016-05-29 09:00:00/120/90/70 2016-05-30 09:00:00/100/80/110 2016-05-31 09:00:00/105/90/80 2016-05-29 21:50:04/150/20/100 2016-06-04 21:36:17/100/100/100 2016-06-04 21:36:38/100/100/100
私のところはこのようになりました。phpMyAdminと照らし合わせてみると確かにユーザーIDが1の人の情報です。
中身
さて、中身ですが、SQLが一切なくなりました。ORマッパーが中で良いようにしてくれいます。
各テーブルはクラスと1対1に紐づくようにもなっています。例えばusersテーブルはUsersクラスで表されています。
usersテーブルからidが1の人を検索する場合は、Model::factory関数にクラス名(Users)を指定して、テーブルオブジェクトを取得。そのテーブルオブジェクトのfind_one関数にidを指定して検索します。
usersテーブルと紐づいているblood_pressuresテーブルの情報の取得もSQL文を書いていません。has_many関数に関連づいているクラス名とそのカラムを指定しているだけです。
このように、ORマッパーではテーブルをクラスに置き換えます。(モノによってはクラスを直接用意しないこともありますが…)
関連付けに関しても、メンバ変数や関数で解決できるケースが多いです。
DBの更新
DBの更新も対応しています。Idiorm&Parisの場合は以下のように書けば更新されます。
$user->user_name = '新しい値'; $user->save();
対応したカラムに値を入れて、save関数を呼ぶだけです。
新たに行を挿入する場合は
$newUser = Model::factory('Users')->Create(); $newUser->user_name = '新しいユーザー'; $newUser->save();
このような感じです。テーブルのCreate関数で新規で作り、値を設定し、saveでDBに反映する。という流れです。
メリット
- プログラムで記述する内容と、テーブルやカラムの内容が紐づいているので、特に簡単な処理においてはSQLを書くのに比べて見やすさが絶大です。
- SQL文を知らなくてもDB操作がお手軽にできます。
- SQLインジェクションなんかの脆弱性対策も一緒にしてくれていることが多いです。(100%とは言えませんが…)
- 再利用しづらいSQL文をプログラム中に書かなくて良いです。
デメリット
- 各ORマッパーごとでの仕様や使い勝手の違いがとても大きいです。
- 複雑なSQL文は実行できないか、できても煩雑になることが多いです。
- 複雑なテーブル構成の場合に上手くマッピングできない事があります。
- パフォーマンスが落ちることが多いです。
- SQL文やDBの事をあまり知らなくても使えてしまう、というのが諸刃の剣です。
- DBの設計変更でマッピングされたクラスの変更が発生します。(が、それは往々にして手動です。)
リレーショナルモデルとオブジェクト指向は結局相いれないので、RDBの機能を最大限使えば使うほど歪みが出てくることが多いです。
例えば、上記血圧のサンプルで以下のようなコードを記述しても期待通りの動きはしません。
// 取得した血圧を1増やす foreach ($bloodPressures as $data) { $data->high += 1; } $user->save();
$userから取得した血圧情報を変更して、$user->saveをしていますが、これは保存されません。
$data->save()を都度呼ぶと保存はされますが、userが持っている血圧情報をuserを介して更新するというのはオブジェクト指向的には普通ですが、このORマッパーではそれは出来ません。
とは言っても、逆に言えばあまりRDBの機能を使っていない場合なら、あまりデメリットなく恩恵を受けれることが多いと考えています。
ちょっとしたツールでSQL文調べながら作るのはメンドクサイ…といったケースではそれなりに有用ではないでしょうか。