2017-09-27 10:50
如果您以前曾在大型应用程序上工作过,您可能已经注意到,迟早会出现笨重的控制器。即使您使用存储库或服务类来从控制器中提取逻辑,随着时间的推移,依赖关系,方法和代码行的数量也会增加。
让我介绍一下请求处理程序。这个概念非常简单,但很多PHP开发人员并不为人所知。请求处理程序基本上是一个控制器,但限于一个单一的操作。这个概念非常类似于由Paul M. Jones提出的Action-Domain-Responder模式,MVC模式的一个替代方案就是针对Web应用程序的响应流更加明确的请求。
构建请求处理程序的好方法是使用可调用类。可调用类是使用PHP的魔术__invoke
方法的类,将其转换为Callable,这允许将其调用为函数。这是一个可调用类的简单示例:
class Greeting {
public function __invoke($name) {
echo 'Hello ' . $name; }
}
$welcome = new Greeting();
$welcome('John Doe'); // Hello John Doe
在这一点上,你可能在想 “我为什么要这么做?”。我知道这是一个荒谬的例子。但是,与使用可调用和依赖注入的代码结合使用时,会很有意义。一个很好的用例是Laravel或Slim中的路由请求处理:
Route::get('/{name}', Greeting::class);
这只是打扰你的头脑吗?没有?我们来比较一下你通常写的内容:
Route::get('/{name}', 'SomeController@greeting');
还没?除了更好的代码外,还有更多的好处。让我继续讨论使用请求处理程序与控制器的一些优点。
SOLID的第一个原则是“单一责任”。在我看来,有很多行动的控制者打破这个原则。请求处理程序提供了一个很好的解决方案,将这些操作分成自己的类,使它们更容易维护,重构和测试。
以下是从a提取的2个请求处理程序的示例UsersController
,它处理用户个人资料的编辑和保存:
class EditUserHandler
{
public function __construct(
UserRepository $repository,
Twig $twig
) {
...
}
public function __invoke(Request $request, Response $response)
{
...
}
}
class UpdateUserHandler
{
public function __construct(
UserRepository $repository,
UpdateUserValidator $validator,
ImageManager $resizer,
Filesystem $storage
) {
...
}
public function __invoke(Request $request, Response $response)
{
...
}
}
这给我们带来了下一个优势;
您最近是否为您的控制器编写了单元测试?您可能最终创建了与您的测试无关的嘲讽依赖。因为请求处理程序将不同的控制器操作分成不同的类,所以您只需要为该特定操作注入或绑定嘲笑。
测试提示:使您的功能测试类别尽可能具体,然后使用每个测试来描述重要的规则/能力。
如果您有每个请求处理程序的测试文件,这基本上是会发生的。这对这些巨大的控制器测试文件来说是一个很大的改进。
PhpStorm和其他编辑器具有强大的重构选项。但是,如果您使用默认的Laravel或Slim方式将控制器方法绑定到路由,则可能遇到问题。重命名为:
Route::get('/{name}', Greeting::class);
比这更容易:
Route::get('/{name}', 'SomeController@greeting');
请求处理程序提供了一个很好的替代控制器。控制器操作分为单个请求处理程序类,负责单个操作。这导致代码更容易维护,重构和测试。
您是否应该继续使用请求处理程序替换所有控制器?可能不会。对于小型应用程序,为了简单起见,将操作分组可能是有意义的。当我开始在Teamleader工作时,我才发现请求处理程序,我不觉得需要很快回到控制器。