<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<!-- EN-Revision: 16509 -->
<sect3 id="zend.controller.actionhelpers.autocomplete">
    <title>AutoComplete</title>

    <para>
        多くの <acronym>AJAX</acronym> 用 javascript ライブラリでは、
        オートコンプリート機能を提供しています。
        これは、ユーザがタイプした内容にマッチする可能性のある候補の一覧を表示するものです。
        <emphasis>AutoComplete</emphasis> ヘルパーは、
        このような場合に使用できるレスポンスを返すためのものです。
    </para>

    <para>
        オートコンプリート機能の実装方法は JS ライブラリによって異なるので、
        <emphasis>AutoComplete</emphasis> では多くのライブラリで使用する共通機能を抽象化しています。
        そして、個々のライブラリにあわせた実装を用意しています。
        返り値の型は、<acronym>JSON</acronym> 形式の文字列の配列か
        <acronym>JSON</acronym> 形式の配列の配列
        (内部の配列は、選択リストを作成する際に使用するメタデータの連想配列)
        あるいは <acronym>HTML</acronym> となります。
    </para>

    <para>
        どの実装についての基本的な使用法は同じです。
    </para>

        <programlisting language="php"><![CDATA[
class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // 何かの処理をします...

        // エンコードしたレスポンスを送信します
        $this->_helper->autoCompleteDojo($data);

        // あるいは明示的に
        $response = $this->_helper->autoCompleteDojo
                                  ->sendAutoCompletion($data);

        // あるいは単純にオートコンプリート用のレスポンスを準備します
        $response = $this->_helper->autoCompleteDojo
                                  ->prepareAutoCompletion($data);
    }
}
]]></programlisting>

    <para>
        デフォルトでは以下のような作業を行います。
    </para>

    <itemizedlist>
        <listitem><para>
                レイアウト機能と ViewRenderer を無効にする。
        </para></listitem>

        <listitem><para>
                適切なレスポンスヘッダを設定する。
        </para></listitem>

        <listitem><para>
                レスポンスボディにエンコード/フォーマットしたデータを設定する。
        </para></listitem>

        <listitem><para>
                レスポンスを送信する。
        </para></listitem>
    </itemizedlist>

    <para>
        このヘルパーでは次のようなメソッドが使用できます。
    </para>

    <itemizedlist>
        <listitem><para>
                <methodname>disableLayouts()</methodname> は、レイアウト機能と
                ViewRenderer を無効にします。一般に、これは
                <methodname>prepareAutoCompletion()</methodname> の中でコールされます。
        </para></listitem>

        <listitem><para>
                <methodname>encodeJson($data, $keepLayouts = false)</methodname>
                はデータを <acronym>JSON</acronym> 形式にエンコードし、オプションでレイアウト機能の有効/無効
                を切り替えます。一般に、これは
                <methodname>prepareAutoCompletion()</methodname> の中でコールされます。
        </para></listitem>

        <listitem><para>
                <methodname>prepareAutoCompletion($data, $keepLayouts = false)</methodname>
                は、各種具象実装にあわせてレスポンスデータをフォーマットし、
                オプションでレイアウト機能の有効/無効を切り替えます。
                返り値は実装によって異なります。
        </para></listitem>

        <listitem><para>
                <methodname>sendAutoCompletion($data, $keepLayouts = false)</methodname>
                は、各種具象実装にあわせてフォーマットしたレスポンスデータを送信します。
                これは、<methodname>prepareAutoCompletion()</methodname> をコールしたあとでレスポンスを送信します。
        </para></listitem>

        <listitem><para>
                <methodname>direct($data, $sendNow = true, $keepLayouts =
                    false)</methodname> は、このヘルパーをヘルパーブローカのメソッドとしてコールする場合に使用します。
                <varname>$sendNow</varname> フラグは、
                <methodname>sendAutoCompletion()</methodname> と
                <methodname>prepareAutoCompletion()</methodname> のどちらをコールするかを指定するものです。
        </para></listitem>
    </itemizedlist>

    <para>
        現在 <emphasis>AutoComplete</emphasis> がサポートしている <acronym>AJAX</acronym>
        ライブラリは、Dojo と Scriptaculous です。
    </para>

    <sect4 id="zend.controller.actionhelpers.autocomplete.dojo">
        <title>Dojo でのオートコンプリート</title>

        <para>
            Dojo には、オートコンプリートのためだけのウィジェットはありません。
            しかし、ComboBox と FilteringSelect
            のふたつのウィジェットがオートコンプリート機能を持っています。
            どちらのウィジェットも、QueryReadStore
            を実装したデータを必要とします。詳細は
            <ulink url="http://dojotoolkit.org/book/dojo-book-0-9/part-3-programmatic-dijit-and-dojo/data-retrieval-dojo-data-0">dojo.data</ulink>
            のドキュメントを参照ください。
        </para>

        <para>
            Zend Framework では、単純な数値添字の配列を
            AutoCompleteDojo ヘルパーに渡します。
            そうすると、適切な形式の <acronym>JSON</acronym> オブジェクトを返します。
        </para>

        <programlisting language="php"><![CDATA[
// コントローラのアクション内で
$this->_helper->autoCompleteDojo($data);
]]></programlisting>

        <example id="zend.controller.actionhelpers.autocomplete.dojo.example1">
            <title>Zend MVC を使用した、Dojo でのオートコンプリート</title>

            <para>
                Zend <acronym>MVC</acronym> で Dojo によるオートコンプリートを使用するには、
                いくつかの準備が必要です。オートコンプリートを使用したい
                ComboBox 用にフォームオブj稀有とを作成し、
                オートコンプリートの結果を提供するためのコントローラアクションを作成し、
                オートコンプリートアクションに接続するための
                独自の QueryReadStore を作成し、
                サーバ側でオートコンプリートを行わせるための javascript
                を作成することになります。
            </para>

            <para>
                まずは、必要となる javascript を見ていきましょう。
                Dojo は javascript によるオブジェクト指向プログラミングを行うための
                完全なフレームワークで、ちょうど <acronym>PHP</acronym> における Zend Framework
                のようなものです。その中には、
                ディレクトリ構造を用いて擬似的な名前空間を作成する機能もあります。
                ここでは、Dojo の配布ファイルの Dojo
                ディレクトリと同じ階層に 'custom' ディレクトリを作成します。
                そのディレクトリの中に <filename>TestNameReadStore.js</filename>
                という javascript ファイルを作成し、次のようなコードを書きます。
            </para>

            <programlisting language="javascript"><![CDATA[
dojo.provide("custom.TestNameReadStore");
dojo.declare("custom.TestNameReadStore", dojox.data.QueryReadStore, {
    fetch:function (request) {
        request.serverQuery = { test:request.query.name };
        return this.inherited("fetch", arguments);
    }
});
]]></programlisting>

            <para>
                このクラスは、単に Dojo 自身の QueryReadStore
                クラスを継承したものです。継承元のクラス自体は抽象クラスです。
                そこにリクエスト用のメソッドを定義し、'test'
                要素に割り当てています。
            </para>

            <para>
                次に、オートコンプリートを行うためのフォーム要素を作成します。
            </para>

            <programlisting language="php"><![CDATA[
class TestController extends Zend_Controller_Action
{
    protected $_form;

    public function getForm()
    {
        if (null === $this->_form) {
            $this->_form = new Zend_Form();
            $this->_form->setMethod('get')
                ->setAction(
                    $this->getRequest()->getBaseUrl() . '/test/process'
                )
                ->addElements(array(
                    'test' => array('type' => 'text', 'options' => array(
                        'filters'        => array('StringTrim'),
                        'dojoType'       => array('dijit.form.ComboBox'),
                        'store'          => 'testStore',
                        'autoComplete'   => 'false',
                        'hasDownArrow'   => 'true',
                        'label' => 'Your input:',
                    )),
                    'go' => array('type' => 'submit',
                                  'options' => array('label' => 'Go!'))
                ));
        }
        return $this->_form;
    }
}
]]></programlisting>

            <para>
                ここでは、単に 'test' と 'go' メソッドのみを持つフォームを作成します。
                'test' メソッドは、特別な Dojo 固有の属性
                dojoType、store、autoComplete および hasDownArrow
                を追加します。dojoType では、これから ComboBox
                を作成することを指定します。そして、それを 'testStore' のデータストア
                (キー 'store') にリンクします。詳細は後ほど説明します。
                'autoComplete' を <constant>FALSE</constant> に設定することで、
                最初にマッチしたものを自動選択するのではなく
                マッチしたものの一覧を表示するよう Dojo に指示します。
                最後に 'hasDownArrow' でセレクトボックス風の下向き矢印を作ります。
                これで、マッチしたものを表示したり隠したりできるようになります。
            </para>

            <para>
                では、フォームを表示するためのメソッドと
                オートコンプリートの処理用のエンドポイントを作成してみましょう。
            </para>

            <programlisting language="php"><![CDATA[
class TestController extends Zend_Controller_Action
{
    // ...

    /**
     * 最初のページ
     */
    public function indexAction()
    {
        $this->view->form = $this->getForm();
    }

    public function autocompleteAction()
    {
        if ('ajax' != $this->_getParam('format', false)) {
            return $this->_helper->redirector('index');
        }
        if ($this->getRequest()->isPost()) {
            return $this->_helper->redirector('index');
        }

        $match = trim($this->getRequest()->getQuery('test', ''));

        $matches = array();
        foreach ($this->getData() as $datum) {
            if (0 === strpos($datum, $match)) {
                $matches[] = $datum;
            }
        }
        $this->_helper->autoCompleteDojo($matches);
    }
}
]]></programlisting>

            <para>
                <methodname>autocompleteAction()</methodname>
                ではいくつかの作業を行っています。
                まず、POST リクエストを受け取ったことを確実にし、
                'format' パラメータの値を 'ajax' に設定します。
                これにより、余計なクエリがアクションに送られることを減らします。
                次に、'test' パラメータの内容を確認し、私たちのデータと比較します
                (ここでは、<methodname>getData()</methodname> の実装は意図的に控えています。
                何らかのデータソースを使用することになるでしょう)。
                最後に、マッチした内容を AutoCompletion ヘルパーに送信します。
            </para>

            <para>
                これでバックエンド側の準備はすべて整いました。
                次に、ページのビュースクリプト側ではどうすればいいのかを考えてみましょう。
                まず、データストアを用意しなければなりません。
                次にフォームをレンダリングし、最後に適切な Dojo ライブラリ
                (使用するデータストアも含む) を読み込みます。
                ビュースクリプトを見てみましょう。
                適宜コメントを入れてあります。
            </para>

            <programlisting language="php"><![CDATA[
<?php // データストアの準備 ?>
<div dojoType="custom.TestNameReadStore" jsId="testStore"
    url="<?php echo $this->baseUrl() ?>/unit-test/autocomplete/format/ajax"
    requestMethod="get"></div>

<?php // フォームのレンダリング ?>
<?php echo $this->form ?>

<?php // Dojo 関連の CSS の、HTML head での読み込み ?>
<?php $this->headStyle()->captureStart() ?>
@import "<?php echo $this->baseUrl()
?>/javascript/dijit/themes/tundra/tundra.css";
@import "<?php echo $this->baseUrl() ?>/javascript/dojo/resources/dojo.css";
<?php $this->headStyle()->captureEnd() ?>

<?php // 必要な Dojo ライブラリを含む javascript の、
   // HTML head での読み込み ?>
<?php $this->headScript()
        ->setAllowArbitraryAttributes(true)
        ->appendFile($this->baseUrl() . '/javascript/dojo/dojo.js',
            'text/javascript',
            array('djConfig' => 'parseOnLoad: true'))
        ->captureStart() ?>
djConfig.usePlainJson=true;
dojo.registerModulePath("custom","../custom");
dojo.require("dojo.parser");
dojo.require("dojox.data.QueryReadStore");
dojo.require("dijit.form.ComboBox");
dojo.require("custom.TestNameReadStore");
<?php $this->headScript()->captureEnd() ?>
]]></programlisting>

            <para>
                headStyle や headScript といったビューヘルパーのコールに注意しましょう。
                これらはプレースホルダで、ビュースクリプトをレンダリングする際に
                <acronym>HTML</acronym> の head セクションとなります。
            </para>

            <para>
                これで、Dojo のオートコンプリートを動作させるための準備がすべて整いました。
            </para>
        </example>
    </sect4>

    <sect4 id="zend.controller.actionhelpers.autocomplete.scriptaculous">
        <title>Scriptaculous でのオートコンプリート</title>
        <para>
            <ulink url="http://wiki.script.aculo.us/scriptaculous/show/Ajax.Autocompleter">Scriptaculous</ulink>
            は、所定の形式の <acronym>HTML</acronym> レスポンスを受け取ることを想定しています。
        </para>

        <para>
            このライブラリで使用するヘルパーは 'AutoCompleteScriptaculous' です。
            このヘルパーにデータの配列を渡せば、Ajax.Autocompleter
            に対応した形式の <acronym>HTML</acronym> レスポンスができあがります。
        </para>
    </sect4>
</sect3>
<!--
vim:se ts=4 sw=4 et:
-->
