FMCakeMixのUser Guide.pdfを参考に、CakePHP x FileMakerでWebアプリの作成方法を紹介する。FX.phpに添付されているFileMakerファイルをベースに、Paginationを使った一覧画面の実装方法を。5回目よりCakePHP, FMCakeMixの規約、FileMakerデータベース、database.php、モデル、Booksの登録・一覧・詳細・編集・削除処理とひととおりの処理を紹介した。前回に引き続き、Authors/Booksの連携、FX.php・FMCakeMixで2番目のフィールド値を使用する方法、値一覧を使った実装の簡易化を中心に紹介していこう。

Hello, FMCakeMix! チュートリアル / FX.php・FMCakeMixで2番目のフィールド値を取得する方法

FileMakerの便利機能のひとつ、値一覧。あらかじめデータベースに格納したい値をリスト化しておくことで、入力者は値を選択するだけでデータを入力することが可能になる。

この値一覧には、あらかじめデベロッパ側で値をリスト化しておく「カスタム値を使用」のほかに「フィールドの値を使用」という機能が用意されている。この機能は特定のテーブルに格納されているフィールド情報をそのままリスト化する。たとえば社員マスタテーブルにおいて社員ID(最初のフィールド値)と社員名(2番目のフィールド値)を値一覧化しておけば、見積書や請求書などを作成する画面で、簡単に社員名を選択出来るようになるというわけだ。

値一覧「フィールドの値を使用」機能。最初のフィールド値と2番目のフィールド値を一覧化

入力画面にて2番目のフィールド値を選択すると、フィールドには最初のフィールド値が入る

値一覧の元になっているテーブルでフィールドの内容が変更されたり、レコードが増減しても自動的にリストが更新されるので、デベロッパはこれらのことについて気にする必要がない。便利なこの値一覧機能だが、FX.phpを利用するFMCakeMixではデフォルトの状態だと「2番目のフィールド値」を取得することができない(執筆時点。FileMaker API for PHPではgetValueListsTwoFields(), getValueListTwoFields()で取得可能)。2番目のフィールド値を使えるようにするには、FX.php本体を若干修正する必要がある。

FX.phpの修正箇所は次のとおり。

FX.php修正箇所diff

--- FX_orig.php 2010-11-16 03:54:52.000000000 +0900
+++ FX.php      2010-11-16 04:07:19.000000000 +0900
@@ -418,11 +418,10 @@
                 }
                 $this->valueLists[$this->currentValueList] = array();
                 $this->currentFlag = "values";
-                $this->currentValueListElement = -1;
+                $this->currentValueListElement = '';
                 break;
             case "value":
-                $this->currentValueListElement++;
-                $this->valueLists[$this->currentValueList][$this->currentValueListElement] = "";
+                $this->currentValueListElement = $attrs['DISPLAY'];
                 break;
             case "database":
                 foreach ($attrs as $key => $value) {
@@ -466,7 +465,11 @@
                 $this->fxError = $data;
                 break;
             case "values":
-                $this->valueLists[$this->currentValueList][$this->currentValueListElement] .= preg_replace($this->UTF8SpecialChars, $this->UTF8HTMLEntities, $data);
+                $key = '';
+                $value = '';
+                $key = preg_replace($this->UTF8SpecialChars, $this->UTF8HTMLEntities, $data);
+                $value = mb_ereg_replace( '^' . preg_replace($this->UTF8SpecialChars, $this->UTF8HTMLEntities, $data) . ' ' , '', $this->currentValueListElement);
+                $this->valueLists[$this->currentValueList][$key] .= $value;
                 break;
         }
     }

FileMaker ServerのWeb公開エンジンではバージョン10以降、値一覧の2番目のフィールド値がFMPXMLRESULT文法のXMLにふくまれるようになっている。FX.phpの値一覧を取得するロジック内において、<VALUE>エレメントのDISPLAY属性から最初のフィールド値および2番目のフィールド値を取得。<VALUE>の値には最初のフィールド値が格納されるので、これをキーとし、DISPLAY属性の先頭から最初のフィールド値を削除したものを値として格納している。

FX.phpを修正したら、前回の最後に修正した書籍の登録画面を表示してみよう。

FX.php修正後の書籍登録画面(books/add)。前回はただ数字が表示されているだけのプルダウンだったが、著者(Author)で登録している著者名(2番目のフィールド値)が表示されるように

authorsテーブル内のレコード分だけ著者をプルダウンで選択できる。<option>のvalueには、値一覧の最初のフィールド値が格納される

レコード登録後のFileMaker。author_idにauthorsテーブルのidが格納されている

FileMakerに適切なidが格納されるようになった。それではほかの画面でも著者名を表示させてみよう。実装方法はおもに

  • idを使用したリレーション先のフィールドをFileMakerレイアウト上に配置。FMCakeMixのビューではリレーション先のフィールドを表示させる
  • コントローラ側で値一覧を随時取得。フィールドに格納されているidと値一覧の配列を使い、idに対応する値を表示させる

上記2つの案がある。レイアウトに配置されているフィールド数や、値一覧の数、値一覧を表示させるためのリレーション構成などでパフォーマンスがおおきく変わってくる。それぞれ場面に適した実装を心がけたい。ここでは登録画面同様、値一覧をもちいた実装をおこなう。

booksコントローラ記述例(一覧, 詳細, 編集) - /app/controllers/books_controller.php

function index()
{
    $this->paginate = array
    (   
        'limit' => 5
    );  
    $schema = array();
    $schema = $this->Book->schema();
    $this->set('schema', $schema);

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

function view($id = null)
{
    $this->Book->id = $id;
    $this->set('book', $this->Book->read());
    $schema = array();
    $schema = $this->Book->schema();
    $this->set('schema', $schema);
}

function edit($id = null)
{
    if (!$id && empty($this->data))
    {   
        $this->Session->setFlash(__('不正なidです', true));
    }   
    if (!empty($this->data))
    {   
        if ($this->Book->save($this->data))
        {   
            $this->Session->setFlash(__('書籍情報を保存しました', true));
            $this->redirect(array('action'=>'index'));
        }   
        else
        {   
            $this->Session->setFlash(__('書籍情報の保存に失敗しました', true));
        }   
    }   
    if (empty($this->data))
    {   
        $this->data = $this->Book->read(null, $id);
    }   
    $schema = array();
    $schema = $this->Book->schema();
    $this->set('schema', $schema);
}

booksビュー記述例(一覧) - /app/view/books/index.ctp

<h1>書籍一覧</h1>

<p style="text-align: center">
<?php
    echo $paginator->counter
    (
        array
        (
            'format' => __('%page% / %pages% ページ,  <span class="foundCount">%count%</span>件中、%start% - %end% 件目を表示', true)
        )
    );
    echo '<br>';
    echo $paginator->prev('<< '.__('前のページ', true), array(), null, array('class'=>'disabled', 'tag' => 'span'));
    echo ' | ' . $paginator->numbers() . ' | ';
    echo $paginator->next(__('次のページ', true).' >>', array(), null, array('tag' => 'span', 'class' => 'disabled'));
?>
</p>

<table cellpadding="0" cellspacing="0">
    <tr>
        <th>
            <?php echo $paginator->sort('Id' , 'id');?>
        </th>
        <th>
            <?php echo $paginator->sort('Title' , 'title');?>
        </th>
        <th>
            <?php echo $paginator->sort('Author', 'author_id');?>
        </th>
        <th>
            <?php echo $paginator->sort('Created', 'created');?>
        </th>
        <th>
            <?php echo $paginator->sort('Modified', 'modified');?>
        </th>
    </tr>
    <?php
    $i = 0;
    foreach ($books as $book)
    {
        ?>
        <tr>
            <td>
                <?php
                echo $html->link
                (
                    h($book['Book']['id']),
                    array
                    (
                        'controller' => 'books',
                        'action' => 'view',
                        'id' => $book['Book']['id']
                    )
                ); ?>
            </td>
            <td>
                <?php echo h($book['Book']['title']); ?>
            </td>
            <td>
                <?php echo h($schema['author_id']['valuelist'][$book['Book']['author_id']]); ?>
            </td>
            <td>
                <?php echo h(date('Y/m/d H:i:s', $book['Book']['created'])); ?>
            </td>
            <td>
                <?php echo h(date('Y/m/d H:i:s', $book['Book']['modified'])); ?>
            </td>
        </tr> 
        <?php
    }
    ?>
</table>

<p style="text-align: center">
<?php
    echo $paginator->counter
    (
        array
        (
            'format' => __('%page% / %pages% ページ,  <span class="foundCount">%count%</span>件中、%start% - %end% 件目を表示', true)
        )
    );
    echo '<br>';
    echo $paginator->prev('<< '.__('前のページ', true), array(), null, array('class'=>'disabled', 'tag' => 'span'));
    echo ' | ' . $paginator->numbers() . ' | ';
    echo $paginator->next(__('次のページ', true).' >>', array(), null, array('tag' => 'span', 'class' => 'disabled'));
?>
</p>

<ul>
    <li><?php echo $html->link('書籍情報を登録する', array ( 'controller' => 'books', 'action' => 'add') ); ?></li>
    <li><?php echo $html->link('書籍情報を全削除', array ( 'controller' => 'books', 'action' => 'delete_all' ), null, '書籍情報をすべて削除します。よろしいですか?'); ?></li>
</ul>

booksビュー記述例(詳細) - /app/view/books/view.ctp

<h1>書籍詳細</h1>

<table cellpadding="0" cellspacing="0">
    <tr>
        <th>
            Id
        </th>
        <td>
            <?php echo h($book['Book']['id']); ?>
        </td>
    </tr>
    <tr>
        <th>
            Title
        </th>
        <td>
            <?php echo h($book['Book']['title']); ?>
        </td>
    </tr>
    <tr>
        <th>
            Author
        </th>
        <td>
            <?php echo h($schema['author_id']['valuelist'][$book['Book']['author_id']]); ?>
        </td>
    </tr>
    <tr>
        <th>
            Created
        </th>
        <td>
            <?php echo h(date('Y/m/d H:i:s', $book['Book']['created'])); ?>
        </td>
    </tr>
    <tr>
        <th>
            Modified
        </th>
        <td>
            <?php echo h(date('Y/m/d H:i:s', $book['Book']['modified'])); ?>
        </td>
    </tr>
</table>

<ul>
    <li><?php echo $html->link('書籍情報を編集', array ( 'controller' => 'books', 'action' => 'edit', 'id' => $book['Book']['id'] ) ); ?></li>
    <li><?php echo $html->link('書籍情報を削除', array ( 'controller' => 'books', 'action' => 'delete', 'id' => $book['Book']['id'] ), null, '「' . $book['Book']['title'] . '」を削除してもよろしいですか?'); ?></li>
    <li><?php echo $html->link('書籍情報一覧', array ( 'controller' => 'books', 'action' => 'index' ) ); ?></li>
</ul>

booksビュー記述例(編集) - /app/view/books/edit.ctp

<h1>書籍編集</h1>

<?php
    echo $form->create('Book', array('action' => 'edit'));
    echo $form->input('title',array('label'=>'書籍名'));
    echo $form->input('author_id',array('label'=>'著者','options' => $schema['author_id']['valuelist']));
    echo $form->end('書籍情報を編集');
?>

<ul>
    <li><?php echo $html->link('書籍情報詳細に戻る', array ( 'controller' => 'books', 'action' => 'view', 'id' => $this->data['Book']['id'] ) ); ?></li>
</ul>

コントローラで登録(add)と同様に、スキーマ情報を取得。ビューにおいて$schema変数で値一覧にアクセスできるようにset()。ビュー側ではFileMakerに格納されているidと値一覧の配列を使用し、idに対応する著者名を表示するように修正をおこなった。実際にWebブラウザで動作を確認してみる。

書籍一覧画面(books/index)

書籍詳細画面(books/view)

書籍編集画面(books/edit)。3画面で著者名が表示されるように

これで書籍に著者情報を紐づけることが可能になった。FMCakeMixチュートリアルもいよいよ大詰め。次回はFileMakerのポータルに相当するUIの実装方法を紹介する。