[←CoolNotify]|←FileMaker情報|←Browsing Mac OS X
English Japanese
更新
新居雅行/msyk@msyk.net
INTER-Mediatorは、ブラウザで表示するデータベース利用アプリケーション(Webアプリケーション)を簡単に作成するための一種のフレームワークです。PHPとJavaScriptを使っていますが、利用においては2つあるいは3つのPHPステートメントを記述するだけです。ほぼ、ノンプログラミングと言えるでしょう。どんな感じなのかはこちらのPDFを別ウインドウでご覧ください。
利点
|
制限や対応予定の機能
|
FileMaker ServerないしはMySQLの稼働が必要です。FileMaker ServerではXML共有を開始していることを確認してください。また、PHPでは、mbstringが動作していることを確認してください。Windows版のPHPではデフォルトではmbstringのDLLがコメントされていて読み込まれていないことが一般的ですので、ご注意ください。
HTMLファイルは普通に作成すればかまいませんが、JavaScriptからDOMモデルで呼び出しを行うので、なるべくいい加減なタグ付けはしない方がいいでしょう。CSSやJavaScript等は自由に作成してかまいませんが、JavaScriptについては、たくさんのソースが自動生成されるので、いくらか考慮が必要になります。
「フォーム形式」と「リスト形式」について、フィールド名等の設定については、次のようになっています。InitializePage関数の引数に指定する内容については後の部分で解説しますが、これらの設定のパターンに従っているものとします。つまり、「フィールド名」のみの表示は、フォーム形式で、nitializePage関数の第1引数の最初のテーブルの場合のみで、この場合は最初のテーブルから得られた最初のレコードだけを利用します。
| 形式 | 第1引数の'records'の値 | NAMEなどの属性や第1引数での指定 | |
|---|---|---|---|
| 第1引数の1つ目のテーブル | 第1引数の2つ目以降のテーブル | ||
| フォーム形式 | 1 | フィールド名 | テーブル名@フィールド名 |
| リスト形式 | 2以上 | テーブル名@フィールド名 | テーブル名@フィールド名 |
INTER-Mediatorを利用するには、HTMLについて、以下のような修正が必要です。HTMLファイルをPHPプロセッサが処理するように、サーバ側の設定が必要になります。多くの場合、拡張子は.phpとなるはずです。加えて次のように修正をします。
<html>
<head>
:
<?php
require_once( 'INTER-Mediator/INTER-Mediator.php' );
InitializePage(…);
?>
:
</head>
<body onload="doAtTheStarting();" onbeforeunload="return doAtTheFinishing();">
:
<?php GenerateConsole(); ?>
:
</body>
</html>
InitializePage関数については、次のような仕様になっています。パラメータにデータベース処理等に必要な設定をすべて与えることになります。以下の表で、シングルクォーテーションでくられている文字は、そのまま指定する決められたキーワードです。「(なし)」と示された箇所は、同一レベルの前の要素が繰り返されるという意味で、結果的に数値によるインデックスの配列となるという意味です。
| 引数 | 概要 | 配列指定 | 値 | |||
|---|---|---|---|---|---|---|
| 第1次元 | 第2次元 | 第3次元 | 第4次元 | |||
| 第1引数 [必須] | テーブルおよび フィールド情報 | (なし) | 'name' | テーブル名[必須]。'view'がなければすべての処理を適用する「テーブル名」、'view'があれば書き込みや更新処理のときの「テーブル名」 | ||
| 'view' | 指定すると読み込みのときの「テーブル名」に使用される。書き込みは常に'name'だが、読み込む先をこの指定によって違うものにできる | |||||
| 'records' | 1ページに何レコード分表示するか。最初のテーブルの設定のみ有効。省略すると1 | |||||
| 'key' | 主キーフィールド名 | |||||
| 'foreign-key' | 外部キーフィールド名 | |||||
| 'query' | (なし) | 'field' | フィールド名 | |||
| 'value' | 検索値 | |||||
| 'operator' | 検索値の解釈 | |||||
| 'sort' | (なし) | 'field' | フィールド名 | |||
| 'direction' | 昇順か降順か | |||||
| 'repeat-control' | 繰り返し表示する部分に、レコードの追加や削除の機能を追加する。値には文字列として「insert」「delete」の文字、あるいは両方をつなげた文字を指定する | |||||
| 'script' | (なし) | 'db-operation' | データベース処理の前後に行う処理の指定。スクリプトを実行する場面で、値は'load', 'update', 'new', 'delete'のいずれか | |||
| 'situation' | スクリプトの実行を行うタイミングで、'pre', 'presort', 'post'のいずれかの値を取る('presort'はFileMaker Serverのみサポート) | |||||
| 'definition' | MySQLは指定したSQLコマンドを実行、FileMaker Serverはスクリプト名を指定。(FileMaker Serverでforegn-keyの指定されたテーブルの読み込み時の設定はすべて無視される) | |||||
| 'global' | (なし) | db-operation | 設定する場面で、値は 'load', 'update', 'new', 'delete'のいずれか(グローバルの設定はFileMaker Serverのみ) | |||
| field | グローバルフィールド名 | |||||
| value | グローバルフィールドに与える値 | |||||
| 第2引数 | オプション | 'separator' | テーブル名とフィールド名を区切る文字(既定値は「@」) | |||
| 'accept-get' | この要素が存在すれば、$_GETで得られたキーと値のセットを検索条件に含める。値は何でも良い | |||||
| 'accept-post' | この要素が存在すれば、$_POSTで得られたキーと値のセットを検索条件に含める。値は何でも良い | |||||
| 'auto-save' | この要素が存在すれば、自動保存モードで動作する。値は何でも良い | |||||
| 'new-record' | この要素が存在すれば、最初に新規レコードを作成した状態でページが表示される。値は何でも良い | |||||
| 'formatter' | (なし) | 'field' | データコンバータを適用するフィールド名 | |||
| 'converter-class' | データコンバータクラス名 | |||||
| 'parameter' | コンバータクラスに与えるパラメータ | |||||
| 'validation' | (なし) | 'field' | データ確認を行うフィールド名 | |||
| 'rule' | 確認処理の種類 | |||||
| 'option' | 確認処理のパラメータ | |||||
| 'trigger' | (なし) | field | トリガを適用するフィールド名 | |||
| 'event' | イベントの種類 | |||||
| 'function' | イベントが発生したときに呼び出される関数 | |||||
| 第3引数 | データベース情報 | 'db-class' | データアクセスクラスの名前 | |||
| 'db' | 接続するデータベース名 | |||||
| 'user' | データベース接続のためのユーザ名 | |||||
| 'password' | データベース接続のためのパスワード | |||||
| 第4引数 | デバッグ | trueならデバッグモード(配列ではなくbooleanを指定) | ||||
以下はパラメータ指定のサンプルです。
InitializePage(
array(
array(
'records' => 1,
'name' => 'person_layout',
'key' => 'id',
'query' => array(
array( 'field'=>'id', 'value'=>'5', 'operation'=>'eq' ),
array( 'field'=>'date', 'value'=>'1/1/2009', 'operation'=>'gte' ),
),
'sort' => array(
array( 'field'=>'id', 'direction'=>'ascend' ),
),
),
array(
'name' => 'contact_to',
'key' => 'id',
'foreign-key' => 'person_id',
'repeat-control' => 'insert delete',
),
array(
'name' => 'history_to',
'key' => 'id',
'foreign-key' => 'person_id',
'repeat-control' => 'delete',
),
),
array(
'formatter' => array(
array( 'field' => 'contact_to@datetime', 'converter-class' =>'FMDateTime' ),
array( 'field' => 'contact_to@startdate', 'converter-class' =>'FMDateTime' ),
array( 'field' => 'contact_to@enddate', 'converter-class' =>'FMDateTime' ),
),
),
array( 'db-class' => 'FileMaker_FX',
'db' => 'TestDB',
'user' => 'web',
'password' => 'password'
),
true // debug
);
第1引数の最初のテーブルで指定する'records'キーは全体の動作に影響します。フォーム型のページ、つまり1レコードとその関連レコードを表示するタイプの場合は、'records'に対する値は「1」にしてください。そして、第1引数の最初のテーブルが、フォーム型ページに表示する主たるテーブルとなるようにします。1つ目のテーブルは'foreign-key'の指定は不要です。そして、2つ目以降のテーブルのうち、'foreign-key'キーの値が設定されていれば、1つ目の'key'キーで示すフィールドと突き合わせをして、関連テーブルとして表示します。
'records'の値が2以上の場合は、第1引数で指定した最初のテーブルからの'records'の値の数だけレコードを取り出します。結果として、TABLE要素で用意した繰り返し可能な領域に、それらのレコードが展開されるようにします。'records'で指定した数のレコードごとを表示する機能は、第1引数の最初のテーブルにだけ適用されます。それ以後のテーブルについて、'foregin-key'の指定があれば、最初のテーブルのレコードと連動しますが、ない場合には、単に指定した検索条件でレコードを取り出し表に展開します。
第1引数は複雑な構成になっていますが、いずれにしても、どのテーブルからデータを取ってくるかを指定するものと考えればよいでしょう。'key'キーの値は、そのテーブルの主キーフィールドの名前です。'key'キーの指定は原則として必須です。普通は自動連番の機能を利用した数値フィールドを指定することになるでしょう。なお、複数のフィールドによる主キーは対応していません。そして、'foreign-key'キーには外部キーのフィールド名を指定します。'criteria'および'sort'は、さらに複数の設定ができるように、それ自体が配列となっています。'foreign-key'、'criteria'および'sort'は省略可能です。'criteria'および'sort'で指定する検索条件やソート順序は、FileMakerのWebアクセスのパラメータとしておなじみのものです。これらは、データベースから読み込み時のみ適用されます。'repeat-control'は、主としてフォーム型のページの場合に、繰り返しレコードのテーブルに、レコードの削除や行の追加のコントロールを追加するものです。
第2引数の'formatter'は、特定のフィールドに対してデータベースからの読み書きを行うときに、指定したクラスの変換メソッドを通すものです。処理は、データアクセスクラス内で、データベース処理の直前直後に行われます。こうした変換のためのクラスを「データコンバータクラス」と呼ぶことにします。パラメータは配列を指定しますが、キーにHTML上でのNAME/TITLE属性に指定するフィールド名(テーブル名とセパレータを含む場合もある)を指定します。キーに対する値はクラス名を指定します。実際に定義されているのは「DataConverter_FMDateTime」という名前のクラスですが、「FMDateTime」だけ、つまり「DataConverter_」の記述は省略します。INTER-Mediatorには「DataConverter_FMDateTime」クラスが付属しています。これは、FileMakerの日付が月/日/年の順序でしか受け付けないため、ブラウザの言語地域情報をもとに、ユーザ向けの日付時刻の表現と、FileMakerでの表現を変換するクラスです。
第3引数は、データベースアクセスに関する設定です。これらは、サーバ側のparams.phpファイルに記述し、ここは省略してもかまいません。ここに指定をすると、この指定値が使われますが、省略するとparams.phpファイルの内容が有効となります。'db-class'キーに対する値は、データベースアクセスのためのクラス名を指定します。「DB_FileMaker_FX」クラスはINTER-Mediatorに含まれており、FX.phpを利用してXML共有しているFileMakerに対してアクセスを行うクラスです。'db-class'キーの値には、「DB_」をのぞいた「FileMaker_FX」という文字列を指定します。このクラスを継承したり、あるは新たにクラスを作った場合にはその名前を指定します。'db'キーにはアクセスするFileMakerのデータベースのファイル名を指定します。そのファイルにXML共有でアクセス可能なユーザとパスワードも指定しておきます。データベースへの書き込みを行うには、データベースのユーザ名とパスワードを、必ずparams.phpファイルに記述する必要があります。パスワードをサーバ外に出さないために、関数呼び出しで指定したパスワードは最初のデータベースからの読み取りのときだけ利用されます。
InitializePage関数の第1引数で検索条件を指定します。FileMaker ServerのXML共有でのURLに指定する文字に基づいています。従って、式に書くような複雑な条件は指定されず、すべての条件に対してのANDないしはORしか指定はできません。
'query'/*/'operation' に指定可能な演算子は、'eq', 'cn', 'bw', 'ew', 'gt', 'gte', 'lt', 'lte', 'neq' のいずれかです。'query'以下の配列が「array( 'field'=>'__operation__', 'operation'=>'or' )」となっていれば、複数の条件をORで結びつけて解釈します。これがない場合はAND条件となります。
'sort'/*/'direction' に指定可能な記号は、'ascend', 'descend' のいずれかです。前者が昇順、後者が降順です。
FileMaker Server 10の付属ドキュメント「FileMaker® Server 10 カスタム Web 公開 with XML and XSLT」のp89などを参照してください。
第3引数の'db-class'は、「FileMaker_FX」と指定をします。
SQLデータベースでは検索条件、つまりWHERE句を式やサブクエリーなどの複雑な設定が可能です。INTER-Mediatorではそこまでのサポートはしないつもりですが、ある程度複雑な処理を記述できるようにはなっています。ここで無理に複雑な記述をするよりも、ビュー等の利用により効率的な設定をしてください。
基本的には単に、'field'によるフィールド名、'operation'による演算子、'value'による値の設定により「{フィールド名} {演算子} '{値}'」という項を作ります。値と演算子のどちらかは省略が可能です。演算子を省略すると=を指定したものとして見なします。フィールド名/演算子/値のセットが複数あれば、ANDで結びます。その他以下のバリエーションがあります。'field'の値が文字列で「__clause__」の場合は、最初の配列の'value'に対する値だけが有効で、そこに指定した句がすべてそのままWHERE句の内容としてSQL文が構築されます。「WHERE」は含めないでください。ANDとORについてはある程度の式を作れるようにしてありますが、例と併せてご覧下さい。
| 'field'の値 | 対応する値 | 機能 |
|---|---|---|
| フィールド名 | 検索値 | WHERE句の1つの項 |
| __operation__ | 'ex'以外 | そこまでの項をカッコで囲み、ORを適用して次の項を続ける |
| __operation__ | 'ex' | ANDとORを入れ替える |
| __clause__ | WHERE句の内容 | WHERE句を直接指定する |
| 'query'の指定例 | 生成されるWHERE句 |
|---|---|
| array( 'field'=>'id', 'operation'=>'<', 'value'=>'3' ), array( 'field'=>'location', 'operation'=>'=', 'value'=>'201' ), | WHERE (id = '3' AND location = '201') |
|
array( 'field'=>'id', 'operation'=>'<', 'value'=>'3' ), array( 'field'=>'location', 'operation'=>'=', 'value'=>'201' ), array( 'field'=>'__operation__' ), array( 'field'=>'location', 'operation'=>'=', 'value'=>'202' ), |
WHERE (id = '3' AND location = '201') OR (location = '202') |
|
array( 'field'=>'id', 'operation'=>'<', 'value'=>'3' ), array( 'field'=>'location', 'operation'=>'=', 'value'=>'201' ), array( 'field'=>'__operation__' ), array( 'field'=>'location', 'operation'=>'=', 'value'=>'202' ), array( 'field'=>'category', 'operation'=>'>', 'value'=>'101' ), | WHERE (id = '3' AND location = '201') OR (location = '202' AND category = '101') |
|
array( 'field'=>'id', 'operation'=>'<', 'value'=>'3' ), array( 'field'=>'location', 'operation'=>'=', 'value'=>'201' ), array( 'field'=>'__operation__' , 'operation'=>'ex'), array( 'field'=>'location', 'operation'=>'=', 'value'=>'202' ), array( 'field'=>'category', 'operation'=>'>', 'value'=>'101' ), | WHERE (id < '3' OR location = '201') AND (location = '202' OR category > '101') |
|
array( 'field'=>'id', 'operation'=>'>', 'value'=>'3' ), array( 'field'=>'mail', 'operation'=>'IS NULL' ), | WHERE (id > '3' AND mail IS NULL) |
MySQLの場合、'sort'/*/'direction' に指定できるキーワードは、ASCないしはDESCとなります。
第3引数の'db-class'は、「MySQL」と指定をします。
本文内のナビゲーションを配置したいところでGenerateConsole関数を呼び出してください。DIV要素を生成するので、通常はバーの形式で見えるはずです。CLASS属性も設定してあるので、独自の見栄えにCSSを利用して設定できます。この関数の引数を省略すると、以下のナビゲーション要素をすべて表示します。特定の要素だけを表示したい場合は、表示したい要素を以下の文字列で指定します。
| ナビゲーション要素 | 文字列 | 要素のタグ | 要素のCLASS |
|---|---|---|---|
| 何レコード目かの表示 | pos | SPAN | easypage_navigation_info |
| 前後のレコードへの移動 | nav | SPAN | easypage_navigation_link |
| 新規レコード | new | SPAN | easypage_navigation_link |
| レコード削除 | delete | SPAN | easypage_navigation_link |
| 保存 | save | SPAN | easypage_navigation_link |
FileMakerではデータベースの諸定義が若干、一般的なリレーショナルデータベースと異なっています。フォーム形式で、ポータルに展開する繰り返しレコードをWebページに表示する状況でどのような指定をすればいいかを説明しましょう。ここでは、personという個人個人の情報を管理するテーブルを「1」として、それに対応する「多」のテーブル(つまりポータルで展開するテーブル)として、連絡情報を記録するcontactとその人の履歴を管理するhistoryの2つのテーブルがあるとします。「テーブル定義」と「テーブルオカーレンス(TO)」をきちんと区別しなければなりませんので、ネーミングをちょっとわざとらしくしています。
ポイントとなるのは、主となるレコードであるpersonのTOをperson_layoutに展開するまでは、普通にFileMakerを使った状況と変わりありません。加えて、「多」のテーブルに相当するTOと同一名称のレイアウトを作ります。つまり、ポータルにアサインされる名前はTOの名前になりますので、その名前と同じレイアウトが必要となります。これらがない場合には、ブラウザからの「保存」はできません。一方、保存をしないのであれば、「多」のテーブルに対応するレイアウトは作成しなくてもかまいません。
それぞれのレイアウトには、必ず、主キーと外部キーのフィールドが必要です。Webページ上に見せないとしても、必ず主キーと外部キーのフィールドはレイアウトに配置してください。もちろん、何が主キーで何が外部キーなのかをきちんと把握しておく必要がありますが、連番のフィールドを作るのが一般的なのは言うまでもありません。
一般にWebアプリケーションはフォームとSUBMIT属性を持つボタンなどによる「送信」をするのが一般的ですが、Ajaxの仕組みを利用することで、通常のデスクトップアプリケーションと同じような「保存」つまり、ページの更新を伴わないデータの保存が可能です。その仕組みを取り入れました。
データベースから得られた文字列から所望する文字列に変換および逆変換するデータコンバータクラスを定義し、オプションとして指定すればOKです。FileMakerの日付時刻のためのクラスと、数値のカンマ区切りや通貨表示のためのクラスは用意されています。データコンバータクラスに必要なメソッドは、INTER-Mediator/DataConverter_template.phpというファイルを参照してください。
Webサーバで公開している場所がいいでしょう。ページアクセスのためのWebページのさらに下位のフォルダにあることを想定しています。INTER-Mediator.php、operation_save.php、operation_delete.phpをWebサーバ経由でアクセスできる必要があります。逆に保存やレコード削除をしないのなら、PHPがアクセスできるところであればWebサーバの公開ディレクトリ内である必要はありません。INTER-Mediator.jsはJavaScriptファイルですが、INTER-Mediator.phpがローカルファイルシステムにあるファイルとして中身を全部読み出してクライアントに送るので、INTER-Mediator.js自体をWebサーバで公開している必要はありません。
以下の「フォーム形式でマスター/ディテール関係のテーブルではないテーブルを出力したい」をご覧ください。
skip=1の場合、2つ目以降にforeign-keyを指定しないテーブルを定義してください。そして、そのテーブルの内容を展開するTABLE要素をHTMLページ内に作ってください。InitlalizePage関数の引数で指定した検索条件やソート条件に対応したレコードが、1つ目のテーブルの値に関係なく表示されます。sample_list.phpでは、常に同じ「落合」という名前がある東京都の地域の郵便番号が表示されています。
FileMakerの場合は異なるレイアウト、一般的なRDBではビューを利用して、異なるテーブル名でデータベースアクセスできるようにすれば可能です。
フィールドごとのデータ変換はデータコンバータクラスを定義して行います。複数のフィールドにまたがるような処理は、データアクセスクラスを継承して、独自にクラスを定義すればよいでしょう。データアクセスクラスのデータ処理メソッドは、レコードやフィールドを、配列でやりとりします。現在あるデータアクセスクラスを継承して新たなクラスを作成し、変更処理があるメソッドを作り込み、その他は親クラスの機能を利用すればよいでしょう。変更がある場合でも、親クラスのメソッドを呼び出すことで、基本的なデータ処理は可能なので、複数のフィールドをまとめたり、ばらしたりといった処理は配列のプログラムで記述できます。こうした仕組みで組み込めないことは、データベース側のストアドプロシージャ等を使うことになります。
現在は対応していませんが、独自にJavaScriptで機能を組み込むトリガーの処理は実現させる予定です。そのとき、たとえばテキストフィールドのonchangeイベントに対して自分で定義した関数を呼び出すように設定しておきます。関数は、イベントが発生したオブジェクトへの参照を引数として呼び出されるようにします。さらに、INTER-Mediatorが供給する関数を利用することで、たとえばTABLE要素の同一TR要素にある、フィールド名で指定した別の要素を参照できるようにしますので、計算結果を保存するくらいは簡単に作成可能です。
必要な情報を指定するため、ある程度複雑になるのは仕方ないでしょう。冗長と思われるかもしれませんが、配列のキー(文字インデックス)について、データに依存しないようにするために、このような多次元配列の指定となりました。サンプルをコピー&ペーストして修正するといった手法で効率よく作成してください。(効率的に引数を作成するユーティリティをどなたか作りませんか?)
開発者は主として付属のサンプルでの動作確認を行っています。たとえば、リスト型でINPUTタグがあるような場合で、チェックができていないパターンもあるかもしれっません。どのような状況で保存ができないのかをレポートしてください。
不要です。
InitializePage関数の設定から、余分なフィールドがやってくる可能性があることは十分に予測できるでしょう。また、対応するフィールドのないINPUT要素はどうなるのでしょうか? いずれも、「適当に」無視をしてエラーは出しません。データベースのフィールドで対応するINPUT要素がない場合には、単に無視されるだけです。対応するフィールドのないINPUT要素には何もデータは表示されません。データベースのデータが表示されないテキストフィールドがある場合は、そのNAME属性のフィールド名などが間違っていることが多いでしょう。
InitializePage関数の第2引数で「trigger」を指定したとき、指定した関数が呼び出されます。呼び出される関数を、HTMLページ側のヘッダのSCRIPT要素に記述しておきます。このとき、それらの関数は引数にイベントが発生したオブジェクトへの参照を代入して呼び出されます。つまり、仮引数を1つ取った形式で関数を定義しておきます。呼び出されたときには、そのイベントが発生したテキストフィールドなどを参照した結果が得られます。そして、それらの関数では以下の関数を利用できます。
| 関数 | 動作 |
|---|---|
| getElementNodeByName( itemName, originalObj ) | 繰り返し行で同じ行にある別のオブジェクトへの参照を取得する。originalObjと同じテーブルの行にあるNAME属性等がitemNameのオブジェクトへの参照を返す |
| getElementNodesByName( itemName ) | 繰り返し行での同一フィールドの全てのオブジェクトへの参照を得る。NAME属性等がitemNameのすべてのオブジェクトへの参照の配列を返す |
以下の関数は、HTMLページ側のヘッダのSCRIPT要素に記述可能です。そして、INTER-Mediator側より、それぞれ該当するタイミングで呼び出しが行われます。引数はありません。
| 関数名 | 呼び出されるタイミング |
|---|---|
| beforeSave | 「保存」を行う前 |
| afterSaveComplete | 「保存」を行い成功した後 |
| afterFieldModified | テキストフィールドの修正を行った後 |
| beforeDeleteRecord | 「レコード削除」を行う前 |
| afterDeleteRecord | 「レコード削除」を行い成功した後 |
| beforeNewRecord | ナビゲーションにある「新規レコード」をクリックした直後 |
| afterNewRecord | 「新規レコード」をクリックし処理を行った後 |
| beforeTableRowDelete | テーブルの行の「削除」をクリックした直後 |
| afterTableRowDelete | 「削除」をクリックし処理を行った後 |
| beforeTableRowInsert | テーブルの「行の挿入」をクリックした直後 |
| afterTableRowInsert | 「行の挿入」をクリックし処理を行った後 |
(今のところここまで…カミングスーン!)