Zend Action Controller - refactoring strategy

I built the first web service on the Zend Framework (1.10), and now I'm looking for ways to refactor some logic in my action controllers so that it is easier for me and the rest of my team expands and supports the service.

I can see where there are opportunities for refactoring, but I do not know what the best strategies are. The best documentation and controller guides only talk about small applications, and don't really discuss how to abstract away from more repetitive code that penetrates to larger scales.

The basic structure for our action controllers:

  • Extract XML message from request body. This includes validation against the relaxNG scheme for specific actions.
  • Prepare an XML Response
  • Confirm the data in the request message (incorrect data throws an exception - the message is added to the response, which is sent immediately)
  • Perform database action (select / insert / update / delete)
  • Return success or failure of the action, with the necessary information.

A simple example is this action, which returns a list of suppliers based on a flexible set of criteria:

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

So, knowing where the similarities are in my controllers, what is the best strategy for refactoring, keeping it Zend-like and also tested using PHPUnit?

I really thought about abstracting the larger controller logic into the parent class (Lib_Controller_Action), but this makes unit testing a more complex way, which seems wrong to me.

+6
source share
2 answers

Two ideas (just creating an answer from the comments above):

  • Include community in service / storage classes? Such classes would be testable, would be useful for controllers, and could make controller code more compact.

  • Gather community in action assistants.

+1
source

Since you must do this step every time a request is executed, you can save this technique, analyze and check the received request in Zend_Controller_Plugin, which will be executed every PreDispatch all controllers. (Only if your XML request is standardized) (if you use XMLRPC , REST or the standard way to create requests to your service, you can view these modules created in ZF)

Data validation (action-specific) can be performed in the controller method (which will then be called by the action (s) requiring it) (if your parameters are specific to one or many actions of this controller) or you can do this using Factory templates and Builder in case you have many common parameters between controllers / actions

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

Then your builder will call certain parameters to check classes based on the needs of a particular builder (which will improve reuse)

In your controller, if all the necessary parameters are valid, you pass processing to the correct method of the correct model

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

To remove all data processing operations from the controllers, which would have to check whether the input is normal, and then send accordingly.

Save the results (error or sequence) in a variable that will be sent to the view

Process the data (format and escape (replace <with & lt; if they should be included in the response, for example)), send the view to XML helper to the assistant, then print ( echo ) the data in the view (which will be the answer for your user).

 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 } 
+1
source

Source: https://habr.com/ru/post/905786/


All Articles