Привет всем!
Хотелось бы высказать свое мнение, сформировавшееся в итоге рефакторинга и чистки кода по мере увеличения опыта работы с Zend Framework.
Буду рад если мои мысли хоть кому-то помогут сделать код немного чище, часто бывает — кому то продождать проект после Вас или вместе с Вами…
Первая статья для песочницы — уже готов к тому что меня закидают камнями, но почему то все равно хотелось высказаться :)
Если все ж люди приймут написаное — расскажу о построении полнотекстового поиска поиска с Zend Search Lucene и всеми граблями с которыми я столкнулся адаптируя его к UFT8 а точнее к русскому языку (в итоге ищет — и при чем довольно прилично).
Хотелось бы высказать свое мнение, сформировавшееся в итоге рефакторинга и чистки кода по мере увеличения опыта работы с Zend Framework.
- Массивы или обьекты?
Когда только начинал изучать зенд я (как думаю и другие) копировал то что видел в мануале. Но приходит время и понимаешь что код нужно рефакторить(один из принципов экстремального програмирования — если есть возможность сделать рафакторинг — сделай его) иначе с ростом количества строк кода растет время размышлений когда что то надо изменить.
Для начала хочу сказать что для работы с таблицами(add/update/remove) я никогда не использую конструкции типа —
$table->insert(array('propName'=>'PropValue','anotherProp'=>'....'));
Раз Zend работает на PHP5 то думаю надо использовать все прелести ООП.
$newRow = $table->createRow();
$newRow->propName = 'PropValue';
$newRow->anotherProp = '....';
$newRow->save();
Думаю сразу заметно что такой код читабельнее и что не маловажно такой код — тестируемый(важно для тех кто использует автоматизированные тесты как PHPUnit). А так же обьекты намного удобнее использовать во view(оссобенно людям знакомым со смарти, верстальщики думаю скажут вам спасибо)
Есть и другие плюсы такого подхода — допустим вы создали запись в базе и собираетесь работать с этим же обьектом далеее — при этом вам нужно знать его id(использую mysql, autoinclrement) и другие свойства, которые не обязательно устанавливаются программно а к примеру хранимой процедурой или тригерами.
Используя $newRow->save(); в случае успешного сохранения записи вы получаете обновленный обьект.
Т.е после $newRow->save(); $newRow->id уже будет проинициализирован.
- Обьекты наследники Zend_Db_Table — это не модель, и нужно их использовать только для работы с данной таблицей.
В некоторых примерах мы видим что таких классов как User_Model нет, да и зачем они если у нас уже есть Users для работы с таблицей —
примеры для того и примеры что бы дать толчек начинающим но нивкоем случае не инструкция на всю жизнь.
Прямое обьяснение зачем нужно иметь по два класса User_Model=>Users — Users только для работы с таблицей, User_Model использует Users но может использоваться так же и другие классы моделей или выполнять специфическую логику(она может быть очень разная в зависимости от разрабатываемой программы).
- Не дублируйте код — скорее всего Вам же его потом и переписывать.
В Zend Quick Start есть наглядный пример толкающий дублировать код —
protected function _getModel()
{
if (null === $this->_model) {
// autoload only handles "library" compoennts. Since this is an
// application model, we need to require it from its application
// path location.
require_once APPLICATION_PATH . '/models/GuestBook.php';
$this->_model = new Model_GuestBook();
}
return $this->_model;
}
Это подойдет если у нас один котроллер и в контроллере нужен только один класс модели — если их три(или 5?) и все они используются в нескольких контроллерах то понятно что ненужного кода будет очень много. Решений проблемы как всегда множество — сделать общий базовый класс для контроллеров, я пришел к другому решению —
Создал один класс по шаблону factory — назовем его Lookup(кому как больше нравится. я назвал его так потому что название короткое а он часто ипользуемый)
class Lookup{
protected $_userModel;
public static function get(){
$registry = Zend_Registry::getInstance();
if (!isset($registry->lookup)){
$registry->lookup = new Lookup();
}
return $registry->lookup;
}
public function user(){
if (null === $this->_userModel) {
require_once APPLICATION_PATH . '/models/User.php';
$this->_userModel = new Model_User();
}
return $this->_userModel;
}
}
//этот код не притендует на оскар, возможны и другие конструции.
Теперь в любом контроллере если нам нужно обратиться к какой то модели
//конечно Lookup должен быть погружен, но лучше грузить один класс чем для каждой таблицы/модели
$userModel = Lookup::get()->user();//и никаких getTable
- Часто нужна специальная логика относительно отдельной записи(Zend_Db_Table_Row) — например проверка на какие то ограничения(например маты в посте, или еще что)
лучше создать отдельный класс — rowClass
class User_Row extends Zend_Db_Table_Row_Abstract{....}
и при инициализации таблицы его легко указать для использования
$this->_table = new Users(array('rowClass'=> 'User_Row'));
Использование собственных классов rowClass дает взможность скрыть(инкапсулировать) некоторые моменты логики которые могут затруднить чтение кода в том же контроллере позволяя сосредоточиться на проблеме а не на куче кода.
Zend дает нам мощный инструмент для работы с базами данных, главное понимать как этот инструмента работает и не стоит кидаться и перемешивать его с другими фреймворками (например Zend+Smarty+Doctrine) — есть ситуации когда это необходимо, но в большинстве случаев это делается только из за того что человек уже знает тот же Doctrine и не желает учить что то новое.
Буду рад если мои мысли хоть кому-то помогут сделать код немного чище, часто бывает — кому то продождать проект после Вас или вместе с Вами…
Первая статья для песочницы — уже готов к тому что меня закидают камнями, но почему то все равно хотелось высказаться :)
Если все ж люди приймут написаное — расскажу о построении полнотекстового поиска поиска с Zend Search Lucene и всеми граблями с которыми я столкнулся адаптируя его к UFT8 а точнее к русскому языку (в итоге ищет — и при чем довольно прилично).