本ドキュメントはVer.5.3までのものです

本ドキュメントに記載された内容は、Ver.5.3までのものです。Ver.5.4-devからは、こちらの内容をご覧ください。

データコンバータクラスの利用方法

IM_Entry関数の第2引数で、'formatter'を利用して、特定のコンテキストのフィールドに対して、データベースの読み書き前後にフィルタを設定することができます。このときに使うクラスをデータコンバータクラスと呼びます。自分で作成もできますが、以下のものが最初から組み込まれています。自分で作成する場合はDataConverter_template.phpを参照してください

DataConverter_AppendPrefix.php parameterに指定した文字列を前につける
DataConverter_AppendSuffix.php parameterに指定した文字列を後につける
DataConverter_Currency.php parameterに指定した小数以下の桁数で、数値を通貨で表示する
DataConverter_FMDateTime.php FileMaker Serverの出力する「月/日/年」形式の日付や時刻を整える
DataConverter_HTMLString.php 改行をBRタグ要素、そして<や>や&を参照形式に変換する。parameterにautolinkと指定するとURLにAタグ要素を付加してリンクに変換する。バージョン4.2以降においてparameterにnoescapeと指定すると参照形式への変換を無効にする。データベースへの書き込み時はそのままの文字列
DataConverter_MySQLDateTime.php MySQLの出力する「年-月-日」形式の日付や時刻を整える
DataConverter_Number.php parameterに指定した小数以下の桁数で、数値をカンマ付きで表示する
DataConverter_NumberBase.php NumberやCurrencyの基底クラス
DataConverter_NullZeroString.php 書き込み時にデータが''ならNULLとする、読み込み時は逆変換
DataConverter_template.php データコンバータのクラスのテンプレート

データベースクラスを自作する

データベースクラスを独自に作成する場合には、以下のようなPHPのクラスが基本となります。もちろん、データベースクラスを1から作れるのですが、たぶん、そういうニーズはまずないと思います。データベースをPDOで使うならINTER-MediatorのDB_PDO.php、FileMaer Serverを使うならDB_FileMaker_FX.phpを拡張したクラスを定義します。基底クラスの方のファイルも読み込みが必要です。以下の例は、INTER-Mediatorフォルダと同じフォルダに、このデータベースクラスのファイルがあるというわけです。自分が作るクラスでも、最初にDBをつけないといけません。IM_Entry関数の引数などでの設定は、この場合DB_をのぞいた「MyOriginalDB」を指定します。

require_once( "INTER-Mediator/DB_PDO.php" );
class DB_MyOriginalDB extends DB_PDO.php {
    function getFromDB($dataSourceName) {
        /* ここにプログラムを追加できる */
        $result = super::getFromDB($dataSourceName);
        /* ここにプログラムを追加できる */
        $this->mainTableCount = count( $result ); //レコード数
        return $result;
    }
    
    function setToDB($dataSourceName) {
        /* ここにプログラムを追加できる */
        super::setToDB($dataSourceName);
        /* ここにプログラムを追加できる */
    }
    
    function newToDB($dataSourceName) {
        /* ここにプログラムを追加できる */
        $lastKeyValue = super::newToDB($dataSourceName);
        /* ここにプログラムを追加できる */
        return $lastKeyValue;
    }
    
    function deleteFromDB($dataSourceName) {
        /* ここにプログラムを追加できる */
        $result = super::deleteFromDB($dataSourceName);
        /* ここにプログラムを追加できる */
    }
}

自分で作るクラスでは、要はCRUDに対応した4つのメソッドをオーバーライドすることによって、独自の仕組みを組み込むことができます。また、4つのうち、特にプログラムの追加がない場合には、定義する必要はありません。いずれの関数も、コンテキスト名を引数に取ります。そして、現在処理しているコンテキスト名が設定されてこれらのメソッドを呼び出します。

getFromDBメソッドは、データベースからデータを取り出します。取り出し結果は連想配列の配列です。連想配列のキーがフィールド名になっています。そこから、特定のフィールドの値を元に集計をしたり、あるいは必要な値だけに取り除くなどの処理を、親クラスのgetFromDBメソッドを呼び出す後に入れればいいでしょう。リクエスト時と同じフィールド名を返す必要はなく(つまりINTER-Mediatorはそのチェックをしていない)、自由に連想配列の配列を作ります。そして、ページファイルの方で適切なフィールド名を指定すれば、集計結果が表示されます。なお、ページファイルで、一定レコード数ごとのページングをしているときには、mainTableCountメンバ変数にレコード数、つまり返す配列の要素数も指定します。何か問題が発生したのなら、array() を返します。以下のプログラムは売り上げのテーブルから集計を行う例です。

$result = super::getFromDB($dataSourceName);
if ( $dataSourceName == 'ItemsInvoice' ) {
    $summary = array();
    foreach( $returnValue as $record ) {
        $summary[ $record[ 'itemNo' ]] += $record[ 'qty' ] * $record[ 'unitPrice' ];
        }
    return array( $summary );
}

getFromDBメソッドを呼び出す前は、検索条件の追加なども可能です。たとえば、IM_Entry関数の第一引数に指定している配列が、$this->dataSourceで得られます。また、現在のコンテキストが$this->dataSourceで得られる配列の何番目かが$this->getIndexOfDataSource($dataSourceName)で得られます。例えば特定の名前のフィールドがあれば、それをFileMaker Serverのグローバルフィールドに設定して、検索条件から取り除くといったことも行えます。

$this->dataSource[$this->getIndexOfDataSource($dataSourceName)]['global'] = array(...);

setToDBとdeleteFromDBメソッドは、いずれも値の更新や削除のときに呼び出されるメソッドです。返り値は論理値で、処理が成功すればtrueで失敗ならfalseです。

newToDBは新規レコードの作成です。メソッドの返り値は、新たに作ったレコードのキーフィールドの値である必要があります。問題があれば返り値をfalseにします。

データベースクラス内で便利なメソッド

現状、未整理です。DB_Base.phpに多くは定義されています。以下は抜粋です。

メンバ変数/メソッド 役割
$this->dataSource IM_Entry関数の第1引数の配列
$this->currentUser ログインしたユーザ名
$this->setDebugMessage($str) デバッグメッセージに追加する
$this->setErrorMessage($str) エラーメッセージに追加する
$this->setExtraCriteria($field, $operator, $value) 検索条件の追加
$this->setExtraSortKey($field, $direction) ソート条件の追加

データベースアクセス処理の拡張クラスを定義する

データベース側の処理に割り込むには「データベースクラスを自作する」で説明する手法(「データアクセスクラスのサブクラスを利用する方法」と定義します)とここで説明する手法(「拡張処理クラスを定義する方法」とします)の2通りがあります。「データアクセスクラスのサブクラスを利用する方法」だと、すべてのコンテキストに対する処理を記述しなければなりません。言い換えれば、複数あるコンテキストの1つだけに対して処理を加えたい場合、そのコンテキストのときだけプログラムが実行されるようにする必要があります。コンテキストが増減したときのメンテナンスタスクが増えます。「拡張処理クラスを定義する方法」だと、特定のコンテキストにだけ利用されるクラスを指定するので、「コンテキストごとに分岐させる」処理は不要です。

IM_Entry関数の第一引数、つまりコンテキストの中に「extending-class」でクラス名を指定します。ここで指定したクラス名は任意のものでかまいませんが、そのクラスをPHPが認識できる場所に記述しておく必要があります。クラスは以下のようなインタフェースをインプリメントする必要があります。つまり、CRUDに対応する4種類のデータアクセス処理の前後にメソッドを含めることができるのです。

interface Extending_Interface_BeforeGet {
    function doBeforeGetFromDB($dataSourceName);
}
interface Extending_Interface_AfterGet {
    function doAfterGetFromDB($dataSourceName, $result);
    // function countQueryResult($dataSourceName);
}                        
interface Extending_Interface_BeforeSet {
    function doBeforeSetToDB($dataSourceName);
}                        
interface Extending_Interface_AfterSet {
    function doAfterSetToDB($dataSourceName, $result);
}                        
interface Extending_Interface_BeforeNew {
    function doBeforeNewToDB($dataSourceName);
}                        
interface Extending_Interface_AfterNew {
    function doAfterNewToDB($dataSourceName, $result);
}                        
interface Extending_Interface_BeforeDelete {
    function doBeforeDeleteFromDB($dataSourceName);
}                        
interface Extending_Interface_AfterDelete {
    function doAfterDeleteFromDB($dataSourceName, $result);
}

Deleteを除くdoAfter____FromDBメソッドの$result引数は、対象レコードが連想配列の配列の形式で入っています。1レコードがフィールド名をキーとした連想配列になっており、レコードの数だけその配列があります。doAfterGetFromDBでは複数のレコードが得られますが、doAfterSetToDBやdoAfterNewToDBでは1レコードだけが返されます。基本的に、これらのメソッドでは、2つ目の引数をそのまま返すようにプログラムを作成しますが、ここで、データベースからの取得結果を加工して返しても構いません。たとえば、doAfterGetFromDBメソッドで集計処理などを行い、同様に連想配列の配列にして結果を返します。また、新たなフィールドを追加するなども行えます。新たなフィールドを追加するには、$this->dbClass->setUpdatedRecord(フィールド名, データ, レコード番号) で可能ですが、レコード番号を省略すると最初のレコードにセットされます。なお、setUpdatedRecordで変更した場合、返り値は、$this->dbClass->updatedRecord()としてください。

以下は、データベースからレコードを取得した後に呼び出されるクラスの定義例です。MyProgramというクラス名は任意ですが、2つのメソッドはインタフェースに決められたものを定義します。doAfterGetFromDBはデータベースからデータを取得した後に呼び出されます。このとき、ナビゲーションを使ってページ送りの処理をしているのなら、「レコードの個数」をcountQueryResultメソッドで返す必要があります。ナビゲーションがない場合にはこのメソッドの実装は不要です。また、ナビゲーションを使う場合でも、レコード総数がdoAfterGetFromDBの処理の後でも変わらないのであれば、countQueryResultメソッドは不要です。PHPではoptionのメソッドの定義をインタフェースできないので、定義上はコメントでこのメソッドを記載しています。

class MyProgram implements Extending_Interface_AfterGet {
    function doAfterGetFromDB($dataSourceName, $result) {
        foreach( $result as $record ) {
            foreach( $record as $field => $value ) {
                :
            }
        }
        return $result;
    }
}

countQueryResultメソッドを記述する場合の例を以下に示します。countQueryResultはdoAfterGetFromDBよりも後に呼び出されます。countQueryResultによって返されるのは、実際のレコード数ではなく、検索条件に合ったレコード数です。つまり、1万個のレコードのうち40個を表示している場合には、1万という数値を返す必要があります。doAfterGetFromDBで集計処理などをすると、1万レコードを取得して、それが20個などになると思います。その場合は20個を返さないといけません。この場合、自分でメンバ変数(dataCount)を定義して、そこに値を残しておくのが分かりやすい方法でしょう。

class MyProgram implements Extending_Interface_AfterGet {
    var $dataCount;
    
    function doAfterGetFromDB($dataSourceName, $result) {
            :
        $thisthis->dataCount = count( $result );
        $return result;
    }
    
    function countQueryResult($dataSourceName) {
        $return $thisthis->dataCount;
    }
}

それぞれのメソッドの$dataSourceNameはコンテキスト名の文字列が入っています。2つのコンテキストでクラスを共有するような場合には分岐のための手がかりになります。doAfterDeleteFromDBとdoAfterSetToDBメソッドの2つ目の引数はデータベース処理が失敗したかどうかをbooleanで示され、trueなら処理が成功です。これらのメソッドでもやはり引数で得られた値を返さないといけません。

開発者が作成するクラスで「DB_UseSharedObjects」クラスの子クラスにした場合は、他のオブジェクトへの参照が得られます。メソッドが呼び出された段階で、メンバ変数はオブジェクトを参照した状態になります。

DB_UseSharedObjectsを継承したときに利用できるメンバ変数
メンバ変数 参照するもの
$this->dbSettings DB_Settingsクラスのインスタンス
$this->logger DB_Loggerクラスのインスタンス
$this->authCommon DB_AuthCommonクラスのインスタンス
$this->formatter DB_Formattersクラスのインスタンス