2012-01-12 18 views
6

Ho creato un servizio Web di prima esecuzione su Zend Framework (1.10), e ora sto cercando i modi per ridefinire parte della logica nei miei controller di azione in modo che sia più facile per me e il resto della mia squadra per espandere e mantenere il servizio.Zend Action Controller - strategia di refactoring

Posso vedere dove ci sono opportunità per il refactoring, ma non sono chiaro sulle migliori strategie su come. La migliore documentazione e tutorial sui controller parlano solo di applicazioni su piccola scala e non discutono in realtà su come astrarre il codice più ripetitivo che si insinua in scale più grandi.

La struttura di base per i nostri controllori di azione sono:

  1. estratto messaggio XML dal corpo della richiesta - Questo include la convalida contro uno schema relaxNG specifica azione
  2. Preparare la risposta XML
  3. convalidare i dati nel messaggio di richiesta (i dati non validi generano un'eccezione - un messaggio viene aggiunto alla risposta che viene inviata immediatamente)
  4. Eseguire l'azione del database (selezionare/inserire/aggiornare/eliminare)
  5. Rientro successo o il fallimento di azione, con le informazioni richieste

Un semplice esempio è questa azione che restituisce un elenco di fornitori sulla base di un insieme flessibile di criteri:

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(); 
    } 
} 

Così - sapere dove i punti in comune sono nei miei controller, qual è la migliore strategia per il refactoring mantenendola simile a Zend e anche testabile con PHPUnit?

Ho pensato di astrarre maggiormente la logica del controllore in una classe genitore (Lib_Controller_Action), ma ciò rende la verifica dell'unità più complicata in un modo che mi sembra sbagliato.

+2

Forse spingere la comunanza verso il basso nelle classi servizio/repository? Tali classi sarebbero testabili, sarebbero utilizzabili su tutti i controller e potrebbero rendere il codice del controller più compatto. –

+3

Un altro approccio sarebbe quello di raccogliere comunanza in aiutanti di azione. –

risposta

1

due idee (solo la creazione di una risposta dalle osservazioni di cui sopra):

  1. Spingere comunanza giù in servizio/classi repository? Tali classi sarebbero testabili, sarebbero utilizzabili su tutti i controller e potrebbero rendere il codice del controller più compatto.

  2. Raccogliere la comunanza in azioni di aiuto.

+0

Hai collegamenti a qualche esempio? Se includessero il codice di test, sarebbe fantastico: test Zend e TTD/automatizzati sono ancora abbastanza nuovi per me. – HorusKol

+1

Bene, ecco la parte in cui agita le braccia freneticamente per distrarre da una mancanza di specifiche, indicando le ricerche su Google per PHPUnit/TDD. Alcuni riferimenti specifici: (1) Trovo che i test unitari nella stessa ZF siano piuttosto istruttivi. (2) Mi piace il [codice sorgente del blog Dasprids] (http://site.svn.dasprids.de/trunk/) per esempi di utilizzo dei servizi per snellire il codice del controller. (3) I repository di Doctrine2 sono buoni esempi di spingere l'accesso ai dati in una classe di repository. (4) [Questo articolo] (http://devzone.zend.com/1218/action-helpers-in-zend-framework/) di MWOP è una buona introduzione ai soccorritori. –

+0

Grazie per questo - il tempo di andare avanti con alcune letture – HorusKol

1

Dal momento che si deve fare questo passo ogni volta che viene effettuata una richiesta, è possibile memorizzare che ricevono, analizzare e convalidare la richiesta ricevuta in uno Zend_Controller_Plugin che sarebbe eseguito ogni PreDispatch di tutti i controllori.(Utilizzabile solo se la tua richiesta XML è standardizzata) (Se usi XMLRPC, REST o un modo standard per compilare richieste al tuo servizio, potresti guardare avanti quei moduli costruiti in ZF)

La convalida dei dati (azione specifica) potrebbe essere fatto in un metodo di controllo (che verrebbe quindi chiamato dall'azione o dalle azioni che lo richiedono) (se i parameteri sono specifici per una o più azioni di quel controller) oppure lo si potrebbe fare con i modelli Factory e Builder nel caso in cui si dispone di un sacco di params condivisi tra i controllori/azioni

// 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(); 
    } 
} 

il costruttore sarebbe quindi chiamare dei parametri specifici s convalidare le classi in base a che le esigenze specifiche del costruttore (che migliorerebbero riutilizzabilità)

nel controller, se ogni params richiesti sono validi, si passa al trattamento per il giusto metodo del modello giusto

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

Quale rimuoverà tutte le operazioni di elaborazione dati dai controller che dovrebbero solo verificare se l'input è ok e quindi inviare di conseguenza.

memorizzare i risultati (errore o succes) in una variabile che saranno inviati alla vista

elaborare i dati (formato e sfuggono (sostituire < con & lt; se devono essere inclusi nella risposta per esempio)), inviare a un helper di visualizzazione per creare XML quindi stampare (echo) i dati nella vista (che sarà la risposta per l'utente).

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 
} 
Problemi correlati