В хороших книжках по ООП часто пишут, что наследованием нельзя увлекаться, нужно предпочитать делегирование или делать так, чтобы они работали совместно. К сожалению, не всегда можно быстро догадаться, как применить сухую теорию на практике (а когда наконец-то доходит, удивляешься «что тут сложного?»), поэтому надеюсь мой опыт кому-нибудь пригодится.
И так сначала о проблемной области:
31 Controller Action, большинство из них имеет методы indexAction(), addAction(), editAction(), searchAction().
проблема №1: большинство, но не все. В остальных наличие этих методов варьируется,
проблема №2: методы editAction() и addAction() массивные сами по себе, и почти одинаковые для всех контроллеров, отличаются инициализация формы, и сохранение модели.
Как я это решил, покажу сразу в коде.
Фрагмент базового класса контроллеров
Class Common_Controller extends Zend_Controller_Action { /** * Класс модели связанной с контроллером. * @var string; */ protected $modelClass = ''; /** * Класс формы редактирования модели связанной с контроллером. * @var string; */ protected $editFormClass = ''; /** * JS файл, если он нужен, для редактирования модели связанной с контроллером. * @var string; */ protected $jsModelFile = ''; /** * Создает модель связанную с контроллером, * если $id передан - ищет в базе, если нет - создает новую. * * @param mixed $id; * @return Model_Record $model; */ protected function modelFactory( $id = null ) { $modelClass = $this->modelClass; if ( $id ) { $model = $modelClass::find( $id ); } else { $model = $modelClass::create(); } return $model; } /** * Создает форму редактирования связанную с контроллером. * * @param Model_Record $model; * @return Zend_form $form; */ protected function formFactory( Model_Record $model ) { $formClass = $this->editFormClass; $form = new $formClass(); $form->setDefaults( $model->toArray(1) ); return $form; } /** * Заполняем модель данными из формы и сохраняем. * * @param Model_record $model; * @param Zend_Form $form; */ protected function save( Model_Record $model , Zend_Form $form ) { $model->fromArray($form->getValues(), false); $model->save(); } /** * Хелпер редактирования сущности, * этому методу делегируются вызовы editAction() в производных контроллерах. */ protected function _editActionHelper() { $id = $this->_request->getParam('id'); if ( !$id ) { throw new Zend_Controller_Action_Exception('страница не найдена' , 404); } // модель поднимаем $model = $this->modelFactory($id); if ( !$model ) { throw new Zend_Controller_Action_Exception('страница не найдена' , 404); } $this->view->model = $model; // заголовок страницы $this->view->PageTitle = $model->getFullTitle(); // форму редактирования создаем $form = $this->formFactory( $model ); $this->view->form = $form; // если страница загружена get-ом - прокидываем дальше (через форму) реферер, куда вернуться после сохранения if ( $this->_request->isGet() ) { $form->redirect->setValue( $_SERVER['HTTP_REFERER'] ); } // проверяем, есть ли права на редактирование записи // ... // блокируем запись $model->lock(); // js файл подключаем if ( $this->jsModelFile ) { $this->view->headScript()->appendFile( '/js/models/' . $this->jsModelFile ); } // сохраняем данные if ( isset($_POST['save']) || isset($_POST['saveExit']) ) { // валидация if ( $form->isValid( $this->_request->getPost() ) ) { // пробуем сохранить try { Model::connection()->beginTransaction(); $this->save( $model, $form ); Model::connection()->commit(); $model->releaseLock(); $this->view->Flash()->addSuccess( 'Success !' ); // решаем, что делать после сохранения // вернуться на эту же страницу $redirect = '/' . $this->_request->getControllerName() . '/edit/id/' . $model->ID . '?redirect=' . $this->_request->getParam('redirect', '/'); // сохранить и выйти if ( isset($_POST['saveExit']) ) { $redirect = $this->_request->getParam('redirect', '/'); } $this->_redirect( $redirect ); } catch (Exception $e) { Model::connection()->rollback(); $this->view->Flash()->addError( $e->getMessage() ); } } else { $this->view->Flash()->addError("Форма заполнена с ошибками"); } } } }
Protected метод modelFactory() создает экземпляр модели, связанной с конкретным контроллером. Класс модели указывается в переменной $this->modelClass и в большинстве случаев, кастомизация на этом заканчивается. Если модель должна быть инициализирована по особенному, то просто переопределяем этот метод в конкретном контроллере.
Protected метод formFactory() создает форму редактирования, кастомизация аналогично modelFactory().
Protected метод save() сохраняет в переданную модель данные из переданной формы, здесь также есть место для маневра, если в конкретном контроллере сохранение сущности получается развесистым. Наличие такого метода в контроллере может вызвать сомнение, поэтому поясню, в save() допускаются только вызовы дополнительных методов модели, никаких sql запросов нет, для этого в модели мы определяем методы вроде addTag(), setChannles() и т.п. вместо одного непрозрачного метода saveFromArray().
При такой композиции, модель и форма ничего не знают друг о друге, а контроллер играет роль интегратора.
Последний protected метод в этом фрагменте это _editActionHelper(), если в производном классе нам понадобится поддержка редактирования сущности, то мы просто добавляем в него метод:
public function editAction() { $this->_editActionHelper(); }
Аналогично для других общих методов. Фрагмент производного контроллера для примера:
Class Video extends Common_Controller { protected $modelClass = 'Video'; protected $editFormClass = 'Form_Video'; protected function save( Model_Record $model , Zend_Form $form ) { parent::save( $model , $form ); $model->setChannels( $form->channels->getValue() ); } public function editAction() { $this->_editActionHelper(); } public function addAction() { $this->_addActionHelper(); } public function indexAction() { $this->_indexActionHelper(); } }
P.S. просьба, если что-то ни так, пишите в комментарии, это будет полезно всем читателям.
ссылка на оригинал статьи http://habrahabr.ru/post/213971/
Добавить комментарий