20 August, 2009

Мой опыт в создании модульного приложения на Zend Framework: Часть 5

Подытожим сегодня подключение fck и кое-что еще.

Итак. Задача - подключить fckeditor. Первый шаг, традиционно, - погуглить :) Полушутка. А подключать будем с помощью jquery, для чего скачаем актуальные:
- fckeditor;
- jquery;
- плагин для jquery
и подключим это всё дело. Но сначала надо создать форму для добавления статьи (фцк юзаю там и в новостях) с textarea, на который, собственно, и будет вешаться фцк. А вешаться он будет следующим образом:
function loadFck(e)
{
$('#'+e).fck({path: baseUrl+'/js/fckeditor/'});
}

Эта функция лежит в файле admin.js (о чем ниже; так же, как и о baseUrl), и ее надо будет вызвать в нужном месте. На вход приходит id текстэйрии (textarea), к которому будет приаттачен фцк.

Вот, собственно, и всё. Большинство примеров на этом и заканчиваются. Но всё же надо поподробней. Начать хотя бы с того, что мы юзаем jquery. И юзаем его хитро. With a little help from my friend (с) у меня есть замечательный вью хелпер jquery, который неплохо бы подключить. Иничу я его так:
protected function _initJquery()
{
$view = $this->getResource('view');
$view->jquery();
}

Но этого было недостаточно - нужно указать путь к вью хелперам. Поэтому также в буцтрапе я иничу и вью:
protected function _initView()
{
if ($this->hasResource('view'))
$view = $this->getResource('view');
else
$view = new Zend_View();
$view->getHelper('BaseUrl')->setBaseUrl($this->getOption('baseurl'));
$view->setEncoding('UTF-8');
$view->addHelperPath('../application/views/helpers', 'Common_View_Helper');
return $view;
}

Интересно, что просто получить ресурс view я не смог. Без проверки выдает ошибку. Поэтому на всякий случай проверка (рожденная коллективным разумом).

Итак, указали путь к плагину и - внимание - задали baseUrl. Я наивно полагал, что он (бейзурл) определится сам. Но пришлось задать. $this->getOption('baseurl') предательски получает переменную конфига ("логично", правда?). В конфиге у меня первой строчкой добавилось baseurl = "http://localhost/мой_проект/public". При этом при попытке написать имя переменной верблюжьимОбразом сей опшен был нулл. Что тоже "логично", правда?

А сей бейзурл мы указали для того, чтобы грузить джаваскрипты и цссы. Пока последних нету, так что
protected function _initHeadScript()
{
$view = $this->getResource('view');
$view->headScript()->prependScript('var baseUrl = "'.$view->baseUrl().'";')
->prependFile($view->baseUrl().'/js/jquery.js');
}

С помощью prependScript() прописываем наш бейзурл для использования в наших джаваскриптовых манипуляциях. (Все эти _init* - в буцтрапе.)

Теперь идем в Admin_ArticleController:
public function init()
{
$this->_model = new Admin_Model_Article();
if(in_array($this->getRequest()->getActionName(), array('add', 'edit'))) // loading fck only if need
{
$this->view->headScript()->appendFile($this->view->baseUrl().'/js/fckeditor/fckeditor.js')
->appendFile($this->view->baseUrl().'/js/jquery.FCKEditor.pack.js');
}
}

Ну тут вроде понятно - комментарий решает. Добавлю только, что второй файл - это плагин для jquery, а модель - приватнная переменная для использования при операциях с БД. Тут же, в addAction() (а позже и в edit) вызываем нашу самую первую функцию:
public function addAction()
{
$form = new Admin_Form_Article();
$this->view->form = $form;
$fckText = $form->getElement('text')->getId();
$this->view->jquery("loadFck('{$fckText}')");
}


Интересные пироги приключились при попытке подключить admin.js, в которой находится loadFck() только для админского модуля (хотя, писуя сию строку, я подумал, что с нижеследующим можно было бы не заморачиваться, буде у нас файл default.js). Вспомнил я внезапно про Admin_Bootstrap - буцтрап модуля. Натурально, следует в нем подгрузить admin.js с помощью _initHeadScript(), аналогичной тому, что в дефолтном буцтрапе. Но вот беда: не нашелся хелпер headScript(). Где-то я с подобным сталкивался, подумал я и вспомнил, как полчаса назад у меня не виделся хелпер baseUrl() по причине отсутствия _initView(). И застучали пальцы по клавишам...
protected function _initView()
{
$view = $this->getApplication()->getResource('view');
return $view;
}

В этом ините нету ничего лишнего. Я даже выкинул из него setBaseUrl(). К $this->getApplication() я пришел опытным путем. Чтобы получить $view, я создавал инстанс дефолтного бутстрапа и хотел из него что-то взять аж через конфиг... а, точно - я ж устанавливал бейзурл. Но конструктор бутстрапа хотел $application, и получил $this->getApplication(). После чего для меня стало очевидно, что создавать инстанс нет смысла, и можно обойтись просто $this->getApplication()...

19 August, 2009

Random and Instant

There are some places to go
And where they are I don't know
And if you ask me some time
I'd rather bring you some wine

16 August, 2009

Мой опыт в создании модульного приложения на Zend Framework: Часть 4

Подошла очередь форм.

Решил, что так, как писали мы раньше - создание формы в контроллере - некрасиво. Захотелось мне вынести формы в отдельную папочку forms в руте модуля.

Первое, с чем приходится столкнуться, - номенклатура. Как помнится, у меня два модуля: default и admin. Соответственны и неймсппейсы. Т. е. в дефолтном модуле я создал уже ранее модель Default_Model_Feedback в папке models, а в админском - Admin_Model_User в папке admin/models. Аналогичным образом создаем и формы. Например, первая форма, которую я создал - форма логина Default_Form_Login.

В начале меня вводило в ступор и непонимание то, что я не мог создать, например, файл forms/LoginForm.php так, чтобы он увиделся аналогично контроллерам (ср. controllers/UserController.php). Очевидно, таковое было обусловлено моим стремлением к перфекционизму: мне хотелось, чтобы всё было красиво, и сия пелена застилала мне глаза :) Однако, трезво взглянув на вещи, я вижу, что, пожалуй, я был не прав. Хотя вот что меня смутило. Согласно мануалу ZF, Zend_Application_Module_Autoloader, comes with the following mappings:
api/ => Api
forms/ => Form
models/ => Model
DbTable/ => Model_DbTable
plugins/ => Plugin
Т. е. выходит, что у нас будут, если будут, Default_Api_Foo, Default_Plugin_Bar etc.

Дальше. Создал я свой класс формы. В нем создаю конструктор, в который передаю $options = null. Ну и внутри создаю форму, прежде вызвав конструктор предка:

parent::__construct($options);
$this->setMethod('post')
->setAction('user/login')
->setName('loginForm');
$login = $this->createElement('text', 'login')
->setLabel('Логин');
$password = $this->createElement('password', 'password')
->setLabel('Пароль');
$submit = $this->createElement('submit', 'submit')
->setLabel('Войти');
$this->addElements(array($login, $password, $submit));


Внимание: конструктор ничего не возвращает! Вероятно, это логично, но я попался.

12 August, 2009

Мой опыт в создании модульного приложения на Zend Framework: Часть 3

Дошло дело и до аутентификации.

Сначала - ACL (access control level). Создал файл Spod_Acl (в своем "подразделении" библиотеки), в нем прописал роли и ресурсы, а тж. allow и deny. Здесь всё.

Затем - Auth. Свой, по аналогии, Spod_Auth, как мы писали ранее и я не совсем понимал, я не писал. Точнее, не писал по тому принципу, по которому он сделан в одной из статей на девзоне. Я создал класс Spod_Authenticator, в котором, с позволения сказать, перегрузил метод класса Zend_Auth_Adapter_DbTable authenticate() - просто для того, чтобы было проще. Т. к. в "свой" authenticate() я поместил вызов и обработку "того", родного зендовского метода аутентификации:

public function authenticate($login, $password)
{
$authAdapter = new Zend_Auth_Adapter_DbTable($this->_dbAdapter, $this->_dbTable, $this->_identityColumn, $this->_credentialColumn, 'MD5(?)');
$authAdapter->setIdentity($login);
$authAdapter->setCredential($password);
return $this->_auth->authenticate($authAdapter);
}


Как видим, параметры - логин и пароль (мы ж аутентификацию пишем). А методы класса проиничены в конструкторе. В итоге вызывать надо так:

$sAuth = new Spod_Authenticator('users', 'us_login', 'us_pass');
$result = $sAuth->authenticate($login, $password);


Долго думал и страдал над ошибкой "No adapter found..." - точно не вспомню формулировку, но суть в том, что для Zend_Auth_Adapter_DbTable нужен наш адаптер к базюке. Конфиг адаптера - в апп.ини. Казалось бы, взять и вытащить по аналогии с другими ресурсами бутстрапа, типа $view = $this->getResource('view');. Я так и пытался. NULL, и хоть ты тресни... В итоге выцепил из старых проектов фактори конфига, получение из него db (натурально, этот самый адаптер пихаю в реестр и в конструкторе своего аутентификатора получаю). Так и сделал. Занялся следующей частью проекта... как тут случайно и внезапно наскочил в доке на то, что db и его адаптер надо получать так:

$resource = $bootstrap->getPluginResource('db');
$db = $resource->getDbAdapter();

02 August, 2009

Мой опыт в создании модульного приложения на Zend Framework: Часть 2

Вторая часть задачи.

В моем проекте должен быть, как я уже писал, админский модуль; а вот фронт-энд должен быть разделен на две одинаковые по структуре части. Первоначально я думал сделать для каждой из них свой отдельный модуль. Но ведь структура один в один, и я решил, что это неразумно. И тут мне на помощь вновь пришел Ероен Кеппенс! Похоже, я торчу ему пиво...

В своей другой статье он рассказал, как он сделал админский псевдо-модуль - "попросту" попрописывал руты. Я немного видоизменил его код, убрав из рутов :module - я не понял, зачем ему надо было его указывать - на мой взгляд, по крайней мере, в моем проекте, это кажется излишеством. Таким образом, у меня получилось три, а не четыре, рута. Кроме того, в конструктор Zend_Controller_Router_Route я не добавлял _layout - учитывая LayoutLoader в первой части (я, Ероен), я решил, что это лишнее. Вместо этого я написал роутеру, что мой псевдомодуль - на самом деле дефолтный модуль. Но также, чтобы различать, в каком псевдомодуле я нахожусь, там же прописал и переменную, которая видна в getRequest().

В итоге получилось:

$route = new Zend_Controller_Router_Route(
'ophtalmology/:controller/:action/*',
array('module' => 'default', 'section' => 'ophtalmology'));
$front->getRouter()->addRoute('ophtalmology_controller_action', $route);


и далее по тексту.

Мой опыт в создании модульного приложения на Zend Framework: Часть 1

Решил сделать все "правильно". Пишу, чтобы запомнились шаги - что-то типа self-manual...

Итак, упрощенная задача: приложение с двумя модулями - дефолтным и админским.

Естественно, сперва скачал последний фреймвёрк.

Затем создал системную переменную zf и в нее поместил путь к zf.bat (Zend Framework/bin/zf.bat).

Потом из командной строки командой zf create project создал проект. При этом вызывал из папки, в которой следовало создать проект, но почему-то тул попросил указать путь к проекту.

Не забыл скопировать библиотеку в мой_проект/library.

Дальше пошел гуглить :) Т. к. интересовало, как люди создают модульные проекты. В принципе, некий опыт был, но он, на мой взгляд, был каким-то... не совсем правильным.

Нашел статью некого Ероена Кеппенса, который очень мне помогает (начальное написание проекта все еще в процессе) - вот она.

До момента написания LayoutHelper сделал, в общем-то, всё, как и у него:
- zf create module admin;
- добавил в application.ini строки
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules[]


За исключением того, что не добавил буцтрап в админский модуль и пока еще не проинитил в буцтрапе приложения _initAppAutoload() - т. к. мне непонятно, почему здесь мы видим App - в квикстарте никакого App нету... будем посмотреть.

Дальше создал папку application/views/layouts, в нее поместил default.phtml и admin.phtml - лэяуты для дефолтного и админского модулей соответственно.

Дальше, как и у мистера Кеппенса, добавил в конфиг следующее:
resources.layout.layoutPath = APPLICATION_PATH "/views/layouts"
resources.layout.layout = default
admin.resources.layout.layout = admin


А вот потом, несколько подумав, решил таки воспользоваться его хелпером для подгрузки лэяутов. Долго думал, куда же мне положить этот хелпер - у автора он лежит в его собственной лайбрари, но в предыдущих проектах мы клали экшен хелперыы прямо в approot, что мне кажется не совсем правильным - грубо говоря, засирается папка, да и как-то не по-зендовски это. В итоге, было решено в папке мой_проект/library создать папку для, так сказать, собственной библиотеки: мой_проект/library/Spod. И хелпер поместил по пути, аналогичному тому, где лежат экшен хелперы в родной зендовской библиотеке, предварительно, естественно, посоздавав соответствующие папки: мой_проект/library/Spod/Controller/Action/Helper/LayoutLoader.php.

Ну и проинитил в буцтрапе, как и автор.

После этого, обновив страничку, обнаружил ошибку, что мой класс не найден. Вспомнил, что автор в начале статьи прописал свою библиотеку в конфиг. Так же сделал и я:
autoloaderNamespaces.spod = "Spod_"

Работает so far :)