Разработка → Zend Framework: стремимся к MVC для Javascript, CSS

YuriPikin 6 октября 2009 в 16:05 4,3k
При постепенном изучении Zend Framework и построении с его помощью базовых приложений я заметил, что клиентский js-код и инлайновые стили попадают в скрипты вида, при этом они начинают занимать почти половину всего скрипта. В принципе, ничего плохого в этом нет, но мне такая мусорка начала давить на глаза. Кроме того, инлайновый js постепенно превращается в жестко привязанную к скрипту вида конструкцию, полностью зависимую как от самого скрипта, так и от переданных контроллером данных. При всем этом не оставляет желание писать на javascript код максимально красивый при помощи библиотеки jQuery.

Итак, что в данном случае можно сделать. Первая же мысль — вынести инлайновые скрипты и стили во внешние файлы и подключать их через помощники вида headLink и headStyle. Если со стилями все примерно понятно — достаточно специфический стиль для конкретного вида вынести в css файл и подключать его по мере необходимости, то js по-прежнему зависим от данных.

В качестве первого решения напишем плагин к frontController для подключения по мере необходимости нужных файлов:

  1. class My_Controller_Plugin_Webinit extends Zend_Controller_Plugin_Abstract
  2. {
  3.     public function preDispatch()
  4.     {
  5.         $controllerName    = $this->_request->getControllerName();
  6.         $actionName        = $this->_request->getActionName();
  7.         
  8.         $view = Zend_Layout::getMvcInstance()->getView();
  9.         
  10.         if ( file_exists(APPLICATION_CSS_FOLDER.'/'.
  11.             $controllerName.'/'.
  12.             $actionName.'.css') )
  13.         {
  14.             $view->assign('cssControllerAction',$controllerName.'/'.$actionName.'.css');    
  15.         }
  16.         
  17.         if ( file_exists(APPLICATION_JS_FOLDER.'/'.
  18.             $controllerName.'/'.
  19.             $actionName.'.js') )
  20.         {
  21.             $view->assign('jsControllerAction', $controllerName.'/'.$actionName.'.js');    
  22.         }
  23.     }
  24. }
* This source code was highlighted with Source Code Highlighter.


Не забываем в Bootstrap классе подключить созданный плагин:

  1. protected function _initFrontControllerPlugins()
  2. {
  3.     $frontController = Zend_Controller_Front::getInstance();
  4.     $frontController->registerPlugin( new My_Controller_Plugin_Webinit() );
  5. }
* This source code was highlighted with Source Code Highlighter.


Как уже понятно, используемые css и js файлы размещаются в соответствии с полной аналогией размещения скриптов вида. Только вместо /application/views/script/controllerName/actionName/ корневыми директориями для них будет соответственно public/css/ и public/js/. Для этого нужно объявить три константы в index.php:

  1. defined('APPLICATION_PUBLIC_FOLDER')
  2.     || define( 'APPLICATION_PUBLIC_FOLDER',
  3.              dirname(__FILE__) );
  4.  
  5. defined('APPLICATION_CSS_FOLDER')
  6.     || define( 'APPLICATION_CSS_FOLDER',
  7.              realpath(APPLICATION_PUBLIC_FOLDER . '/css') );             
  8.  
  9. defined('APPLICATION_JS_FOLDER')
  10.     || define( 'APPLICATION_JS_FOLDER',
  11.              realpath(APPLICATION_PUBLIC_FOLDER . '/js') );
* This source code was highlighted with Source Code Highlighter.


Плагин позволяет подключить в нашем Layout нужные файлы следующим кодом:

  1. <? if ($this->cssControllerAction) $this->headLink()->appendStylesheet('/css/'.$this->cssControllerAction)?>
  2.  
  3. <? if ($this->jsControllerAction) $this->headScript()->appendFile('/js/'.$this->jsControllerAction)?>
* This source code was highlighted with Source Code Highlighter.

Открываем браузер — нужные файлы подключились.

Остается вопрос с жаваскриптом. Мы вынесли код во внешний файл в пределах public директории. Но есть необходимость, например, вывести приветственный текст на основе словаря функцией alert(). Естественно, для перевода используется Zend_Translate, но опять же, как быть, если все наши данные доступны только в скрипте вида?

Для собственных задач я посчитал идеальным решением организацию неймспейсов. К примеру, для переводчика внутри одного скрипта вида нам необходим ограниченный набор фраз. Значит мы можем организовать хранилище этих фраз и отдать их необходимому обработчику на уровне js.

Вот как это происходит.

В основном js-файле моего проекта application.js происходит настройка глобальных методов, объектов и обработчиков. Сюда можно добавить:

  1. $(document).ready(function(){
  2.     $.TRANSLATION = {};
  3. });
* This source code was highlighted with Source Code Highlighter.


Хранилище огранизовано, теперь нужно заполнить его.

Для этого в скрипте вида нам понадобится следующий код:

  1. <script type="text/javascript">
  2. $(document).ready(function(){
  3.     $.data($.TRANSLATION, "Nice to see you!", '<?=$this->translate("Nice to see you!")?>');
  4. </script>
* This source code was highlighted with Source Code Highlighter.


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

  1. alert( $.data($.TRANSLATION, "Nice to see you!") );
* This source code was highlighted with Source Code Highlighter.


Итак, мы получили следующую картину: для контроллера index, экшена index существует файл в паблик-директории /js/index/index.js, являющийся набором функций для работы. При этом все необходимые данные попадают в хранилище данных непосредственно в скрипте вида, а само хранилище объявляется глобально для всех скриптов вида в основном js-файле приложения.

По модели MVC. Немного притянуто за уши, но все же.

В качестве контроллера (реакцией на пользователя) выступает плагин к frontController, организующий подключение, скрипт layout-а и основа — application.js.

В качестве модели выступает скрипт вида, накапливающий данные для js хранилища.

В качестве вида выступает подключаемый код javascript, полностью отвечающий за клиентское представление.

В хранилище можно накапливать любые данные для дальнейшего использования.
Проголосовать:
+3
Сохранить: