# AutoComplete

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

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

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

    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);
        }
    }

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

- レイアウト機能と ViewRenderer を無効にする。

- 適切なレスポンスヘッダを設定する。

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

- レスポンスを送信する。

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

- `disableLayouts()` は、レイアウト機能と ViewRenderer を無効にします。一般に、これは `prepareAutoCompletion()` の中でコールされます。

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

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

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

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

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

## Dojo でのオートコンプリート

Dojo には、オートコンプリートのためだけのウィジェットはありません。 しかし、ComboBox と FilteringSelect のふたつのウィジェットがオートコンプリート機能を持っています。 どちらのウィジェットも、QueryReadStore を実装したデータを必要とします。詳細は dojo.data のドキュメントを参照ください。

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

    // コントローラのアクション内で
    $this->_helper->autoCompleteDojo($data);

### Zend MVC を使用した、Dojo でのオートコンプリート

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

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

    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);
        }
    });

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

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

    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;
        }
    }

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

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

    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);
        }
    }

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

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

    <?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() ?>

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

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

## Scriptaculous でのオートコンプリート

Scriptaculous は、所定の形式の HTML レスポンスを受け取ることを想定しています。

このライブラリで使用するヘルパーは 'AutoCompleteScriptaculous' です。 このヘルパーにデータの配列を渡せば、Ajax.Autocompleter に対応した形式の HTML レスポンスができあがります。