はじめに

検索結果を一覧するというユーザーインタフェースはWebアプリケーションでは一般的に見られる仕組みです。その仕組みを、宣言的な記述のみで構築できるようになっています。

前提知識

INTER-Mediatorに関して、以下の内容をすでに知っている事を前提とします。

  • data-im属性への記述によりフィールドの内容が表示されること
  • TABLEタグを使った表の中で、複数のレコードが繰り返し表示されるようになっていること
  • 一定数ごとのレコードを表示する仕組みと、表示範囲を切り替えるページネーションの仕組みがあること
  • 定義ファイルのコンテキストには、検索条件が指定できること

作成例について

利用するデータベースは、郵便番号と地名が含まれているもので、サンプルのデータベースにあるpostalcodeという名称のものです。レポジトリ内では、Samples/Practices/search_page1.htmlを開く事で、実際に稼働させることができます。Samples/index.htmlにあるサンプルの一覧では「Practices」にある「search」の「search(no JavaScript)」のリンクをクリックして表示できます。

まず、どのような動作になるのかを見て行くことにしましょう。最初にページを開くと、郵便番号と地名がともかく一覧されています。サンプルのデータベースには、ある時期の東京都のデータのみを収録しています。検索条件は何もなく、おそらく全てのデータがリストされていると想定されます。ここで、ページネーション(ページ移動)の「>」ボタンをクリックすると、次々と10件ずつレコードが表示されます。また、何件目のデータを現在表示しているのかという情報も見えています。

ここで、「検索条件」に適当な地名(ここでは「市谷」)を入力してReturnキーを押します(「Search」ボタンをクリックしてもかまいません)。すると、一覧表には、「市谷」を含む地名やビル名のものだけに絞り込まれました。全部で68件の地名があることが分かります。「>」ボタンでページ移動ができますが、ページ移動後にも、検索条件はそのまま見えていることにも注意を払いましょう。

「表示件数」のポップアップメニューから「4」を選択すると、これまで10件ごとに表示されていた一覧表が、4件ごとの表示に切り替わります。切り替えると、以後、ずっと4件ずつ表示されるようになります。

検索条件に「171」と入力してReturnキーを押しました。すると、郵便番号に171が含まれる地名だけが絞り込まれています。また、ここで、「郵便番号」と書かれた右側の▼をクリックすると、郵便番号の逆順で表示されるようになりました。つまり、一覧表示が「郵便番号」の逆順にソートされて表示されるようになっています。「<<」ボタンを押して最初のページを表示すると、いちばん大きな数値になる郵便番号の地名が、リストの最初の行に見えています。

検索条件に、「市ヶ谷」という文字列を指定して検索してみます。検索結果が0件のときに表示する行が見えています。なお、東京には、市ヶ谷という駅はありますが、地名では「市谷」と記述しており、この名前の地名がないこともわかります。

定義ファイル

定義ファイルのSamples/Practices/search_def.phpの内容は以下の通りです。コンテキストは、postalcodeという名前のものが1つだけあり、これはテーブル名と同様なので、tableやviewキーの値は指定していません。1ページあたりのレコード数は20で、後からの変更をしても30より多くの数を一度にページに出す事はできません。そして、pagingキーがtrueなので、ページネーションのコントロールがページ上に表示されます。

データベースはMySQLを使うのでdb-classは「PDO」を指定していますが、FileMaker Server向けには設定が変わります。それ以外の接続情報、アカウント、パスワードは、INTER-Mediator/params.phpファイルにあるものをそのまま使います。

require_once(dirname(__FILE__) . '/../../INTER-Mediator.php');

IM_Entry(
array(
    array(
        'name' => 'postalcode',
        'records' => 20,
        'maxrecords' => 30,
        'paging' => true,
    ),
),
null,
array('db-class' => 'PDO'),
    false
);

ページファイル

ページファイルについては、ボディ部のみを示します。もちろん、ヘッダ部で、前述の定義ファイルSCRIPTタグで読み込んだ状態になっています。このほかに何も読み込みは行っていません。つまり、このページは、プログラムとしては、BODYタグ要素のonload属性にあるINTERMediatorを動作させる1行だけなのです。

<body onload="INTERMediator.construct()">
<div>
    検索条件:
    <input type="text" data-im="_@condition:postalcode:f3,f7,f8,f9:*match*">
    表示件数:
    <select type="text" data-im="_@limitnumber:postalcode">
        <option value="4">4</option>
        <option value="10">10</option>
        <option value="20" selected>20</option>
    </select>
    <button data-im="_@update:postalcode">search</button>
</div>
<div id="IM_NAVIGATOR">Navigation Controls by INTER-Mediator</div>
<table border="1" id="resultTable">
    <thead>
    <tr>
        <th>郵便番号
            <span style="cursor: pointer" data-im="_@addorder:postalcode:f3:asc">▲</span>
            <span style="cursor: pointer" data-im="_@addorder:postalcode:f3:desc">▼</span>
        </th>
        <th>都道府県</th>
        <th>市区町村</th>
        <th>町域名
            <span style="cursor: pointer" data-im="_@addorder:postalcode:f9:asc">▲</span>
            <span style="cursor: pointer" data-im="_@addorder:postalcode:f9:desc">▼</span>
        </th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td><div data-im="postalcode@f3"></div></td>
        <td><div data-im="postalcode@f7"></div></td>
        <td><div data-im="postalcode@f8"></div></td>
        <td><div data-im="postalcode@f9"></div></td>
    </tr>
    <tr data-im-control="noresult">
        <td colspan="4">検索結果はありません。あるいは検索前です。</td>
    </tr>
    </tbody>
</table>

</body>

それぞれの動作の実現

一覧表示の実現

postalcodeテーブルでは、f3フィールドに郵便番号、f7〜f9フィールドに都道府県、市区町村、町域名が収められています。ページファイル内のTABLE要素によるテーブル部分で、その一覧表示を行っています。TBODYタグ要素の中には2つのTR要素があり、その最初の方の要素の子要素にTDがいくつかあり、そこにf3、f7〜f9のそれぞれのフィールドを示すターゲット指定がdata-im属性に設定されています。

postalcodeコンテキストに対して検索して得られた郵便番号と地名の情報に対して、TBODYの最初のTR要素がレコードの数だけ繰り返されて、そしてそれぞれDIVタグ要素のテキストとしてフィールドの値が設定されて表示されます。初期状態では、定義ファイルのコンテキストにあるrecordsキーの値に応じて最大10レコードまでとなります。この仕組みによって、一覧表が作成されています。

検索結果が0のときの一覧表

TBODYの内容を解析して、このTBODYがエンクロージャーとして機能します。そして、postalcodeコンテキストに対して検索処理を行います。このとき、検索結果が0件の場合、data-im-control属性が「noresult」のものを残して、エンクロージャー内の他のリピーターは削除され、その結果を表示します。また、一方で、検索結果が0件でない場合、data-im-control属性が「noresult」のものを削除して残った結果をリピーターとして識別して、そのリピーターをレコードの数だけ複製してページを表示します。

ページネーションのコントロールの表示

postalcodeコンテキストでは、pagingキーの値がtrueなのでページネーションコントロールによるページ切り替えが可能です。そして、ページネーションのコントロールを表示するためには、id属性が「IM_NAVIGATOR」の要素を配置しておく必要があります。TABLEタグのすぐ上にその要素が見えており、この場所にページネーションコントロールを自動的に構築します。

検索のユーザインタフェース

ページファイルの最初の方に、検索条件を指定するINPUTタグ要素があります。この要素はデータベースとは関係なく、ページ上で存在するものです。そのため、id属性(値はcondition)を設定していますが、加えて、data-im属性にローカルコンテキストを示す「_@condition:postalcode:f3,f7,f8,f9:*match*」という記述があります。この属性名を持つテキストフィールドは、コロンで区切られていて、最初の「condition」は決められたキーワードです。次の「postalcode」はどのコンテキストに対して検索をかけるのかということを指定しています。3つ目は検索対象のフィールドをカンマで書き並べると、それぞれのフィールドに対するOR検索が行われます。4つ目の「*match*」は、テキストフィールドに入力した文字列がどこかに含まれるという条件で検索することを示しています。このようなdata-im属性の指定により、ユーザーインタフェース上のテキストフィールドの値がコンテキストのデータベースへのクエリーに条件として付加され、検索結果が一覧されます。

このテキストフィールドに文字カーソルがある状態でReturnキーなどを押すと、コンテキストへの更新処理とみなして再度検索し、テーブルの一覧を作り直します。ユーザーは「検索によって更新された」ように見えるということです。

Searchボタンをクリックして検索する

ページファイルでは、BUTTONタグを使って「Search」ボタンを作成しています。ボタンのdata-im属性に「_@update:postalcode」と指定されています。この記述により、ボタンをクリックすると、postalcodeコンテキストに対する更新を行うので、現状の設定での検索がその場で行われます。

フィールド名の横の▲▼をクリックして、そのフィールドで並べ替える

例えば、郵便番号の▲ボタンは、spanタグ要素として記述して、data-im属性値は「_@addorder:postalcode:f3:asc」となっています。この記述があると、要素をクリックしたときにpostalcodeコンテキストに対する更新を行うと同時に、その後の記述から明らかなように、f3フィールドの昇順で並べ替えるという条件を付加します。

まとめ

検索して一覧するというユーザーインタフェースは当たり前のように作成されていますが、INTER-Mediatorはこのように、手続き的なプログラミングをまったくしなくても、ある程度仕組みのページを作成できるようになりました。AND条件で検索したいときには、テキストフィールドを並べます。そのとき、空白のフィールドは検索条件として含めないといった動作も、一切のプログラミングなく動作します。なお、これ以上の複雑な検索条件指定が必要な場合には、『プラクティス:検索をしてその結果を一覧表示する(JavaScript版)』の記事を参照してください。