Pull to refresh

Zend_Form_Element: создание своего элемента

Reading time 4 min
Views 1.8K

0. Intro.

В процессе разработки достаточно часто нужно использовать различные кастомные селекты, инпуты, загрузщики файлов и прочее. В этом случае приходится писать дополнительные обработчики на стороне клиента, так как ZF из коробки не знает ничего, кроме стандартных элементов форм (капча есть исключение). В этой статье будет рассмотрено создание элемента на базе facebook-like TextboxList плагина для jQuery, который выглядит вот так:

habr 1

1. Теория.

Собственно, для того, что бы создать новый элемент, нужно соблюсти два условия:
  1. нужен класс элемента, наследуемый от Zend_Form_Element_Xhtml;
  2. view helper, который будет отвечать за отрисовку;
  3. название элемента должно быть в нашем namespace (в этом случае Eve_) и оно должно быть корректно настроено.

2. Практика.

2.1. Создаем Eve_Form_Element_TextboxList
Структура класса состоит из следующих основных частей:
  1. указания помощника вида, который отрендерит элемент;
  2. задания опций по умолчанию;
  3. перегрузка метода для установки (либо объединения) опций (опционально);
  4. остальные опции\методы на этом этапе не нужны и их можно подсмотреть в Zend_Form_Element.
Главные задачи класса элемента — это управление валидаторами\декораторами\фильтрами и обработке опций с последующей их передачей помощнику вида.
Исходник класса в листинге с комментариями:

class Eve_Form_Element_TextboxList extends Zend_Form_Element_Xhtml
{
    /**
     * Имя хелпера
     * @var string
     */
    public $helper = 'formTextboxList';
    
    /**
     * Опции по-умолчанию
     * @var array
     */
    public $options = array(
        'js_main'   => '/js/jquery.textboxlist.js', // путь к главному скрипту плагина
        'js_autocomplete'   => '/js/jquery.textboxlist.autocomplete.js', // путь к скрипту плагина для плагина (поддержка подсказок)
        'js_growinginput'   => '/js/jquery.growinginput.js', // зависимость скрипта
        'use_autocompletion' => '0', // использовать подсказки или нет
        'autocomplete_script' => null, // backend, откуда брать подсказки
        'css_main' => '/css/textboxlist.css', // стили
        'css_autocomplete' => '/css/textboxlist.autocomplete.css',
    );

    /**
     * Высчитывает только указанные пользователем опции и их устанавливает
     *
     * @param  array $options
     * @return Eve_Form_Element_TextboxList
     */
    public function setOptions(array $options)
    {
        /**
         * В отличии от самого фреймворка, здесь выбираются только опции, которые были заданы 
         * на этапе описания эелемента
         */
        $diff = array_intersect_key($options, $this->options);
        $this->options = array_merge($this->options, $diff);
        
        /**
         * затем все кастомные опции необходимо удалить, иначе они попадут в html как аттрибуты элемента
         */
        foreach ($diff as $key => $option) {
            unset ($options[$key]);
        }
        
        parent::setOptions($options);
        return $this;
    }
    
}

2.2. Создаем помощник вида Eve_View_Helper_FormTextboxList
Класс должен иметь метод совпадающий с названием класса, который вызовется автоматически при обращении к помощнику.
Код в листинге + комменты:

class Eve_View_Helper_FormTextboxList extends Zend_View_Helper_FormElement
{
    /**
     * Generates a 'textboxList' element.
     *
     * @access public
     *
     * @param string|array $name If a string, the element name.  If an
     * array, all other parameters are ignored, and the array elements
     * are extracted in place of added parameters.
     *
     * @param mixed $value The element value.
     *
     * @param array $attribs Attributes for the element tag.
     *
     * @return string The element XHTML.
     */
    public function formTextboxList($name, $value = '', $attribs = null, $options = null)
    {
        $id = $name;
        $elemId = $this->view->escape($id);

        $xhtml = '<input type="text" name="' . $this->view->escape($name) . '" id="' . $this->view->escape($id) . '"';
        // установка значения по умолчанию
        if (!empty($value)) {
            $xhtml .= ' value="' . $this->view->escape($value) . '"';
        }

        // присвоение аттрибутов уже к html элементу
        $xhtml .= $this->_htmlAttribs($attribs);
        $xhtml .= '/>' . PHP_EOL;
        
        /**
         * Здесь стоит заметить, что код нуждается в дополнение, т.к. в случае, если на странице будет
         * больше одного такого элемента, то нет необходимости в повторной инициализации.
         */
        // add content and end tag
        $xhtml .= $content . ($this->view->doctype()->isXhtml() ? '/>' : '>') . PHP_EOL;
        
        $this->view->headScript->appendFile($options['js_growinginput']);
        $this->view->headScript->appendFile($options['js_main']);
        $this->view->headScript->appendFile($options['js_autocomplete']);
        
        $this->view->headLink->appendStylesheet($options['css_main']);
        $this->view->headLink->appendStylesheet($options['css_autocomplete']);
        
        $xhtml .= '<script type="text/javascript">
            var tl_' . $elemId . ' = new $.TextboxList("#' 
                . $elemId. '", {unique: true, plugins: {autocomplete: {}}});
        ';
        
        if ((int) $options['use_autocompletion'] == 1) {
            if (!$options['autocomplete_script']) {
                throw new Zend_View_Exception('No autocompletion backend is set for ' . __CLASS__ . ' plugin.');
            } else {
                $.getJSON('" . $options['autocomplete_script'] . "', null, function (data) {
                        tl.plugins['autocomplete'].setValues(data);
                        tl.getContainer().removeClass('textboxlist-loading');
                });";
            }
        }
        $xhtml .= '</script>';
        return $xhtml;
    }
    
}

2.3. Инициализация через конфиг.
Она ничем не отличается от привычной многим:

elements.categories.type = textboxList
elements.categories.options.label = Categories
elements.categories.options.use_autocompletion = 1
elements.categories.options.autocomplete_script = /categories/ajax/get-all/

3. Outro.

Таким образом, были создан отдельный элемент, который рисует совершенно новый тип инпутов.
Jquery плагин можно найти вон там — http://devthought.com/projects/jquery/textboxlist/.
Плагин имеет достаточно опций, которые не были реализованы в рамках этого компонента.И да, плагин не под свободной лицензией.

UPD:1. обновлено (см. этот коммент
Tags:
Hubs:
+17
Comments 20
Comments Comments 20

Articles