2012-01-12 5 views
6

Я построил первый веб-сервис на Zend Framework (1.10), и теперь я ищу способы рефакторинга некоторой логики в моих контроллерах действий, чтобы было легче для меня и всей моей команды, чтобы расширить и поддерживать обслуживание.Zend Action Controller - стратегия рефакторинга

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

Базовая структура для наших контроллеров действий являются:

  1. Извлечения XML сообщение из тела запроса - Это включает в себя проверку на действия конкретного RelaxNG схему
  2. Подготовьте ответ XML
  3. проверки достоверности данных в сообщении запроса (неверные данные генерируют исключение - сообщение добавляется в ответ, который отправляется немедленно)
  4. Выполнение действия базы данных (select/insert/update/delete)
  5. Возвращение успех или неудачу действий, с необходимой информацией

Простой пример это действие, которое возвращает список поставщиков, основанный на гибком наборе критериев:

class Api_VendorController extends Lib_Controller_Action 
{ 
    public function getDetailsAction() 
    { 
     try { 
      $request = new Lib_XML_Request('1.0'); 
      $request->load($this->getRequest()->getRawBody(), dirname(__FILE__) . '/../resources/xml/relaxng/vendor/getDetails.xml'); 
     } catch (Lib_XML_Request_Exception $e) { 
      // Log exception, if logger available 
      if ($log = $this->getLog()) { 
       $log->warn('API/Vendor/getDetails: Error validating incoming request message', $e); 
      } 

      // Elevate as general error 
      throw new Zend_Controller_Action_Exception($e->getMessage(), 400); 
     } 

     $response = new Lib_XML_Response('API/vendor/getDetails'); 

     try { 
      $criteria = array(); 
      $fields = $request->getElementsByTagName('field'); 
      for ($i = 0; $i < $fields->length; $i++) { 
       $name = trim($fields->item($i)->attributes->getNamedItem('name')->nodeValue); 
       if (!isset($criteria[$name])) { 
        $criteria[$name] = array(); 
       } 
       $criteria[$name][] = trim($fields->item($i)->childNodes->item(0)->nodeValue); 
      } 

      $vendors = $this->_mappers['vendor']->find($criteria); 
      if (count($vendors) < 1) { 
       throw new Api_VendorController_Exception('Could not find any vendors matching your criteria'); 
      } 

      $response->append('success'); 
      foreach ($vendors as $vendor) { 
       $v = $vendor->toArray(); 
       $response->append('vendor', $v); 
      } 

     } catch (Api_VendorController_Exception $e) { 
      // Send failure message 
      $error = $response->append('error'); 
      $response->appendChild($error, 'message', $e->getMessage()); 

      // Log exception, if logger available 
      if ($log = $this->getLog()) { 
       $log->warn('API/Account/GetDetails: ' . $e->getMessage(), $e); 
      } 
     } 

     echo $response->save(); 
    } 
} 

Так что - зная, где общие черты находятся в моих контроллерах, что является лучшей стратегией для рефакторинга, сохраняя его Zend-подобным, а также проверяемым с помощью PHPUnit?

Я действительно думал об абстрагировании большей логики контроллера в родительский класс (Lib_Controller_Action), но это делает модульное тестирование более сложным способом, который кажется мне неправильным.

+2

Может быть, вытесните общность в классы обслуживания/хранилища? Такие классы будут тестируемыми, будут использоваться для контроллеров и могут сделать код контроллера более компактным. –

+3

Другим подходом было бы собрать общность в помощниках действий. –

ответ

1

Две идеи (просто создать ответ с комментариями выше):

  1. принудительную общностью вниз в сервис/классы хранилища? Такие классы будут тестируемыми, будут использоваться для контроллеров и могут сделать код контроллера более компактным.

  2. Соберите общность в помощники действий.

+0

У вас есть ссылки на какие-либо примеры? Если бы они включали тестовый код, это было бы здорово - Zend и TTD/автоматическое тестирование по-прежнему новы для меня. – HorusKol

+1

Ну, вот часть, где я отчаянно машу руками, чтобы отвлечься от недостатка специфики, указывая на то, что Google ищет PHPUnit/TDD. Некоторые конкретные ссылки: (1) Я считаю, что модульные тесты в ZF очень поучительны. (2) Мне нравится [исходный код блога Dasprids] (http://site.svn.dasprids.de/trunk/) для примеров использования сервисов для уточнения кода контроллера. (3) Репозитории Doctrine2 являются хорошими примерами передачи доступа к данным в класс репозитория. (4) [Эта статья] (http://devzone.zend.com/1218/action-helpers-in-zend-framework/) от MWOP - хорошее введение в помощники действий. –

+0

Спасибо за это - время, чтобы продолжить с некоторым чтением – HorusKol

1

Поскольку вы должны сделать этот шаг каждый раз, когда запрос сделан, вы можете сохранить, что получать, анализировать и проверять полученный запрос в Zend_Controller_Plugin, который будет запускать каждый PreDispatch всех контроллеров.(Только если вы выполняете стандартизацию вашего XML-запроса) (Если вы используете XMLRPC, REST или какой-либо стандартный способ создания запросов к вашей службе, вы можете просмотреть эти модули, созданные в ZF)

Проверка данных (конкретное действие) может быть выполнено в методе контроллера (который затем будет вызван действием (-ами), которым это требуется) (если ваши параметры специфичны для одного или многих действий этого контроллера), или вы можете сделать это с помощью шаблонов Factory и Builder в том случае, если у вас есть много общего Params между контроллерами/действиями

// call to the factory 
$filteredRequest = My_Param_Factory::factory($controller, $action, $paramsArray) // call the right builder based on the action/controller combination 

// the actual Factory 

class My_Param_Factory 
{ 
    public static function factory($controller, $action, $params) 
    { 
     $builderClass = "My_Param_Builder_" . ucfirst($controller) . '_' . ucfirst($action); 

     $builder = new $builderClass($params); 

     return $builder->build(); 
    } 
} 

Вашим строитель будет затем вызвать конкретный параметр ы проверки классов на основе того, что потребности конкретных модифицирующих (которые могли бы улучшить повторное использование)

В контроллере, если каждый необходимый PARAMS действителен, вы проходите обработку правильного метода правой модели

$userModel->getUserInfo($id) // for example 

Которая удалит все операции обработки данных с контроллеров, которые должны были бы проверить, нормально ли вход, и затем отправить соответственно.

Сохранение результата (ошибка или Succes) в переменную, которая будет послан к представлению

обработки данных (формат и побег (заменить < с & ЛТ, если они должны быть включены в ответ на пример)), отправьте помощнику представления для сборки XML, затем распечатайте (echo) данные в представлении (который будет ответом для вашего пользователя).

public function getDetailsAction() 
{ 
    if ($areRequestParamsValid === true) { 
     // process data 
    } else { 
     // Build specific error message (or call action helper or controller method if error is general) 
    } 

    $this->view->data = $result 
} 
Смежные вопросы