おかげさまで本コラムは連載が開始して50回を超え、1周年を迎えることができた。あらためて読者とスタッフの皆様に感謝の気持ちをお伝えしたい。

さて、FileMakerとPHPで作るWebアプリの開発環境も、連載当初から比較するとかなり広がりを見せてきた。前回でFileMaker API for PHPのクラス・機能の紹介がひととおり終わったが、今後はFX.phpとの機能・パフォーマンス比較を取りあげていこうと思う。

その前に一段落したいま、番外編としてここ数ヶ月で注目度が上がっているFMCakeMixについて触れておきたい。

CakePHP×FileMaker = FMCakeMixとは

おなじみの人気PHPフレームワーク「CakePHP」において、データソースとしてなんとFileMakerを使えるようにしたドライバがある。Beezwax Datatools, Inc.、Alex Gibbons氏が中心となり開発・リリースしている「FMCakeMix」だ。

FMCakeMixはChris Hansen氏が開発・リリースしているFX.phpを利用し、CakePHP 1.2のFileMakerデータソースドライバとして動作する。デベロッパはFMCakeMixを使うことでデータベースを意識することなく、Webアプリの開発が可能となる。いくつかの制限があるものの、CakePHPの使い勝手を知っているデベロッパならそのままFileMakerでWebアプリが開発できるようになるのだ。

FMCakeMixは昨年の3月20日(米国時間)に同社によって公開。その後githubに移行し、11月17日(米国時間)に更新、現在にいたる。なお、同ライブラリはThe MIT Licenseのもとで公開されている。

CakePHP+FMCakeMixを使った設定・開発方法については各マニュアルを参照されたい。なお、このFMCakeMixだが、日本語といったマルチバイト環境でそのまま使おうとするといくつか問題が生じる。今回はFMCakeMixを日本語環境で使う方法を簡単に紹介しよう。

Hello, FMCakeMix! - 日本語環境で使う方法

ここでの動作環境は次のとおり。

  • OS: MacOS X 10.6.2
  • PHP: 5.3.0
  • FileMaker Server: 10.0.2.206
  • CakePHP: 1.2.6
  • FX.php: 4.5.1 + FX_patched091010 (※)

※ FileMaker Serverの「XMLを用いたカスタムWeb公開」機能にはサロゲートペア文字の取扱いに問題があり、正常にパースできない不正なXMLを出力する。この問題を新居雅行氏がPHP側で解決し、成果物が同氏のWebサイト上で公開されている。この場をお借りして、心より感謝の意を表したい。

まずは必要なライブラリをダウンロードする。ダウンロード先とファイル名は次のとおり。

ライブラリ名 ダウンロード先 バージョン番号/更新日 備考
CakePHP cakephp's cakephp1x at master - GitHub 1.2.6  
FMCakeMix beezwax's FMCakeMix at master - GitHub 2009/11/17 alexgb's FMCakeMixFMCakeMix - File - ProjectTrackerでも公開されているが、こちらはバージョンが若干古くなっているので注意
FX.php FX.php -- The Best Way from FileMaker to the Web 4.5.1  
FX.phpパッチ FX.php日本語対応版/FX_charset FX_patched091010 FX.phpのみ

cakephp's cakephp1x at master - GitHubより、赤枠部をクリックしてCakePHP 1.2.6をダウンロード

beezwax's FMCakeMix at master - GitHubより、赤枠部をクリックしてFMCakeMix最新版をダウンロード

FX.php -- The Best Way from FileMaker to the Webより、赤枠部をクリックしてFX.php 4.5.1をダウンロード

FX.php日本語対応版/FX_charsetより、赤枠部をクリックしてパッチがあたったFX.phpをダウンロード

これらをダウンロードし、次の順番でデプロイする。

  1. CakePHPアーカイブを解凍、特定のディレクトリに展開
  2. FMCakeMixアーカイブを解凍、dbo_fmcakemix.phpを<CakePHPルート>/cake/libs/model/datasources/dbo/以下に配置
  3. fx.zipを解凍、FX.php, FX_constants.php, FX_Error.php, FX_Fuzzy_Debugger.php, Developerディレクトリを<CakePHPルート>/vendors/以下に配置
  4. FX_patched091010.zipを解凍、FX.phpを<CakePHPルート>/vendors/FX.phpに上書き

CakePHPアーカイブを解凍し、成果物一式をデプロイ

FMCakeMixアーカイブを解凍し、dbo_fmcakemixをcake/libs/model/datasources/dbo/以下にデプロイ

fx.zipを解凍し、赤枠で囲ったファイル・ディレクトリ一式をvendors/以下にデプロイ。FX.phpは新居氏作成のパッチがあたったファイルを使用する

CakePHP+FMCakeMixのマニュアルに従って各種コンフィグを設定したあと、モデル・コントローラ・ビューを作成する。コラム中に利用したFileMakerファイルを利用して、簡単な一覧画面を作成した。

FileMaker DB情報 名前
ファイル名 fmapi_test.fp7
テーブル名 fmapi_table
レイアウト名 fmapi_list

本コラムでおなじみ「fmapi_list」レイアウト。マルチバイト環境を想定し、既存のフィールド「ft_text」を「ft_日本語フィールド」に変更している

モデル - app/models/cake.php

<?php

class Cake extends AppModel
{
    var $fmDatabaseName = 'fmapi_test';
    var $defaultLayout = 'fmapi_list';
}

コントローラ - app/controller/cakes_controller.php

<?php

class CakesController extends AppController
{
    var $name = 'Cakes';

    function index()
    {
        $this->paginate = array
        (
            'limit' => 5,
            'page' => 1
        );

        $this->set('cakes', $this->paginate());
    }

}

ビュー - app/view/cakes/index.ctp

<table>
    <tr>
        <th>-recid</th>
        <th>-modid</th>
        <th><?php echo $paginator->sort('ft_serial', 'ft_serial'); ?></th>
        <th><?php echo $paginator->sort('ft_日本語フィールド', 'ft_日本語フィールド'); ?></th>
        <th><?php echo $paginator->sort('ft_date', 'ft_date'); ?></th>
        <th><?php echo $paginator->sort('ft_num', 'ft_num'); ?></th>
    </tr>
    <?php
    foreach ($cakes as $record)
    {
    ?>
    <tr>
        <td><?php echo h($record['Cake']['-recid']); ?></td>
        <td><?php echo h($record['Cake']['-modid']); ?></td>
        <td><?php echo h($record['Cake']['ft_serial']); ?></td>
        <td><?php echo h($record['Cake']['ft_日本語フィールド']); ?></td>
        <td><?php echo h($record['Cake']['ft_date']); ?></td>
        <td><?php echo h($record['Cake']['ft_num']); ?></td>
    </tr>
    <?php
    }
    ?>
</table>

Webブラウザで作成したアプリケーションにアクセスする。

作成したWebアプリケーションにアクセス。「ft_日本語フィールド」に格納されているマルチバイト文字が実体参照になっている

「ft_num」でソート。問題なく処理できている

「ft_日本語フィールド」でソートすると、レコードが表示されなくなる

ご覧いただいたとおり、FMCakeMixではいまのところ、日本語といったマルチバイト環境で動作させるといくつかの問題が生じるようだ。筆者が現時点で確認しているのは次の2点。

  1. レコードを取得したとき、フィールド内のマルチバイト文字が実態参照として扱われてしまう
  2. フィールド名にマルチバイト文字が含まれている場合、該当フィールドでPaginatorを使ったソート処理をおこなうとレコードの取得に失敗する

これらの問題はソースコードの修正で回避することが可能だ。

レコードを取得したとき、フィールド内の2バイト文字が実態参照として扱われてしまう

FX.phpでFileMakerに接続する際、SetCharacterEncoding(), SetDataParamsEncoding()を使用して適切な文字コードを指定する。dbo_fmcakemix.phpのfunction connect()部分に次の2行を追加すればOKだ。

$this->connection->SetCharacterEncoding('utf8');
$this->connection->SetDataParamsEncoding('utf8');

フィールド名にマルチバイト文字が含まれている場合、該当フィールドでソート処理をおこなうとレコードの取得に失敗する

実際にリクエストしているクエリを確認すると、マルチバイト文字が含まれたフィールド名をソートした場合にのみ、フィールド名の先頭にモデル名がふくまれていることがわかる。

「ft_num」でソートした場合のクエリ。問題なし

「ft_日本語フィールド」でソートした場合のクエリ。sortfield.1で指定している値の先頭に「Cake.」とモデル名が残っている。その結果、FileMaker側で「102: フィールドが見つかりません」エラーが発生し、レコードの取得に失敗する

この問題を解決するには、フィールド名を置換する正規表現のパターンを修正すればOKだ。dbo_fmcakemix.phpの「add the params」や「set sort order」コメント文ちかくの正規表現パターンを次のとおりに修正する。

修正前

$pattern = '/(\w+)\.(-*\w+)$/i';

修正後

$pattern = '/(\X+)\.(-*\X+)$/iu';

上記の修正は取扱い文字コードがUTF-8の場合のみにのみ効果を発揮する。UTF-8以外の文字コードを使用している場合は、preg_replace()を使った正規表現ではなく、mb_eregi_replace()を使った実装におきかえよう。

これら2点の修正後に「ft_日本語フィールド」でソート。問題なく動作するように

上記2点の修正をおこなった場合の各差分は次のとおり。修正の参考にしてほしい。

--- dbo_fmcakemix.php.orig  2010-03-05 15:00:07.000000000 +0900
+++ dbo_fmcakemix.php   2010-03-09 03:18:46.000000000 +0900
@@ -98,6 +98,9 @@
         $this->connection = new FX($config['host'],$config['port'], $config['dataSourceType'], $config['scheme']);
         $this->connection->SetDBPassword($config['password'],$config['login']);

+        $this->connection->SetCharacterEncoding('utf8');
+        $this->connection->SetDataParamsEncoding('utf8');
+
        $this->connected = true; //always returns true
         return $this->connected; 
     } 
@@ -255,7 +258,7 @@
            if(!empty($orderCondition)){
                foreach($orderCondition as $field => $sortRule) {
                    $string = $field;
-                   $pattern = '/(\w+)\.(-*\w+)$/i';
+                   $pattern = '/(\X+)\.(-*\X+)$/iu';
                    $replacement = '${2}';
                    $plainField = preg_replace($pattern, $replacement, $string);

@@ -919,7 +922,7 @@

            foreach($queryData['conditions'] as $conditionField => $conditionValue) {
                $string = $conditionField;
-               $pattern = '/(\w+)\.(-*\w+)$/i';
+               $pattern = '/(\X+)\.(-*\X+)$/iu';
                $replacement = '${2}';
                $plainField = preg_replace($pattern, $replacement, $string);
                $this->connection->AddDBParam($plainField, $conditionValue, 'eq');
@@ -931,7 +934,7 @@
            if(!empty($orderCondition)){
                foreach($orderCondition as $field => $sortRule) {
                    $string = $field;
-                   $pattern = '/(\w+)\.(-*\w+)$/i';
+                   $pattern = '/(\X+)\.(-*\X+)$/iu';
                    $replacement = '${2}';
                    $plainField = preg_replace($pattern, $replacement, $string);

@@ -1200,4 +1203,4 @@


 } 
-?>
\ No newline at end of file
+?>

FMCakeMixを使ったWebアプリ開発の場合、CakePHPの仕様上、1画面を表示する際に2回以上のリクエストが発生する。本コラムでも取りあげてきたとおり、FileMaker Serverの応答速度は、MySQLやPostgreSQLと比較するとどうしても見劣りする。同時接続ユーザ数やテーブル構造といった、ある程度規模の大きなアプリケーションの開発にFMCakeMixが向いてるかどうかは要検証といったところだ。おなじアプリケーション内でも、処理によってFMCakeMixでの実装と、FX.phpでの実装を分けた方がいいかも知れない。

FMCakeMixの使い方については、追ってこのコラムにて紹介していくつもりだ。それまで待てないというデベロッパは、ぜひdbo_fmcakemix.phpを読みながらCakePHPでのFileMakerなWebアプリにチャレンジしてみてほしい。