From 98346f28c2bdcc47aff35ffc7725b12cda69059a Mon Sep 17 00:00:00 2001 From: stollr Date: Fri, 31 Mar 2023 08:58:23 +0200 Subject: [PATCH] Trigger event to allow updating the response based on view data at one central place --- DependencyInjection/FOSRestExtension.php | 1 + Resources/doc/2-the-view-layer.rst | 27 ++++++++++++++ View/ViewHandler.php | 12 ++++++ View/ViewResponseEvent.php | 47 ++++++++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 View/ViewResponseEvent.php diff --git a/DependencyInjection/FOSRestExtension.php b/DependencyInjection/FOSRestExtension.php index 8d54fbcad..f1bb1a397 100644 --- a/DependencyInjection/FOSRestExtension.php +++ b/DependencyInjection/FOSRestExtension.php @@ -293,6 +293,7 @@ private function loadView(array $config, XmlFileLoader $loader, ContainerBuilder $config['view']['empty_content'], $config['view']['serialize_null'], ]); + $defaultViewHandler->addMethodCall('setEventDispatcher', [new Reference('event_dispatcher')]); } private function loadException(array $config, XmlFileLoader $loader, ContainerBuilder $container): void diff --git a/Resources/doc/2-the-view-layer.rst b/Resources/doc/2-the-view-layer.rst index 17260a7b1..dea01e998 100644 --- a/Resources/doc/2-the-view-layer.rst +++ b/Resources/doc/2-the-view-layer.rst @@ -198,6 +198,33 @@ data transformer. Fortunately, the FOSRestBundle comes with an This way, the data structure remains untouched and the person can be assigned to the task without any client modifications. +Update the Response based on View Data +-------------------------------------- + +If you have the need to globally modify the response depending on the view data +you can use the ``ViewResponseEvent``. It allows you to do some custom stuff +at one central place and reduce repetitive work. + +Here's an example how to use it: + +.. code-block:: php + namespace App\EventListener; + + use FOS\RestBundle\View\ViewResponseEvent; + use Symfony\Component\EventDispatcher\Attribute\AsEventListener; + + #[AsEventListener] + class PaginationViewResponseListener + { + public function __invoke(ViewResponseEvent $event): void + { + $view = $event->getView(); // you have access to the view and the response + $response = $event->getResponse(); + // ... + } + } + + Configuration ------------- diff --git a/View/ViewHandler.php b/View/ViewHandler.php index 6e638fbb2..a5db50e64 100644 --- a/View/ViewHandler.php +++ b/View/ViewHandler.php @@ -19,6 +19,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * View may be used in controllers to build up a response in a format agnostic way @@ -52,6 +53,7 @@ final class ViewHandler implements ConfigurableViewHandlerInterface private $urlGenerator; private $serializer; private $requestStack; + private $dispatcher = null; private $options; private function __construct( @@ -198,9 +200,19 @@ public function createResponse(View $view, Request $request, string $format): Re $response->headers->set('Content-Type', $mimeType); } + if (null !== $this->dispatcher) { + $event = new ViewResponseEvent($view, $response, $request); + $this->dispatcher->dispatch($event); + } + return $response; } + public function setEventDispatcher(EventDispatcherInterface $dispatcher): void + { + $this->dispatcher = $dispatcher; + } + /** * Gets a response HTTP status code from a View instance. * diff --git a/View/ViewResponseEvent.php b/View/ViewResponseEvent.php new file mode 100644 index 000000000..cefd07cc8 --- /dev/null +++ b/View/ViewResponseEvent.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\RestBundle\View; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Allows to update the Response an the basis of view data. + */ +class ViewResponseEvent +{ + private $view; + private $response; + private $request; + + public function __construct(View $view, Response $response, Request $request) + { + $this->view = $view; + $this->response = $response; + $this->request = $request; + } + + public function getView(): View + { + return $this->view; + } + + public function getResponse(): Response + { + return $this->response; + } + + public function getRequest(): Request + { + return $this->request; + } +}