Как стать автором
Обновить

Zend Framework: Работа с таблицами и мысли вслух о чистоте кода

Привет всем!

Хотелось бы высказать свое мнение, сформировавшееся в итоге рефакторинга и чистки кода по мере увеличения опыта работы с 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 а точнее к русскому языку (в итоге ищет — и при чем довольно прилично).
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.