vkr / 控制器委派包
一个用于 Symfony2/3 的包,允许在构建其他可重用包时使用委派模式
Requires
- php: >=5.6
- symfony/symfony: ~2.8|~3.0
Requires (Dev)
- phpunit/phpunit: ~4.8
This package is auto-updated.
Last update: 2022-02-01 13:00:26 UTC
README
这个小包允许在 Symfony 控制器中使用委派模式。其主要用途是在创建一个需要一些控制器逻辑但不需要定义完整控制器和模板的可重用组件时。可以说,此包的目的是提供一种比默认的 Symfony 包继承更轻量级、更无烦恼的包继承系统。
此包仅由三个类组成 - 两个抽象和一个具体。它本身不执行任何操作,设计为作为其他可重用包的依赖项使用。
此包除了 Symfony 本身外没有其他依赖。
哲学
假设你有一些可重用代码,这些代码涉及到大量对容器的操作和调用其他有用的控制器方法,如 createForm()
。当然,你可以将这些放入实际的控制器中,但你还需要定义一个视图。控制器操作和视图都可以通过包继承来覆盖,但这涉及到遵循命名约定,这可能不方便。例如,自定义 FOSUserBundle
并不是世界上最容易的事情。
此包背后的哲学认为,对于客户端编码人员来说,调用每个控制器操作中的委派方法会更容易,这些方法将接受与实际控制器相同的参数,但可能返回除 HTTP 响应之外的内容。然后控制器将按照其认为必要的方式解析委派的结果。
因此,可以说实际控制器充当委派类的装饰器。
使用方法
创建一个应该扩展 VKR\ControllerDelegationBundle\Delegates\AbstractDelegate
的委派类。这个类是容器感知的,并且可以通过 $this->controller
访问所有控制器方法。它应该返回在父构造函数下以 $this->delegateResponse
为名的 DelegateResponse
类的相同实例。
以下是如何使用它的方法
public function myDelegatedAction($someArgument)
{
...
$viewData = [
'templateVar' => 'value',
];
return $this->delegateResponse->setViewData($viewData);
}
DelegateResponse
的所有设置方法都返回其实例。你也可以这样写
$this->delegateResponse->setViewData($viewData);
return $this->delegateResponse;
然后预期客户端编码人员将创建一个扩展 VKR\ControllerDelegationBundle\Controller\AbstractDelegatedController
的控制器,并在其中编写以下方法
public function myDelegatedAction($someArgument)
{
$delegate = new MyDelegate($this);
$delegateResponse = $delegate->myDelegatedAction($someArgument);
$parsedResponse = $this->parseDelegateResponse($delegateResponse);
return $this->render('my/template.html.twig', $parsedResponse);
}
parseDelegateResponse()
是获取模板变量的快捷方式。通常建议控制器方法和委派方法具有相同的签名。
处理重定向
如果有任何你希望返回的重定向,事情就变得更复杂了。概念是,委派类创建者不应硬编码任何路由名称。这些被替换为特殊标记,如 'success' 或 'failure'。然后在你的委派类中这样做
return $this->delegateResponse->setRouteToRedirect($this->redirectRoutes['success']);
为了解析这些标记,它们必须首先作为 $requiredRedirectRoutes
类属性声明
protected $requiredRedirectRoutes = ['success'];
请注意,必需路由列表不能在操作方法中声明或重新定义。
然后,应该将此代码添加到您的控制器中
$redirectRoutes = ['success' => 'my_success_route'];
$delegate = new MyDelegate($this, $redirectRoutes);
...
return $this->parseDelegateResponse($delegateResponse);
如果您的代理可以返回重定向和非重定向的值,则控制器将如下所示
$parsedResponse = $this->parseDelegateResponse($delegateResponse);
if ($parsedResponse instanceof RedirectResponse) {
return $parsedResponse;
}
return $this->render('my/template.html.twig', $parsedResponse);
如果DelegateResponse
对象同时包含重定向和非重定向的值,则重定向将具有优先权。
如果您的路由带有参数,您可以按如下方式修改您的控制器
$redirectRoutes = ['success' => ['my_success_route', ['foo' => 'bar']]];
最后,如果您想跳过一些必填参数,可以使用_default
键
$redirectRoutes = ['_default' => 'my_default_route'];
有时,您可能希望使用外部链接进行重定向,例如API调用。以下是实现方法
#Delegate
return $this->delegateResponse->setUrlToRedirect('https://api.facebook.com');
处理额外的渲染调用
假设在您的控制器逻辑中,您需要渲染一些不会由控制器返回的内容。这里最好的例子是发送电子邮件。在这种情况下,您只需向代理操作传递一个额外的参数,然后在代理中对其进行某种解析即可。
#Controller
public function sendEmailAction()
{
$emailData = [
'subject' => 'My subject',
'template' => 'my/email/template.html.twig',
'templateVars' => [
'foo' => 'bar'
],
];
$delegate = new RegistrationDelegate($this);
$delegateResponse = $delegate->myDelegatedAction($emailData);
}
在您的代理中,您可以使用
$this->controller->render($emailData['template'], $emailData['templateVars']);
注意,模板名称和模板变量不应在代理类中硬编码。
最佳实践
- 将您的代理类放在一个单独的文件夹中,并将
Delegate
附加到它们的类名上。 - 为每个控制器方法使用一个代理方法。代理方法和对应控制器方法的名称应相同。
- 不要在控制器构造函数或由构造函数调用的任何方法中初始化代理对象。虽然这本身不会导致错误,但使用构造函数可能会在未来引发错误,如果您在代理的构造函数中调用一些容器方法。在Symfony中,容器在控制器对象创建后填充内容。
- 不要在代理类中放置业务逻辑,并尽量保持它们瘦。
- 不要对代理类进行单元测试。代理类应仅保留控制器逻辑,而控制器逻辑是不可进行单元测试的。