webmunkeez / adr-bundle
为Symfony设计的Action-Domain-Responder模式。
Requires
- php: >=8.1
- phpdocumentor/reflection-docblock: ^5.3
- symfony/config: ^6.4
- symfony/dependency-injection: ^6.4
- symfony/http-foundation: ^6.4
- symfony/http-kernel: ^6.4
- symfony/property-access: ^6.4
- symfony/property-info: ^6.4
- symfony/serializer: ^6.4
- twig/twig: ^3.8
Requires (Dev)
- phpunit/phpunit: ^9.6
- symfony/browser-kit: ^6.4
- symfony/css-selector: ^6.4
- symfony/framework-bundle: ^6.4
- symfony/phpunit-bridge: ^6.4
- symfony/twig-bundle: ^6.4
- symfony/uid: ^6.4
- symfony/yaml: ^6.4
- dev-main / 2.3.x-dev
- v2.3.0
- v2.2.7
- v2.2.6
- v2.2.5
- v2.2.4
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
- v2.1.13
- v2.1.12
- v2.1.11
- v2.1.10
- v2.1.9
- v2.1.8
- v2.1.7
- v2.1.6
- v2.1.5
- v2.1.4
- v2.1.3
- v2.1.2
- v2.1.1
- v2.1.0
- v2.0.8
- v2.0.7
- v2.0.6
- v2.0.5
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- v2.0.0
- v1.0.21
- v1.0.20
- v1.0.19
- v1.0.18
- v1.0.17
- v1.0.16
- v1.0.15
- v1.0.14
- v1.0.13
- v1.0.12
- v1.0.11
- v1.0.10
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
This package is auto-updated.
Last update: 2024-09-08 20:46:16 UTC
README
此包在Symfony应用程序中释放了Action-Domain-Responder模式。
安装
使用Composer安装此包
$ composer require webmunkeez/adr-bundle
将包添加到您的应用程序内核中
// config/bundles.php return [ // ... Webmunkeez\ADRBundle\WebmunkeezADRBundle::class => ['all' => true], // ... ];
使用
动作
动作是一个可调用的类,必须实现\Webmunkeez\ADRBundle\Action\ActionInterface
final class StoryDetailAction implements \Webmunkeez\ADRBundle\Action\ActionInterface { public function __invoke(): Response { return $this->render($data); } public function render(?\Webmunkeez\ADRBundle\Response\ResponseDataInterface $data = null): Response { return new Response(...); } }
但,它可以是一个更经典的控制器,实现相同的接口
final class StoryController implements \Webmunkeez\ADRBundle\Action\ActionInterface { public function detail(): Response { return $this->render($data); } public function render(?\Webmunkeez\ADRBundle\Response\ResponseDataInterface $data = null): Response { return new Response(...); } }
(每个实现ActionInterface
的服务都自动标记为controller.service_arguments
)
响应者
响应者是服务,它接收数据(一个实现\Webmunkeez\ADRBundle\Response\ResponseDataInterface
的对象)并将其返回为Symfony\Component\HttpFoundation\Response
实例。
它可以是一个包含HTML或JsonResponse或任何你想要的内容的响应,只要它是Symfony\Component\HttpFoundation\Response
实例。
在此包中,有一个响应者管理器\Webmunkeez\ADRBundle\Response\Responder
,您可以将其注入到您的动作(或控制器)中。
响应者管理器获取您的应用程序中的所有响应者(它使用编译器传递来获取所有标记为webmunkeez_adr.responder
的服务并按优先级排序)并找到正确的响应者以渲染响应。
final class StoryDetailAction implements \Webmunkeez\ADRBundle\Action\ActionInterface { private \Webmunkeez\ADRBundle\Response\Responder $responder; public function __construct(\Webmunkeez\ADRBundle\Response\Responder $responder) { $this->responder = $responder } public function __invoke(): Response { return $this->render($data); } public function render(?\Webmunkeez\ADRBundle\Response\ResponseDataInterface $data = null): Response { return $this->responder->render($data); } }
您可以使用\Webmunkeez\ADRBundle\Response\ResponderAwareInterface
和\Webmunkeez\ADRBundle\Response\ResponderAwareTrait
来自动注入响应者
final class StoryDetailAction implements \Webmunkeez\ADRBundle\Action\ActionInterface, \Webmunkeez\ADRBundle\Response\ResponderAwareInterface { use \Webmunkeez\ADRBundle\Response\ResponderAwareTrait; public function __invoke(): Response { return $this->render($data); } public function render(?\Webmunkeez\ADRBundle\Response\ResponseDataInterface $data = null): Response { return $this->responder->render($data); } }
您还可以使用\Webmunkeez\ADRBundle\Action\ActionTrait
来简化代码
final class StoryDetailAction implements \Webmunkeez\ADRBundle\Action\ActionInterface, \Webmunkeez\ADRBundle\Response\ResponderAwareInterface { use \Webmunkeez\ADRBundle\Response\ResponderAwareTrait; use \Webmunkeez\ADRBundle\Action\ActionTrait; public function __invoke(): Response { return $this->render($data); } }
或者直接扩展\Webmunkeez\ADRBundle\Action\AbstractAction
final class StoryDetailAction extends \Webmunkeez\ADRBundle\Action\AbstractAction { public function __invoke(): Response { return $this->render($data); } }
响应者是实现\Webmunkeez\ADRBundle\Response\ResponderInterface
的类(因此,它们自动标记为webmunkeez_adr.responder
)
final class XmlResponder implements \Webmunkeez\ADRBundle\Response\ResponderInterface { private RequestStack $requestStack; private SerializerInterface $serializer; public function __construct(RequestStack $requestStack, SerializerInterface $serializer) { $this->requestStack = $requestStack; $this->serializer = $serializer; } public function supports(): bool { return 'xml' === $this->requestStack->getCurrentRequest()->getPreferredFormat(); } public function render(?\Webmunkeez\ADRBundle\Response\ResponseDataInterface $data = null): Response { $xml = $this->serializer->serialize($data, 'xml'); $response = new Response($xml); $response->headers->set('Content-Type', 'text/xml'); return $response; } }
如您所见,有两个方法:supports
定义激活响应者的条件,以及render
来创建响应。
核心响应者
提供了两个核心响应者
HtmlResponder
\Webmunkeez\ADRBundle\Response\HtmlResponder
使用Twig
渲染HTML模板。要指定模板,您必须使用\Webmunkeez\ADRBundle\Attribute\Template
use Webmunkeez\ADRBundle\Attribute\Template; #[Template('story/detail.html.twig')] final class StoryDetailAction implements \Webmunkeez\ADRBundle\Action\ActionInterface { ... }
如果请求包含HTTP_ACCEPT text/html
头,则此响应者处于活动状态(警告:此响应者需要一个twig模板,否则将抛出\Webmunkeez\ADRBundle\Exception\RenderingException
异常)。
其优先级为priority: -20
。
JsonResponder
\Webmunkeez\ADRBundle\Response\JsonResponder
使用Serializer
渲染JSON(您可以使用\Webmunkeez\ADRBundle\Attribute\SerializationContext
指定序列化上下文)
use Webmunkeez\ADRBundle\Attribute\SerializationContext; #[SerializationContext(['groups' => 'group_one'])] final class StoryDetailAction implements \Webmunkeez\ADRBundle\Action\ActionInterface { ... }
如果请求包含HTTP_ACCEPT application/json
头,则此响应者处于活动状态。
其优先级为priority: -10
。
自定义响应者
您可以根据我之前的XmlResponder
示例编写自己的响应者,通过实现\Webmunkeez\ADRBundle\Response\ResponderInterface
。
实现此接口的服务将自动标记为webmunkeez_adr.responder
,优先级为0
,您可以更改它(在您的service.yaml
中或通过静态的getDefaultPriority
方法;参见https://symfony.com.cn/doc/current/service_container/tags.html#tagged-services-with-priority)。
您可以定义“通用”响应者,如html、json、xml等。但您也可以定义更具体的,通过检查$request->attributes->get('_controller')
来仅使响应者为特定动作
final class CustomResponder implements \Webmunkeez\ADRBundle\Response\ResponderInterface { private RequestStack $requestStack; private Environment $twig; public function __construct(RequestStack $requestStack, Environment $twig) { $this->requestStack = $requestStack; $this->twig = $twig; } public function supports(): bool { $controller = $this->requestStack->getCurrentRequest()->attributes->get('_controller'); $actionClass = false !== strpos($controller, '::') ? substr($controller, 0, strpos($controller, '::')) : $controller; return CustomResponderAction::class === $actionClass; } public function render(?\Webmunkeez\ADRBundle\Response\ResponseDataInterface $data = null): Response { $data = array_merge($data, ['customResponder' => true]); $html = $this->twig->render($this->requestStack->getCurrentRequest()->attributes->get('_template_path'), $data); return new Response($html); } }
渲染异常监听器
如果有未捕获的\Webmunkeez\ADRBundle\Exception\RenderingException
,它将被此监听器捕获,并抛出一个包含原始异常的\Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException
。
异常监听器
如果存在未被捕获的 \Throwable
,它将被此监听器捕获,该监听器将抛出包含原始异常的 \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
。
HTTP 异常监听器
如果您请求一个带有 HTTP_ACCEPT application/json
头的 Action
,并且如果该 Action
抛出一个实现了 \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface
的异常,其内容将自动以 JSON 读取格式序列化到 Response
响应体的内容中。