Свой Form Element со своим Render в Zend Framework 2

Иногда очень полезно инкапсулировать в элемент формы какую-то кастомную логику, а так же его отображение и фильтрацию, для повторного ипользования.
На примере 3 date time picker я приведу пример, как сделать элемент Element\ с версткой Bootstrap.

Для начала необходимо подключить расширение для bootstrap, которое добавит jquery метод $.datetimepicker() и возможность написания верстки под этот элемент.
Ссылки на проект:
http://eonasdan.github.io/bootstrap-datetimepicker/
https://github.com/Eonasdan/bootstrap-datetimepicker

Я подключил нужные js и css в layout шаблоне на весь модуль application. Код для примера:

Далее можно уже пользоваться этим модулем, просто дублируя каждый раз верстку в нужных местах. Сначала я попробовал пользоваться этим элементом так:

Из приведенного выше кода видно, что я пользуюсь Zend модулем TwbBundle. Этот модуль рендерит все стандартные элементы формы сразу в bootstrap верстке.
Далее я делаю два элемента datetimepicker, при этом дублирую всю верстку два раза, а меняется только название и значение элемента. Естесственно так оставлять нельзя.
Так же в конце шаблона я подключаю js.

Внутри

В данном случае я отключаю выбор времени и формат делаю YYYY-MM-DD, так было нужно по бизнес логике. Но элемен оставляю datetimepicker в виду того, что возможно позже понадобится время и я его использую в других местах.

Учитывая дублирование похожей верстки для элемента надо вынести все это отдельно используя возможности Zend 2.

Для начала создадим класс элемента:

Он будет пустой, но он нужен, т.к. работать с ним я буду в классе Form таким образом

Внутри элемента можно было бы добавить фильтры и валидацию. Мне это было не нужно, но я приведу код для примера. Таким образом можно инкапсулировать в элемент логику валидации.

По умолчанию наш новый элемент будет рендерится как

Нам же нужна кастомная bootstrap верстка. Для этого надо расширить стандартный \Form\View\Helper\FormElement и зарегестрировать свой вариант в сервисах. Так же нам надо создать свой View Helper, который будет наследоваться от \Form\View\Helper\AbstractHelper и являться по сути плагином. Связывается это все через конфиг.

Для начала создаем расширение для хелпера.

Как видно тут мы делаем проверку по элементу, и если элемент это наш кастомный элемент, то для него регистрируем плагин рендерер. Само определение плагина такое.

Как видно я реализовал рендеринг двумя способами. Сначала я написал HTML прямо внутри класса, чтобы проверить, что оно работает. Но данное решение выглядит не красиво, лучше вынести верстку в шаблон, что и сделано в методе renderTpl(). Сам шаблон находится по указанному пути и вот его содержимое.

Видим знакомую верстку, только инкапсулированную в элемент и рендерер. Теперь, после задания элемента для формы в месте, где рендерится форма все стало гораздо проще.

В форме

В шаблоне

Стоит обратить внимание на вызов formDateTimePicker(). Именно таким образом вызывается наш зарегистрированный рендерер.

Видно, на сколько стал чище и понятнее клиентский код и повысилась возможность повторного использования.

В шаблон рендеринга я не включал вызов jquery ф-ии datetimepicker, т.к. иначе вызов дублируется при каждом отображении formDateTimePicker().