// 
//     Licensed to the Apache Software Foundation (ASF) under one
//     or more contributor license agreements.  See the NOTICE file
//     distributed with this work for additional information
//     regarding copyright ownership.  The ASF licenses this file
//     to you under the Apache License, Version 2.0 (the
//     "License"); you may not use this file except in compliance
//     with the License.  You may obtain a copy of the License at
// 
//       http://www.apache.org/licenses/LICENSE-2.0
// 
//     Unless required by applicable law or agreed to in writing,
//     software distributed under the License is distributed on an
//     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//     KIND, either express or implied.  See the License for the
//     specific language governing permissions and limitations
//     under the License.
//

= PHP WebアプリケーションでのAjax入門
:jbake-type: tutorial
:jbake-tags: tutorials 
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: PHP WebアプリケーションでのAjax入門 - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, PHP WebアプリケーションでのAjax入門

このドキュメントでは、Ajaxの概要を説明し、Ajax関連のテクノロジを使用するときにより短時間で効率よくプログラミングできるNetBeans IDEの機能を示します。Ajaxの基本的な機能を学びながら、テキスト・フィールドの自動補完を行う単純なアプリケーションをビルドします。このドキュメントでは、link:http://weblogs.java.net/blog/gmurray71/archive/2005/12/using_ajax_with_1.html[+Javaテクノロジを使用するAjaxの使用+]に公開しているGreg Murrayの記事とサンプル・アプリケーションを使用します。

Ajaxは「Asynchronous JavaScript and XML」(非同期JavaScriptとXML)の略です。Ajaxは基本的に、ユーザーによるWebページの操作をWebアプリケーションで効率的に処理する方法であり、ユーザーが操作するたびにページをリフレッシュしたり、ページ全体を再ロードしたりする必要を減らします。その結果、デスクトップ・アプリケーションやプラグインに基づいたWebアプリケーションのような、ブラウザを使用したスムーズな操作が可能になります。Ajaxの相互作用はバックグラウンドで非同期で処理されます。このとき、ユーザーはページの操作を続けることができます。Ajaxの相互作用はJavaScriptコードによって開始されます。Ajaxの相互作用が完了したら、JavaScriptがそのページのHTMLソースを更新します。変更は、ページをリフレッシュすることなく、すぐに反映されます。Ajaxの相互作用を使用して、ユーザーがフォームに入力している間にサーバー側ロジックを使用してフォーム・エントリを検証したり、サーバーから詳細なデータを取り出したり、ページ上のデータを動的に更新したり、ページから部分フォームを送信したりできます。


image::images/netbeans-stamp-80-74-73.png[title="このページの内容は、NetBeans IDE 7.2、7.3、7.4および8.0に適用されます"]


*このチュートリアルを完了するには、次のソフトウェアとリソースが必要です。*

|===
|ソフトウェアまたはリソース |必須バージョン 

|link:https://netbeans.org/downloads/index.html[+NetBeans IDEのPHPバンドル版+] |7.2, 7.3, 7.4, 8.0 

|link:http://www.oracle.com/technetwork/java/javase/downloads/index.html[+Java Development Kit (JDK)+] |7または8 

|link:http://www.php.net/downloads.php[+PHPエンジン+] |PHP5 

|link:http://httpd.apache.org/download.cgi[+Apache Webサーバー+] |2.2 
|===

*注意: *

* 使用しているオペレーティング・システムによっては、*AMPパッケージを使用してPHPの開発環境を構成できる場合があります。このパッケージにはPHPエンジンとApache Webサーバーが含まれます。環境の構成方法については、link:../../trails/php.html[+PHPの学習+]を参照してください。
* このチュートリアルは、使用する各テクノロジ(HTML、CSS、JavaScript、およびPHPなど)に関する実用的な知識がある方を対象としています。コードによって提供される機能の概要は説明しますが、コード行ごとの処理は_説明しません_。
* プロジェクトを、正常に機能するソリューションと比較する必要がある場合は、link:https://netbeans.org/projects/samples/downloads/download/Samples%252FPHP%252FMyAjaxApp.zip[+サンプル・アプリケーションをダウンロード+]できます。IDEの新規プロジェクト・ウィザード([Ctrl]-[Shift]-[N]、Macの場合は[⌘]-[Shift]-[N])で「PHP」を選択し、「既存のソースを使用するPHPアプリケーション」プロジェクト・タイプを選択します。ウィザードで、コンピュータにダウンロードされているソースを指定します。



[[overview]]
== アプリケーションの概要

ユーザーが作曲家に関する情報を検索できるWebページを考えます。このページには、ユーザーが作曲家の名前を入力できるフィールドがあります。サンプル・アプリケーションのエントリ・フィールドには、オートコンプリート機能があります。つまり、ユーザーが作曲家の名前の一部を入力すると、Webアプリケーションでは、入力した文字から名または姓のいずれかが始まるすべての作曲家のリストを表示してその名前を補完します。オートコンプリート機能によってユーザーは作曲家の名前を完全に覚えている必要がなく、また求めている情報を直観的かつ容易に入手できます。

image::images/sample-app.png[title="ブラウザに表示されたサンプル・アプリケーション"]

検索フィールドへの自動補完の実装は、Ajaxを使用して実行できます。Ajaxは、`XMLHttpRequest`オブジェクトを使用してクライアントとサーバーの間でリクエストとレスポンスを非同期で受け渡しすることで機能します。次の図は、クライアントとサーバーの間で行われる通信のプロセス・フローを示します。

image::images/ajax-process-flow.png[title="Ajaxのプロセス・フロー図"]


この図には、次の手順のプロセス・フローを示しています。

1. ユーザーが、たとえば名前を入力しているときにキーを解除して、イベントをトリガーします。すると、`XMLHttpRequest`オブジェクトを初期化する関数へのJavaScriptコールが行われます。
2. `XMLHttpRequest`オブジェクトが、イベントをトリガーしたコンポーネントのIDを含むリクエスト・パラメータと、ユーザーが入力した値で構成されます。次に、`XMLHttpRequest`オブジェクトがWebサーバーへの非同期リクエストを実行します。
3. Webサーバーでは、サーブレットやリスナーなどのオブジェクトがリクエストを処理します。データ・ストアからデータが取り出され、XMLドキュメント形式のデータを含むレスポンスが作成されます。
4. 最後に、コールバック関数を使用して`XMLHttpRequest`オブジェクトがXMLデータを受け取って処理し、新しいデータを含むページを表示するようにHTMLのDOM (Document Object Model)を更新します。

このチュートリアルでは、前述の図で示したプロセス・フローに従ってオートコンプリートのシナリオを構築する方法を示します。最初に、プレゼンテーション用、および`XMLHttpRequest`オブジェクトの生成に必要な機能用のクライアント側ファイルを作成します。次に、PHPベースのテクノロジを使用してデータ・ストアとビジネス・ロジックを作成してサーバー側を設定します。最後に、クライアント側に戻り、`callback()`と、HTMLのDOMを更新するためのその他のJavaScript機能を実装します。



[[client1]]
== クライアント側のプログラミング: 第1部

最初に、IDEで新しいPHPアプリケーション・プロジェクトを作成します。

1. 「ファイル」>「新規プロジェクト」を選択します。「カテゴリ」から「*PHP*」を選択します。「プロジェクト」で「*PHPアプリケーション*」を選択し、「*次*」をクリックします。
2. ステップ2の「名前と場所」で、プロジェクト名に「`MyAjaxApp`」と入力します。「ソース・フォルダ」フィールドでコンピュータ上でのプロジェクトの場所を指定できます。他のオプションはデフォルトのままにして、「*次*」をクリックします。

image::images/php-name-location.png[title="新規PHPプロジェクト・ウィザード - 「名前と場所」パネル"]

[start=3]
. ステップ3の「実行構成」で、アプリケーションのデプロイ方法を指定します。*AMPパッケージを構成してPHP開発環境を設定した場合は、ドロップダウン・リストから「*ローカルWebサイト*」を選択し、ブラウザに表示するプロジェクトのURLを指定します。

[start=4]
. 「ファイルをソース・フォルダから別の場所にコピー」オプションを選択します。次に、「フォルダにコピー」フィールドに、サーバー上のデプロイ先のパスを入力します。(Apacheでは、デフォルトの`htdocs`ディレクトリを使用します。)
[.feature]
--
image:images/php-run-config.png[role="left", link="images/php-run-config.png"]
--

[start=5]
. 「*終了*」をクリックします。IDEによってプロジェクト・フォルダがファイル・システム内に作成され、プロジェクトがIDEで開きます。

プロジェクト・ウィザードを使用して、  フレームワークのサポートをプロジェクトに追加することもできます(ウィザードのステップ4)。

デフォルトの`index.php`インデックス・ページが生成され、IDEのエディタで開きます。また、「プロジェクト」ウィンドウにプロジェクトが表示されます。

image::images/php-proj-win.png[title="MyAjaxAppプロジェクトが表示された「プロジェクト」ウィンドウ"]

[start=6]
. コーディングを始める前に、アプリケーションを実行してみて、IDE、サーバー、ブラウザの間の構成が正しく設定されていることを確認します。

IDEのエディタで、indexページに次の`echo`文を追加します。

[source,php]
----

<?php
    // put your code here
    *echo "<h2>Hello World!</h2>";*
?>

----

[start=7]
. 「プロジェクト」ウィンドウでプロジェクト・ノードを右クリックし、「実行」を選択します。IDEによってデフォルトのブラウザが開き、`index.php`で作成したメッセージ「Hello World」が表示されます。

*注意:* プロジェクトを設定できない場合、またはIDE、サーバー、およびブラウザ間で通信を確立できない場合は、link:project-setup.html[+PHPプロジェクトの設定+]を参照して、詳細な手順を確認してください。環境の構成については、link:../../trails/php.html[+PHPの学習+]を参照してください。


[[html]]
=== HTMLエディタの使用

image::images/palette.png[title="HTML要素が表示されたパレット"] 

環境が正しく設定されていることを確認したら、まず、ユーザーに表示するオートコンプリート・インタフェースの開発から始めます。作成するインデックス・ページにはサーバー側のスクリプト要素は必要ないため、まずHTMLページを作成し、そのページをアプリケーションのエントリ・ポイントとして設定します。

IDEを使用する利点の1つは、作業を行うエディタには一般にコード補完機能が用意されていて、コーディングするときに適用すれば生産性を大幅に向上できることです。IDEのエディタは通常、使用しているテクノロジに適応するので、HTMLページで作業しているときにコード補完のキーの組合せ([Ctrl]-[Space])を押すとHTMLのタグと属性の候補が表示されます。後述するように、CSSやJavaScriptなどその他のテクノロジも同様です。

IDEのパレットも便利な機能です。パレットには、コーディングするテクノロジで一般的に適用される要素の使いやすいテンプレートが用意されています。項目をクリックして、ソース・エディタで開いているファイル内の任意の位置にドラッグするのみです。

この図のように大きなアイコンを表示するには、パレット内を右クリックし、「大きなアイコンを表示」を選択します。


1. 「プロジェクト」ウィンドウで「`MyAjaxApp`」プロジェクト・ノードを右クリックし、「新規」>「HTMLファイル」を選択します。
2. HTMLファイル・ウィザードで、ファイル名に「`index`」と入力し、「*終了*」をクリックします。新しい`index.html`ファイルがエディタで開きます。
3. このファイルの既存の内容を次の内容に置き換えます。

[source,html]
----

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Auto-Completion using AJAX</title>
    </head>
    <body>
        <h1>Auto-Completion using AJAX</h1>
    </body>
</html>

----

[start=4]
. テキスト・フィールドの目的を説明するテキストを追加します。次のテキストをコピーして、`<h1>`タグのすぐ下に貼り付けることもできます。

[source,html]
----

<p>This example shows how you can do real time auto-completion using Asynchronous
    JavaScript and XML (Ajax) interactions.</p>

<p>In the form below enter a name. Possible names that will be completed are displayed
    below the form. For example, try typing in "Bach," "Mozart," or "Stravinsky,"
    then click on one of the selections to see composer details.</p>

----

[start=5]
. ページにHTMLフォームを追加します。この操作を行うには、IDEのパレットに表示されている要素を使用します。パレットが開いていない場合は、メイン・メニューから「ウィンドウ」>「パレット」を選択します。次に「HTMLフォーム」の下にある「フォーム」要素をクリックし、ページ内に追加した`<p>`タグの下までドラッグします。「挿入フォーム」ダイアログ・ボックスが表示されます。次の値を指定します。

* アクション: autocomplete.php
* メソッド: GET
* 名前: autofillform

image::images/php-insert-form.png[title="「挿入フォーム」ダイアログ"]

「OK」をクリックします。指定した属性を含むHTMLの`<form>`タグがページに挿入されます。(GETはデフォルトで適用されるので、明示的に宣言しません。)


[start=6]
. HTML表をページに追加します。パレットの「HTML」カテゴリの下で「表」要素をクリックし、`<form>`タグの間の位置までドラッグします。「挿入表」ダイアログ・ボックスが開きます。次の値を指定します。

* 行: 2
* 列: 2
* 境界線のサイズ: 0
* 幅: 0
* セルの間隔: 0
* セルのパディング: 5

image::images/insert-table.png[title="「挿入表」ダイアログ"]

[start=7]
. ソース・エディタ内を右クリックし、「フォーマット」を選択します。これでコードの体裁が整います。フォームは次のようになります。

[source,html]
----

<form name="autofillform" action="autocomplete.php">
  <table border="0" cellpadding="5">
    <thead>
      <tr>
        <th></th>
        <th></th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td></td>
        <td></td>
      </tr>
      <tr>
        <td></td>
        <td></td>
      </tr>
    </tbody>
  </table>
</form>

----

[start=8]
. 表の1行目の1列目に次のテキストを入力します(*太字*部分が変更箇所)。

[source,html]
----

<td>*<strong>Composer Name:</strong>*</td>
----

[start=9]
. 1行目の2列目では、パレットから「テキスト入力」フィールドをドラッグしないで、次のコードを手動で入力します。

[source,html]
----

<input type="text"
    size="40"
    id="complete-field"
    onkeyup="doCompletion();">

----
入力するときは、IDEに組み込まれているコード補完サポートを使用してみてください。たとえば、「`<i`」と入力して[Ctrl]-[Space]を押します。カーソルの下に候補のリストが表示され、選択されている要素の説明が上のボックスに表示されます。ソース・エディタでコーディングしているときはいつでも[Ctrl]-[Space]を押して候補を表示できます。候補が1つのみの場合は、[Ctrl]-[Space]を押すと要素名が自動的に補完されます。
image:images/code-completion.png[title="[Ctrl]-[Space]を押してソース・エディタでトリガーされたコード補完"]
入力した`onkeyup`属性はJavaScript関数`doCompletion()`を指しています。この関数は、フォームのテキスト・フィールド内でキーが押されるたびにコールされ、Ajaxの<<flow-diagram,フロー図>>に示したJavaScriptコールに対応します。

[start=10]
. JavaScriptエディタでの作業に移る前に、アプリケーションのエントリ・ポイントとして`index.php`ファイルを新しい`index.html`ファイルに置き換えます。

これを行うには、「プロジェクト」ウィンドウでプロジェクト・ノードを右クリックし、「プロパティ」を選択します。「*実行構成*」カテゴリを選択し、「開始ファイル」フィールドに「`index.html`」と入力します。image:images/php-entry-point.png[title="「プロジェクト・プロパティ」ウィンドウで、アプリケーションのエントリ・ポイントを指定します。"]

[start=11]
. 「OK」をクリックして変更を保存し、「プロジェクト・プロパティ」ウィンドウを終了します。

[start=12]
. プロジェクトを実行して、ブラウザでどのように表示されるかを確認します。「プロジェクトの実行」(image:images/run-project-btn.png[])ボタンをクリックします。`index.html`ファイルがデフォルトのブラウザに表示されます。
image:images/index-page.png[title="プロジェクトを実行してブラウザに現在の状態を表示"]


[[javascript]]
=== JavaScriptエディタの使用

IDEのJavaScriptエディタには、インテリジェントなコード補完、意味解釈の強調表示、名前の即時変更機能とリファクタリング機能など、多数の高度な編集機能が用意されています。IDEでのJavaScriptの編集機能の詳細は、link:http://www.oracle.com/pls/topic/lookup?ctx=nb8000&id=NBDAG[+NetBeans IDEによるアプリケーションの開発ユーザーズ・ガイド+]のlink:http://docs.oracle.com/cd/E50453_01/doc.80/e50452/dev_html_apps.htm#BACFIFIG[+JavaScriptファイルの作成+]を参照してください。詳細は、link:http://wiki.netbeans.org/JavaScript[+http://wiki.netbeans.org/JavaScript+]を参照してください。

JavaScriptのコード補完は、`.js`ファイル内でコーディングするとき、および他のテクノロジ(HTML、RHTML、JSP、PHPなど)を使用しているときに`<script>`タグ内で自動的に提供されます。JavaScriptエディタの使用中は、IDEによって、JavaScriptの「オプション」パネルで指定するブラウザのタイプとバージョンに従って、ブラウザの互換性情報が表示されます。JavaScriptの「オプション」パネルを開くには、「ツール」>「オプション」(Macの場合は「NetBeans」>「プリファレンス」)を選択してから「その他」>「JavaScript」を選択します。

image::images/php-javascript-options.png[title="JavaScriptの「オプション」パネル"]

IDEでは、Firefox、Internet Explorer、Safari、およびOperaをデフォルトでサポートしています。JavaScriptの「オプション」パネルでは、コード補完を適用するJavaScriptエンジンのバージョンを指定することもできます。

アプリケーションにJavaScriptファイルを追加し、`doCompletion()`の実装を始めます。

1. 「プロジェクト」ウィンドウでプロジェクト・ノードを右クリックし、「新規」>「JavaScriptファイル」を選択します。(「JavaScriptファイル」がリストにない場合は「その他」を選択します。次に、新規ファイル・ウィザードで「その他」カテゴリから「JavaScriptファイル」を選択します。)
2. ファイル名を`javascript`にし、「終了」をクリックします。新しいJavaScriptファイルが「プロジェクト」ウィンドウに表示され、エディタで開きます。
3. 次のコードを`javascript.js`に入力します。

[source,php]
----

var req;
var isIE;

function init() {
    completeField = document.getElementById("complete-field");
}

function doCompletion() {
        var url = "autocomplete.php?action=complete&amp;id=" + escape(completeField.value);
        req = initRequest();
        req.open("GET", url, true);
        req.onreadystatechange = callback;
        req.send(null);
}

function initRequest() {
    if (window.XMLHttpRequest) {
        if (navigator.userAgent.indexOf('MSIE') != -1) {
            isIE = true;
        }
        return new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        isIE = true;
        return new ActiveXObject("Microsoft.XMLHTTP");
    }
}

----

上のコードは、Firefox 3およびInternet Explorerバージョン6と7の単純なブラウザ互換性チェックを行います。互換性の問題に対してさらに堅牢なコードを取り込むには、link:http://www.quirksmode.org[+http://www.quirksmode.org+]のlink:http://www.quirksmode.org/js/detect.html[+ブラウザ検出スクリプト+]を使用することを検討してください。


[start=4]
. `index.html`に戻り、JavaScriptファイルへの参照を`<head>`タグの間に追加します。

[source,html]
----

<script type="text/javascript" src="javascript.js"></script>

----

[Ctrl]-[Tab]を押すと、エディタ内で開いているページを簡単に切り替えることができます。


[start=5]
. `init()`へのコールを開始`<body>`タグ内に挿入します。

[source,html]
----

<body *onload="init()"*>

----
このようにすると、ページがロードされるたびに`init()`がコールされます。

`doCompletion()`には次の役割があります。

* サーバー側で利用できるデータを含むURLを作成すること
* `XMLHttpRequest`オブジェクトを初期化すること
* 非同期リクエストをサーバーに送信するように`XMLHttpRequest`オブジェクトに要求すること

`XMLHttpRequest`オブジェクトはAjaxの中核であり、HTTPを使用してXMLデータを非同期で送信するときの事実上の標準になっています。相互作用が_非同期_であるということは、リクエストの送信後にブラウザではページ内で引続きイベントを処理できることを意味します。データはバックグラウンドで送信され、ページをリフレッシュしないで自動的にページにロードできます。

`XMLHttpRequest`オブジェクトは実際には`initRequest()`で作成し、これは`doCompletion()`からコールされます。この関数では、ブラウザで`XMLHttpRequest`を認識できるかどうかを確認し、認識できる場合は`XMLHttpRequest`オブジェクトを作成します。そうでない場合は、`ActiveXObject` (Internet Explorer 6で`XMLHttpRequest`に相当する)を確認し、識別された場合は`ActiveXObject`を作成します。

相互作用が非同期であるかどうかに関係なく、`XMLHttpRequest`オブジェクトを作成するときは、URL、HTTPメソッド(`GET`または`POST`)の3つのパラメータを指定します。前述の例では、これらのパラメータは次のとおりです。

* URL `autocomplete.php`、およびユーザーが`complete-field`に入力したテキスト

[source,php]
----

var url = "autocomplete.php?action=complete&amp;id=" + escape(completeField.value);
----
* `GET` (HTTPの相互作用で`GET`メソッドを使用することを示します)
* `true` (相互作用は非同期であることを示します)

[source,php]
----

req.open("GET", url, true);
----

相互作用を非同期に設定する場合は、コールバック関数を指定します。この相互作用のコールバック関数は次の文で設定します。


[source,php]
----

req.onreadystatechange = callback;
----

そして、`callback()`関数を<<callback,後で定義>>する必要があります。HTTPの相互作用は`XMLHttpRequest.send()`のコール時に開始します。このアクションは、前述の<<flow-diagram,フロー図>>でWebサーバーに送信されているHTTPリクエストに対応します。



[[serverside]]
== サーバー側のプログラミング

NetBeans IDEでは、PHPを使用したWeb開発が総合的にサポートされています。*AMPパッケージを使用して開発環境を設定し、IDEで短時間で効率よく編集とデプロイができます。IDEでは、ローカル・サーバーに加えて、FTPまたはSFTPを使用してリモートでも環境を構成できます。また、link:http://xdebug.org/[+XDebug+]など外部のデバッガを構成し、IDEの「PHPオプション」ウィンドウから(「ツール」>「オプション」を選択、Macの場合は「NetBeans」>「プリファレンス」を選択し、「PHP」タブを選択)、link:http://www.phpunit.de/[+PHPUnit+]を使用した単体テストを設定できます。PHPエディタには、コード補完、構文の強調表示、出現箇所のマーク、リファクタリング、コード・テンプレート、ドキュメントのポップアップ、コード・ナビゲーション、エディタの警告、およびNetBeans 6.9の場合は形式の不正な構文のエラー・バッジなどの、標準の編集機能があります。PHPのサポートのスクリーンキャストについては、link:../intro-screencasts.html[+NetBeansのビデオ・チュートリアルとデモ+]のページを参照してください。

アプリケーションにデータベースが必要な場合、IDEでは、ほとんどの主要なデータベース、特にMySQLがサポートされています。詳細は、link:../../articles/mysql.html[+NetBeansのMySQLのスクリーンキャスト+]とlink:../../../features/ide/database.html[+データベース統合の説明+]を参照してください。

ここでビルドしているオートコンプリート・アプリケーションのビジネス・ロジックでは、データ・ストアからデータを取り出してリクエストを処理し、レスポンスを作成して送信する必要があります。これは、`autocomplete`という名前のPHPファイルを使用して、ここで実装します。ファイルのコーディングを始める前に、データ・ストアと、ファイルからデータにアクセスするために必要な機能を設定します。

* <<data,データ・ストアの作成>>
* <<business,ビジネス・ロジックの作成>>


[[data]]
=== データ・ストアの作成

この単純なアプリケーションでは、ビジネス・ロジックで`composers`配列に含まれるエントリからデータを取得できるようにする`Composer`というクラスを作成します。次に、その配列を使用して作曲家のデータを保持する`ComposerData`というクラスを作成します。

1. 「プロジェクト」ウィンドウで「`MyAjaxApp`」プロジェクト・ノードを右クリックし、「新規」>「PHPクラス」を選択します。
2. クラス名を`Composer`にし、「終了」をクリックします。クラスが作成され、エディタで開きます。
3. 次のコードをクラス内に貼り付けます(変更箇所は*太字*で表示)。

[source,php]
----

<?php

class Composer {

    *public $id;
    public $firstName;
    public $lastName;
    public $category;

    function __construct($id, $firstName, $lastName, $category) {
        $this->id = $id;
        $this->firstName = $firstName;
        $this->lastName = $lastName;
        $this->category = $category;
    }*
}

?>
----

`ComposerData`クラスを作成します。

1. 「プロジェクト」ウィンドウで「`MyAjaxApp`」プロジェクト・ノードを右クリックし、「新規」>「PHPクラス」を選択します。
2. クラス名を`ComposerData`にし、「終了」をクリックします。クラスが作成され、IDEのエディタで開きます。
3. `require`文をクラスの先頭に追加し、作成した`Composer.php`クラスをこのクラスが要求するように指定します(変更箇所は*太字*で表示)。

[source,php]
----

<?php

*require "Composer.php";*

class ComposerData {

}
----

[start=4]
. エディタで、次のコードをクラス内に貼り付けます(変更箇所は*太字*で表示)。

[source,php]
----

<?php

require "Composer.php";

class ComposerData {

    *public $composers;

    function __construct() {
        $this->composers = array(
            new Composer("1", "Johann Sebastian", "Bach", "Baroque"),
            new Composer("2", "Arcangelo", "Corelli", "Baroque"),
            new Composer("3", "George Frideric", "Handel", "Baroque"),
            new Composer("4", "Henry", "Purcell", "Baroque"),
            new Composer("5", "Jean-Philippe", "Rameau", "Baroque"),
            new Composer("6", "Domenico", "Scarlatti", "Baroque"),
            new Composer("7", "Antonio", "Vivaldi", "Baroque"),

            new Composer("8", "Ludwig van", "Beethoven", "Classical"),
            new Composer("9", "Johannes", "Brahms", "Classical"),
            new Composer("10", "Francesco", "Cavalli", "Classical"),
            new Composer("11", "Fryderyk Franciszek", "Chopin", "Classical"),
            new Composer("12", "Antonin", "Dvorak", "Classical"),
            new Composer("13", "Franz Joseph", "Haydn", "Classical"),
            new Composer("14", "Gustav", "Mahler", "Classical"),
            new Composer("15", "Wolfgang Amadeus", "Mozart", "Classical"),
            new Composer("16", "Johann", "Pachelbel", "Classical"),
            new Composer("17", "Gioachino", "Rossini", "Classical"),
            new Composer("18", "Dmitry", "Shostakovich", "Classical"),
            new Composer("19", "Richard", "Wagner", "Classical"),

            new Composer("20", "Louis-Hector", "Berlioz", "Romantic"),
            new Composer("21", "Georges", "Bizet", "Romantic"),
            new Composer("22", "Cesar", "Cui", "Romantic"),
            new Composer("23", "Claude", "Debussy", "Romantic"),
            new Composer("24", "Edward", "Elgar", "Romantic"),
            new Composer("25", "Gabriel", "Faure", "Romantic"),
            new Composer("26", "Cesar", "Franck", "Romantic"),
            new Composer("27", "Edvard", "Grieg", "Romantic"),
            new Composer("28", "Nikolay", "Rimsky-Korsakov", "Romantic"),
            new Composer("29", "Franz Joseph", "Liszt", "Romantic"),

            new Composer("30", "Felix", "Mendelssohn", "Romantic"),
            new Composer("31", "Giacomo", "Puccini", "Romantic"),
            new Composer("32", "Sergei", "Rachmaninoff", "Romantic"),
            new Composer("33", "Camille", "Saint-Saens", "Romantic"),
            new Composer("34", "Franz", "Schubert", "Romantic"),
            new Composer("35", "Robert", "Schumann", "Romantic"),
            new Composer("36", "Jean", "Sibelius", "Romantic"),
            new Composer("37", "Bedrich", "Smetana", "Romantic"),
            new Composer("38", "Richard", "Strauss", "Romantic"),
            new Composer("39", "Pyotr Il'yich", "Tchaikovsky", "Romantic"),
            new Composer("40", "Guiseppe", "Verdi", "Romantic"),

            new Composer("41", "Bela", "Bartok", "Post-Romantic"),
            new Composer("42", "Leonard", "Bernstein", "Post-Romantic"),
            new Composer("43", "Benjamin", "Britten", "Post-Romantic"),
            new Composer("44", "John", "Cage", "Post-Romantic"),
            new Composer("45", "Aaron", "Copland", "Post-Romantic"),
            new Composer("46", "George", "Gershwin", "Post-Romantic"),
            new Composer("47", "Sergey", "Prokofiev", "Post-Romantic"),
            new Composer("48", "Maurice", "Ravel", "Post-Romantic"),
            new Composer("49", "Igor", "Stravinsky", "Post-Romantic"),
            new Composer("50", "Carl", "Orff", "Post-Romantic"),
        );
    }*
}

?>

----


[[business]]
=== ビジネス・ロジックの作成

受信リクエストによって受け取る`autocomplete` URLを処理するロジックを実装します。前の項で説明したように、ファイル・ウィザードを使用して新しいPHPファイルを作成するかわりに、ここでは既存の`index.php`ファイルを変更します。

1. 「プロジェクト」ウィンドウで`index.php`ファイル・ノードをクリックします。ファイル名が編集可能になり、名前を変更できるようになります。

image::images/edit-file-name.png[title="ファイル・ノードをクリックして名前を編集"]

[start=2]
. ファイル名を`autocomplete`にし、[Enter]を押します。新しい`autocomplete.php`ファイルをダブルクリックし、エディタに表示します。
. このファイルの既存のコードを次のスクリプトに置き換えます。

[source,php]
----

<?php

require_once("ComposerData.php");

session_start();

$composerData = new ComposerData();
$composers = $composerData->composers;

$results = array();
$namesAdded = false;

// simple matching for start of first or last name, or both
if(isset($_GET['action']) &amp;&amp; $_GET['action'] == "complete") {
    foreach($composers as $composer) {
        if(!is_numeric($_GET['id']) &amp;&amp;

            // if id matches first name
            (stripos($composer->firstName, $_GET['id']) === 0 ||

            // if id matches last name
            stripos($composer->lastName, $_GET['id']) === 0) ||

            // if id matches full name
            stripos($composer->firstName." ".$composer->lastName, $_GET['id']) === 0) {

                $results[] = $composer;
        }
    }

    // prepare xml data
    if(sizeof($results) != 0) {
        header('Content-type: text/xml');
        echo "<composers>";
        foreach($results as $result) {
            echo "<composer>";
            echo "<id>" . $result->id . "</id>";
            echo "<firstName>" . $result->firstName . "</firstName>";
            echo "<lastName>" . $result->lastName . "</lastName>";
            echo "</composer>";
        }
        echo "</composers>";
    }
}

// if user chooses from pop-up box
if(isset($_GET['action']) &amp;&amp; isset($_GET['id']) &amp;&amp; $_GET['action'] == "lookup") {
    foreach($composers as $composer) {
        if($composer->id == $_GET['id']) {
            $_SESSION ["id"] = $composer->id;
            $_SESSION ["firstName"] = $composer->firstName;
            $_SESSION ["lastName"] = $composer->lastName;
            $_SESSION ["category"] = $composer->category;

            header("Location: composerView.php");
        }
    }
}

?>
----

*注意: * composerView.phpファイルについては、このチュートリアルでは説明しません。そのようなファイルを作成して検索の最終結果を確認できます。ファイルのサンプルは、link:https://netbeans.org/projects/samples/downloads/download/Samples%252FPHP%252FMyAjaxApp.zip[+sample application+]に含まれています。

このように、Ajax処理用のサーバー側コードを記述するために新たに学習することはありません。XMLドキュメントを交換する場合は、レスポンスのコンテンツ・タイプを`text/xml`に設定します。Ajaxでは、プレーン・テキストを交換でき、クライアントのコールバック関数によって評価または実行できるJavaScriptのスニペットを交換することもできます。一部のブラウザでは結果がキャッシュに保存される場合があるので、Cache-Control HTTPヘッダーを`no-cache`に設定する必要がある場合もあります。

この例では、`autocomplete.php`ファイルによってXMLドキュメントが生成され、このXMLドキュメントには、名または姓のいずれかがユーザーが入力した文字で始まる作曲家がすべて含まれています。このドキュメントは、前述の<<flow-diagram,フロー図>>に示すXMLデータに対応します。`XMLHttpRequest`オブジェクトに返されるXMLドキュメントの例を示します。


[source,xml]
----

<composers>
    <composer>
        <id>12</id>
        <firstName>Antonin</firstName>
        <lastName>Dvorak</lastName>
    </composer>
    <composer>
        <id>45</id>
        <firstName>Aaron</firstName>
        <lastName>Copland</lastName>
    </composer>
    <composer>
        <id>7</id>
        <firstName>Antonio</firstName>
        <lastName>Vivaldi</lastName>
    </composer>
    <composer>
        <id>2</id>
        <firstName>Arcangelo</firstName>
        <lastName>Corelli</lastName>
    </composer>
</composers>

----



[[client2]]
== クライアント側のプログラミング: 第2部

サーバーのレスポンスを処理するコールバック関数を定義し、ユーザーに表示するページに変更を反映するために必要な機能を追加する必要があります。そのためには、HTMLのDOMを変更する必要があります。最後に、IDEのCSSエディタを使用して、単純なスタイル・シートをプレゼンテーションに追加できます。

* <<callback,コールバック機能の追加>>
* <<htmldom,HTML DOMの更新>>
* <<stylesheet,スタイル・シートの添付>>


[[callback]]
=== コールバック機能の追加

コールバック関数は、HTTPの相互作用中に`XMLHttpRequest`オブジェクトの「`readyState`」プロパティが変化したとき、非同期でコールされます。ここでビルドしているアプリケーションでは、コールバック関数は`callback()`です。`doCompletion()`では、`callback`を関数の「`XMLHttpRequest.onreadystatechange`」プロパティとして設定しました。ここで、コールバック関数を次のように実装します。

1. `javascript.js`をエディタで開き、次のコードを入力します。

[source,java]
----

function callback() {
    if (req.readyState == 4) {
        if (req.status == 200) {
            parseMessages(req.responseXML);
        }
    }
}

----

`readyState`が「4」のとき、HTTPの相互作用は完了しています。`XMLHttpRequest.readState`のAPIは、設定できる値が5つあることを示します。これらを次に示します。

|===
|`readyState`の値 |オブジェクト・ステータスの定義 

|0 |非初期化 

|1 |ロード中 

|2 |ロード済 

|3 |対話式 

|4 |完了 
|===

`parseMessages()`関数は、`XMLHttpRequest.readyState`が「4」で、`status` (リクエストのHTTPステータス・コード定義)が「200」、つまり成功の場合にのみコールされます。`parseMessages()`は、次の<<htmldom,HTML DOMの更新>>で定義します。


[[htmldom]]
=== HTML DOMの更新

受信するXMLデータは`parseMessages()`関数で処理します。このとき、`appendComposer()`、`getElementY()`、および`clearTable()`などの補助的関数を使用します。また、オートコンプリート・ボックスとして機能する2番目のHTML表、要素を`javascript.js`で参照可能にするための要素のIDなど、新しい要素をindexページに追加する必要があります。最後に、`index.php`内の要素のIDに対応する新しい変数を作成し、前に実装した`init()`関数で初期化し、`index.php`がロードされるたびに必要とされる機能を追加します。

*注意: *次の手順で作成する関数と要素は、相互に依存して動作します。この項の手順を最後まで行い、コードが完成してからその内容を確認することをお薦めします。

1. `index.html`をエディタで開き、前に作成したHTML表の2行目として次のコードを入力します。

[source,xml]
----

<tr>
    *<td id="auto-row" colspan="2">

    <td/>*
</tr>
----
この新しい行は「`auto-row`」として識別でき、オートコンプリート・ボックスを形成する新しいHTML表を挿入するための、JavaScriptコード用のハンドルの役割を果たします。

[start=2]
. `javascript.js`をエディタで開き、次の3つの変数をファイルの先頭に追加します。

[source,java]
----

var completeField;
var completeTable;
var autoRow;
----

[start=3]
. 次の*太字*の行を`init()`関数に追加します。

[source,java]
----

function init() {
    completeField = document.getElementById("complete-field");
    *completeTable = document.createElement("table");
    completeTable.setAttribute("class", "popupBox");
    completeTable.setAttribute("style", "display: none");
    autoRow = document.getElementById("auto-row");
    autoRow.appendChild(completeTable);
    completeTable.style.top = getElementY(autoRow) + "px";*
}
----
`init()`の目的の1つは、indexページのDOMを変更する他の関数から`index.html`内の要素にアクセスできるようにすることです。上記のスクリプトは、新しいHTML`表`を作成し、`popupBox`クラスを追加して、要素のスタイルを`display: none`に変更します。最後に、`id`が`auto-row`である要素を取得し、ここに新しい`表`を挿入します。つまり、このコードを実行するときには、変更されたHTMLは次のようになります。

[source,xml]
----

<tr>
    <td id="auto-row" colspan="2">
        *<table class="popupBox" style="display: none"></table>*
    <td/>
</tr>
----

[start=4]
. `appendComposer()`を`javascript.js`に追加します。

[source,java]
----

function appendComposer(firstName,lastName,composerId) {

    var row;
    var cell;
    var linkElement;

    if (isIE) {
        completeTable.style.display = 'block';
        row = completeTable.insertRow(completeTable.rows.length);
        cell = row.insertCell(0);
    } else {
        completeTable.style.display = 'table';
        row = document.createElement("tr");
        cell = document.createElement("td");
        row.appendChild(cell);
        completeTable.appendChild(row);
    }

    cell.className = "popupCell";

    linkElement = document.createElement("a");
    linkElement.className = "popupItem";
    linkElement.setAttribute("href", "autocomplete.php?action=lookup&amp;id=" + composerId);
    linkElement.appendChild(document.createTextNode(firstName + " " + lastName));
    cell.appendChild(linkElement);
}
----
この関数は、表の新しい行を作成し、3つのパラメータによって関数に渡されたデータを使用して作曲家へのリンクを挿入してから、行をindexページの`complete-table`要素に挿入します。

[start=5]
. `clearTable()`を`javascript.js`に追加します。

[source,java]
----

function clearTable() {
    if (completeTable.getElementsByTagName("tr").length > 0) {
        completeTable.style.display = 'none';
        for (loop = completeTable.childNodes.length -1; loop >= 0 ; loop--) {
            completeTable.removeChild(completeTable.childNodes[loop]);
        }
    }
}
----
この関数は`complete-table`要素の表示を'none'に設定し(非表示にし)、作成された既存の作曲家の名前エントリを除去します。

[start=6]
. `getElementY()`を`javascript.js`に追加します。

[source,java]
----

function getElementY(element){

    var targetTop = 0;

    if (element.offsetParent) {
        while (element.offsetParent) {
            targetTop += element.offsetTop;
            element = element.offsetParent;
        }
    } else if (element.y) {
        targetTop += element.y;
    }
    return targetTop;
}
----
この関数は、親要素の縦方向表示位置を見つけるために適用します。これは、要素の実際の表示位置はブラウザのタイプとバージョンによって異なることが多いため必要です。`complete-table`要素は、作曲家の名前が表示されるときに、この要素が存在する表の右下にシフトします。正しい縦方向の配置は`getElementY()`で決まります。

*注意: *link:http://www.quirksmode.org/[+http://www.quirksmode.org/+]にある`offset`に関するlink:http://www.quirksmode.org/js/findpos.html[+説明+]を参照してください。


[start=7]
. `callback()`関数を変更して、サーバーから新しいデータを受け取るたびに`clearTable()`をコールするようにします。オートコンプリート・ボックスに作曲家のエントリがある場合は、新しいエントリが入力される前に除去されます。

[source,java]
----

function callback() {

    *clearTable();*

    if (req.readyState == 4) {
        if (req.status == 200) {
            parseMessages(req.responseXML);
        }
    }
}
----

[start=8]
. `parseMessages()`を`javascript.js`に追加します。

[source,java]
----

function parseMessages(responseXML) {

    // no matches returned
    if (responseXML == null) {
        return false;
    } else {

        var composers = responseXML.getElementsByTagName("composers")[0];

        if (composers.childNodes.length > 0) {
            completeTable.setAttribute("bordercolor", "black");
            completeTable.setAttribute("border", "1");

            for (loop = 0; loop < composers.childNodes.length; loop++) {
                var composer = composers.childNodes[loop];
                var firstName = composer.getElementsByTagName("firstName")[0];
                var lastName = composer.getElementsByTagName("lastName")[0];
                var composerId = composer.getElementsByTagName("id")[0];
                appendComposer(firstName.childNodes[0].nodeValue,
                    lastName.childNodes[0].nodeValue,
                    composerId.childNodes[0].nodeValue);
            }
        }
    }
}
----

`parseMessages()`関数は、`autocomplete.php`ファイルから返されるXMLドキュメントのオブジェクト表現をパラメータとして受け取ります。この関数はプログラムでXMLドキュメント内を横断し、各エントリの`firstName`、`lastName`、および`id`を抽出して、このデータを`appendComposer()`に渡します。その結果、`complete-table`要素の内容が動的に更新されます。たとえば、次のようなエントリが生成され、`complete-table`に挿入されます。


[source,xml]
----

<tr>
    <td class="popupCell">
        <a class="popupItem" href="autocomplete?action=lookup&amp;id=12">Antonin Dvorak</a>
    </td>
</tr>

----

`complete-table`要素の動的な更新は、Ajaxを使用して行われる通信のプロセス・フローの中で最後の手順を表します。この更新は、前述の<<flow-diagram,フロー図>>のプレゼンテーションに送信されるHTMLとCSSデータに対応します。


[[stylesheet]]
=== スタイル・シートの添付

これで、アプリケーションの機能に必要なコードが完成しました。作業の結果を確認するため、今すぐアプリケーションを実行してみてください。

1. プロジェクトを実行して、ブラウザでどのように表示されるかを確認します。「プロジェクトの実行」(image:images/run-project-btn.png[])ボタンをクリックします。`index.html`ファイルがブラウザに表示されます。
image:images/no-css.png[title="スタイル・シートなしでの正常なデプロイメント"]

アプリケーションにスタイル・シートを添付するには、CSS (Cascading Style Sheet)ファイルを作成し、プレゼンテーション・ページからそのファイルにリンクします。CSSファイルで作業する場合、IDEには、コード補完サポートや、スタイル・シート・ルールの作成に役立つ他のいくつかの機能が提供されています。これには次のものが含まれています。

* *CSSスタイル・ビルダー: *選択したコントロールやウィジェットを使用してルールを作成できるように設計されたインタフェースです(「ウィンドウ」>「その他」>「CSSスタイル・ビルダー」)。
* *CSSプレビュー:* ルール内にカーソルを置くと、そのルールの宣言ブロックに従ってサンプル・テキストがレンダリングされるプレビュー・ウィンドウです(「ウィンドウ」>「その他」>「CSSプレビュー」)。
* *スタイル・ルール・エディタ: *クラス、ID、HTML要素に基づいてルールを作成し、ドキュメント階層における位置を設定できるダイアログです(CSSエディタのツールバーの左上側にある「ルールを作成」(image:images/style-rule-editor-btn.png[])ボタン)。

NetBeans 6.9は、「名前の変更のリファクタリング」機能と「使用状況を検索」機能をサポートしています。これは、CSSファイルのみでなく、CSSコードが埋め込まれているすべてのファイルでサポートされます(HTML、PHPなど)。CSSのクラス、ID、およびタイプ要素を、すべてのプロジェクト・ファイルでリファクタリングできます。このリファクタリングのサポートを利用するには、任意のCSS要素上で[Ctrl]-[R]を押し、表示されたダイアログで名前変更アクションを実行します。また、名前変更アクションを実行する前に、変更をプレビューすることもできます。「使用状況を検索」機能のサポートを利用するには、CSS要素を右クリックし、「使用状況を検索」を選択します。詳細は、link:http://wiki.netbeans.org/wiki/index.php?title=NewAndNoteworthy69m1&section=T-25#Web_Languages[+NewAndNoteworthy69m1+]を参照してください。

スタイル・シートをアプリケーションに添付するには、次の手順を行います:

1. 「プロジェクト」ウィンドウでプロジェクト・ノードを右クリックし、「新規」>「Cascading Style Sheet」を選択します(「Cascading Style Sheet」が表示されない場合は、「その他」を選択します。次に、新規ファイル・ウィザードで「その他」カテゴリから「Cascading Style Sheet」を選択します。)
2. 「CSSファイル名」テキスト・フィールドに、「`stylesheet`」と入力します。
3. 「終了」をクリックします。新しいファイルが「プロジェクト」ウィンドウに追加され、IDEのエディタで開きます。
4. `stylesheet.css`に、次のルールを入力します。IDEのコード補完サポートを利用するには、候補を呼び出したい場所で[Ctrl]-[Space]を押します。

[source,java]
----

body {
   font-family: sans-serif;
   font-size: smaller;
   padding: 50px;
   color: #555;
   width: 650px;
}

h1 {
   letter-spacing: 6px;
   font-size: 1.6em;
   color: #be7429;
   font-weight: bold;
}

h2 {
   text-align: left;
   letter-spacing: 6px;
   font-size: 1.4em;
   color: #be7429;
   font-weight: normal;
   width: 450px;
}

table {
   width: 550px;
   padding: 10px;
   background-color: #c5e7e0;
}

td {
   padding: 10px;
}

a {
  color: #be7429;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

.popupBox {
  position: absolute;
  top: 170px;
  left: 140px;
}

.popupCell {
   background-color: #fffafa;
}

.popupCell:hover {
  background-color: #f5ebe9;
}

.popupItem {
  color: #333;
  text-decoration: none;
  font-size: 1.2em;
}
----

CSSエディタ内を右クリックして「CSSの確認」を選択し、CSSコードの妥当性のチェックを実行します。エラーがあった場合は「出力」ウィンドウ(「ウィンドウ」>「出力」)に表示されます。


[start=5]
. エディタで`index.html`ページに切り替え([Ctrl]-[Tab]を押します)、`<head>`タグの間にスタイル・シートへの参照を追加します。

[source,java]
----

<link rel="stylesheet" type="text/css" href="stylesheet.css">

----

[start=6]
. アプリケーションをもう一度実行します。インデックス・ページが、作成したスタイル・シートを使用してブラウザに表示されます。文字を入力するたびに非同期のリクエストがサーバーに送信され、`autocomplete.php`によって作成されたXMLデータが返されます。さらに文字を入力すると、新しい一致リストを反映して作曲家の名前の数が減ります。



[[conclusion]]
== まとめ

これでAjax入門を終了します。ここでは、Ajaxは単にHTTPを使用してバックグラウンドで情報を交換し、その結果に基づいてページを動的に更新していることを学習しました。

ここでビルドしたアプリケーションは、オートコンプリート・ボックスで作曲家の名前を選択しても何も起こらないなど、完全ではありません。link:https://netbeans.org/projects/samples/downloads/download/Samples%252FPHP%252FMyAjaxApp.zip[+サンプル・アプリケーションをダウンロード+]すると、PHPテクノロジを使用して、これを実装する方法を確認できます。また、ユーザーがデータ・ストアにない名前をリクエストしないように検証する方法を検討することもできます。これらの手法については、link:../../trails/php.html[+NetBeansのPHPの学習+]にある他のチュートリアルで紹介しています。

link:/about/contact_form.html?to=3&subject=Feedback: Introduction to Ajax (PHP)[+ご意見をお寄せください+]



[[seeAlso]]
== 関連項目

link:https://netbeans.org/[+netbeans.org+]でのAjaxおよびPHPテクノロジの詳細は、次のリソースを参照してください。

* link:wish-list-tutorial-main-page.html[+PHPを使用するウィッシュ・リストCRUDアプリケーションの作成+]。IDEでのPHPのサポートを使用してCRUDアプリケーションを作成する方法を説明する、9つのステップのチュートリアルです。
* link:../../docs/web/js-toolkits-jquery.html[+jQueryを使用した、Webページの見た目と使いやすさの向上+]。jQueryコアおよびUIライブラリをNetBeansプロジェクトに統合する方法について説明しています。
* link:../../docs/web/js-toolkits-dojo.html[+JSONを使用したDojoツリーのArrayListへの接続+]。JavaOneハンズオン・ラボに基づいて、このドキュメントではDojoツリー・ウィジェットをWebページに統合する方法や、サーバー側からJSON形式でツリー・リクエストにレスポンスできるようにする方法について説明しています。
