Php Zend Action Controller-重构策略
我已经在Zend Framework(1.10)上构建了一个首次运行的web服务,现在我正在寻找重构我的动作控制器中的一些逻辑的方法,以便我和我的团队的其他成员更容易扩展和维护该服务 我知道哪里有重构的机会,但我不清楚如何进行重构的最佳策略。关于控制器的最佳文档和教程只讨论小规模的应用程序,而没有真正讨论如何抽象更重复的代码,这些代码会逐渐扩展到更大的规模 我们的动作控制器的基本结构是:Php Zend Action Controller-重构策略,php,model-view-controller,zend-framework,refactoring,Php,Model View Controller,Zend Framework,Refactoring,我已经在Zend Framework(1.10)上构建了一个首次运行的web服务,现在我正在寻找重构我的动作控制器中的一些逻辑的方法,以便我和我的团队的其他成员更容易扩展和维护该服务 我知道哪里有重构的机会,但我不清楚如何进行重构的最佳策略。关于控制器的最佳文档和教程只讨论小规模的应用程序,而没有真正讨论如何抽象更重复的代码,这些代码会逐渐扩展到更大的规模 我们的动作控制器的基本结构是: 从请求体提取XML消息-这包括针对特定于操作的relaxNG模式的验证 准备XML响应 验证请求消息中的数据
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();
}
}
类Api\u供应商控制器扩展Lib\u控制器\u操作
{
公共函数getDetailsAction()
{
试一试{
$request=新的Lib_XML_请求('1.0');
$request->load($this->getRequest()->getRawBody(),dirname(_文件)'/../resources/xml/relaxng/vendor/getDetails.xml');
}catch(Lib\u XML\u请求\u异常$e){
//日志异常(如果记录器可用)
如果($log=$this->getLog()){
$log->warn('API/Vendor/getDetails:验证传入请求消息时出错',$e);
}
//提升为一般错误
抛出新的Zend\u控制器\u操作\u异常($e->getMessage(),400);
}
$response=new Lib_XML_response('API/vendor/getDetails');
试一试{
$criteria=array();
$fields=$request->getElementsByTagName('field');
对于($i=0;$i<$fields->length;$i++){
$name=trim($fields->item($i)->attributes->getNamedItem('name')->nodeValue);
如果(!isset($criteria[$name])){
$criteria[$name]=array();
}
$criteria[$name][]=trim($fields->item($i)->childNodes->item(0)->nodeValue);
}
$vendors=$this->_映射程序['vendor']->查找($criteria);
if(计数($供应商)<1){
抛出新的Api_VendorController_异常(“找不到任何符合您标准的供应商”);
}
$response->append('success');
foreach($供应商作为$供应商){
$v=$vendor->toArray();
$response->append('vendor',$v);
}
}捕获(Api\u供应商控制器\u例外$e){
//发送失败消息
$error=$response->append('error');
$response->appendChild($error,'message',$e->getMessage());
//日志异常(如果记录器可用)
如果($log=$this->getLog()){
$log->warn('API/Account/GetDetails:'。$e->getMessage(),$e);
}
}
echo$response->save();
}
}
那么-知道我的控制器中的共性在哪里,在保持Zend风格和PHPUnit可测试性的同时重构的最佳策略是什么
我确实考虑过将更多的控制器逻辑抽象到父类(Lib_controller_Action)中,但这使得单元测试变得更加复杂,在我看来这是错误的。两个想法(仅从上面的评论中创建一个答案):
由于每次发出请求时都必须执行此步骤,因此可以将接收、解析和验证接收到的请求存储在Zend_Controller_插件中,该插件将在所有控制器的每个
PreDispatch
中运行。(只有在XML请求标准化的情况下才可以这样做)(如果您使用XMLRPC
、REST
或某种标准方式来构建对服务的请求,您可以期待在ZF中构建这些模块)
数据的验证(特定于操作)可以在控制器方法中完成(然后由需要它的操作调用)(如果您的参数特定于该控制器的一个或多个操作)或者,如果控制器/操作之间有许多共享参数,您也可以使用模式工厂
和生成器
// 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();
}
}
然后,构建器将根据特定的构建器需求调用特定的参数来验证类(这将提高可重用性)
在控制器中,如果每个必需的参数都有效,则将处理传递给正确模型的正确方法
$userModel->getUserInfo($id) // for example
这将从控制器中删除所有数据处理操作,控制器只需检查输入是否正常,然后相应地进行调度
将结果(错误或成功)存储在将发送到视图的变量中
处理数据(格式化和转义(例如,如果响应中包含它们,则替换为),发送给视图帮助程序以生成XML,然后在视图中打印(echo
)数据(这将是用户的响应)
也许可以将公共性下推到服务/存储库类中?这样的类是可测试的,可以跨控制器使用,并且可以使控制器代码更加紧凑。另一种方法是将通用性收集到动作助手中。您有任何示例的链接吗?如果它们包括测试代码,那就太好了——Zend和TTD/自动测试对我来说还是很新的
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
}