Chapter 2
データベースへのクエリーと一覧表示
この章は、INTER-Mediator Ver.10をもとに記載しました。一部、Ver.11に関する記述もあります。
まず最初にシンプルなページを作りながら、設定した内容がどのように機能するのかを少しずつ見ていくことにしましょう。この章と次の章は、主に、データベースにあるデータをWebページの上に展開するための設定を見ていきます。また、演習環境の利用方法を詳しく説明しますので、次の章以降は、演習環境の使用方法についてはあまり触れません。この章の中で十分に理解してから次に進むようにしてください。
2-1データベースからの取り出し設定
データベースからのクエリ(あるいは読み出し)の処理は、データベースを利用する基本でもあります。加えて、単に読み出し結果をブラウザー上に表示するだけでなく、見やすく表示させるということも重要です。レイアウトを厳密に構成できることがHTMLの特徴ですから、データベースの内容をきれいに構成してWebブラウザーで表示させることが可能になります。最初に、さまざまな設定のための基本について解説します。
INTER-Mediatorとデータベース
INTER-Mediatorは、データベースを利用したWebアプリケーション開発のためのフレームワークです。INTER-Mediatorとデータベースは別々の存在として、インストール等されている必要があります。また、INTER-Mediatorでのアプリケーションを作る上では、データベース上にアプリケーションの稼働が可能なテーブル設計等(一般には「スキーマ設計とその適用」)が完了している必要があります。テーブルの作成など、データベース側の運用を直接サポートする機能は現在のVer.10のINTER-Mediatorには含まれていません。
利用可能なデータベースは大きく分けて2種類があります。PHP言語でのデータベース抽象化レイヤーのひとつである「PDO(PHP Data Objects)」を利用する方法と、FileMaker Serverを利用する方法です。PDOでは、さらにデータベースエンジンごとにドライバーが内部で用意されていますが、Ver.10現在、MySQL、PostgreSQL、SQLite、SQL Serverの4つのデータベースをサポートしています。そして、SQL Serverを除く3つのデータベースエンジに関して、開発時にテストケースを適用して確認をしています。PDOがサポートするその他のデータベースエンジン(例えば、Oracleなど)は、理論上は利用可能ですが、開発側でテストを実施していないために実際にサンプル等での動作保証はできません。原則、いずれもSQLデータベースである点での違いは薄いのですが、細かな点での記述方法の違いなどもあり、INTER-Mediatorで提供された素材そのままで運用することを誰も確認していない状況と考えてください。
FileMaker Serverについては、XML共有を利用する方法と、FileMaker Data APIを利用する方法をサポートしています。XML共有を使うために、FX.phpというライブラリを利用します。そのため、XML共有を使う場合はかなり古いFileMaker Serverであっても稼働はします。FileMaker Data APIを使う場合はVer.18以降のFileMaker Serverを利用するようにしてください。それ以前のData APIは使えなくはないのですが、ライブラリのFMDataAPIのバージョンを古くするなどの対処が必要になります。
なお、本コースでは、原則として、演習環境で動作するMySQLを利用して演習を進め、データベースエンジンごとに違いがある部分については、その都度説明をします。実際にデータベースを利用して開発をされた方はご存知の通り、同じSQLデータベースでも、けっこう違いが大きいものです。INTER-Mediatorでのデータベースの扱いとしては、ひとつの考え方として、「INTER-Mediatorが必要なSQLステートメントを生成してデータベースに投げる」というイメージを考えてください。そのため、INTER-Mediator側の作業に入ると、データベースは完全に抽象化されて、データベースごとの違いを意識せずとも使えるようになっています。概念的に同じ特性を持つデータベースとして考えて差し支えないものになっています。INTER-Mediatorがデータベースエンジンの間の細かな違いを吸収していると考えてけっこうです。特に、基本的にSQLデータベースではないFileMakerとMySQLを、同じような手法で利用できるようになっています。
PDOでの接続に必要な情報
MySQLを始めとするPDOを利用した接続で必要になる情報は、必要な設定を行う前に確認しておく必要があります。MySQLとPostgreSQLについては、ソケット経由での接続と、TCP/IPのポートに対して接続する方法があります。データベースとWebサーバーが同一のホストの場合、どちらでも利用できます。異なるホストの場合は、ポート接続を行います。SQLiteについては、ファイルを直接読み書きしているので、同一のホストである必要があります。
いずれのデータベースについても、データベースエンジン自体の指定だけでなく、「データベース名」の指定が必要になります。一般に、データベースエンジンは、データベースを運用するソフトウェアを示す用語ですが、ひとつのデータベースエンジンは、同時に複数のデータベースを運用することが可能です。SQLデータベースでは、ひとつのデータベースの中に、複数のテーブルやビューといったエンティティを定義でき、テーブルには複数のフィールドが定義されていて、データの記録ができるようになっています。
INTER-MediatorでPDOを利用する場合、合計5つの設定項目を確定させる必要があります。これらの実際の設定方法は、この後に、演習の中で行うことにします。コロン(:)の左側が設定の種類を示すキーワードで、右側はその設定値です。キーワードはINTER-Mediatorで決められたものですが、値は状況により変わります。PDOを使う場合には、db-classに対する値は「PDO」で一定です。dsnは、データベースに接続するためのさまざまな情報をひとまとめにしたもので、すぐ後に説明します。userとpasswordは、データベースを利用するために必要なアカウントの情報で、データベースのセットアップ時に指定しているものです。optionsには、一般には設定はなにもしなくてもかまいません。
(ソケットを利用する接続の場合)
db-class:PDO
dsn:mysql:unix_socket=/var/run/mysqld/mysqld.sock;dbname=test_db;charset=utf8mb4
user:web
password:password
options:(設定不要)
(ホスト名を指定する接続の場合)
dsn:mysql:host=localhost;dbname=test_db;charset=utf8mb4
dsnについて説明をします。dsnは、半角のセミコロン(;)で区切った文字列で、それぞれは「キーワード=値」の形式のデータが含まれたものです。dsnはData Source Nameの略ですが、単に名前だけでなく、さまざまな情報を指定します。最初の「mysql」は、データベースエンジンの種類であり、MySQLの場合は常に「mysql」です。
ソケットを使う場合には、「unix_socket=パス」の形式で記述します。パスは、セットアップの方法やあるいは設定ファイルの書き方で変わってきます。TCP/IPのポートに接続する場合には、「host=ホスト名」によってデータベースエンジンが稼働しているホストのIPアドレスやホスト名、FQDN等を指定します。通常は、MySQLはポート番号3306で稼働していますが、既定のポート番号の場合はポート番号の指定は不要です。サーバーの設定により異なるポートで運用している場合には「port=ポート番号」の記述も追加します。unix_socketとhostの指定は両立せず、どちからのみを指定するのが原則です。
接続先のデータベースは「dbname=データベース名」で指定します。データベース名は、実際に使うデータベース名に変更します。本コースでは、サンプルアプリケーションを稼働するためのデータベース「test_db」をそのまま利用しますので、常に「dbname=test_db」で利用します。
データベースで利用する文字セットを「charset=文字セット名」で記述します。MySQLの場合は、このdsnの設定に文字セットを可能な限り設定することをお勧めします。一般には、文字セット名は「utf8」でかまいませんが、絵文字や一部の漢字にあるような4バイトのUnicode文字もサポートするために「utf8mb4」という文字セットをMySQL Ver.5.5.3以降でサポートする記述を利用します。これ以前のMySQLでは、utf8しか使えません。4バイトの文字というのは、32ビットコードという意味ではなく、UTF-8での文字表現で4バイトを使用する文字ということです。従来からよく利用されてきた一般的な文字列の多くは日本語の場合UTF-8では3バイトになります。しかしながら、「𥔎」という文字は、Unicodeでは、U+2550Eというコードが割り当てられており、UTF-8でエンコードした場合、F0 A5 94 8Eという4バイトでの表現となります。OSがサポートする文字種が増えているだけに、可能な限りutf8mb4で運用する方が望ましいと言えます。
MySQLとの接続に必要なアカウントの作成と、アクセス権の設定は、例えばリスト2-1-2のように行います。このユーザー「web」はlocalhost経由での接続が許されており、さらにGRANTステートメントで指定されたステートメントの実行のみが、データベースtest_dbに対して定義されています。
CREATE USER 'web'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT, INSERT, DELETE, UPDATE ON TABLE test_db.* TO 'web'@'localhost';
PostgreSQLを利用する場合の設定はリスト2-1-3の通りです。MySQLと原則同一ですが、データベースエンジンが違うので、dsnの書き方が異なり、「pgsql」で始めます。userとpasswordは、データベース側に定義した利用可能なアカウントを指定します。文字セットの指定は、INTER-Mediator側の指定では行わないでも一般には大丈夫です。データベースの設定でエンコード指定が行われているのが一般的です。なお、PHPのマニュアルによると、PDOを利用したPostgreSQL利用時にソケット接続するためには、dsnを「pgsql:user=web dbname=test_db password=password」のように記述すれば良いとなっていますが、INTER-Mediatorでは利用しているAPIの都合上、Ver.10現在、この方法での接続はできません。hostを利用して接続先を指定してください。
db-class:PDO
dsn:pgsql:host=localhost;port=5432;dbname=test_db
user:web
password:password
options:(設定不要)
CREATE USER web PASSWORD 'password';
GRANT ALL PRIVILEGES ON SCHEMA im_sample TO web;
GRANT ALL PRIVILEGES ON im_sample.person TO web;
GRANT ALL PRIVILEGES ON im_sample.postalcode TO web;
:
SQLiteを利用する場合、データベース自体はファイルとして提供され、ドライバーがそのファイルに直接的にやりとりすることになります。リスト2-1-5に設定をまとめますが、ファイルアクセスということが原則のため、データベース自体にアカウントの仕組みはなく、userとpasswordは指定する必要がありません。dsnには、SQLiteを使用することを示す「sqlite:」の文字列と、それに続いてデータベースファイルの絶対パスを記述します。設定はこれだけです。エンコードについても、SQLite Ver.3ではデータベース自体がUTF-8を前提としているため、特に指定する必要はありません。
db-class:PDO
dsn:sqlite:/var/db/im/sample.sq3
user:(設定不要)
password:(設定不要)
options:(設定不要)
SQL Serverでのデータベース設定はdsnはシンプルにserverキーでホストを指定し、databaseキーでデータベース名を指定するだけで可能です。また、GRANTも現在のデータベース内のオブジェクトにまとめて設定することができるので、シンプルな記述で可能です。
db-class:PDO
dsn:sqlsrv:server=localhost;database=test_db
user:web
password:password
options:(設定不要)
CREATE DATABASE test_db COLLATE Japanese_CI_AI;
USE test_db;
CREATE LOGIN web WITH PASSWORD='password', CHECK_POLICY=OFF;
CREATE USER web;
GRANT DELETE, INSERT, REFERENCES, SELECT, UPDATE TO web;
GO
FileMaker Serverへの接続
FileMaker Serverは、Web公開エンジンの設定をしておく必要があります。単にサーバーをインストールするだけではなく、Web共有可能にセットアップしなければなりません。また、WebDirectについては、有効にしておく必要はありません。もちろん、INTER-Mediatorとは別にWebDirectを利用するのであれば、オンにしておきます。
FileMaker Data APIを利用する場合は、やはりFileMaker Serverの管理コンソールで、FileMaker Data APIを有効にしておく必要があります。
一方、XML共有を利用する場合は、FileMaker Serverをセットアップした後、そのコンピュータ上で以下のようにコマンドを入力する必要があります。これらのコマンドによって、XML共有とPHP共有が有効になります。を有効にしておく必要があります。コマンド入力後、FileMaker Serverの管理コンソールにログイン可能なユーザ名とそのパスワードを指定する必要があります。
fmsadmin set cwpconfig enablexml=true
fmsadmin set cwpconfig enablephp=true
PHPの稼働についても、バージョンごとに方法が異なり、FileMaker Server Ver.19の最新版に関してPHPはFileMaker Serverとは別にセットアップが必要になっています。なお、本コースの購入者の方々に対しては、FileMaker Serverそのもののサポートも行いますので、うまく稼働ができない場合には、筆者まで連絡を取ってください。
FileMaker Serverで公開するデータベースについては、XML共有もしくはData APIからのアクセスができるようにしておく必要があります。「ファイル」メニューの「管理」の「セキュリティ」を選択して、初期状態にあるadmin以外に、新たにユーザーを作成するのが良いでしょう。また、ゲストでの運用も可能な限り行わない方が良いでしょう。ここでは、webという名前のユーザーを選択しています。そして、このユーザーはデータの入出力ができればいいので、最大でも「データ入力のみ」のアクセス権セットを選択しておきます。可能であれば、より権限の狭いアクセス権セットにする方が好ましいでしょう。
そして、INTER-Mediatorから利用するユーザーのアクセス権セットについては、FileMaker Pro上で拡張アクセス権の設定を行います。XML共有を使う場合には、「XML Web共有でのアクセス - FMSのみ (fmxml)」および「PHP Web共有でのアクセス - FMSのみ (fmphp)」の拡張アクセス権を設定します。Data APIを使用する場合には、「FileMaker Data APIでのアクセス - FMSのみ (fmrest)」のチェックをオンにします。このように、アクセス権の設定が変わるので、「データ入力のみ」のアクセス権セットを既定値のまま保持したい場合は、このアクセス権セットを複製して、複製した方のセットをINTER-Mediatorから利用するユーザーに割り当ています。さらに、このデータベースをFileMaker Proから開くには、「FileMakerネットワークによるアクセス (fmapp)」もチェックを入れます。INTER-Mediatorに付属しているTestDB.fmp12では、adminユーザーにのみ「FileMakerネットワークによるアクセス」のチェックを入れてあります。
FileMaker Serverおよびデータベースに関して以上の準備をすれば、INTER-Mediator側の設定を決めることができます。FIleMakerデータベースについては、リスト2-1-9のような項目を設定します。設定項目は多くなりますが、FileMaker ServerのXML共有あるいはData APIを利用するための設定を基本的にすべて設定可能にしているためです。それぞれの場合の設定例を示します。
db-class:FileMaker_FX
database:TestDB
user:web
password:password
server:192.168.56.1
port:80
protocol:http
datatype:FMPro12
db-class:FileMaker_DataAPI
database:TestDB
user:web
password:password
server:localhost
port:443
protocol:https
cert-verifying:true
db-classは、INTER-MediatorにFileMaker Server用の機能を利用するように指定する部分と考えてください。XML共有を使う場合には、FileMaker_FX、DataAPIを使う場合にはFileMaker_DataAPIを指定します。databaseは、FileMaker Serverで公開されているデータベースのファイル名を指定します。拡張子はあってもなくてもかまいません。userとpasswordは、FileMakerのデータベースに定義した、データの読み書き可能なアカウントのものを指定します。serverは、FileMaker Serverが稼働しているホストのIPアドレスや、あるいはホスト名を指定します。同一のサーバーであればlocalhostや127.0.0.1を指定します。portは、FileMaker Serverの設定に依存しますが、一般には80ないしは443になると思われます。そして、protocolもFileMaker Serverの設定に依存しますが、一般にはhttpあるいはhttpsになると思われます。FileMaker Data APIについては、原則としてhttpsで443ポートにする必要があります。datatypeはXML共有の場合にのみ必要で、サーバーのバージョンに応じたものを指定するような雰囲気もあるかもしれませんが、FMPro12以降は特に動作に違いはありませんので、あまり気にすることはありません。「FMPro12」等を設定しておけば当面は問題ないと思われます。cert-verifyingはData APIの時に必要で、FileMaker Serverへの接続において、httpsの証明書の検証を行うかどうかです。既定値はtrueで行うようになっていますが、その場合serverがlocalhostだった場合に証明書のセットアップが事実上できないことになります。そこで、cert-verifyingをfalseにすることで、証明書は使うが検証はしない状態にできます。この場合、証明書はFileMaker Serverがセットアップ時に用意したものになっている可能性もあります。要するに、暗号化は行うけどサーバのホスト名の検証が省略されるということです。原則として、テスト運用等での利用にとどめてください。
このセクションのまとめ
このセクションでは、INTER-Mediatorで使用するデータベースに関して、INTER-Mediatorに設定する情報にどのようなものがあるかを紹介しました。ここで集めた情報を実際に設定する方法は、この後引き続いて説明を行います。
2-2ページ構築のための基本設定
サンプルデータベースにある郵便番号のテーブルからその一覧を表示するWebページを作成します。フィールドの値をページ上に表示させることや、複数のレコードが取得された場合の繰り返し処理を説明します。ここでは、さまざまな設定をひとつひとつ追っていくことにします。
Webページの基本構成
INTER-Mediatorで作成するWebページは、最終的にはブラウザーでアクセスした結果、データベースの内容が表示されたり、あるいはデータベースへの入力や編集ができるようになっている必要があります。そうした働きの全体像を示したのが図2-2-1です。Webアプリケーションとして共通の処理をフレームワークとして提供し、用途によって異なる部分をその都度開発するというの基本です。まず、開発に先立って、スキーマが適用されたデータベースがあることが前提です。これら以外に、HTMLで記述する「ページファイル」と、PHPで記述する「定義ファイル」を用意します。
ページファイルは、実際にブラウザーが解釈してレイアウトされたページとしてレンダリング可能なHTMLであり、最終的なページを構成するためのテンプレート、つまり、データを埋め込む前の状態を記述したものと考えてください。例えば、データベースの内容を表にしたいのなら、TABLE、TR、TDといったタグで、1レコード分の表示をどのようにするのかをHTMLで記述するのが典型的な例です。
一方、定義ファイルには、データベースの接続に関する設定を始めとして、さまざまな動作の設定を記述します。ページファイルにすべてを記述しない理由のひとつは、ある種の設定の共通化を行う場合を想定しているからです。例えば、ひとつの定義ファイルを複数のページファイルで利用するといった状況です。さらに、HTMLのタグは設定が多くなると可読性が落ちます。そのため、ページファイルには定義ファイルのどの設定を使うのかを記述するというのが基本的な考え方になります。また、後で説明するように、データベースにあるデータを単にテーブルとして扱うのではなく、「コンテキスト」として、抽象化された対象として取り扱うことが動作の基礎をなしており、定義ファイルはコンテキストを定義するファイルとして存在しています。
図2-2-1では、フレームワークが提供する部分と、さらにフレームワークの動作を拡張したり、間に入って処理系の違いを仲介するデータコンバーターなどの拡張を示す部分についても記載があります。これらの拡張コンポーネントについては、コースの後半に解説を行います。ここよりいくつかの章は、ページファイルや定義ファイルを作成することで実現する範囲の機能を説明します。
実際に稼働させると、ページファイルとフレームワークの一部がブラウザーにダウンロードされて、ページの生成を始めます。その途中で必要に応じてデータベース処理を行います。
定義ファイルに定義するコンテキスト
実際のファイルの記述方法を説明する前に、まず、「コンテキスト」の概念を説明しましょう。図2-2-2は、コンテキストを中心とした、INTER-MediatorによるWebページの構成です。データベースのデータをそのままページに持ち込むではなく、コンテキストという中間的なものを定義します。コンテキストの実体、すなわち、コンテキストから得られるデータは、テーブル形式のデータ(つまり、「リレーション」)と考えてください。
このコンテキストは、テーブルと同一ではなく、意味が付加されたテーブル形式のデータです。ここでの意味というのは、具体的には検索条件や並べ替えフィールドの指定、そしてそれらの条件を適用した結果、得られたテーブル形式のデータを示すものです。つまり、成り立ちに意味があるデータです。例えば、住所録のテーブルがあるとします。その中で、利用者が興味があるのは、「学校の同期生」「家族と親戚」「取引先関係」など、その一部のデータであり、一部を抽出するためには意味をきちんと定義しなければなりません。住所録に対してなんらかの意味を与えることによって、得られるデータをコンテキストとします。もちろん、適切なフィールドの定義や、きちんとしたデータの入力など、さまざまな条件を満たさないと、コンテキストは完成しませんが、テーブルそのものではなく、意味を付与したコンテキストを中心にして、Webページを構成するという手法をINTER-Mediatorでは採用しています。
そして、ページファイルの一部分が、コンテキストに対応付けられます。その規則についてはこの後すぐに説明をします。実際にブラウザー上で構成されるHTMLは、テンプレートとしてのページファイルの内容と、コンテキストで得られるデータを合成したものです。
定義ファイルの記述方法
定義ファイルはPHPで記述します。PHPの文法を駆使したプログラムを記述するのではなく、主として、キーと値のセット(連想配列)による、コンテキストの定義やデータベースの接続のための設定などを記述します。したがって、プログラムを作成する知識は必要ではありませんが、一方でPHPのルールに従った連想配列を記述する必要があるため、基本文法についてはある程度知っていると作業はスムーズでしょう。リスト2-2-1は定義ファイルの記述例です。
<?php
require_once('INTER-Mediator/INTER-Mediator.php');
IM_Entry(
// ここから第1引数
array(
array(
'name' => 'postalcode', 'table' => 'postalcode', 'view' => 'postalcode',
'records' => 10, 'maxrecords' => 100,
'paging' => true, 'key' => 'id',
'query' => array(
array( 'field' => 'f3', 'value' => '1%', 'operator' => 'LIKE' )
),
'sort' => array(
array( 'field' => 'f3', 'direction' => 'ASC' )
),
'repeat-control' => 'confirm-insert confirm-delete',
),
),
// ここから第2引数
array(
),
// ここから第3引数
array( 'db-class' => 'PDO', 'dsn' => 'mysql:host=localhost;charset=utf8mb4',
'user' => 'web', password => 'password' ),
// ここから第4引数
2
);
定義ファイルは拡張子が.phpのファイルに保存します。そして、中身はPHP言語なので、冒頭には「<?php」の記述があります。続くrequire_onceにより、INTER-Mediatorの本体を読み込みます。サーバーのファイルシステム上で、この定義ファイルと、INTER-MediatorフォルダーにあるINTER-Mediator.phpファイルとの間の相対パスを記述します。この例の場合は定義ファイルと同じフォルダーにINTER-Mediatorフォルダーがあるので、指定のようなパスになります。そして、定義ファイルの残りの部分は、IM_Entry関数の呼び出しです。この関数の呼び出しを行うことは常に決まっています。引数は表2-2-1に示します。連想配列のキーに相当する文字列はすでに決められたものとなっており、決められていないキーは、デバッグ時にはエラーを出すようにしています。キーに指定する文字列とその機能は、本コースで順次説明します。
引数 | 設定する値 | 解釈される内容 |
---|---|---|
1 | コンテキスト定義の配列 | コンテキスト定義は連想配列で記述する |
2 | オプション設定の配列 | コンテキスト外部に設定する内容 |
3 | データベース接続の配列 | データベース接続に必要な情報 |
4 | デバッグ情報 | falseならデバッグしない、2ならデバッグ情報をページ上に表示する |
もし、PHPのプログラムのファイルを直接開いて編集したくない場合には、INTER-Mediator内に組み込みの「定義ファイルエディター(Definition File Editor)」も用意していますので、こちらをお使いください。INTER-Mediatorの演習環境では簡単に定義ファイルを利用できるようになっています。このエディターは、Webアプリケーションの形式になっており、ブラウザーでデータを入力して保存できるようになっています。INTER-Mediatorの演習環境上では、各ファイルやディレクトリに適切なアクセス権が設定されているので保存が可能ですが、Webブラウザーの稼働ユーザーの権限で、定義ファイル自体に書き込みが可能になっていないと、書き込み時にエラーが出ます。その場合はファイルのアクセス権を改めて調べてみてください。
この後の演習で、実際に、ファイルの内容を編集する方法も含めて、作業手順を示しながら、作成方法を学習します。
ページファイルの記述方法
ページファイルは、拡張子が.htmlで、HTMLで記述するWebページのテンプレートとなるファイルです。まず、ヘッダーセクションでは、定義ファイルを、SCRIPTタグで読み込みます。定義ファイルはPHPで記述されたファイルですが、Webサーバー経由、つまりスクリプトが実行した結果は、JavaScriptのプログラムが返されます。拡張子は.phpですが、MIMEタイプはtext/javascriptで返される、JavaScriptのプログラムであり、それがINTER-Mediatorのクライアントサイドで稼働するプログラムの本体になります。
以上の点から、ページファイルのヘッダーからBODYタグの部分は、リスト2-2-2のような書き方が一般的な記述方法になります。もちろん、ヘッダーには他にスタイルシートの読み込みやMETAタグ、他のJavaScriptのプログラムの読み込みがあってもかまいません。定義ファイルをSCRIPTタグで読み込む部分が必須の記述になります。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>INTER-Mediator Sample</title>
<script type="text/javascript" src="def01.php"></script>
</head>
:
なお、Ver.5.4-devの途中までは、INTER-Mediatorを動作させるきっかけを記述する必要がありました。「INTERMediator.construct(true);」というJavaScriptのプログラムを記述する必要があったのです。そのために、BODYタグのonload属性か、SCRIPTタグないしは別途拡張子が.jsのファイルを用意して、そこでwindow.onloadに対して無名関数を定義するなど行っていました。Ver.5.4-devの途中より、このconstructメソッドの呼び出しは自動的に行われるようになりましたので、記述の必要は無くなりました。
実際にデータベースのデータを表示するのは、BODYタグの内部になります。ここでは、コンテキストに「postalcode」という名前のコンテキストがあり、そこには、f3、f7、f8、f9といったフィールドがあることが既知であるとします。このとき、以下のように記述を行います。一般的なHTMLでのTABLEタグを利用したテーブルですが、一般的なページとの違いは、data-im属性があることです。data-im属性の値は「コンテキスト名@フィールド名」と記述します。すると、最初のTDタグ要素では、postalcodeコンテキストから得られたレコードのf3フィールドの値が、TDタグ要素のテキスト要素、つまり、<td>と</td>の間に挟み込まれるといった処理をINTER-Mediatorが行います。
<body>
<table>
<thead>
<tr><th>f3</th><th>f8</th>
<th>f9</th><th>f10</th></tr>
</thead>
<tbody>
<tr>
<td data-im="postalcode@f3"></td>
<td data-im="postalcode@f7"></td>
<td data-im="postalcode@f8"></td>
<td data-im="postalcode@f9"></td>
</tr>
</tbody>
</table>
</body>
もし、ここで、postalcodeコンテキストが複数のレコードからなる場合はどうなるでしょうか? INTER-Mediatorは、TBODYタグの内部のTRタグ要素を、レコードごとに用意して、追加します。つまり、この場合はTRタグ要素がひとつありますが、レコードが10個あれば、TRタグ要素は10個並び、レコードの順に前からTBODYタグ要素の子要素として追加されて行きます。このとき、繰り返されるTRタグ要素を「リピーター」、その親要素であるTBODYタグ要素を「エンクロージャー」と呼びます。つまり、エンクロージャーの子要素にあるリピーターが、レコードの数だけ繰り返すということをINTER-Mediatorは行います。
表2-2-2のようないくつかのレコードがコンテキストから得られた場合、INTER-Mediatorはクライアントサイドでリスト2-2-4のようなタグ要素を生成します。その結果、テンプレートとしてのページファイルの内容にコンテキストで得られたデータを合成して、ブラウザーの画面に表示されることになります。
id | f3 | f7 | f8 | f9 |
---|---|---|---|---|
1 | 1000000 | 東京都 | 千代田区 | 以下に掲載がない場合 |
2 | 1020072 | 東京都 | 千代田区 | 飯田橋 |
3 | 1020082 | 東京都 | 千代田区 | 一番町 |
4 | 1010032 | 東京都 | 千代田区 | 岩本町 |
5 | 1010047 | 東京都 | 千代田区 | 内神田 |
: | : | : | : | : |
<body>
<table>
<thead>
<tr><th>f3</th><th>f8</th>
<th>f9</th><th>f10</th></tr>
</thead>
<tbody>
<tr>
<td data-im="postalcode@f3">1000000</td>
<td data-im="postalcode@f7">東京都</td>
<td data-im="postalcode@f8">千代田区</td>
<td data-im="postalcode@f9">以下に掲載がない場合</td>
</tr>
<tr>
<td data-im="postalcode@f3">1020072</td>
<td data-im="postalcode@f7">東京都</td>
<td data-im="postalcode@f8">千代田区</td>
<td data-im="postalcode@f9">飯田橋</td>
</tr>
<tr>
<td data-im="postalcode@f3">1020082</td>
<td data-im="postalcode@f7">東京都</td>
<td data-im="postalcode@f8">千代田区</td>
<td data-im="postalcode@f9">一番町</td>
</tr>
:
</tbody>
</table>
</body>
ここではTDタグにdata-im属性を記述しましたが、DIVやSPAN、Pなどの汎用的なタグにも記述できます。また、タグのテキストだけでなく、タグの属性にもフィールドのデータを差し込むことができます。ここでは、エンクロージャーとしてのTBODYとリピーターとしてのTRの組み合わせの中で、TRタグの内部にdata-im属性を持つタグ要素が存在しています。INTER-Mediatorはdata-im要素を持つタグ要素を「リンクノード」として識別し、リンクノードの上位ノードを検索して、リピーターの範囲を決めます。そして、そのリピーターの中で使われているコンテキストを求めてデータベースアクセスし、得られた結果を合成するといった動作を行います。
演習データベースの内容をリスト表示する
以下、演習を通じて、データベースに入力されている郵便番号の情報を、Webページ上に一覧表示をしてみます。演習は、演習環境を利用している前提で手順を記載しますので、実際にお手元で作業を行ってみてください。
データベースの内容を確認する(MySQL)
まず最初に、データベースの内容をチェックします。MySQLについては以下のようにして、コマンドラインで参照しますが、コマンドラインに慣れていない方は、画面ショットの最後の結果だけを見て、データベースの中身を確認するだけでもかまいません。
mysql -u web -h 127.0.0.1 -P 13306 --password=password test_db
select * from postalcode limit 6;
- データベースtest_dbに、postalcodeテーブルがある
- このテーブルのフィールド構成は、f3が郵便番号、f7〜9が都道府県・市区町村・町域名となっている
- フィールドidは整数値で、レコードを特定する一意な値が設定されている
- フィールドmemoがあり、データは入っていない。このフィールドは文字列型である
データベースの内容を確認する(FileMaker)
FileMaker Server上のサンプルデータベース「TestDB.fmp12」を開き、「postalcode」レイアウトを参照してみます。FileMaker Proで開いて確認しておきましょう。
- データベースTestDBに、postalcodeテーブルおよび同名のTOCがあり、postalcodeレイアウトで表示されている
- このテーブルのフィールド構成は、f3が郵便番号、f7〜9が都道府県・市区町村・町域名となっている
- フィールドidは整数値で、レコードを特定する一意な値が設定されている
- フィールドmemoがあり、データは入っていない。このフィールドは文字列型である
定義ファイルを編集する
db-classは「PDO」のままでかまいません。dsnに「mysql:host=db;dbname=test_db;charset=utf8mb4」と入力します。そして、userに「web」、passwordに「password」と入力します。
db-classを「FileMaker_DataAPI」に書き換えます。databaseは「TestDB」、userに「web」、passwordに「password」、serverに「gateway.docker.internal」、portに「443」、protocolに「https」、cert-vefifyingに「false」と入力します。
MySQLの場合、ApacheやPHPが稼働しているコンテナから、MySQLが稼働しているコンテナへ接続が必要です。この場合、コンテナ間をつなぐ仮想ネットワーク上で利用できる名前があります。「db」は、仮想ネットワークで使えるMySQLコンテナのサーバ名です。Docker Composerの設定ファイルで定義されています。gateway.docker.internalはコンテナからホストOSを参照可能なサーバ名で、Docker自身がこの名前でのアクセスを提供しています。
ページファイルを編集する
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title></title>
</head>
<body>
<table>
<thead>
<tr><th>郵便番号</th><th>住所</th></tr>
</thead>
<tbody>
<tr><td>1111111</td><td>東京特許許可局</td></tr>
</tbody>
</table>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title></title>
<script type="text/javascript" src="def01.php"></script>
</head>
<body>
<table>
<thead>
<tr><th>郵便番号</th><th>住所</th></tr>
</thead>
<tbody>
<tr>
<td data-im="postalcode@f3"></td>
<td>
<span data-im="postalcode@f7"></span>
<span data-im="postalcode@f8"></span>
<span data-im="postalcode@f9"></span>
</td>
</tr>
</tbody>
</table>
</body>
データベースからの取り出し結果の表示
作成したファイルの確認
演習環境で変更するページファイルや定義ファイルは、IMApp_Trialレポジトリの中のsrcフォルダに存在します。ページファイルの中身は、ページファイルエディター上で見えるものと同じですが、定義ファイルのそのものの内容を確認してみます。
<?php
//todo ## Set the valid path to the file 'INTER-Mediator.php'
require_once('INTER-Mediator/INTER-Mediator.php');
IM_Entry(array (
0 =>
array (
'name' => 'postalcode',
'table' => 'postalcode',
'view' => 'postalcode',
'records' => 10,
'maxrecords' => 100,
'paging' => true,
'key' => 'id',
),
),
array (
),
array (
'db-class' => 'PDO',
'dsn' => 'mysql:host=localhost;dbname=test_db;charset=utf8mb4',
'user' => 'web',
'password' => 'password',
),
false);
?>
定義ファイルの中身と、定義ファイルエディターの設定の対比をしてみてください。連想配列のキーに相当する部分はページ上では固定された文字列となっており、値に相当する部分が入力可能なテキストフィールドになっています。ここで見えているデータベース接続に関するキーと値は『2-1 データベースからの取り出し設定』ですでに説明した通りです。コンテキストの設定については、この後に説明をします。
なお、定義ファイルを構成する上では、「0 =>」のような、数値のインデックスはあってもなくてもかまいません。手作業で定義ファイルを作成するときには省略しますが、定義ファイルエディターが機械的にファイルを生成するので、余計な記述が含まれてしまっています。
演習のまとめ
- 定義ファイルとページファイルを作成することで、データベースの内容をWebページ上に表示できます。
- 定義ファイルは、データベース接続に関する設定や、コンテキストの定義があります。
- ページファイルはWebページのテンプレートとなります。
- フィールドのデータを挿入したいタグ要素に、data-im属性でコンテキスト名とフィールド名を記述します。
- ヘッダー部のSCRIPTタグを利用して、定義ファイルをJavaScriptとして呼び出して、ページに組み込みます。
- データベースの内容をページの中に埋め込むページ合成の処理は、ページロードしたときに自動的に行われます。
コンテキストの定義内容について
ここまでの演習で作成した定義ファイルdef01.phpのコンテキスト定義の部分を解説しましょう。まず、コンテキストの定義は、PHPではひとつの連想配列となっています。その要素として、nameキーによる値は必ず必要です。nameキーは文字通りコンテキストの名称であり、ページファイルのリンクノードで、どのコンテキストのデータを取り出すのかを示すために利用します。nameキーの名前は、一般的にはプログラミング言語の変数命名規則に則って付けておくのが問題は少ないでしょう。一部に稼働する場合があるかもしれませんが、nameキーの値には空白文字を入れないようにしてください。
実際に利用するデータベースは、読み出し、つまりクエリーのときにはviewキーで指定した値を利用します。つまり、viewキーには、データベースのテーブルやビューの名前を指定します。しかしながら、viewキーの値がない場合には、nameキーの値を使います。
一方、編集やレコード作成、レコード削除といった書き込み処理の場合は、tableキーで指定した値を利用します。こちらはテーブルを指定するのが一般的と思われます。このtableキーの値も指定がなければ、nameキーの値を利用します。
以上のように、viewやtableキーにより、読み書き対象のデータベース側のエンティティを個別に指定はできますが、もし、postalcodeテーブルがあるとして、nameキーの値が「postalcode」で、viewやtableキーを省略しているとしたら、postalcodeテーブルに対して読み書きを行います。これが一番シンプルな定義になります。さらにsourceキーもデータベース側のエンティティを指定するのに利用できますが、こちらはすぐ後の『データベース上での同一エンティティであることを示すsourceキー』で説明します。
viewやtableを別に用意しているのは、同一のテーブルでも、状況により(つまり、コンテキストにより)、異なる結果を得たい場合に対応します。同じpostalcodeでも、「新宿区の郵便番号」と「足立区の郵便番号」というコンテキストを伴う利用がある場合、nameキーはそれぞれsinjuku_postalcode、adachi_postalcodeとしておき、viewはいずれも同じpostalcodeにするという使い方が考えられます。そして、次のセクションで説明する検索条件を適切に指定をしていれば、それぞれのコンテキストは名前に従った結果が得られるということになります。
なお、FileMakerでは、viewやtableキーの値としては、レイアウト名を指定します。TO名(リレーションシップのタブで定義するボックスの名称)やテーブル名ではないので、注意をしてください。FileMaker ServerのXML共有およびFileMaker Data APIの制約があり、レイアウトを経由する方法でのWeb利用しかできません。
keyキーに対する値は、そのテーブルの主キーを指定します。複数のフィールドは指定できません。ただし、読み出しのみの場合には、この設定はなくてもかまいませんが、編集や削除の処理で、この値を利用します。また、挿入時にも挿入したレコードを特定するためにこの設定を利用します。つまり、更新処理がなければkeyキーの設定は不要ですが、更新処理が行われる場合には必須です。FileMakerの場合、「'key' => '-recid'」という指定が可能です。これは、システムが背後でレコードに自動的に割り振る通し番号を主キーとして利用する設定です。
recordsキーは、検索結果の中からいくつのレコードを実際に取り出すのかということを指定するものです。postalcodeテーブルには何千ものレコードがありますが、10レコードだけが表示されています。この場合、たまたま検索して得られた先頭から10レコードを取り出しています。maxrecordsは、recordsの値を動的に変更する仕組みを使う上で、この数値以上は絶対に上回らないようにするという設定です。いずれも、省略すると、非常に大きな数字を設定します。しかしながら、何万のレコードを一度にページに表示しようとすると多大な時間がかかってしまいます。通常はなんらかの制限が必要ですので、この数値は適切な値を与えておくのが良いでしょう。
pagingキーの値については、別のセクションで説明します。
データベース上での同一エンティティであることを示すsourceキー
Ver.5.3より、コンテキスト定義に、sourceキーの指定ができます。データベース上のエンティティ名に関連するキーとしては、name、view、tableがあり、それらに加えてsourceも利用できます。データベースから読み出しを行うときには、INTER-Mediatorが背後でSQLステートメントを生成していると考えてください(FileMaker Serverは別の方法です)。その時、FROM句に指定されるビューやテーブルの名前は、viewキーがあればその値、なければnameキーの値が使われます。CREATE/UPDATE/DELETコマンドの対象は、tableキーの値で、それがなければnameキーが使わます。例えば、nameキーにテーブル名があれば、そのテーブル名を指定しての読み書きはviewとtableキーの指定はしなくてもできます。しかしながら、表示は何かのビューを使い、更新はそのビューではない特定のテーブルを指定したいような場合に対処できるように、viewとtableキーとして異なる名前を指定できます。
しかし、これだけでは問題があります。あるテーブルのあるレコードのあるフィールドが、ページ上の複数のリンクノードに展開された場合、それが例えば両方ともテキストフィールドであるとします。この時、一方の値を変えると、フィールド更新のためのサーバー通信が行われるとともに、クライアントサイドで同一フィールドとバインドしている別のリンクノードの値を更新します。その時、何を手掛かりにして反映させるコンテキストを探しているかといえば、「viewないしはnameキーの値が同じだが異なるコンテキスト」です。例えば、住所録的なpeopleテーブルがあり、同一ページに全レコードの一覧と、その中で男性の一覧の両方があったとします。前者のコンテキストは、name=allmembers, view=people、後者のコンテキストはname=malemembers, view=peopleとして適切なqueryキーの条件を与えればいいでしょう。こうすれば、同一テーブルから異なるコンテキストを生成して、内容が異なる一覧を1ページ内で生成できます。この時、男性の一覧にあるレコードは、必ず全員のレコードの一覧にもあります。どちらもviewキーがpeopleなので、男性のレコードのひとつのフィールドを変更すると、対応する全員の一覧にあるレコードのフィールド値も更新されます。以下、この動作は「連動」と呼びます。同一のviewキーの値を持つコンテキスト同士なので、INTER-Mediatorにとっての手がかりがあります。
しかしながら、データベースのスキーマに置いて、peopleをもとにしたビューeveryoneとsomeoneが定義してあったとします。それらで表を作るとしたら、nameやviewを使うにしても、everyoneとsomeoneという名前がそれぞれのコンテキスト定義に登場はしますが、コンテキスト定義から各々が同一のpeopleテーブルから導出されていることは分かりません。そうなると、同一のレコードがそれぞれの一覧に見えていて、一方の値を変更したとしても、その変更結果を伝達する手がかりがなく、連動はできません。
このような時にはそれぞれのコンテキスト定義にsourceキーの値を指定して、一方のコンテキストを、name=everyone, source=people、もう一方はname=someone, source=peopleと定義します。ページファイル側は、everyoneあるいはsomeoneをdata-im属性に利用する点は変わりありません。このsourceの設定により、2つのコンテキスト定義から得られるコンテキストは、同一のテーブルから来ていることがINTER-Mediatorに伝わるので、それぞれのリンクノードに見えているフィールド値がユーザーインターフェース上で連動できようになります。
PHPファイル以外の定義ファイルについて
INTER-Mediator Ver.11で、定義ファイルについてはYAMLでの記述を可能にしました。YAMLに対応したことで自動的にJSONでの記述も可能になっています。なお、YAMLで記述可能ではありますが、トレーニングコース(本チュートリアル)では、従来からのPHPファイルで定義ファイルを記述する方法で進めることにします。また、定義ファイルエディタについてはPHPファイルでの従来からの手法にのみ対応していますので、そのこともあってチュートリアルではYAMLでの構築は紹介しません。YAMLを利用した定義ファイルについての詳細は定義ファイルの詳細を参照してください。
前の演習で作成した定義ファイルと同様なYAML形式の定義ファイルは、例えば以下のようになります。ページファイルのファイル名は「page01.html」なので、以下の内容のファイルをpage01.htmlと同じディレクトリに「page01.yaml」というファイル名で保存しておくのが手軽な方法になります。この時、ページファイルのhead部にあるscriptタグによる定義ファイルの読み込み部分は、「/INTER-Mediator/index.php」を参照するようにします。この場合は、ページファイルと定義ファイルのファイル名が同じであることを前提に処理が進められます。
contexts:
- name: postalcode
table: 'postalcode
view: postalcode
records: 10
maxrecords: 100
paging: true
key: id
options: {}
connection:
db-class: PDO
dsn :mysql:host=localhost;dbname=test_db;charset=utf8mb4
use: web
password: password,
debug: 0
このセクションのまとめ
INTER-Mediatorを使ったWebページ構築の基本である、定義ファイルとページファイルを作成することを説明しました。定義ファイルはPHPのプログラムですが、キーと値を与えるデータのセットであり、定義ファイルエディターを利用すればプログラミング言語に従った記述はしなくてもかまいません。定義ファイルの大きな目的は、コンテキストの定義とデータベースへの接続の定義です。コンテキストという中間的な存在を定義して、その内容をページファイルに埋め込みます。ページファイルでは、data-im属性により、コンテキスト名とフィールド名を指定して、フィールドのデータの埋め込みを行います。他に、定義ファイルのSCRIPTタグによる読み込みと、INTER-Mediatorを稼働するきっかけの1行のプログラムの記述が必要です。テーブルの中にdata-im属性を持つリンクノードを定義すれば、レコードの数だけTRタグ要素を複製するため、複数のレコードをページ上に展開する動作も行われます。コンテキストの定義では、name、table、viewという3つのエンティティを指定するキーがあり、同一のテーブルに対して異なる名前のコンテキストを定義して、ページファイル側で使い分けることもできます。recordsキーの値により、レコード数を制限できます。
2-3JavaScriptプログラムの記述
INTER-Mediatorは定義ファイルとページファイルの記述で多くのことができますが、一部の機能やあるいは複雑な処理を構築したいような場合には、JavaScriptによるプログラムが必要になることがあります。ここでは、ページファイルや別のファイルに記述するJavaScriptのプログラムについて基本的なことと、本コースでこの後に出てくるJavaScriptによる機能の呼び出しのための基本的なことを記述します。なお、言語についての説明は本コースでは行いません。
JavaScriptについての知識の確認
すでにJavaScriptについての知識がある方や、これから勉強する方もいらっしゃると思います。INTER-Mediatorで使用するJavaScriptのプログラムを記述するために、どのような知識が必要なのかをまとめておきました。
- 変数や制御構造といった言語の基本の知識はもちろん必要です。特に、配列とオブジェクトについては、読み解いたり記述ができるようになっておいてください。
- JavaScriptでのオブジェクト指向プログラミングの手法は、既存のオブジェクトの利用ができる程度に習熟しておいてください。プロパティの利用はもちろん、メソッドの置き換えや追加を「変数.メソッド名 = function (parameters) {...}」といった記法で行えることなどを知っておきましょう。
- HTMLの要素に対する処理は、DOM(Document Object Model)を基本としています。ただし、INTER-MediatorではDOM処理のための汎用的な仕組みもあるので、ある程度概念として知っておけばよく、DOMを駆使するほどのプログラミングは必要とはしていません。また、原則として既存のタグ要素をオブジェクトとして参照することがほとんどです。少なくともdocument.getElementById(id属性値)で、特定の要素を参照できることは知っておきましょう。
- AJAXに関連する処理は、アプリケーション開発で直接記述することは、INTER-Mediatorを使う範囲ではありません。APIを用意しています。
INTER-Mediatorが定義する変数
INTER-MediatorのAPIについては、本コースで少しずつ説明しますが、表2-3-1のようなグローバル変数がすでに定義された状態になります。ほとんどは、グローバル変数であり、変数が参照するのはオブジェクトです。IMLibContextのみ、クラスとして定義されておりその実態は関数で、実際に利用するときにはそのときに定義した異なる変数名になります。また、INTER-Mediator自体ではそのほかにも変数は定義していますが、APIとして利用するグローバル変数を表にまとめました。
グローバル変数名 | 用途 |
---|---|
INTERMediator | 一番中心になるオブジェクト。ページ合成を行う |
INTERMediatorLib | 機能を構築する際のサポートを行うような小規模の機能をまとめたオブジェクト |
INTERMediatorOnPage | 認証などHTMLページに連動するような機能のクラスで、メソッドの拡張もここで行うものが多い |
INTERMediator_DBAdapter | データベースサーバーに対してのやりとりを行うオブジェクト |
IMLibUI | レコード追加などのユーザーインターフェースから利用できる機能をまとめたオブジェクト |
IMLibContext(クラス) | データベースから取り出した結果を保持するモデルに相当するオブジェクト |
IMLibContextPool | IMLibContextクラスの集合を管理するオブジェクト。APIはこちらのオブジェクトに定義 |
IMLibLocalContext | データベースと独立した、クライアントだけに存在するコンテキスト |
IMParts_Catalog | JavaScriptのコンポーネントを利用するときに使用するオブジェクト |
なお、これらの変数がいつ定義されるかというと、定義ファイルをSCRIPTタグで読み込むときです。定義ファイルはPHPのプログラムとして記述しますが、INTER-Mediatorはその定義ファイルのプログラムを実行することにより、JavaScriptのプログラムを生成してクライアントに伝達します。したがって、拡張子は.phpだけれども、ブラウザーにとってはMIMEタイプがtext/javascriptとなっているJavaScriptのプログラムがサーバーから返ってきたように見えるので、そこで実行が開始されて、これらの変数が定義されます。
JavaScriptを記述する3つの代表的な場所
JavaScriptのプログラムを記述するには、①タグ要素の特定の属性内、②ページファイルのヘッダー、③別ファイルの3つの方法があります。タグ要素の属性内においては、何行にも渡るようなプログラムを記述するのはかえって見づらくなります。通常はひとつあるいは数個の処理ステップで終わらせるような作り方をします。
リスト2-3-1のように、ページファイル(HTMLファイル)のヘッダー部分に、SCRIPTタグでJavaScriptのプログラムを記述することができます。HTMLのコードがダウンロードされて、解析される段階で、SCRIPTタグの内容が実行されると考えれば良いでしょう。したがって、ここでのプログラムを実行する段階では、まだ、ページの要素がロードされた状態にはなっていません。そこで、ロードされたときに実行される関数を定義したり、関数の定義があるのが一般的です。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title></title>
<script type="text/javascript" src="def01.php"></script>
<script type="text/javascript">
INTERMediatorOnPage.doBeforeConstruct = function () {
var params = INTERMediatorOnPage.getURLParametersAsArray();
INTERMediator.clearCondition("postalcode");
if (params["q"]) {
INTERMediator.addCondition("postalcode",
{field: "f3", operator: "LIKE", value: params["q"]});
}
}
</script>
</head>
<body>
:
</body>
</html>
ヘッダー部のSCRIPTタグで記述するJavaScriptのプログラムは、リスト2-3-2のように、別のファイル(page01.js)に記述することもできます。これにより、HTMLとJavaScriptという2つの言語が混在することはなくなり、ファイルはひとつ増えるものの、別々に管理ができます。JavaScriptのプログラムが長くなると、別々のファイルになっている方が、それぞれのファイルの内容を把握しやすくなるでしょう。JavaScriptのプログラムを記述するファイルは拡張子が.jsのファイルで、JavaScripitのプログラムそのものを記述します。すると、それを呼び出しているSCRIPTタグが解析された段階で、ファイルに記述したプログラムが実行されると考えてください。jsファイルのエンコードは、ページファイルのエンコードと同一にするのが基本です。なお、閉じタグの「</script>」を記述する必要があり、「<script type="text/javascript" src="page01.js" />」といった空要素による表現は行いません。空要素にすると、Internet ExplorerやFirefoxで、SCRIPTタグの内容が無視されていたため、空要素にしない記述にしています。HTMLの定義では、内容を持つタグで内容が何もない場合は空要素の記述にはしないとされており、バグというわけではありません。空要素にしても稼働するブラウザーが、拡大解釈していると考えるべきです。
============================ page01.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title></title>
<script type="text/javascript" src="def01.php"></script>
<script type="text/javascript" src="page01.js"></script>
</head>
<body>
:
</body>
</html>
============================ page01.js
INTERMediatorOnPage.doBeforeConstruct = function () {
var params = INTERMediatorOnPage.getURLParametersAsArray();
INTERMediator.clearCondition("postalcode");
if (params["q"]) {
INTERMediator.addCondition("postalcode",
{field: "f3", operator: "LIKE", value: params["q"]});
}
}
このセクションのまとめ
このセクションでは、知っておきたい範囲のJavaScriptのプログラムの知識と、INTER-Mediatorで定義されているグローバル変数、そして、プログラムを具体的にどこに記述するのかといったことを説明しました。
2-4レコードを移動するナビゲーション
たくさんのレコードを決められた数だけのレコードを表示して、表示範囲を切り替えるような機能はWebサイトではよく見られるものです。こうした機能は「ページネーション」と呼ばれます。この機能もINTER-Mediatorではプログラミングの必要はありません。あるid属性を持ったノードが、ページネーションのためのナビゲーションにかわります。しかしながら、そのままだと見栄えがよくありませんので、CSSの設定も必要です。
ページネーションの生成
ページネーションのユーザーインターフェースをページ内に表示させるには、定義ファイルのコンテキストの定義中で、pagingキーによる値をtrueにします。そのpagingキーがtrueになっているコンテキストの表示範囲をページネーションコントローラーで管理をします。このpagingキーは、複数のコンテキストを定義した場合、その中のひとつのコンテキストだけに指定する必要があります。複数のコンテキストでpagingキーの値がtrueの場合、どのコンテキストに適用されるかは定かではありません。
定義ファイル側でpagingキーの設定を行った上で、ページファイルの中に、id属性が「IM_NAVIGATOR」のタグ要素を追加します。要素の種類はなんでもかまいませんが、帯状のコントロールを表示したいのであれば、DIVタグを使うのが便利でしょう。id属性がIM_NAVIGATORのタグ要素の子要素として、ページを前後するボタン等が追加されます。複数のページネーションを設定したい場合には、class属性がIM_NAVIGATORのタグ要素を定義します。例えば、ページトップとボトムにそれぞれページネーションが欲しいような場合にはその方法が使えます。複数のページネーションは同一の機能となります。
以上の動作を演習で追っていきます。
演習ページネーションのナビゲーションを表示する
この章で作成している定義ファイルdef01.phpとページファイルpage01.htmlに対してそのまま編集を続けます。定義ファイルにはpostalcodeというコンテキストひとつだけ定義されていて、pagingキーの値がtrueになっていました。その状態から、ページファイルを変更して、ページネーションが表示されるようにします。
ページファイルの変更
<body>
<div id="IM_NAVIGATOR"></div>
<table>
演習のまとめ
- id属性がIM_NAVIGATORとなっている要素が、INTER-Mediatorによりページネーションのコントロールになります。
- ページネーションのコントロールに必要なスタイルは、テーマの中に設定されているので、通常はそのまま表示されます。
- 定義ファイルのコンテキスト定義で、pagingキーの値がtrueのコンテキストに対して、一定数ずつのレコードを表示します。
- 1ページ内のレコード数は、コンテキスト定義のrecordsキーの値で指定ができます。
ページネーションのカスタマイズ
ページネーションのコントロールのボタンは、SPANタグで表現していますが、例えば、クリック可能なボタンは、class属性がIM_NAV_infoに指定されています。このように、要素に対するクラス設定が自動的になされているので、対応するセレクタに対するスタイルを記述することで、ある程度のカスタマイズは可能です。表2-4-1に、あらかじめ設定されているクラスをまとめておきます。なお、#IM_NAVIGATORについては、当初からつけられたid属性に対応するもので、このセレクタも利用できます。なお、ページネーションのスタイルはテーマの機能と連動しています。具体的なカスタマイズ方法は、『5-6 スタイルの設定を自動化するテーマ』でも説明します。
スタイルシートのセレクタ | ページネーションの該当部分 |
---|---|
#IM_NAVIGATOR | コントローラーの外側 |
.IM_NAV_panel | コントローラー全体 |
span.IM_NAV_info | 文字を表示する部分 |
span.IM_NAV_button | ボタンになる部分(機能するボタン) |
span.IM_NAV_disabled | 機能しないボタンの部分 |
さらにページネーションの中の文言についてもカスタマイズが可能ですが、JavaScriptでのプログラミングが必要になります。JavaScriptのプログラミングについては、『Chapter 6 JavaScriptでのプログラミング』で説明をしますが、ここではその内容を踏まえて結果のみ記載しておきます。
具体的には、INTERMediator.navigationLabelに配列を設定することで、ページネーションの各要素に対するカスタマイズが可能です。配列のインデックスと対応する箇所は表2-4-2に示します。INTERMediator.navigationLabelに何も設定しない場合には、表の既定値が画面に見えます。なお、インデックス9のレコード追加などのボタンについては、コンテキスト定義でのbutton-namesキーでの指定で、既定値ではない名前を設定できます(『3-3 レコードの追加・削除・複製』の『挿入と削除のコントロール』を参照)。
インデックス | 設定対象 | 既定値 | 非表示 |
---|---|---|---|
0 | 最初のレコードに戻る | << | インデックス0〜3 |
1 | 前のページに戻る | < | 無関係 |
2 | 次のページに進む | > | 無関係 |
3 | 最後のレコードに進む | >> | 無関係 |
4 | レコード番号の直前 | レコード番号 | インデックス4〜7 |
5 | レコード番号の範囲の間の文字列 | - | 無関係 |
6 | レコード番号範囲と全レコード数の間の文字列 | / | 無関係 |
7 | 全レコード数の後の文字列 | (空文字列) | 無関係 |
8 | 更新ボタンの名称 | 更新 | インデックス8 |
9 | レコード作成などのボタン | 作成や削除などレコード操作のボタン | インデックス9 |
10 | まとめ保存のボタン | 保存 | インデックス10 |
11 | ログアウトボタンの名称 | ログアウト | インデックス11 |
INTERMediator.navigationLabelへの配列の設定は、ページ合成よりも前である必要があるので、INTERMediatorOnPage.doBeforeConstructに代入する関数内部で記述します。記述方法の例としては、例えばリスト2-4-1のような形式になります。INTERMediator.navigationLabelの右辺に要素が12ある配列を記述しますが、最後のnullが続く部分は省略してもかまいません。それぞれの要素が、表2-4-2のインデックスに対応します。要素がnullだと既定値のままになります。要素に文字列を指定すると、その文字列に置き換わります。インデックスが0、4、8、9、10、11についてはfalseを指定することで、ページネーションの中の要素を表示しなくなります。リスト2-4-1ではインデックスが8のものがfalseになっているので、「更新」ボタンが非表示になり、他の要素はそのまま表示します。
<head>
<script type="text/javascript">
INTERMediatorOnPage.doBeforeConstruct = function () {
INTERMediator.navigationLabel = [null, null, null, null, null, null, null, null, false];
}
</script>
</head>
<body>
</body>
例えば、INTERMediator.navigationLabel = ["最初", "前", "次", "最後"] のように指定すると、<<ボタンは「最初」ボタンなどと変更されます。INTERMediator.navigationLabel = [false, null, null, null, null, null, null, null, null] だと、インデックスの0〜3のものが非表示となり、ページを前後するボタンが画面には出てこなくなります。
このセクションのまとめ
ページネーションは、pagingキーの値をtrueにしたひとつのコンテキストに対して構築され、機能します。また、そのコンテキスト定義のrecordsキーの値に応じて、1ページあたりのレコード数が決まります。ページファイルでは、id属性にIM_NAVIGATORを指定したDIVタグ要素などを配置しておけば、その場所にページネーションのコントロールが構築されます。スタイルシートによって、コントロール内部の要素のカスタマイズができますが、ボタン名などはJavaScriptを利用してカスタマイズが可能です。
2-5検索と並べ替えに関する設定
実際の業務において、データベースの内容を単に表示するだけで事足りる事例はほとんどありません。通常はさまざまな検索条件や並べ替えの条件を指定したりします。こうした設定はINTER-Mediatorでは、定義ファイル上に設定することができます。ここでは固定的な条件を定義ファイル上に記述する方法を紹介しましょう。実行時に条件が決まるような場合には、JavaScriptから設定を行います。
定義ファイルへの条件設定
データベースへのクエリーを行うとき、SQLではWHERE句やORDER BY句で、検索条件やソート対象フィールドを指定します。INTER-Mediatorでは、その設定を行う場所として、まず、コンテキスト中を利用できます。リスト2-5-1はひとつのコンテキスト定義だけを抜き出した設定例です。PHPでの記述がファイルに対してそのまま記述されたものですが、実際の設定は定義ファイルエディターで可能ですので、設定時のキー名とその値を記述する後半の記述で理解しても良いでしょう。queryおよびsortキーに対する値を設定しますが、設定項目が複数あり、その項目がさらに複数あるので、「連想配列の配列」で記述します。後半の記述では、ひとつの塊を示すために、[ ] で囲みます。
(PHPでの記述)array(
'name' => 'postalcode',
'query' => array (
array ( 'field'=>'f3', 'operator'=>'like', 'value'=>'16%' ),
array ( 'field'=>'f8', 'operator'=>'=', 'value'=>'新宿区' )
),
'sort' => array (
array ( 'field'=>'f3', 'direction'=>'desc' )
)
)
(項目のみに注目した記述)
name:postalcode
query:
[field:f3、operator:like、value:16%]
[field:f8、operator:=、value:新宿区]
sort:
[field:f3、direction:desc]
sortキーの配列は、field、directionの2つのキーを持ちます。リスト2-5-1ではひとつの連想配列だけでしたが、複数の連想配列を記述した場合、順番に、並べ替えのキーとして利用します。fieldの指定は必ず必要ですが、directionは省略すると昇順と解釈します。ascが昇順、descが昇順で、サポートするすべてのデータベースはこの書き方で問題ありません(FileMakerでは置き換えを内部で行います)。
queryキーの配列の中は、field、operator、valueの3つのキーを持ちます。それぞれ文字通り、コンテキストの元になるテーブルやビューにあるフィールド名と、検索条件に含める演算子、そして値を指定します。リスト2-5-1の最初の連想配列の場合は、MySQL向けであり、「`f3` like '16%'」というSQLステートメントの断片が記述されます。フィールド名や、値は、実際にデータベースアクセスする前にエスケープ処理を行っているので、この段階での記述ではエスケープ処理の必要はありません。
演算子および値については、データベースエンジンに依存します。例えばMySQLの場合、[field:f3、operator:like、value:16%] により、「16で始まるf3フィールドのデータのあるレコード」が検索されます。%という記述やlike演算子は、MySQLに準拠したものです。したがって、queryキーの記述は、データベースごとによる違いがどうしても発生します。MySQLは多数の演算子をサポートしていますが、FileMakerで使用できるものとの対応は表2-5-1の通りです。MySQLの列のlike演算子は、likeと値に%を含む記述を行うことを意味しており、_は何らかの文字列に置き換わります。同等な条件をFileMaker Server向けに作成するには、[field:f3、operator:bw、value:16] となります。なお、「IS NULL」および「IS NOT NULL」については、operatorにこの記述を行い、valueには何も書かないでおきます。なお、cnやbwといった演算子は、XML共有やPHP共有で使われていたものです。FileMaker Data APIへの対応時に、過去との互換性を確保するために、db-classがFileMaker_FXの場合と同じ検索条件が使えるようにしたため、XML共有特有の演算子が利用できます。しかしながら、FileMaker Data API特有の演算子も利用できます。
MySQL | FileMaker_FX | 条件判断 |
---|---|---|
= | eq | 等しい |
like '%_%' | cn | 含む |
like '_%' | bw | データで始まる |
like '%_' | ew | データで終わる |
> | gt | より大きい |
>= | gte | より大きいか等しい |
< | lt | より小さい |
<= | lte | より小さいか等しい |
!= | neq | 等しくない |
値については、ワイルドカードに対する文字列がデータベースに依存することが注意点です。また、FileMakerの場合で、完全一致にするには、operatorをeqにして、値は「=値」の形式にする必要があります。また、FileMakerで、「フィールド=値1...値2」の形式で検索条件を与えるには、operatorをasis、値を「値1...値2」と指定してください。
日付データについては、データベースエンジンの記述法に従ってください。MySQLなどのSQLデータベースでは「2015-05-25 12:00:00」のような年月日の順に-で結ぶSQLでのルールに従います。FileMakerの場合は、月/日/年の記述つまり「5/25/2015 12:00:00」という記述を行います。
queryの配列が複数ある場合、単に並べて記述されていれば、それらは、ANDで結ばれます。つまり、リスト2-5-1のような条件がある場合には、「`f3` like '16%' and `f8` = '新宿区'」となり、「f3フィールドが16で始まり、かつ、f8フィールドが新宿区」のレコードが抽出されます。
もし、OR条件で記述したい場合は、次のように記述します。2番目の配列により、「ANDとORを入れ替えるという考え方」を適用しています。__operation__もexも決められたキーワードで、その通りの文字列で記述します。以下の場合、「`f3` like '16%' or `f8` = '新宿区'」という条件が生成されます。なお、FileMakerの場合は検索条件に式を記述することができないので、__operation__を記述すると、「すべての演算子がORとなる」という動作になります。FileMakerの場合は「ex」あるいは「or」というキーワードが使えます。FileMakerでORを導入するときは、デバッグモードでどんな検索がされているかを確認しながら設定を行うことをお勧めします。
query:
[field:f3、operator:like、value:16%]
[field:__operation__、operator:ex]
[field:f8、operator:=、value:新宿区]
さらに複雑な条件を設定した例はリスト2-5-3の通りです。fieldが__operation__の場合は、そこまででいったん式をまとめるという意味合いがあります。通常は、andでまとめた式をorでつなげますが、operatorがexの場合は、orでまとめた式をandでつなげるという逆動作になります。なお、複雑な式の記述はここまでです。理論的には、すべての論理式が論理和標準形ないしは論理積標準形で表現できるという定理があるので、この仕様ですべての論理式が記述できると言えるかと思われます。しかしながら、さまざまなロジックが絡む判定がアプリケーションにはつきものです。INTER-Mediatorではこの条件設定以外にもさまざまな手法がありますが、場合によっては、データベースが持つ機能、例えば、ビューを作ったり、ストアドプロシージャを使ったり、FileMaker Serverの場合はスクリプトの呼び出しを行うなどを利用することで、より適切な解決策になることも考えられます。
query:
[field:age、operator:>、value:19]
[field:year、operator:>、value:1980]
[field:__operation__]
[field:age、operator:<、value:39]
[field:year、operator:<、value:2006]
生成される条件:(age > '19' and year > '1980') or (age < '39' and year < '2006')
query:
[field:age、operator:>、value:19]
[field:year、operator:>、value:1980]
[field:__operation__、operator:ex]
[field:age、operator:<、value:39]
[field:year、operator:<、value:2006]
生成される条件:(age > '19' or year > '1980') and (age < '39' or year < '2006')
定義ファイルをPHPのコードとして記述するとき、queryキー内の配列のvalueキーに表2-5-2のようなキーワードを利用することができます。定義ファイルエディターでは、IM_TODAYなどと記述すると、その文字列そのものになりますので、この記述は定義ファイルを別のテキストエディターで開くなど、PHPのコードとして直接編集する場合にだけご利用ください。
キーワード | 置き換わる値 |
---|---|
IM_TODAY | 今日現在の日付 |
IM_NOW | いま現在の日時 |
JavaScriptで検索条件を付加する方法
コンテキストに記述した検索条件やソート条件は、そのコンテキストに対して常に適用される条件になります。これに対して、アプリケーション稼働時に決まるような検索条件を、JavaScriptで付与する手法も用意されています。JavaScriptを利用する方法を使うと、コンテキストの条件に、さらにプログラムで条件をプラスすることができます。例えば、来週締め切りを迎えるプロジェクトを表示させるといったことが可能になります。
具体的には、条件を付与して、ページの合成を行うのが基本です。リスト2-5-4はその例です。diaryというコンテキストへのクエリーを行うときに、「`theDate` <= '2015-01-01'」という検索条件を付与します。コンテキストで決定される条件全体に対して、and条件で、JavaScriptで指定した条件が追加されます。addConditionメソッドは2つの引数を取り、最初がコンテキスト名、次が条件です。条件は、これまで通りfield、operator、valueというプロパティに対する値を指定したオブジェクトです。
<script type="text/javascript">
INTERMediatorOnPage.doBeforeConstruct = function () {
INTERMediator.addCondition("diary", {field: "theDate", operator: "<=", value: "2015-01-01"});
INTERMediator.addSortKey("diary", {field: "theDate", direction: "asc"});
}
</script>
現在設定されている検索条件を得るには、INTERMediator.additionalConditionを利用します。addtionalConditonプロパティに配列が入っています。例えば、リスト2-5-5の実行により、INTERMediator.additionalCondition["diary"]の値が「{field: "theDate", operator: "<=", value: "2015-01-01"}」となります。このプロパティはオブジェクトに保持されるので、明示的に消すまで消えません。したがって、条件が変わるような場合、「INTERMediator.additionalCondition = [];」によって一度クリアする必要がある場合も出てくると考えられます。
並べ替え条件の付与は、同様にINTERMediator.addSortKeyメソッドを利用します。また、参照は、INTERMediator.additionalSortKeyプロパティを利用します。
演習データベース検索に条件を追加する
この演習では、コンテキストに検索条件やソート対象フィールドの設定を付与して、その条件が適用されて検索されていることを確認します。
コンテキストに検索条件を追加する
コンテキストにソート対象フィールドを追加する
演習のまとめ
- コンテキストには検索条件をqueryキーで付与できます。
- 検索条件は、フィールド、演算子、値を与えることで定義できます。
- コンテキストにはソート対象フィールドをsortキーで付与できます。
- フィールド名と、昇順か降順かを与えて定義します。
ユーザーインターフェースの定義だけで検索条件を付与する
データベースの内容を一覧するときに、検索結果を適用するという仕組みを一切プログラムを書かずに実現するために、ボタンやテキストフィールド、あるいは一般的なノードに対して機能を割り当てるという機能があります。要素のdata-im属性を利用して、ローカルコンテキスト(クライアントサイドでデータを記録する一種の「モデル」で、名前は「_」)に特別なキー名でバインドすることで、機能が割り当てられます。以下は具体例でその機能を説明します。
検索条件を付与するテキストフィールド
クエリー時に検索条件を付加するための記法の例がリスト2-5-5です。検索条件は通常、テキストフィールドにキータイプして、returnキーを押して検索されることを期待します。そこで、リスト2-5-5のようなdata-im属性を持ったINPUTタグ要素を記述します。この要素は、コンテキストの外に記述します。中に記述すると、繰り返されてしまうので、外側に記述するのが一般的と思われます。data-im属性の@以前は、ローカルコンテキストを示す_を指定します。@以降は、コロン(:)で4つのセクションに分かれます。それぞれのセクションに記入する内容は、表2-5-3にまとめました。
<input type="text" data-im="_@condition:postalcode:f3,f7,f8,f9:*match*">
セクション | 例 | 記述内容 |
---|---|---|
第1セクション | condition | この文字列「condition」と記述する |
第2セクション | postalcode | コンテキスト名 |
第3セクション | f3,f7,f8,f9 | フィールド名。カンマ区切りで複数の指定も可能で、その場合はOR条件。FileMakerのフィールド名に含まれる「::」は、「;;」に置き換えて指定 |
第4セクション | *match* | 演算子(記述可能な演算子:= != < > <= >= *match match* *match*) |
最初の2つのセクションは、説明通りです。3つ目のセクションのフィールド指定はカンマで区切って複数指定も可能です。複数指定をすると、それぞれのフィールドに対して同じ値の検索条件をORで与えます。演算子は、データベースエンジンに関わらずに、表に示した演算子を記述します。matchを含む演算子は、*の位置に応じて、順に後方一致(*match)、前方一致(match*)、部分一致(*match*)に対応します。
リスト2-5-5で示したテキストフィールドに、例えば「新宿」と入れてEnterキーを押すと、例えばMySQLを使っている場合には「f3 LIKE '%新宿%' OR f7 LIKE '%新宿%' OR f8 LIKE '%新宿%' OR f9 LIKE '%新宿%'」検索条件がコンテキストに付加されて、再度検索を行い、そのコンテキストのエンクロージャー内が更新され、検索結果が表示されます。
テキストフィールドが空白のときには、""やNULLでの条件を設定するわけではなく、このテキストフィールドによる検索条件自体の設定が行われません。
同様なテキストフィールドを2つ以上配置すると、それぞれのテキストフィールドで決まる検索条件に対して、andでの検索が行われます。また、このとき、テキストフィールドが空白だった場合、そのテキストフィールドに対する検索条件は、やはり設定されません。
2つのテキストフィールドをorで結びたい場合や、あるいは空白時に特別な処理をしたいような場合には、単にテキストフィールドを配置した上で、その値を取り出し、addConditionメソッドを使うなどするJavaScriptのプログラムを実行して、検索処理を実施するようにします。
表示件数を指定するポップアップメニュー
リスト2-5-6のポップアップメニューを選択すると、レコードの表示件数をポップアップの選択肢で指定でき、選択と同時にコンテキストが更新されます。data-im属性に指定する「limitnumber」が決められた名前で、コロンより後にはコンテキスト名を記述します。そして、選択肢のためにOPTIONタグ要素を並べますが、選択した項目のvalue属性の値が、表示件数となります。このポップアップメニューのchangeイベントにより、コンテキストを更新します。
<select type="text" data-im="_@limitnumber:postalcode">
<option value="5">5件ずつ</option>
<option value="10">10件ずつ</option>
<option value="30">30件ずつ</option>
</select>
コンテキストの更新ボタン(検索ボタン)
リスト2-5-7のボタンをクリックすると、指定したコンテキストが更新されます。つまり、「検索」ボタンとして機能するということです。「update」が決められた名前で、コロンより後にはコンテキスト名を記述します。clickイベントにより、コンテキストの更新します。
<button data-im="_@update:postalcode">検索ボタン</button>
並べ替えフィールドの指定
リスト2-5-8のSPANタグ内の▲をクリックすると、f3フィールドの昇順で並べ替えを行います。記述するタグはSPANに限らず、onclick属性が適用できるのであれば、なんでもかまいません。clickイベントにより指定したコンテキストが更新されます。
@以降は、コロン(:)で4つのセクションに分かれます。それぞれのセクションに記述する内容は、表2-5-4に記載します。ここで、同一のコンテキストに対する「addorder」の機能を持った要素は連動します。例えば「f3で昇順」の後に「f9の降順」を選択すると、「f9の降順」を最優先とし、続くキーとして「f3で昇順」を設定します。最後に設定した条件が最優先になるようになっています。
<span style="cursor: pointer" data-im="_@addorder:postalcode:f3:asc">▲</span>
セクション | 例 | 記述内容 |
---|---|---|
第1セクション | addorder | この文字列「addorder」を記述する |
第2セクション | postalcode | コンテキスト名 |
第3セクション | f3 | フィールド名。ひとつのみ |
第4セクション | asc | 昇順ならasc、降順ならdesc |
演習検索のユーザーインターフェースを作成する
この演習では、検索条件やソート対象フィールドのユーザーインターフェースをページファイル上に定義して、そのユーザーインターフェースが期待通りの動作をすることを確認します。
検索条件を与えるテキストフィールド
<body>
検索:<input type="text" data-im="_@condition:postalcode:f8,f9:*match*"/>
<div id="IM_NAVIGATOR"></div>
<table>
その他の検索用ユーザーインターフェース要素
<body>
検索:<input type="text" data-im="_@condition:postalcode:f8,f9:*match*"/>
<button data-im="_@update:postalcode">検索</button>
レコード数:<select data-im="_@limitnumber:postalcode">
<option value="5">5</option>
<option value="10" selected="selected">10</option>
<option value="20">20</option>
</select>
<div id="IM_NAVIGATOR"></div>
<table>
<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>
</tr>
</thead>
演習のまとめ
- 検索条件を与えるテキストフィールドを、ページファイルへの設定のみで用意できます。
- 同様に、検索ボタン、1ページの件数、並べ替え指定の要素も、ページファイルへの設定のみで用意できます。
複数の検索枠に関する処理
ここまでは、data-im="_@condition:..." が設定されているテキストフィールドがひとつだけでした、これらが2つ以上ある場合や、あるいはひとつの枠内でキーワードを分離して、それぞれにどのような複合条件を生成するのでしょうか? これについては、JavaScriptのプロパティの値を利用して、カスタマイズ可能です。ページ合成時に設定してもいいですし、あるいはプログラム処理をして検索するような場合には、プロパティ設定を都度都度行うということも可能です。次のようなプロパティが用意されています。
プロパティ | 既定値 | 設定値 |
---|---|---|
INTERMediator.lcConditionsOP1AND | false | 単一のim-data属性に含まれるフィールドの並びに対しての条件は通常はORだが、trueにすればANDにする |
INTERMediator.lcConditionsOP2AND | false | 異なるテキストフィールド間の検索条件は通常はORだが、trueにすればANDになる。 |
INTERMediator.lcConditionsOP3AND | false | falseなら検索キーワードをそのまま指定、trueなら検索キーワードを空白で分解してORで結合、'AND'なら分解してANDで結合 |
変数の解釈だけではわかりにくいと思われるので、ちょっと複雑ですが、設定例で見てみましょう。検索窓としては、次のようなものが3つあったとします。検索窓に入力した検索キーワードは、value属性で表示します。ここで、testtableコンテキストが定義されており、そのコンテキストでは、queryキーに「num1 > 0」という検索条件が設定されていたとします。なお、_@condition: で追加する検索条件と、コンテキスト自体に設定されている検索条件は、常にANDで結合されます。
# 検索条件のテキストフィールドが3つある
<input data-im="_@condition:testtable:num1:>=" value="10">
<input data-im="_@condition:testtable:num1:<=" value="99">
<input data-im="_@condition:testtable:vc1,vc2,vc3:*match*" value="test word">
# .lcConditionsOP1AND = false, .lcConditionsOP2AND = false, .lcConditionsOP3AND = false(既定値)
SELECT * FROM `testtable` WHERE ((`num1` > 0))
AND (
(`num1` >= 10)
OR (`num2` <= 99)
OR((`vc1` LIKE '%test word%'
OR `vc2` LIKE '%test word%'
OR `vc3` LIKE '%test word%')));
# .lcConditionsOP1AND = true, .lcConditionsOP2AND = false, .lcConditionsOP3AND = false
SELECT * FROM `testtable` WHERE ((`num1` > 0))
AND (
(`num1` >= 10)
OR (`num2` <= 99)
OR ((`vc1` LIKE '%test word%'
AND `vc2` LIKE '%test word%'
AND `vc3` LIKE '%test word%')));
# .lcConditionsOP1AND = false, .lcConditionsOP2AND = true, .lcConditionsOP3AND = false
SELECT * FROM `testtable` WHERE ((`num1` > 0))
AND (
(`num1` >= 10)
AND (`num2` <= 99)
AND ((`vc1` LIKE '%test word%'
OR `vc2` LIKE '%test word%'
OR `vc3` LIKE '%test word%')));
# .lcConditionsOP1AND = false, .lcConditionsOP2AND = false, .lcConditionsOP3AND = true
SELECT * FROM `testtable` WHERE ((`num1` > 0))
AND (
(`num1` >= 10)
OR (`num2` <= 99)
OR (((`vc1` LIKE '%test%'
OR `vc2` LIKE '%test%'
OR `vc3` LIKE '%test%')
OR (`vc1` LIKE '%word%'
OR `vc2` LIKE '%word%'
OR `vc3` LIKE '%word%'))))
# .lcConditionsOP1AND = false, .lcConditionsOP2AND = false, .lcConditionsOP3AND = 'AND'
SELECT * FROM `testtable` WHERE ((`num1` > 0))
AND (
(`num1` >= 10)
OR (`num2` <= 99)
OR(((`vc1` LIKE '%test%'
OR `vc2` LIKE '%test%'
OR `vc3` LIKE '%test%')
AND (`vc1` LIKE '%word%'
OR `vc2` LIKE '%word%'
OR `vc3` LIKE '%word%'))))
SQLの集計処理
INTER-Mediatorはコンテキスト定義やあるいは動作上の状況から自動的にSQLステートメントを生成します。通常のリレーション取得はそれでもいいのですが、SUM関数などを使う集計処理(アグリゲーション)を行うようなSQLを生成させたいことがあるでしょう。集計処理を伴うビューを利用してその結果から検索をして必要な集計結果を取り出すこともできますが、その方法では集計として不要なレコードの処理も行うことになるかもしれません。そこで、コンテキスト定義やあるいはその他の検索条件の指定も含めたSQLコマンドの記述ができるようになっています。
SQLコマンドの記述ができるように、コンテキスト定義にaggregation-select、aggregation-from、aggregation-group-byという3つのキーが記述できます。これらのキーがあると、viewキーはSQL生成では無視されます。また、読み込み処理のみをサポートし、更新等の処理は行えないコンテキストとなるので、tableキー、keyキーは実質的に使われません。aggregation-select、aggregation-fromは両方とも指定する必要があります。これらのキーを設定すると、コンテキストからの読み込みに次のようなSQLを生成します。つまりaggregation-で始まるキーに加えて、query、relation、sort、recordsキーの値や、JavaScriptで動的に追加した検索条件などが加味されたSQLのSELECTステートメントが生成されてデータベースに送られます。
SELECT [aggregation-selectの値]
FROM [aggregation-fromの値]
WHERE [query, relation, その他による検索条件]
ORDER BY [sort, その他によるソート条件]
GROUP BY [aggregation-group-byの値]
LIMIT [recordsの値]
START [オフセット値]
なお、STARTについては、引き渡しは実装しましたが、Ver.5.3現在、0でのみ利用してください。つまり、ページネーションは利用できないということです。パフォーマンスを考慮して、レコード数のカウントは、SQLの結果の数と同じにしてあるので、結果的に1ページ分しか出てこないでしょう。これは、後々改良をすることとします。また、このコンテキストは、PDOでしか利用できず、FileMaker Serverでは利用できません。
この機能によるパフォーマンス向上の効果を説明しましょう。例えば、大量の売り上げデータがあって、月ごとに集計したいとします。集計する方法は、SQLだけでなく、計算プロパティを使う方法ありますが、大量なので、処理を効率的にしたいため、データベース側で集計したいとします。aggregation-*キーがない場合には、月ごとの売り上げ集計結果が1レコードとなるようなビューを作成しておき、検索条件(例えば、年と月を指定)をビューに適用することになります。しかし、そのような動作だと、一旦ビューを構築するために全部のデータの集計を行うこともあり、一部のデータだけを使うという動作にならず、十分なパフォーマンスが得られません。しかし、aggregation-selectに「SUM(price)」のような記述が含まれていれば、WHERE句で対象月に絞り込んでクエリーを実施した上で集計されるので、全部のデータを取り出して処理をするということはなく、より最適化されたSQLが発行されます。
FROMを独立して指定できるようにしているので、ここに、「テーブル名 JOIN テーブル名 ON 条件」という記述によるテーブル結合もできます。aggregation-*キーはそのまま指定されるようになっていて、とりあえず、現状ではフィールド名のクォートなどはしていません。セキュリティ的に問題になる可能性もありますが、クライアントのユーザーによって改変できない内容なので、構築時に注意をしておけば基本的には問題ないでしょう。
このセクションのまとめ
データベースへのクエリーでは、検索条件やソート対象フィールドの指定が欠かせません。INTER-Mediatorでは、コンテキストへの設定、およびJavaScriptでの動的な設定をサポートします。加えて、ユーザーインターフェース要素へのルールに従った記述により、例えばテキストフィールドを配置するだけで検索条件を設定する機能を追加することもできます。
2-6設定ファイルparams.php
コンテキストなどを記述する定義ファイルにはさまざまな設定が可能ですが、さらに全ての定義ファイルあるいはページファイルに対して設定が可能なファイルが用意されています。ファイル名は決め内で「params.php」となっています。同一の設定は、定義ファイルよりもparams.phpの方が優先されるので、共通の設定はもちろんparams.phpに設定することで、各定義ファイルでの記述をしなくても設定が適用されるようになります。
params.phpについて
INTER-Mediatorで作成するアプリケーションの設定は、ほとんどが定義ファイルに記述できます。定義ファイルには複数のコンテキストを記述できます。ひとつのページファイルに対してひとつの定義ファイルを作るのが基本ですが、複数のページファイルからひとつの定義ファイルを利用するのもかまいません。一般的なアプリケーション開発ではたくさんの定義ファイルを作るのが一般的な状況でしょう。そうなると、その中で共通の設定をどこかにまとめて書きたくなります。特に、データベースへの接続設定は、さまざまな場所に記述できますが、データベースの接続設定はアプリケーション全体で一通りしかないこともあり、そうならばひとつの場所に1回だけ記述するのが適切です。
こうした、「定義ファイルをまたがった設定」をサポートするために、ファイル名を決め打ちしたparams.phpファイルを利用できます。「INTER-Mediator」フォルダーすなわちレポジトリのルートにparams.phpファイルがあり、PHPでのプログラムで記述されています。プログラムファイルではありますが、実際には変数に値を設定している程度のものなので、クォーテーションの対応や行の最後のセミコロンを忘れない限りは大きく間違えることはないでしょう。しかしながら、サーバーの状況によって設定を変えるなどはプログラムでも実現可能です。
params.phpファイルは、いくつかの置き場所が利用できます。次の順序でparams.phpを探し、最初に見つかったファイルだけを利用します。最初の2つは、params.phpファイル自体をINTER-Mediatorと分離して定義できるので、後からINTER-Mediatorをアップデートしてもparams.phpファイルは上書きされず、そのまま残ります。
- INTER-Mediatorが「vendor/inter-mediator/inter-mediator」というパスに存在する場合、vendorと同じレベルのlibフォルダー直下のparams.php。この設定は、INTER-Mediatorの「アプリケーション形式」(INTER-Mediator自体をcomposerでインストールする場合)を想定しています。
- INTER-Mediatorのひとつ上の階層のフォルダにあるparams.php、つまり、INTER-Mediatorフォルダと同じ場所にあるparams.php
- INTER-Mediatorフォルダにあるparams.php
データベース接続情報の管理
データベースへの接続の基本的なことは『2-1 データベースからの取り出し設定』で、そして実際に定義ファイル上での記述方法は『2-2 ページ構築のための基本設定』で説明してきました。この設定をparams.phpを使って共通化する方法を説明しましょう。
まず、定義ファイルのIM_Entryの第3引数に指定する連想配列のキーをリスト2-6-1にまとめましたが、それに対応するparams.php内での変数名と対比して示しました。
キー | 値 | params.phpでの変数名 |
---|---|---|
'db-class' | データアクセスクラスの名前(例:PDO、FileMaker_DataAPI) | $dbClass |
'dsn' | [PDO]接続時に指定するDSN | $dbDSN |
'option' | [PDO]オプション指定。array型で指定 | $dbOption |
'database' | [FileMaker_DataAPI]データベース名 | $dbDatabase |
'user' | [PDO][FileMaker_DataAPI]ユーザー名 | $dbUser |
'password' | [PDO][FileMaker_DataAPI]パスワード | $dbPassword |
'server' | [FileMaker_DataAPI]サーバーアドレス(例:127.0.0.1) | $dbServer |
'port' | [FileMaker_DataAPI]サーバーポート(例:80) | $dbPort |
'protocol' | [FileMaker_DataAPI]サーバーへの接続プロトコル(例:HTTP) | $dbProtocol |
'datatype' | [FileMaker_DataAPI]サーバーがサポートするデータ形式(例:FMPro14) | $dbDataType |
'cert-vefifying' | [FileMaker_DataAPI]サーバー証明書の検証を行うかどうか(既定値はtrue) | $certVerifying |
例えば、リスト2-6-1は、INTER-Mediatorに含まれているparams.phpの内容です。そして、リスト2-6-2は、定義ファイルのIM_Entry関数の第3引数で、db-classキーしか設定していません。この定義ファイルでは、db-classは「PDO」が指定されていますが、その他の情報は、リスト2-6-1から取得します。PDOで実際に使用される設定は、DSN、ユーザー名、パスワードです。それぞれ、変数の$dbDSN、$dbUser、$dbPasswordに代入されているものが、実際にページ合成等で利用されます。この場合、$dbServerから$dbProtocolまでの変数はFileMakerの場合だけに使われる変数なので、実際の作動時には無視することになります。
<?php
$dbClass = 'PDO';
$dbUser = 'web';
$dbPassword = 'password';
$dbServer = '127.0.0.1';
$dbPort = '80';
$dbDataType = 'FMPro12';
$dbDatabase = 'TestDB';
$dbProtocol = 'HTTP';
$dbDSN = 'mysql:unix_socket=/tmp/mysql.sock;dbname=test_db;charset=utf8';
$dbOption = array();
:
array(
'db-class' => 'PDO',
),
データベース接続に対する設定は、コンテキスト定義、IM_Entry関数の第3引数、そしてparams.phpのいずれでも設定が可能です。コンテキスト定義内でも、db-classなどのキーで指定可能で、この設定はそのコンテキスト単独に適用されます。IM_Entry関数の引数だと定義ファイルのコンテキストすべてに適用されます。params.phpだと複数の定義ファイルすべてに渡って設定が適用されます。もちろん、値が「PDO」の設定もparams.phpに記述することはできるのですが、定義ファイル内にデータベースエンジンの種類が分かる記述があった方が、定義ファイルの編集時に参照しやすいという効果もあります。効率の良い設定方法を採用して、運用管理をしましょう。
設定ファイルparams.phpに記述するアプリケーションの動作に関する設定
データベースの接続以外にparams.phpファイルに指定できる変数のうち、アプリケーションの動作に関するものを表2-6-2にまとめました。それぞれ、実際の説明のところでも、params.phpファイルへの指定が可能な点を記載しますので、設定方法は、解説を参考にしてください。INTER-Mediatorに含まれているparams.php(レポジトリにあるものはこちらをクリック)には、コメントにしているものが多いものの、すべての変数が定義されているので、コメントを外したり、あるいは値を書き換えるだけで通常は利用できると思われます。値の指定例も、INTER-Mediatorに含まれるparams.phpに記載があります。
変数名 | 既定値 | 用途 |
---|---|---|
$prohibitDebugMode | false | trueならデバッグモードに一切入らないようにする |
$appLocale | "ja_JP" | アプリケーションのロケール |
$appCurrency | "JP" | アプリケーションの通貨。省略時は$appLocaleの設定に対応する |
$defaultTimezone | (未定義) | PHPの設定ファイルの不備などで、PHPのdate_default_timezone_setが適切になされていないときに指定するタイムゾーン |
INTER-Mediator自身や定義ファイルの呼び出しパスをカスタマイズする
INTER-Mediatorの動作自体を変更したいと考える場合に利用可能なparams.phpの設定を表2-6-3にまとめました。通常、これらの設定を変更する必要があるときはほぼないとは思いますが、サーバーの状況や動作を改造したいなどの場合に使える可能性があります。
変数名 | 既定値 | 用途 |
---|---|---|
$documentRootPrefix | (未定義) | 定義ファイルへのパスに追加の文字列が必要な場合に指定する |
$scriptPathPrefix | ""(空文字列) | $_SERVER['SCRIPT_NAME']が正しいパスを返さないとき、パスの前につける文字列 |
$scriptPathSuffix | ""(空文字列) | $_SERVER['SCRIPT_NAME']が正しいパスを返さないとき、パスの後につける文字列 |
$callURL | (未定義) | 定義ファイルでダウンロードされたスクリプト内部で、さらに定義ファイルを別のパスで呼び出す場合のURL |
INTER-Mediator自身へのアクセスをクライアントから行うために、INTER-Mediatorフォルダーのルートを識別する必要があります。定義ファイルで呼び出されたクラス(GenerateJSCode.php)内では、自分自身のサーバー上での絶対パスから、$_SERVER['DOCUMENT_ROOT']によって得られたWebサーバーのルートへの絶対パスを取り除くことで、クライアントから見たINTER-Mediatorフォルダーのルートを求めています。しかしながら、共有サーバーを利用するような場合に、パスのズレが発生してしまうことがあります。その場合、params.phpファイルに$documentRootPrefixを定義して、$_SERVER['DOCUMENT_ROOT']で得られる前に設定するパスを記述します。こうして、パスのズレを修正することができます。なお、INTER-Mediatorのパスは、スタイルを設定するテーマの機能でのみ利用されているため、パスのズレがあっても、基本的な動作は行います。
定義ファイルはサーバー側で実行します。PHPの仕組みにより、$_SERVER['SCRIPT_NAME']によって、自分自身、つまりサーバー上で稼働している定義ファイルのパスが分かります。このパスは、Webサーバーのルートからのパスです。クライアント側にロードされたINTER-MediatorのJavaScript部分は、この値をもとに、定義ファイルを何度もクライアントから呼び出します。
しかしながら、クライアント側からの呼び出しパスが、$_SERVER['SCRIPT_NAME']によって得られた値と異なることがあります。例えば、ユーザーのホームフォルダーにあるpublic_htmlフォルダーをルートにする「~user」といったチルダとユーザー名を記述したURLを記述する場合があります。この時にparams.phpファイルに記述する$scriptPathPrefix、$scriptPathSuffixを利用して、$_SERVER['SCRIPT_NAME']によって得られた値の前後に文字列を追加したり、$callURLによってクライアントから定義ファイルを呼び出すパスを完全に違うものにすることができます。これらの方法で、正しくクライアントから定義ファイルを呼び出すようにすることができるようになります。
params.phpファイルに記述する$callURLは、定義ファイルの呼び出しURLを変更できます。通常は、定義ファイルの呼び出しURLは自動的に処理されるのですが、他のフレームワークと連動してINTER-Mediatorを利用するような場合には、こうした設定が必要になるかもしれません。なお、この変数の利用においては、INTER-Mediatorのソースおよび対象とするフレームワークのソースを読み込んだ上での改造が必要な場合になります。本書では詳細は説明しません。
このセクションのまとめ
params.phpファイルへは、データベースの設定などを記述することができ、通常はこの設定を、アプリケーション全体、すなわち複数の定義ファイルを設定するのと同じ効果が得られます。データベース設定以外にも設定可能な項目もありますが、データベース設定で利用されることが多いでしょう。「INTER-Mediator」フォルダーの中だけでなく、同じ階層にparams.phpを配置することもできます。「INTER-Mediator」フォルダーの入れ替えをアップデート時に行うとすれば、「INTER-Mediator」フォルダーと同じ階層にparams.phpを置く方が間違えて消してしまう危険性は低くなります。