Sql_Query は簡単なテーブルへのクエリーに関するクエリーフォームを次のように 生成します。 フィールド名のリスト、比較演算子、入力フィールドが表示されます。 ユーザーは、SQL 標準の演算子を用いて表示された任意の列について任意の値を 検索できます。 複数の検索条件も指定可能で、検索条件はANDやOR演算子を用いて結合することが できます。
クエリー条件の個数は可変にできます。この場合、ユーザーは適当なボタンを 用いてクエリーウィジェットを絞ったり拡大したりできます。
全てのボタンラベルやインターフェース上の他のメッセージは変更可能であり、 言語辞書に格納されています。現時点では de(独語)と en (英語)に関する辞書が提供されています。
classname | シリアル化ヘルパ: このクラスの名前。 |
persistent_slots | シリアル化ヘルパ: 全ての持続的スロットの名前 |
conditions | クエリ条件の個数 |
input_size | 入力フィールドで見える最大文字数 |
input_max | 入力フィールドの最大入力文字数 |
method | フォーム送信手順(GETまたはPOST) |
lang | 使用する言語辞書 |
translate | カラム名を変換するかどうかのフラグ |
container | マスターコンテナを作成するかどうかのフラグ |
variable | リサイズボタンを作成するかどうかのフラグ |
|
dict | GUI言語辞書 |
compare | SQL比較関数辞書
|
初期化関数。現時点では内容がありません。
この関数は、SQLクエリー選択フォーム用にHTMLを生成して返します。
フォーム内の全ての変数は、$base
というプレフィックスで始まり、
下線文字の後に数字のインデックス番号が付加されます。異なる base 文字を
使用することにより一つのページに複数の Sql_Query インスタンスを持つことができます。
この関数は、クエリーを行うSQLテーブルのフィールド名を知る必要があります。
$option
にこれらのフィールド名の配列
($translate
は空となります)もしくはフィールド名から
長い名前へのハッシュ($translate
はon
に設定されます)
のいずれかを指定することが可能です。
$class
にCSSクラス名が指定されている場合、
生成されるフォームの全てのタグはCSSスタイルシートクラスによってタグ付けされます。
$class
はオプションであり、空の場合にはクラス属性は生成されません。
$target
はSQLクエリーフォームのターゲットのURLです。
これはオプションであり、空の場合は自分自身を参照するフォームが生成されます(推奨)。
この関数は、SQLクエリ選択フォームを描画するHTMLを有する文字列を返します。
form()
によって生成されたページが送信された際には、
多くのパラメータを評価し、ユーザーの選択に応じたSQLのwhere句の
検索条件に変換する必要があります。
where()
関数はこれらの全ての面倒を見ます。
この関数は、form()
をコールする際に使用したプレフィックス$base
を必要とします。
$incr
パラメータはオプションであり、"More"もしくは"Fewer"ボタンが
使用された際に追加あるいは削除するクエリ条件の行数を定義します。デフォルト値は1です。
この関数は、SQLクエリにおいて "where"句としてエラーなく使用された文字列を返します。
この関数は、where()
の全ての動作を行いますが、クエリ条件ウィンドウの
サイズを変えません。
Sql_Query
クラスは直接使用することが可能です。
持続化するとより便利となりますので、prepend.php3
ファイル
の適当な場所にrequire("sqlquery.inc")
を追加することを
お勧めします。
検索結果を表示し整形する優れた方法についてはこの章のTableクラスを 参照下さい。データベースに接続する優れた方法については、DB_Sql クラス (コアクラスの一つです)を参照下さい。
以下のコードサンプルはかなり長いですが、 Sql_Query, DB_Sql,Tableクラスを用いてデータベースのテーブルにクエリを行う 完全に動作する例となっています。
<?php
// この例を動作させるためには、preprend.incでsqlquery.incと
// table.incをrequire()することが必要!
page_open(array("sess" => "Example_Session"));
$db = new DB_Example; // これはDB_Sqlのサブクラス
$t = new Table; // 結果整形用
$t->heading = "on"; // 見出しが必要だ
?>
<html>
<head><title>Testseite</title>
<style type="text/css"><!--
h1 { font-family: arial, helvetica, sans-serif; color: #d33e30 }
table.test { background-color: #eeeeee }
th.test { font-family: arial, helvetica, sans-serif }
td.test { font-family: arial, helvetica, sans-serif }
table.query { background-color: #cccccc }
td.query { font-face: arial, helvetica, sans-serif }
--></style>
</head>
<body bgcolor="#ffffff">
<h1>Testpage</h1>
<?php
// 以下のフィールドは選択可能
$field = array(
"username" => "Login Name",
"password" => "Password",
"perms" => "Permissions"
);
// 最初にこのページを開いたときには、$q は存在しません
if (!isset($q)) {
$q = new Sql_Query; // 以下のような問い合わせフォームを作る
$q->conditions = 1; // ... まず最初に単一の問い合わせ条件を持ち、
$q->translate = "on"; // ... 列名は変換され、
$q->container = "on"; // ... 見易いコンテナテーブルを持ち、
$q->variable = "on"; // ... 検索条件の個数は可変であり、
$q->lang = "en"; // ... 英語で、よろしく
$sess->register("q"); // これを忘れちゃいけない!
}
// このページを二度目に開いたときは、$baseで指定した名前の配列が
// 設定されており、$queryを作成する必要があります。
// $q が作成したSql_Query オブジェクトの場合、$baseを"q"に
// 設定してはいけません... :-)
if (isset($x)) {
$query = $q->where("x", 1);
}
// どんな場合でもフォームを今表示しなければいけません。
// ここでの"x"と$q->whereが一致していなければならないということに
// 注意してください。CSS"query"クラスとして全要素にタグを付けます。
printf($q->form("x", $field, "query"));
printf("<hr>");
// 適正なクエリ文字列を有しているか?
if ($query) {
// 検索条件を表示する
printf("Query Condition = %s<br>\n", $query);
// クエリ実行
$db->query("select * from auth_user where ". $query);
// 結果の出力 (CSS class test としてタグづけ)
printf("Query Results = %s<br>\n", $db->num_rows());
$t->show_result($db, "test");
}
page_close();
?>
</body>
</html>
Tableクラスは2次元の連想配列データやデータベースクエリの結果を テーブルとして整形する優れた方法です。 Tableとそのサブクラスに配列またはクエリ結果のいずれかを指定することにより、 全ての値を含むテーブルの正しいHTMLを出力することが可能となります。 Table はそれ自体に簡単なフィルタリング能力を持っており、サブクラス化しなくても 単独で使用可能です。Tableの全ての機能を利用するには、専用のサブクラスを作成する必要があります。
checkオプションと同時に使用した際、テーブルはHTML要素FORM
の一部であると仮定されます。
それぞれの表の行の前にinput type="checkbox"
を作成する
コードが生成されます。
このチェックボックスは行番号に対応する連番を持つ配列として構成されます。
その配列名はcheck
インスタンス変数にセットされた内容が常に使われます。
表の各行を生成する際に、2種類のカラムフィルタのどちらか1つが使用されます。
fields
インスタンス変数に値が指定されている場合、その配列中で
フィールド名がキーになっているカラムだけがその配列内の順番で出力されます。
つまり、fields
インスタンス変数にarray("a", "c", "e")
を
指定した場合、a
, c
, e
というカラムだけが
生成される表の要素になるということです。
fields
に値を指定しない場合、全てのデータ列がeach()
で
走査され、filter
中の正規表現に一致する名前の全てのカラムが表に表示されます。
デフォルトでは、この正規表現はアルファベットで始まり、残りが英数字または "_" (下線)
になっている全てのカラム名を通過させます。
これがデフォルトとして選ばれたのは、DB_Sqlデータベースクラスがデータベースからデータを
取得するためにmysql_fetch_array()
を内部的に用いており、この関数が
数値インデックスと本当のカラム名の両方の形式で2重に全てのカラムを返すからです。
デフォルトのフィルターは正しいカラム名で全てのデータを一度だけ表示させます。
加えて、インスタンス変数map_cols
によりカラム名の再マッピング
が可能です。map_cols
に値が定義されている場合、見つかったカラム名を
新しい名前で置き換えます。
すなわち、fname
、lname
、mydate
というカラムを
持つテーブルを以下のコードを用いることにより名前
, 姓
,
日付
に対応させることができます。
(なお、ここで$tは初期化したTableクラスのオブジェクトです)
$t->map_cols = array("fname" => "名前",
"lname" => "姓",
"mydate" => "日付");
インスタンス変数map_cols
は、これと同じテクニックを用いて異なる言語の名前に
カラムを対応づけることができます。
派生クラス用にインスタンス変数add_extra
が追加されています。
この変数に値が指定されている場合、関数table_heading_row_add_extra()
および
table_row_add_extra()
がコールされます。Table クラスにおいてはこれらの関数
は何もしませんが、派生クラスでこれらの関数に必要とされるさらなる機能性を提供するよう
オーバーライドすることができます。すなわち、その行の編集
、削除
、
詳細表示
機能を提供するハイパーリンクをこれらの関数に容易に追加することができ、
大幅なカスタマイズを行えます。
Tableのサブクラスの一つであるCSV_Tableは最小の努力であなたのデータをCSV形式で
生成することができるように提供されています。CSV(コンマ区切りのデータ)は、MySQLの
LOAD DATA INFILE
文でインポートしたり、多くの表計算ソフトのインポート機能に
よるインポートが可能です。
Tableクラスは、現在モジュール化により高レベル、中レベル、低レベルの関数を提供しています。
プログラマーは、シンプルな高レベル関数を使うことや、複雑さの度合いに応じて中または
低レベルの関数のパワーを用いることができます。過去の互換性を維持するためのあらゆる努力を
していますが、Tableクラスを広範囲に使用するならば新しい関数に慣れ親しんでおくのもよい
考えです。通常、高レベルと中レベルのサポート関数は show_
で始まりますが、
低レベル関数ではそうではありません。
classname (クラス名) |
シリアル化ヘルパ: このクラスの名前 |
check | 設定されている場合、チェックオプションが有効になります。 |
filter | 表示するカラムを選択する正規表現。 |
fields | 表示するカラム名のリスト。 |
heading | フラグ: 値が指定されている場合、見出しが生成されます。 |
map_cols | 静的なカラム名を置換されるカラム名のリスト。 |
add_extra | フラグ: 値が指定されている場合、見出しと行用の拡張関数が呼ばれます。 |
上で説明したフィルタリング則に基づき、2次元配列(またはハッシュ)$ary
を
テーブルとして整形し表示します。$class
に値が指定されていたら、
それぞれのHTML要素はそこで名前が指定されたクラスに属するものとしてタグが生成されます。
これはカスケーディング・スタイルシートを使用する場合に有用です。
show()
と同じですが、start
を始点としてnum
個の
要素だけが表示されます。
$db
の結果セットを整形し表示します。$db
は、クエリー
を発行したDB_Sql
のサブクラスであることが想定されています。
Table
は、$db->next_record()
を繰り返しコールして
クエリーの結果セットから全ての結果を取得し、テーブル形式で出力します。
show_result()
と同じですが、start
を始点としてnum
個の要素だけを表示します。
指定した配列を走査し、データの各行をHTMLテーブルの行として表示します。
指定したデータベースオブジェクトを走査し、各レコードをHTML テーブルの行として表示します。
指定した配列を走査し、データの各行をHTMLテーブル行として表示します。ただし、
$start
要素まで表示を開始せず、$num
行表示した後も表示しません。
指定したデータベースオブジェクトを指定し、各レコードをHTMLテーブル行として表示します。
ただし、$start
レコードまで表示を開始せず、$num
個のレコードを
表示した後も表示しません。
HTMLヘッダー行を生成するために指定した配列を使います。
HTMLヘッダー行を生成するために指定したデータベースオブジェクトを使います。
指定した配列を走査し、それぞれのアイテムをHTMLテーブルヘッダーセルの中で表示します。
指定した配列を走査し、それぞれのアイテムをHTMLテーブルセルの中で表示します。
この関数は、Table
のサブクラスでオーバーライドすることができます。
テーブル作成の一番最初で呼び出され、テーブルを開くためのHTMLを出力します。
(例えばprintf("<table%s>\n", $class?" class=$class":"");
)
この関数は、Table
のサブクラスでオーバーライドすることができます。
テーブル作成の一番最後で呼び出され、テーブルを閉じるためのHTMLを出力します。
(例えばprintf("<table>\n");/
)
テーブルの列名のリストを生成するための内部ドライバ関数。
テーブルの先頭行を生成するための内部ドライバ関数。
この関数は、Table
のサブクラスでオーバーライドすることができます。
テーブルの先頭セルが生成される度にコールされます。
$col
は現在のカラム番号、$val
はカラム名、$class
は生成される要素のためのHTML CSSクラスです。
ヘッダーセルを開始します。
ヘッダーセルを終了します。
派生クラスのための仮想関数です。この関数は全てのヘッダーセルが生成された後にコールされます。 この関数によりプログラマーはヘッダー行を閉じる前にHTMLコードを追加することができます。
テーブルの行を生成するための内部的なドライバ関数です。
この関数は、Table
のサブクラスでオーバーライドすることができます。
一番最初の行が生成されるときにコールされ、テーブルの行を開始するHTMLを出力します。
$row
は現在の行番号です。$data
はこの行のために列名/値の
ペアのハッシュであり、$class
は生成する全ての要素のためのHTML CSSクラスです。
この関数は、Table
のサブクラスでオーバーライドすることができます。
一番最後の行が生成されるときにコールされ、テーブルの行を閉じるHTMLを出力します。
この関数は、Table
のサブクラスでオーバーライドすることができます。
テーブルのセルが生成されるたびにコールされます。
$row
は現在の行番号、$cell
は現在のセル番号、
$key
は現在のカラム名、$val
はセルの値、$class
は生成されているその要素のHTML CSSクラスです。
セルを開始します。
セルを終了します。
この関数は、そのカラムのチェックボックスオプションと一致する空のヘッダーセルを生成します。
チェックボックスを表示するためのHTMLコードを出力します。この関数は、メンバー変数
$check
に値が設定されているときに実行されます。$check
には、
$data
配列の中のあるキーがセットされている必要があります。
(例えば、$data["myKey"]
ならば$check="myKey"
をセットします)
インスタンス変数$check
に値がセットされているときに限り、指定したデータを
基づきHTMLチェックボックスを生成します。
Tableはそれぞれのページで自動的にインクルードされたり事前に読み込まれたりされません。 Tableクラスを使うページでTableクラスをインクルードし、Tableのインスタンスを作成してください。
<?php
// Table のインクルード
require("table.inc");
// Table インスタンスの生成
$t = new Table;
// 表の見出しを出力する
$t->heading = "on";
次に2次元配列を生成するかデータベースクエリーの準備をし、 それを表示する表を作成、出力します。
// データベースオブジェクトを生成
$db = new DB_Session;
// $tab という2次元配列を生成
$tab = $db->metadata("active_sessions");
// その配列を表示
$t->show($tab, "metadata");
// データベース問い合わせを準備
$db->query("select * from active_sessions");
// 結果を表示
$t->show_result($db, "data");
Formクラス(しばしばOOH Formsと呼ばれます)はHTMLフォームを扱うための便利な ライブラリです。内容の検証のためにJavascriptとサーバー側スクリプトを用いることができ、 カスタマイズおよび拡張が可能になっています。
OOH Formsライブラリは次の5つのファイルから成っています。oohforms.inc,
of_checkbox.inc, of_radio.inc, of_select.inc, of_text.inc, of_textarea.inc
oohforms.inc
は自動的に他のファイルをインクルードします。
使用するフォーム要素用にファイルを手動でインクルードするようにこれを修正することが
可能です。もしくは、複数のインクルードを行う余計な負荷を避けるために
oohforms.inc
に要素ファイルの内容をカット&ペーストしてもかまいません。
サイトにおける適切なファイル構成を決めることは、読者の課題として残されています。
しかし、ほとんどの目的において、require("oohforms.inc")
で十分でしょう。
一般的に oohforms を用いるページの構造は以下のようになっています。
require("oohforms.inc"); // ライブラリをインクルード
$f = new form; // フォームオブジェクトを生成
$f->add_element(...); // フォーム要素を準備
$f->add_element(...);
$f->add_element(...);
if ($submitname) // 処理するデータはあるか?
if ($err = $f->validate()) { // データは適正か?
echo $err; // 適正ではない; エラーを表示
$f->load_defaults(); // 送信されたデータとともにフォームを読み出し
else {
/* Process data */ // データに問題なし; それについての仕事をする
}
$f->start(...); // フォームの表示を開始
$f->show_element(...); // 要素を表示
$f->show_element(...);
$f->show_element(...);
$->finish(); // フォームの最後
明らかにこのテーマについては多くのバリエーションがありますが、これで基本的な部分を カバーしています。各メソッドについては以下に文書化されています。
開始タグ<form>
を出力し、クラスで必要とされる初期状態を
セットアップします。全ての宣言はオプションですが、JavaScriptによる検査を可能に
するために少なくとも一つは使いたいと思います。$jvsname
はフォーム
へのJavaScriptへのリンクを持つために使われる適当な文字列です。空(デフォルト)の場合、
JavaScript検査は提供されません。$method
はフォームを送信するHTTPメソッドです。
デフォルトは"POST"
です。$action
はフォームを
送信するURLです。デフォルトは$PHP_SELF
です。$target
は
フォームの結果を表示するフレームのターゲットです。デフォルトは_self
です。
フォームに追加されたhiddenフィールド、終了タグ</form>
、
そしてJavascript検査コードを出力します。$after
及び$before
はともにオプションです。いずれかが空でない文字列であれば、フォーム送信前または後に、
フォーム送信時に走る追加のJavaScriptを出力します。宣言の順番は全く洗練されて
いないように見えるかもしれませんが、通常は打ちやすいと思います。
通常、あなたはJavascriptで何か高級なことをするために行われる検査が終わるまで待つでしょうから。
検査とは異なり、OOH Formsはここであなたが使っているJavascriptと同等の機能を
サーバー側で与える術を持ちません。
add_element
はある特定のフォーム要素の属性を定義し、それによって
他のクラスメソッドがそれを適切に用い操作することができるようにするために用いられます。
add_element
は引数を1つだけ取ります。それは連想配列であり、
そのキーになる値のペアはフォーム要素の種類とその種々の属性を定義するために用いられます。
これらの属性のうちあるものはHTML属性に対応していますが、それ以外はoohforms
への機能付加を必要とします。それが受け取る属性と値の文法並びに構文は以下で文書化されています。
全ての型の要素が全ての属性を使うわけではないことに注意してください。
この要素の種類。"submit"
, "hidden"
,
"text"
, "textarea"
, "select"
,
"radio"
, "checkbox"
, "file"
のいずれかです。
この要素の名前を示す文字列。この名前は他の操作への引数として用いられ、生成された
HTMLの中でname=""
として使われます。
(従ってPHPにおける変数名にもなります)配列値を持つ要素にしたい場合でも、名前に
[]
を追加しないでください。multiple
属性を
代わりに設定してください。
このフォーム要素のデフォルト値です。フォーム要素のmultiple
属性に
値が設定されていたら、value
は配列にすることができます。これが
select
要素の場合、value
は、文字表記
(options
配列中のlabel
)か、送信される値
(options
配列中のvalue
)のいずれかを参照することもできます。
oohformsにこの要素が配列値であるとみなすよう通知するフラグです。このフラグは
select
要素でよく利用されますが、text
やcheckbox
要素でも同様に用いることができます。このような要素をoohformsがどう扱うかについては、
show_element
に関する記述を参照してください。
開始タグに挿入される追加HTMLコードです。select
要素では、option
タグではなくselect
タグに挿入されます。
text
要素において、テキスト入力ボックスの文字数幅を指定するHTML size
属性に値をセットします。select
要素においては、選択ボックスのサイズ
(一度に見ることができるオプション数)です。select
要素では
size
が1にセットされている場合にのみ検査が実施されます。
これは、複数のオプションを一度に見ることができた場合には、select
検査は
あまり意味がないからです。file
要素においては、アップロード可能な
ファイルサイズの最大値です。
text
要素のために値がセットされていたら、パスワード要素として、
すなわち入力内容がアスタリスク(*)として表示されるようHTML出力します。
submit
要素のために値がセットされていたら、イメージ要素に変換され、
そのイメージのソースURLとしてsrc
の値が用いられます。
text
要素でmaxlength HTML属性としてそのまま使われます。
length_e
に値がセットされていたら、これはtext
要素が
検査機構により許容される入力の最低限の長さを示します。
値がセットされていたら、text
要素が少なくともminlength
個の文字をもっているかどうかが検査されます。length_e
は検査に失敗した
ときにエラー文字列として用いられます。
値がセットされていたら、text
, radio
または
select
要素で検査が実施されます。text
要素においては、
検査はvalid_
正規表現に合致するかどうか検査されます。
radio
要素は、グループ中の一つが選択されたかどうかを検査します。
select
検査はmultiple
に値がなくsize
が1
であるときにしか機能しません。検査はメニューのオプションの一番目を一種の案内
(例えば「アイテムを選択してください」)とみなして受け入れません。どの場合にも、
valid_e
は検査に失敗したときに用いられるエラー文字列です。
valid_e
に値がセットされていたら、テキストフィールドへの入力を
検査するために正規表現が用いられます。もし入力内容全体に一致する正規表現が
ほしいならば ^...$ を使わないといけない点に注意してください。
もし値がセットされていたら、正規表現で大文字小文字を無視します。
multiple
が使用されていないcheckbox
でのみ使用されます。
checked
が設定されている場合、その要素はチェックされた状態で表示されます。
textarea
要素でrows=
としてそのまま使われます。
textarea
要素でcols=
としてそのまま使われます。
textarea
要素でwrap=
としてそのまま使われます。
select
要素で表示されるオプションの配列です。配列の要素が単純な値
(文字列または数値)なら単純にそのとおりに表示され、その特定のオプションの値として
用いられます。
この要素は、それ自体"label"
と"value"
をキーとする連想配列とすることも可能です。この場合、"label"
が表示され、"value"
の値が送信に用いられます。
例:
$f->add_element(array("type"=>"text",
"name"=>"foo",
"valid_regex"=>"^[a-z]*$",
"valid_e"=>"文字のみ",
"icase"=>1,
"value"=>"bar"));
$f->add_element(array("type"=>"checkbox",
"name"=>"compress",
"multiple"=>1));
$f->add_element(array("type"=>"textarea",
"name"=>"comment",
"rows"=>6,
"cols"=>40,
"value"=>""));
$o = array(array("label"=>"以下から選択してください","value"=>0),
array("label"=>"林檎","value"=>1),
array("label"=>"オレンジ","value"=>2),
array("label"=>"梨","value"=>3),
array("label"=>"葡萄","value"=>4));
$f->add_element(array("type"=>"select",
"name"=>"menu",
"options"=>$o,
"size"=>1,
"valid_e"=>"果物を選択してください",
"value"=>"林檎"));
$name
で名前の指定されたフォーム要素を出力します。通常は2番目の引数は
使いません。multiple
属性がセットされたradio
と
chekbox
要素では必須です。というのも、これらの多くは同じ名前をもつからです。
submit
要素で送信ボタンにラベルを定義するためにも用いられます。
value
属性はsubmit
要素では使われません。配列値を取りうる
他の要素(とりわけtext
要素)については、show_element
を
複数回呼ぶことで逐次値が表示されます。
同じ名前をもつPHP変数の値をフォーム要素のデフォルト値としてセットします。 これは送信されたのと同じ値をフォームで再表示したいときに一般的に用いられます。 引数はオプションです。要素名の配列が指定された場合、これらの要素だけが影響を受けます。
フォーム送信内容を検査します。もし全ての要素が適正であれば$result
を返し、
そうでなければ関係するエラーメッセージ(フォーム要素の valid_e
か
length_e
属性)を返します。$result
は、false
が
デフォルトです。2番目の引数もオプションです。これは検査する要素の名前をもつ配列です。
引数で指定した配列に含まれる名前を持つフォーム要素を凍結します。引数がなければ、 全ての要素を凍結します。凍結された要素は、フォームウィジェットではなく静的な 通常のHTMLとして表示されます。この静的表示には、凍結されていないバージョンの要素を 用いたのと同じ効果をシミュレートするための適切なhidden要素が伴っています。
OOH Formsはオブジェクト指向に基づいているので、要素の型を定義するクラスを拡張する
ことで容易にカスタマイズできます。一般的に、派生クラスはコンストラクタを
持たねばならず、またof_element
のself_*関数の好きなものをオーバーライド
してもよいです。既に存在する要素のソースは、上記作業を適切に行うための最良の文書ですが、
若干の注意すべき点を以下に示します。
凍結されていないこの要素のインスタンスを表示します。
$val
は値が1つであれば、show_element
の引数$value
です。$which
は、値が配列となる要素においてインデックスとして用いられます。
この数は、この要素に関してshow_element
が以前にコールされた回数と
等しいです。この関数はhiddenタグ出力の個数を返さなければなりません。
凍結されたこの要素のインスタンスを表示します。凍結した要素を表示するためのHTML 出力に加えて、この種類の凍結されていない要素を送信することの効果を 2重化するためにhiddenフィールド用のタグを出力する必要があります。この関数は 出力するhiddenタグの個数を返します。
この要素の$val
を検証します。適正であれば、false
を返し、
そうでなければ関連するエラー文字列を返します。
この要素を検証するためのJavascriptコードを表示します。$ndx_array
は、
Javascriptのform.element[]配列で用いられる検証の対象になる要素のインデックスをもった
配列です。これはその要素名を名前として持つ配列中の別の[]
がJavascriptを
混乱させないために必要です。
この要素のデフォルト値を$val
としてセットします。$val
を$this->value
にコピーするだけという、この関数のデフォルトの定義で
必要な全てを満たしていますが、それ以外の何かをすることが必要な特別なケースがあるかも
しれません。例としてチェックボックス要素の実装を参照してください。
tpl_form
クラスは、HTMLフォーム展開のための一般的な枠組みとして提供される
ことが意図されています。このクラスはOOH Forms
ライブラリにかなり
依存しているために、関係する文書を読み、理解することが必要です。
中心になるアイデアは、実際にフォームをHTMLとして出力するためにOOH Forms
に何らかのHTMLコードを混ぜたものをコールすることを可能にするような、
tpl_form
のサブクラスによるブラックボックスを生成する点にあります。
アプリケーションはユーザーから何らかの入力を得るためにこのブラックボックスを
使うことが可能です。アプリケーションはユーザーの入力がどう扱われるかだけでなく、
入力データの適正さをどうやって検証するかについて知っている必要はなくなります。
というのも、内部メソッドがその面倒を見てくれるからです。
このアプローチはOOH Forms
にとても似ています(私はそう思います)が、
それがより高いレベルで実現されているのです。OOH Forms
要素は相互に
通信する手段がありませんし、データの一貫性について"簡単な"
確認しかできませんが、tpl_form
では、複雑なデータ評価や処理のための
一連のインターフェースが追加されています。
さらに、get_default_values
とset_default_values
メソッドは、
フィールド名と値を持つハッシュ配列を用いることで、フォーム変数(a BAD THING (tm))
のシリアル化について心配せずに、セッション間でユーザーの入力を維持する
ために用いることができます。
配列がアプリケーションでデータを共有するために用いられていることに注意してください。
これは一種の無駄ではないかと異議を申し立てるかもしれません。というのも、全ての
ユーザー入力データはグローバル変数かHTTP_POST
またはHTTP_GET
グローバルハッシュで見つけられるからです。これは正しいですし、一般的なケースでは
空の配列を保存したり取り出したりするだけでしょう。values
変数は、
フォームの振る舞いがその前にユーザーの入力したデータに依存するような、非常に複雑な
データエントリの設定に使うことが意図されています。この場合、すべてのフォームが
共同してvalues
ハッシュ配列に読み書きすれば、複数のHTMLページを渡り歩いて
ステップバイステップで最終的な結果を構築することができるようになります。
classname | クラス名です。シリアル化、及びフォームを実際にHTML化することが必要なHTML/PHP
コードを含むテンプレートのファイル名を定義するためにdisplay 中で用いられます。
|
error | validate 及びvalidate_input 操作で生成されたエラー
メッセージを保持します。
|
values (値) |
これは"共有メモリエリア"の1種です; フォームとアプリケーションの間で
用いられます。init メソッドの中で読み出され、get_values
メソッドの中で戻されます。
|
form data (フォームデータ) |
(Form オブジェクトの)フォーム情報を保持します。 |
has defaults (デフォルトあり) |
フラグです。フォームのデフォルト値はset_default_values 操作を
経由して渡されます。ユーザーはこれを置き換えるべきではありません。 |
これはクラスのコンストラクタの1種です。$values はget_values
メソッドにより
アプリケーションへ戻されるフォーム変数を格納する連想配列です。
フォームからユーザーが送信した全てのデータが入っている配列を返します。
この配列はあとでset_defaults_values
へ渡されます。
get_default_values
により返されるのと同じ配列からデフォルト値を復元します。
実際にフォームフィールドを表示します。この操作は派生クラスにより上書きしては いけません。代わりにユーザーはこの派生クラスの名前が付いており、自動的にインクルードされる ".ihtml" 拡張子を持つファイルを提供するべきです。
このメソッドはオーバーライドするべきではありません。アプリケーションとフォームの間の
中心的なインターフェースとして想定されています。
デザイナーのニーズを満たすフォームが適切に派生された後、アプリケーションは
get_values
をコールし、配列を返します。
この配列はinit
に渡され、結局process_input
メソッドにより
修正されます。ユーザの入力が不正の場合はfalse
が返されます。
後者の場合は、アプリケーションは適切なデフォルト値を代入してユーザーにフォームを
(再)表示するためにdisplay()
をコールする必要があります。
"デストラクタ"の1種です。メモリーを多少解放する以外に
このメソッドをコールする必要性はありません。アプリケーションからコールされる
可能性がありますが、それ以外では実行されません。true
を返します。
全てのフィールド情報をもったForm
オブジェクトを初期化します。
hiddenフィールドform_name
が自動的にこのルーチンによって追加され、
ユーザーにより既に送信されているかどうかを確かめるために他のメソッドにより使われます。
派生クラスによりこれを上書きするべきでなく、代わりにsetup_fields
を用いるべきです。true
を返します。
要求を満たすフォームフィールド定義を提供するためにこのメソッドをオーバーライドして ください。
ユーザーの入力を検査します。この操作は派生クラスでオーバーライドするべきでは
ありません。validate_input
をその代わりに参照してください。エラー発生時
にはfalse
を返し、関係するerror
変数に値をセットします。
このメソッドは複雑な確認方法(例えば、フィールド1 == "other;" ならば
フィールド2は空ではいけないとか)を提供したい場合には、派生クラスでオーバーライトする
必要があります。エラーが発生した場合にはfalse
を返し、状況に応じた
エラーメッセージをerror
変数にセットする必要があります。
ユーザーデータを処理します。このメソッドは派生クラスでオーバーライドするべきでは
ありません。process_input
及びprocess_default
を代わりに
参照してください。成功したらtrue
を返し、そうでなければfalse
を返します。
このメソッドは派生クラスでオーバーライドするべきです。検査が終わったら実行されます。
フォームへ渡されるデータはvalues
配列へ代入するために用いられることがあります。
このメソッドは派生クラスでオーバーライドすべきです。フォームの検査が失敗したか、 最初にフォームが表示される前に実行されます。もしデータが以前のアクション、占い、 ペンギンの飛行観察や他の物から抽出されたのであれば、フォーム表示を避けるべきです。
あなたは、彼女(アハ)の名前とe-mailアドレスを入力するフォームを持っているとしましょう。 あなたは、このe-mailアドレスが適正かどうかをチェックしたいと思っています。 そうでなければあなたの盲目的データセットアップは失われます。あ・・・ええと・・・ 構文的にe-mailアドレスを検証する単純な正規表現は以下の例のコードで表現されています。
$this->form_data->add_element(array(
"type"=>"text",
"name"=>"email",
"valid_e"=>"Syntax error in E-Mail address.",
"valid_regex"=>"^([-a-zA-Z0-9.]+@[-a-zA-Z0-9]+(\.[-a-zA-Z0-9]+)+)*$"
));
さて、この短いコードは役目を果たしますが、あなたの今日の気分はとってもイカレているので、
アドレスのホスト名の部分をDNSを使って検証したいと思いました。そこで、あなたは入力内容
の中のホスト名を取得し、適正なホスト名ならtrue
、そうでないなら
false
を返すコードをいっしょにおきます。
(ヒント: PHP Code Exchangeで、
"現在有効な"電子メールの検証のプロシージャを見つけるべきです)
素晴らしいコードが書かれたため、アドレスをチェックできます。ユーザーがフォームに入力
した後、ユーザーの入力内容を解析し、構文エラーがないのでアプリケーションから
mycheckhost
をコールする時になりました。
この関数がOKならばデータベースを更新し、そうでなければフォームにデフォルト値をロードし、
再び表示し、ページを閉じ、終了します。
私は同じようなことを多くのフォームで行い、その中のいくつかは大変複雑な検証手順を 持っており、そして私は駄目で読みにくいコードを生産するのは非常に簡単なことに 気づきました。(そうです、私は、データ検証手順の中のロジックを変更しなければいけないとき に実際に悟りました...)
tpl_form
は、フォームを構築する強固なフレームワークを提供するもの
であり、全てのコードは自己包含的でメインのアプリケーションロジックから分離される
ようになります。あなたが気に入ればいいのですが。
さて、コードをいくつか見てみましょう。まず最初にtpl_form
のサブクラス
のクラス宣言です。
class myform extends tpl_form {
var $classname = "myform";
function setup_fields() {
$this->form_data->add_element(array(
"name"=>"email",
..., // 省略された分は以前のコードを参照
));
$this->form_data->add_element(array(
"name"=>"submit",
"type"=>"submit",
"value"=>"submit"
));
}
function validate_input() {
global $email;
list($uname, $hostname) = split("@", $email);
if (! mycheckhost($hostname)) {
$this->error = sprintf("ごめんなさい、%s は不明なホストです。やり直してください", $hostname);
return false;
}
// 追加のチェックはここに...
return true;
}
}
フォームをHTML化するためにHTMLとPHPコードを含んだmyform.ihtml
を
提供する必要があります。最小限の例を以下に示します。
<html>
<body>
<?php
$this->form_data->start_form($this->classname, "POST", $sess->self_url(), "");
printf("%s<br>\n", $this->error);
$this->form_data->show_element("email");
printf("<br>\n");
$this->form_data->show_element("submit");
$this->form_data->finish();
?>
</body>
</html>
tpl_form
クラスは完成し、あとは芸術家としてのちょっとした仕事を残すだけ
になりました... 8-)。真新しいクラスを用いるためにクラス定義コードをアプリケーションに
組み込み、それから...
$mf = new myform;
$mf->init(array()); // 現時点では、厳密に要求されていない
// が、推奨されている
if ($rv = $mf->getdata()) {
$mf->clear(); // これは厳密に要求されているわけではないが、とにかくメモリーを
// 少しでもフリーにすべきである...
global $email;
// あなたのデータを望むように処理
} else {
$mf->display();
}
この大変小さな例が、少なくともすばやい設計とコード分割の観点からtpl_form
クラスの本当の力を理解する助けになることを願っています。
Treeクラスはディレクトリの階層構造やHTMLにおけるメニューの構造といった ツリー構造を表現します。この構造は適切な深さをもったネストした配列の配列として Treeに与えられなければなりません。
Treeの理念は、ツリー構造はいくつもの数学的モデルで表現することができるという 点にあります。 あるモデルは、ネストした配列かポインタ構造のようなデータ構造で、それから多次元グラフ図を 出力するとか、ツリーのある枝を刈ったり完全な枝を挿入したりといったそれ以外の巧妙な処理が できます。しかし、ツリーを、一次元の文字列か、一連の関数のコール (数学的センスにかなり近いです)としてイメージすることも可能です。
ツリー構造からHTMLコードを生成するためには、このようにします。 ブラウザに何をするかを教える、1次元の文字列を最終的に必要とします。Treeクラスは 以下の方法でこの文字列を生成する手助けをします。すなわち、ツリー全体を一巡し、 通った各ステージにおいて複数の関数をコールするという方法です。 この関数を変更するのは作成者の仕事なので、素晴らしいレイアウトを作成されることでしょう。
classname (クラス名) |
シリアル化ヘルパ: クラスの名前 |
delimiter (区切り) |
"path" を短縮した文字 |
tree (木) |
配列の配列の配列 |
outp | 「出力内容」そのもの |
prfx, sufx, flag | 内部用 - outp を生成するいくつかの補助
|
この関数は完全にユーザー主導です!以下に記述した構造をもった配列を生成しなければ なりません。詳細は例を見てください。
build_tree()
によってコールされる独自の関数を生成することに臆病に
ならないでください。例えば、再帰的コール。
これはこのクラスの中でも最も重要な関数です。適正なパラメータが与えられたら、 正しい順序で出力関数を呼び出します。
全ての変数はオプションです。現在サポートされていませんが、もし部分木を表示したい のであれば、これらパラメータは有益かもしれません。
この関数はほとんどの場合内部的に用いられますが、$this->tree
を生成するときに有益かもしれません。この関数は、パスからPHP3の連想配列の
インデックス文字列を生成します。そのパスは文字列ですが、
$this->delimiter
によって区切られた文字列です。$key
が
指定された場合には$path
に追加されます。
(空のパスかそういったものを考えてください)
例:
$t->delimiter="/";
$path= "usr/local/lib";
## $pathは変数として与えられなければならない、なぜなら参照によってコールされるから!
$bla = $t->path_to_index($path,"etc");
## ここで、$pathは"usr/local/lib/etc"です
## ここで、$blaは["usr"]["local"]["lib"]["etc"]です
この関数は内部では使われませんが、ツリー表示を出力するときに有益かもしれません。 これはパスのある深さから1段取り除きます。
例:
$t->delimiter="/";
$path= "usr/local/lib";
$bla = $t->path_to_parent($path);
## $path は、"usr/local"です
## $bla は、["usr"]["local"]です
この関数は、path_to_index
の「参照でコールされないバージョン」です。
この関数は、$keyをパスに追加して返します。
この関数は、path_to_parent
の「参照でコールされないバージョン」です。
この関数は、パスの親を見つけ、それを返します。
この関数は path_to_index()
の「参照でコールされないバージョン」です。
これはパスにより記述されたツリーへの連想キーを返します。
この関数はツリー表示の出力の開始時にgo_through_tree()
によりコールされます。
全ての*tree
-関数はgo_trough_tree()
によりコールされますが、
素晴らしいレイアウトを提供するためにあなたに順番が回ってきます。
私はこれを用いてほぼあらゆる種類のツリーのレイアウトを生成することが可能であると
考えています。変数を見てください。例えば、$depth
は他の方式であらゆる
"level"を扱うことが可能になります。
この関数はツリー表示の出力の開始時にgo_trough_tree()
によりコールされます。
go_trough_tree()
がそれ自身を再帰的に呼び出すたびに毎回コールされます。
これは、現在の要素がそれ以下の要素を持つ時、とも言えます。
この関数は、現在の要素がそれ以下の要素を持たないときにコールされます。
この関数は、growtree()
の"反対"です。現在の要素がこの
サブリストでの最後の要素であるときに、いつもコールされます。
ツリーを離れるときに呼ばれます。
上述のようにgo_trough_tree()
をコールする前にまず$tree
を生成しておかないといけません。
$tree
は適当な深さを持ったネストした一連の配列です。以下がその例です。
$t= new Tree;
$t->tree = array(
"usr" => array(
0 => "許可",
"lib" => "禁止",
"local" => "許可",
"bin" => "禁止",
"etc" => array(
0 => "許可",
"hosts" => "禁止",
"mailcap"=> "許可"
),
"var" => "許可",
"tmp" => "許可"
),
"root" =>"禁止"
);
$t->go_through_tree();
print $t->outp;
これは完全に再帰的な構造です。関数の再帰的コールによりこれを作成する 方法は明らかだと思います。明らかでないならば、以下の例を参照下さい。
ある小さな奇妙な点について説明が必要です。というのも、これは少々混乱を招くからです。 0(ゼロ)という名前の配列は親の要素の値として用いられます。例で示したように、子供 (例えば"etc")を持つ要素は属性("許可"など)を持つことができません。その代わり、 この要素の値は0という名前をもつ擬似的な子供の中に格納されます。この要素が存在しない場合、 値"Array"(おそらく変更が必要なもの)を持つでしょう。
この例の出力結果は、出力関数を変更していない場合は、以下のようになります。
/
^---- usr->'許可' : 'usr' (1) [1/2]
| ^---- lib->'禁止' : 'usr^lib' (2) [2/7]
| O---- local->'許可' : 'usr^local' (2) [3/7]
| O---- bin->'禁止' : 'usr^bin' (2) [4/7]
| O---- etc->'許可' : 'usr^etc' (2) [5/7]
| | ^---- hosts->'禁止' : 'usr^etc^hosts' (3) [2/3]
| | \--- mailcap->'許可' : 'usr^etc^mailcap' (3) [3/3]
| O---- var->'許可' : 'usr^var' (2) [6/7]
| \--- tmp->'許可' : 'usr^tmp' (2) [7/7]
\--- root->'禁止' : 'root' (1) [2/2]
少し分かりにくいですね。左から右にかけてのフィールドは、それぞれ以下のように なっています。
path_to_*
-関数を参照)
この例はハードディスクのディレクトリ構造を一巡します。
以下のコードでそれを読むことができます。
class dir_Tree extends Tree {
var $classname = "dir_Tree";
var $delimiter="/";
var $tdat;
function build_tree ($path=".") {
$this->tree=$this->recurs_dir($path,0);
}
## この例のコードは、私のシステム (P200, 64MB) ではおよそ 20 秒で
## 多くのサブディレクトリを持つ 1000 のディレクトリエントリを読んで
## 出力できます。最大で 4 の深さを持つ 220 のエントリを 2 秒で読みます。
## OK ですね。
function recurs_dir ($path,$depth) {
GLOBAL $flap_out;
$d=opendir($path);
while ( $name=readdir($d) ) {
$pathname=$path . $this->delimiter . $name;
if (is_dir($pathname) && !ereg("\.\.?",$pathname)) {
if (isset($flap_out[$pathname])) {
$array[$name]=$this->recurs_dir($pathname,$depth+1);
}
# 注意: 配列[0]を埋めるのは、配列の残りの部分の後
# にすることが重要です!
$array[$name][0]=$pathname;
} else {
$array[$name]=$pathname;
}
}
closedir($d);
return($array);
}
#################################################
## INとOUTを切り替えます
## これは見せるべき全てのサブパスを含む配列を
## 生成するために使います
##
function flapping ($path) {
GLOBAL $flap_out;
if ($path) {
if (is_dir($path)) {
if (isset($flap_out[$path])) {
unset($flap_out[$path]);
} else {
$flap_out[$path]=$name;
}
}
}
}
}
$t= new dir_Tree;
## $valはGETメソッドにより指定されます。*tree-関数を参照。
$t->flapping($val);
$t->build_tree();
$t->go_through_tree();
print $t->outp;
このコードでは、ツリーの要素全体でinとoutを切り替えやすくしています。
GET メソッド経由でパスを送信し、このパスをflapping()
に渡しています。
$flap_out
-配列全体は(例えばsessionにより)永続化する必要があります。
おそらく、あなたは、$flap_outを見てパスが既に存在するかどうかを調べるガーベージ
コレクションをプログラムするでしょうか?
既知のバグが一つあります。サブパスの名前が$delimiter
文字列を
含んでいる場合です。これを適切に解決するのは無理であり、ツリーを生成するときに
注意しなければなりません。
同じことがサブ配列の値[0]にもあてはまります。この要素は常に親要素として扱われます。
後で[0]-索引に値を代入する必要があります。 私が何を言っているかは上の例を参照してください。これはPHP3に特有だと思います。
また全ての名前を連想インデックスフィールドに挿入しない(制御コードなど)ことも 可能ですが、これはテストしていません。
これは私が頻繁に用いた一連の関数です。
これらは単純なので、私は説明することを止め、単純にコードを挿入することにしました。 おそらくこれの次のリビジョンでは私はもっとまともな説明で置き換えるつもりです。
<?php
##
## Strings2-関数集
##
## Copyright (c) 1998,1999 Alex 'SSilk' Aulbach
##
## これらの関数はとても実用的であり、もし私がもっと C で
## プログラムを書けたら PHP3 に直接組み込んだでしょう。
## でもできませんでした... :-}
##
##
## あなたはこんな文に恐れおののいたことはありませんか?
## echo ($faxnumber) ? sprintf("Fax: %s",$faxnumber) : "";
##
## これは上記のような文を次の文で置き換え、あなたの手助けをします
## p_iftrue($faxnumber,"Fax: %s");
## これで入力すべき文字は半分になり、より明晰に見え、もし $faxnumber が
## 未定義の場合に起きるエラーを解決します。
##
function o_iftrue ($val,$str) {
if (isset($val) && $val) {
return(sprintf($str,$val));
}
}
function p_iftrue ($val,$str) {
print o_iftrue($val,$str);
}
##
## 「一つまたはそれ以上」を出力
##
## この関数は、もし個数によって出力を変えたいときに便利です。
## 例) o_1or2($q->num_rows(),
## "一致するレコードは一つでした",
## "%s 個のレコードが見つかりました");
## は、もし num_rows() が 1 ならば「一致するレコードは一つでした」
## 200 ならば「200 個のレコードが見つかりました」
## を出力します。
##
## もし $val が空か "" であれば空白文字列が返されます!
##
function o_1or2 ($val,$str1,$str2) {
if (isset($val) && $val) {
if (1==$val) {
return(sprintf($str1,$val));
} else {
return(sprintf($str2,$val));
}
} else {
return(false);
}
}
function p_1or2 ($val,$str1,$str2) {
print o_1or2 ($val,$str1,$str2);
}
##
## これは、$valがfalseの場合に何かを出力したい場合です。例えば、
##
## p_0or1($faxnumber,"FAX 番号がありません", "Fax 番号: %s");
##
function o_0or1 ($val,$str1,$str2) {
if (empty($val) || !$val) {
if (isset($val)) {
return(sprintf($str1,$val));
} else {
return($str1);
}
} else {
return(sprintf($str2,$val));
}
}
function p_0or1 ($val,$str1,$str2) {
print o_0or1 ($val,$str1,$str2);
}
##
## %nbsp; による全ての空白文字列を置き換えます
## この関数はブラウザがあなたの行を破壊することを好まず、非常に互換性のある
## 代替品としてタグを代わりに用いるときに用います。
##
## はISO-Latin-1でのコード番号160を持つ本当の空白で
## 置き換えることができます
##
function o_nonbsp ($val) {
return(ereg_replace("[[:blank:]\n\r]"," ",$val));
}
function p_nonbsp ($val) {
print o_nonbsp($val);
}
?>