<sect3 id="zend.controller.actionhelpers.autocomplete">
    <title>AutoComplete</title>

    <para>
        Многие JavaScript-библиотеки для AJAX предоставляют функционал для
        автодополнения, с его помощью отображается список
        возможных соответствий в то время, пока пользователь набирает текст.
        Помощник <code>AutoComplete</code> предназначен для облегчения возврата
        допустимых ответов для функционала такого рода.
    </para>

    <para>
        Поскольку не все JavaScript-библиотеки реализуют автодополнение
        одинаково, то помощник <code>AutoComplete</code> предоставляет в
        абстрактном классе некоторый базовый функционал, необходимый для
        большинства библиотек, и конкретные реализации для отдельных библиотек.
        Возвращаемые данные в
        основном - JSON-массивы строк, JSON-массивы массивов (в которых каждый
        массив-член является ассоциативным массивом метаданных, используемых при
        создании списка), либо HTML.
    </para>

    <para>
        Базовое использование одинаково для всех реализаций:
    </para>

        <programlisting role="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>
                <code>disableLayouts()</code> может использоваться для
                отключения макетов и ViewRenderer. Обычно он вызывается в
                <code>prepareAutoCompletion()</code>.
        </para></listitem>

        <listitem><para>
                <code>encodeJson($data, $keepLayouts = false)</code> будет
                кодировать данные в формат JSON, при этом можно опционально
                включать или отключать макеты. Обычно этот метод вызывается в
                <code>prepareAutoCompletion()</code>.
        </para></listitem>

        <listitem><para>
                <code>prepareAutoCompletion($data, $keepLayouts = false)</code>
                используется для подготовки ответа в формате, необходимом для
                конкретной реализации, при этом можно опционально включать или
                отключать макеты. Возвращаемое значение может варьироваться в
                зависимости от используемой реализации.
        </para></listitem>

        <listitem><para>
                <code>sendAutoCompletion($data, $keepLayouts = false)</code>
                используется для отправки данных в формате, необходимом для
                конкретной реализации. Он вызывает
                <code>prepareAutoCompletion()</code> и затем отправляет ответ.
        </para></listitem>

        <listitem><para>
                <code>direct($data, $sendNow = true, $keepLayouts =
                    false)</code>
                используется, когда помощник вызывается как метод брокера
                помощников. Флаг <code>$sendNow</code> используется для
                определения того, какой метод вызывать -
                <code>sendAutoCompletion()</code> или
                <code>prepareAutoCompletion()</code>. По умолчанию ответ
                отправляется сразу.
        </para></listitem>
    </itemizedlist>

    <para>
        В настоящее время <code>AutoComplete</code> поддерживает AJAX-библиотеки
        Dojo и Scriptaculous.
    </para>

    <sect4 id="zend.controller.actionhelpers.autocomplete.dojo">
        <title>Автодополнение c 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, и он будет возвращен в формате,
            пригодном для использования с хранилищем в Dojo:
        </para>

        <programlisting role="php"><![CDATA[
// в контроллере действий:
$this->_helper->autoCompleteDojo($data);
]]>
        </programlisting>

        <example id="zend.controller.actionhelpers.autocomplete.dojo.example1">
            <title>Автодополнение с Dojo и Zend MVC</title>

            <para>
                Автодополнение с Dojo через Zend MVC требует реализации
                нескольких вещей:
                генерация объекта формы для того ComboBox, для которого нужно
                реализовать автодополнение, действие контроллера для
                обслуживания автодополнения, создание своего QueryReadStore для
                связи с этим действием и генерация javascript-кода для
                инициализации автодополнения.
            </para>

            <para>
                Для начала рассмотрим, что нужно сделать по части
                javascript. Dojo представляет собой полный фреймворк для
                создания объектно-ориентированного кода на языке JavaScript,
                почти так же, как Zend Framework на языке PHP. Он позволяет
                создавать псевдопространства имен, используя для этого иерархию
                директорий. Создадим директорию 'custom' на том же уровне,
                что и директория Dojo. В этой директории создадим файл
                TestNameReadStore.js со следующим содержимым:
            </para>

            <programlisting role="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>
                Этот класс просто наследует от класса QueryReadStore фреймворка
                Dojo, который сам по себе является абстрактным. Мы просто
                определяем метод, по которому производится запрос, и присваиваем
                его элементу 'test'.
            </para>

            <para>
                Далее создадим элемент формы, для которого хотим реализовать
                автодополнение:
            </para>

            <programlisting role="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, он связан
                с хранилищем данных (ключ 'store') 'testStore'. Устанавливая
                'autoComplete' в false, мы говорим Dojo, чтобы он не выбирал
                автоматически первое соответствие, а вместо этого показывал
                список соответсвий. Наконец, 'hasDownArrow' создает стрелку
                наподобие той, что присутствует в выпадающем списке, чтобы можно
                было выводить и убирать список соответствий.
            </para>

            <para>
                Добавим метод для отображения формы и действие для обработки
                автодополнения:
            </para>

            <programlisting role="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>
                В методе <code>autocompleteAction()</code> мы делаем несколько
                вещей. Сначала мы проверяем, был ли произведен POST-запрос и
                имеет ли параметр 'format' значение 'ajax', это помогает
                отсечь большинство ложных запросов. Далее мы получаем
                параметр 'test' и сравниваем его с нашими данными (здесь я
                намеренно опустил реализацию метода <code>getData()</code>,
                источники данных могут быть любыми). В конце выполнения
                передаем найденные соответствия помощнику AutoCompletion.
            </para>

            <para>
                Теперь на очереди написание скрипта вида для страницы с формой.
                В нем нужно установить хранилище данных, вывести
                форму и подключить все необходимые библиотеки
                Dojo, включая наше хранилище данных. Ниже приведен
                скрипт вида с комментариями:
            </para>

            <programlisting role="php"><![CDATA[
<? // установка хранилища данных: ?>
<div dojoType="custom.TestNameReadStore" jsId="testStore"
    url="<?= $this->baseUrl() ?>/unit-test/autocomplete/format/ajax"
    requestMethod="get"></div>

<? // рендеринг формы: ?>
<?= $this->form ?>

<? // подключение css для Dojo в HTML-заголовке: ?>
<? $this->headStyle()->captureStart() ?>
@import "<?= $this->baseUrl() ?>/javascript/dijit/themes/tundra/tundra.css";
@import "<?= $this->baseUrl() ?>/javascript/dojo/resources/dojo.css";
<? $this->headStyle()->captureEnd() ?>

<? // подключение javascript в HTML-заголовке, включая все
   // необходимые библиотеки для Dojo: ?>
<? $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");
<? $this->headScript()->captureEnd() ?>
]]></programlisting>

            <para>
                Обратите внимание на вызовы помощников видов, таких, как
                headStyle м headScript, - это метки заполнения, которые могут
                затем рендериться в HTML-заголовке скрипта макета.
            </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>
            ожидает HTML-ответ в определенном формате.
        </para>

        <para>
            С этой библиотекой используется помощник
            'AutoCompleteScriptaculous'.
            Просто передавайте ему массив данных, и он создает ответ HTML,
            совместимый с Ajax.Autocompleter.
        </para>
    </sect4>
</sect3>
<!--
vim:se ts=4 sw=4 et:
-->
