1対多の展開とリレーションシップ
前のページの例では、2つのテーブルを利用しましたが、2つ目のconnectionテーブルは、あるフィールドに入力する値を記録するものであり、その中の1つが選択されれるという関係でしたが、さらに複雑な関係がある場合について説明をします。
まず、住所録のaddressテーブルとして以下のようなものがあったとします。ここでの例は、con_idは利用しません。
| id | pname | tel | con_id |
| 1 | 山田一郎 | 0123-456-9876 | 1 |
| 2 | 風下寒子 | 0123-456-9876 | 3 |
| 3 | 屋根裏夫 | 0123-456-9876 | 1 |
addressテーブルが顧客を記録していて、営業部員が顧客に対するコンタクト内容を以下のようなcontactテーブルで記録していたとします。idフィールドが主キーになりますが、address_idには該当する顧客に関して、addressテーブルのその顧客のレコードのidフィールド値を記録します。たとえば、以下のid=2のテーブルは、address_idの値が「3」なので、「屋根裏夫」さんに関して、9月14日にメールで展示会の案内をしたということを記録していることになります。
| id | address_id | dt | memo |
| 1 | 2 | 2013-9-12 10:00 | 電話したが不在 |
| 2 | 3 | 2013-9-14 14:00 | メールで展示会の案内をした |
| 3 | 2 | 2013-9-15 17:00 | 電話で展示会の案内をした |
| 4 | 1 | 2013-9-16 13:00 | 訪問して商品説明した |
この2つのテーブルで、顧客ごとに、コンタクト情報を一覧するようなページを作るとします。定義ファイルは以下のような記述となります。ここで新たにrelationというキーワードが登場します。
name: address
key: id
-
name: contact
key: id
relation: [foreign-key: address_id, join-field: id, operator: =]
このrelationキーの値は、複数の項目からなっており、[ ] でその1つの項目を記述することにします。
ここで、 addressという名前のコンテキストがページファイル内で利用されるようなターゲット指定があるとします。このとき、addressテーブルからデータを取り出すのですが、階層関係にある上位のリピータに関して、join-fieldで指定したidフィールドの値を取り出し、addressテーブルでforeign-keyに指定したaddress_idの値とイコールなレコードだけに絞り込みます。この動作は実際のデータを考えた方が分かりやすいので、この後で動作を見ながらが説明します。
ページファイルについては、次のようなものを作成したとします。前のページの例との違いは、外側のテーブルの3列目に、さらにテーブルがあって、そこにcontactテーブルの内容が展開されるという点です。
<table>
<thead>
<tr><th>名前</th><th>電話番号</th><th>分類</th></tr>
</thead>
<tbody>
<tr>
<td class="IM[address@pname]"></td>
<td class="IM[address@tel]"></td>
<td>
<table>
<thead>
<tr><th>日時</th><th>連絡内容</th></tr>
</thead>
<tbody>
<tr>
<td class="IM[contact@dt]"></td>
<td class="IM[contact@memo]"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
上記のページを実際に作ったサンプルは、「実験環境(別ウインドウに表示)」に用意してあります。実験環境のページのsample8.html/phpをご覧ください。
実際にテーブルが作られる様子を、手順を追って説明します。ページ上に存在するテーブル部分を示します。
- BODY要素から探索されて、外側のTBODYとTRが、addressコンテキストを使用するエンクロージャー/リピーターとして識別されます。
- エンクロージャーからリピーターを取り除き、外側のテーブルのボディ部分がない状態になります。
- addressテーブルにアクセスして、データを取り出します。
- 最初のid=1のレコードがあるので、リピーターを複製し、エンクロージャーの子要素に追加します。
- レコードの中にあるデータが、フィールドに応じてリピーター内部のaddressコンテキストのターゲット指定のある要素内に合成されます。つまり、最初のtdに「山田一郎」、次のtdに「0123-456-9876」という文字列データが埋め込まれます。
| 名前 | 電話番号 | 連絡内容 |
| 山田一郎 | 0123-456-9876 |
|
- リピータ内部を探索すると、新たに3列目にさらにエンクロージャー/リピーターとなるTBODY/TRタグ要素が見つかります。ここでもリピーターを一度削除してエンクロージャの中身を空にします。
| 名前 | 電話番号 | 連絡内容 |
| 山田一郎 | 0123-456-9876 |
|
- 現在作業中のリピーターはcontactコンテキストを使用する事が中身を解析する事で判別できます。
- contactコンテキストの定義に従ってcontactテーブルにアクセスします。このとき、relationキーの指定があるので、まず、その中のjoin-fieldを参照し「id」というフィールド名となっています。そこで、上位のリピーターについて、手順5で得ているようにidの値は「1」です。この値をforeign-keyに指定したaddress_idに持つレコードだけを取り出します。operatorは=なので、つまりは、contactテーブルを取り出すときに、address_id=1のレコードだけを取り出します。
- contactテーブルから、id=4の1つのレコードだけが取り出されました。
- 手順5で切り出したリピーターを複製し、contactテーブルから得られたデータを合成します。ここではテーブルのTBODYに1行分のTRタグ要素が追加され、1列目には「2013-9-16 13:00」、2列目には「訪問して商品説明した」という文字が追加されます。
| 名前 | 電話番号 | 連絡内容 |
| 山田一郎 | 0123-456-9876 |
| 日時 | 連絡内容 |
| 2013-9-16 13:00 |
訪問して商品説明した |
|
- 内側のコンテキストによる展開はこれでいったん終了します。また、外側のリピーターに関してはエンクロージャー/リピータのセットは1つだけですので、これで最初のレコードに関する合成が終わります。
- 外側のコンテキストは、2つ目のレコード、つまりid=2のレコードの展開を行います。
- 外側のリピーターの複製を作り、エンクロージャーの子要素に追加します。そして、id=2のレコードのフィールドをリンクノードに合成します。
| 名前 | 電話番号 | 連絡内容 |
| 山田一郎 | 0123-456-9876 |
| 日時 | 連絡内容 |
| 2013-9-16 13:00 |
訪問して商品説明した |
|
| 風下寒子 | 0123-456-9876 |
|
- 内部を探索すると手順6と同様に、エンクロージャーとリピーターのセットが存在します。リピーターを取り除きます。
| 名前 | 電話番号 | 連絡内容 |
| 山田一郎 | 0123-456-9876 |
| 日時 | 連絡内容 |
| 2013-9-16 13:00 |
訪問して商品説明した |
|
| 風下寒子 | 0123-456-9876 |
|
- 手順8と同様に、ここではid=2のaddressテーブルのレコードが上位のリピーター内に展開されているので、contactテーブルに対してaddress_id=2となるレコードに絞り込んでレコードを取り出します。
- id=1とid=3の2つのレコードが取り出されました。
- contactテーブルのid=1のレコードについて、手順14で取り出したリピーターを複製してレコード内のデータを合成して、エンクロージャーに追加します。
| 名前 | 電話番号 | 連絡内容 |
| 山田一郎 | 0123-456-9876 |
| 日時 | 連絡内容 |
| 2013-9-16 13:00 |
訪問して商品説明した |
|
| 風下寒子 | 0123-456-9876 |
| 日時 | 連絡内容 |
| 2013-9-12 10:00 |
電話したが不在 |
|
- contactテーブルのid=3のレコードについて、手順14で取り出したリピーターを複製してレコード内のデータを合成して、エンクロージャーに追加します。
| 名前 | 電話番号 | 連絡内容 |
| 山田一郎 | 0123-456-9876 |
| 日時 | 連絡内容 |
| 2013-9-16 13:00 |
訪問して商品説明した |
|
| 風下寒子 | 0123-456-9876 |
| 日時 | 連絡内容 |
| 2013-9-12 10:00 |
電話したが不在 |
| 2013-9-15 17:00 |
電話で展示会の案内をした |
|
- 内側のコンテキストの展開はこれで終了し、addressテーブルのid=2に対する展開は終了しました。
- addressテーブルのid=3に対する展開も同様に行います。
| 名前 | 電話番号 | 連絡内容 |
| 山田一郎 | 0123-456-9876 |
| 日時 | 連絡内容 |
| 2013-9-16 13:00 |
訪問して商品説明した |
|
| 風下寒子 | 0123-456-9876 |
| 日時 | 連絡内容 |
| 2013-9-12 10:00 |
電話したが不在 |
| 2013-9-15 17:00 |
電話で展示会の案内をした |
|
| 屋根裏夫 | 0123-456-9876 |
| 日時 | 連絡内容 |
| 2013-9-14 14:00 |
メールで展示会の案内をした |
|