まず操作対象となるテーブルを作成します。
基本的に poko2 では、一つのPHPファイルで一つのテーブルを操作します。
今回作成するテストツールは、添付のsimple_dbmanager.sql中の
sampleテーブルを操作します。
(sample_subは、グループ名を保持する外部テーブルです)
(データベースを作成できるユーザーをまだ登録していない場合は、createuser
コマンドなどで登録してください)
以下の説明はユーザー名「fenjin」、データベース名は「testsuite」が利用 可能なものとします。
simple_dbmanager.sqlをSQL文として実行させるには、以下のコマンドを実行
します。
$ psql -f simple_dbmanager.sql testsuite
psqlで testsuite データベースに接続し、実際にテーブルが作成されたか
確認してください。
requireとinclude
アプリケーションの作成に入ります。ファイル名は
"simple_dbmanager.php"
とします。
最初にHTMLヘッダとフッタを作ります。
<html>
<body>
</body>
</html>
次にPHPタグを挿入し、必要なクラスファイルをrequireまたはinclude
します。
<html>
<body>
<?php
$include_dir = ".."; // 適切に変更してください。
require($include_dir."sql_driver/cls_sqldriver.php");
require($include_dir."kernel/cls_form.php");
require($include_dir."kernel/cls_dbmanager.php");
?>
</body>
</html>
$include_dir変数はpoko2をインストールしたディレクトリに合わせて
ください。
例えば、usr/lib/php/php/poko2//以下にインストールした場合、
$include_dir = "/usr/lib/php/php/poko2";
とします。
この段階で実際にファイルにHTTPでアクセスし、エラーが無いか確認します。
php.iniなどで"display_errors"もチェックしておくことを
お勧めします。
php.iniの置き場所は...
$ rpm -ql php | grep php.ini
(RedHatLinut, TurboLinux などでは /etc/httpd/php.ini)usr/local/lib/php.ini/usr/local/lib/php/php.ini/poko2の提供する各種クラスは、そのままでは利用できません。
利用するデータベースやアプリケーションに応じて、適切にクラスを派生させて
利用することになります。したがって、このセクションでまず行うことは
CDbManagerクラスを派生することです。コンストラクタの設定も忘れずに
行います。
...
require($include_dir."kernel/cls_dbmanager.php");
class MyDbManager extends CDbManager {
// PHPでは親クラスのコンストラクタを明示的に呼び出す必要があります。
function MyDbMangaer($SqlConfig, $TableInfo, $TableStruct)
{
// CDbManagerのコンストラクタを呼び出します。
$this->CDbManager($SqlConfig, $TableInfo, $TableStruct);
}
};
?>
...
コンストラクタの引数を説明します。
DBと接続するためのパラメータの連想配列
操作対象テーブル名、そのプライマリキーフィールド名など
テーブルのフィールド情報を格納した多重連想配列
$SqlConfig,
$TableInfo, $TableStruct が連想配列で定義されています。
$TableInfo[FormValueArray]は、DbManagerのメソッド中でPHPに渡される
引数の配列としてどれを使用するかを指定します。通常は下記のうちいずれか
を指示します。
$TableStructを構成する各連想配列についての解説は面倒臭いので省略します。
とりあえず、これら三種類の配列が用意できたらいよいよDbManagerの
インスタンスを作成します。実際にデータベースと接続してみます。
...
class MyDbManager extends CDbManager {
...
};
$ObjDbManager = new MyDbManager(
$SqlConfig,
$TableInfo,
$TableStruct);
$ObjDbManager->SqlConnect();
$ObjDbManager->SqlDisconnect();
?>
...
エラーが発生した場合、SqlConfig配列のパラメータをチェックして下さい。 エラーが無ければ、最後に CForm のインスタンスを作成しておきます。
CFormとはHTMLフォームタグを出力するためのメソッドを集約したクラス です。定義ファイルは /kernel/cls_form.php です。 CDbManagerではレコードの追加や更新時に表示する入力フォームの出力 に CForm を利用しています。
...
class MyDbManager extends CDbManager {
...
};
$ObjForm = new CForm;
$ObjForm->Action = $HTTP_SERVER_VARS[PHP_SELF];
$ObjForm->Method = "post";
$ObjDbManager = new MyDbManager(...);
$ObjDbManager->ObjForm = &$ObjForm;
$ObjDbManager->SqlConnect();
...
念のため、エラーが無いか再度HTTPでアクセスして確認して下さい。
poko2を特徴づけるもの。それはフレームワークを重要視する姿勢です。
つまりテーブル操作を以下のような流れにまとめ、それぞれを動作モード で切替えるような仕掛けにし、一つのPHPファイルで実現できるようにするという 一種の"思想"です。
PHPの参考書で良く使われる実装は、INSERT, UPDATE, DELETE, LISTUP,
DETAIL を(程度の差こそあれ)別ファイルに分ける方法です。
poko2ではファイルで分けるのではなく、URL引数でもってswitch
する方法を採ります。
それを実現するためのフレームワークをこれから準備します。 最初に、動作モードを切替えるためのswitch文を用意します。
...
class MyDbManager extends CDbManager {
(下の1行を付け足して下さい。この意味は「3-3. 入力値チェックの
カスタマイズ」で説明します)
var $IsDCEMessageBuffering = false;
...
$ObjDbManager->SqlConnect();
// HTML出力のバッファ
$html = "";
switch($HTTP_GET_VARS[mode]) {
case "detail":
$pkey = $HTTP_GET_VARS[$ObjDbManager->PKFieldName];
$ObjDbManager->GetDetail($html, $pkey);
print $html;
break;
case "insert":
$ObjDbManager->InsertTable($html);
print $html;
break;
default:
$ObjDbManager->GetList($html);
print $html;
}
$ObjDbManager->SqlDisconnect();
?>
<a href="JavaScript:history.go(-1)" class="pdbm">back</a>
</body>
...
実際にブラウザで確認してみて下さい。
デフォルトでテーブル中のレコードが一覧表示されるはずです。
ID部分にリンクが張ってあります。そのURLを見てみましょう。
http://../simple_dbmanager.php?id=7&mode=detail
となっているはずです。modeがdetailとして渡されています。
では実際にクリックしてみましょう。該当IDのレコードの詳細が表示される
はずです。戻るためには、下部の back リンクを辿ります。
ソースコードに戻ると、switch文で GET 変数のmodeがdetailの
時は、GetDetail()を実行するようになっています。
GetDetail()は、引数で渡された プライマリキーのレコードの詳細を表示
するメソッドです。まさにその通りになりました。
では続いて、ブラウザで直接以下のURLへアクセスして下さい。
http://..../simple_dbmanager?mode=insert
ソースコードを信じれば、modefがinsertなので、InsertTable()
というメソッドへ switchされる筈です。
InsertTable()は、新規レコード追加用の入力フォームを表示するメソッド
です。こんな風にレコード操作を一つのファイルで switch して実現する
ための仕組みが、poko2 の基本思想であり、基本フレームワークです。
注意点ですが、$htmlという変数を渡したのち、printしています。
CDbManagerのテーブル出力系主要メソッドでは、HTML出力バッファへの参照
を受け取り、その中にHTMLテーブルを出力します。そのため、実際に画面に表示
するためには、呼出側で print する必要があります。
(なんでそんな面倒な仕組にしたんだ、と思われる場合は、テンプレートと結合
させる時を想像して下さい。メソッド側の内部で直接 print せずに、呼出側の
HTML出力バッファに書き込むこの仕掛けの方が圧倒的に自由度と柔軟性が高い
筈です。)
ではこれに肉付けをしてみます。具体的にはFormタグやリンクを追加し、
URLを叩かなくてもリンクやボタンをクリックして動作モードを指示できる
ようにします。
そのためには、幾つかの小さな関数を追加すると良いでしょう。
というのは似たようなフォームを出力する場面が多いからです。
というわけで次へ進んで下さい。
肉付け。すなわち、フォームタグやボタン。或はmodeを切替えるURLリンク。
これらは基本的にモード名が違うだけだったり、ボタンの場合はキャプション
が異なるだけで、関数化できます。
しかし、表示箇所や表示フォーマットはアプリケーションにより千差万別です。
テンプレートを使う場合もあるかもしれません。
そうした場合、無闇にライブラリに組み込み、その仕様を理解してカスタム
するよりは、その時々でスクラッチから書いた方が負担が少ないと考えました。
そのためpoko2には、そうしたアプリケーションによって大きく変化
するような関数やクラスは含めないことにしています。
今回、mode変数を指示するにはリンクとフォームの両方を使います。
GET(リンク)INSERTモード: GET(リンク)POST(フォーム)リンクは簡単に出力できるので関数化するまでもありません。
フォームタグに付いては、CForm::HiddenFormなどが使えます。
しかし、フォームタグ使用時のナビゲーションのための Submit ボタンの
出力は二種類できてしまいます。
Submit単独Submit + Reset
...
$ObjDbManager->SqlConnect();
// Submit ボタン単独表示
function print_s_button($submit = "- send -")
{
global $ObjForm;
print("<br><center>\n");
$ObjForm->SubmitForm(array(
"value" => $submit,
"class" => "pdbmbtn"
));
print("</center>\n");
}
// Submit + Reset ボタン表示
function print_sr_button($submit = "- send -", $reset = "- clear -")
{
global $ObjForm;
print("<br><center>\n");
$ObjForm->SubmitForm(array(
"value" => $submit,
"class" => "pdbmbtn"
));
$ObjForm->ResetForm(array(
"value" => $reset,
"class" => "pdbmbtn"
));
print("</center>\n");
}
switch($HTTP_GET_VARS[mode]) {
...
実際に関数を実装する場合、全体のHTMLデザインとの兼ね合いで様々なパターン
がありえます。いずれにせよ、poko2はシンプルなソースコードと自由な拡張性を優先
しているため、こうしたこまごまとした関数は自前で用意しなければなりません。
面倒ですが、その分poko2を"勉強"する時間は少なくなります。
それでは実際に肉づけ、すなわちフレームワークの実装に入ります。
実装すべき操作はINSERT, UPDATE, DELETEがありますが、ここでは代表として
INSERT操作の流れを肉づけしてみます。他の操作に関してもおおよそ同じ流れ
になりますので、最終的なものは実際のソースコードを参照して下さい。
...
case "insert":
$ObjForm->Action .= "?mode=insert_confirm";
$ObjForm->StartForm();
$ObjDbManager->InsertTable($html);
print $html;
// 入力フォームがあるので、Submit + Reset フォーム
print_sr_button();
$ObjForm->EndForm();
break;
case "insert_confirm":
$ObjForm->Action .= "?mode=insert_execute";
$ObjForm->StartForm();
// 入力値にエラーがあると、InsertConfirm() は false を返します。
// エラーが無い場合(true)、insert_execute へナビゲートする単独
// Submit ボタンを出力します。
if($ObjDbManager->InsertConfirm($html)) {
print $html;
print_s_button();
}
$ObjForm->EndForm();
break;
case "insert_execute":
$ObjDbManager->InsertExecute($html);
print $html;
print("<center><a href=\"$HTTP_SERVER_VARS[PHP_SELF]\" ".
"class=\"pdbm\" >list</a></center>\n");
break;
default:
...
INSERTの時は、formのactionオプションで次のモードである
insert_confirmを指示します。insert_confirmの時は次の
insert_executeを指示し、 最後にSQL文の実行、デフォルトの一覧表示に
戻るためのリンクを出力します。
次の二つが非常に重要な注意点です。
switchするための変数はGETである。DbManagerが入力フォームの値を受け取るにはPOSTを使う。switch($HTTP_GET_VARS[mode])」に対応します。"FormValueArray" => "
HTTP_POST_VARS"」の部分です。
この二つがなぜ重要かというと、モード切替え用の変数とフォーム入力値
取得用変数を取り違えるとうまく動かなくなるからです。
例えばcase "insert" 時に $ObjForm->Action で
GET により次のモードを指定していますが、これを
case "insert":
$ObjForm->StartForm();
$ObjForm->HiddenForm(array("mode" => "insert_confirm"));
に変更してみて下さい。- send -」ボタン(submitフォーム)をクリックしても、次の
モードであるinsert_confirmへ切り替わらないはずです。switch文で評価しているのが GET 変数だからです。
では switch 文を POST で評価すれば全てフォームのボタンでナビゲート
できるのでしょうか?
いいえ。できません。
というのは、レコード一覧表示時にプライマリキーとなるフィールドに
自動的に張られるリンクがあるからです。
CDbManager::GetList() メソッドで出力されるこのリンクは、"
mode=detail"という文字列がURLに強制的に付くように調整されています。
これがあるため、モード切替えは GET で行った方がよいわけです。
もっとも、GET用switchをPOST用switchに組み込んでしまう方法もあります。
switch($HTTP_POST_VARS[mode]) {
case "insert_confirm":
...
case "insert_execute":
...
default:
switch($HTTP_GET_VARS[mode]) {
case "detail":
$ObjDbManager->GetDetail(...);
break;
case "insert":
$ObjForm->StartForm();
$ObjForm->HiddenForm(array("mode" => "insert_confirm"));
...
break;
default:
$ObjDbManager->GetList(...);
}
}
こうしても問題ありません。実際、セッション変数を併用した込み入った
アプリケーションなどでは、GET, POST, SESSION それぞれで switch させ、
適当なメソッドを呼び出して連携させる場合も無いわけではありません。