├── .scrutinizer.yml ├── .sensiolabs.yml ├── LICENSE ├── README.md ├── composer.json └── src ├── EventListener ├── ConvertExceptionListener.php ├── ForwardListener.php ├── HasFlashesListener.php ├── NoContentViewListener.php ├── ParamConverterListener.php ├── RedirectListener.php ├── SerializerViewListener.php ├── TemplatingViewListener.php ├── TwigViewListener.php └── ViewListener.php ├── FlashRedirect.php ├── Forward.php ├── HasFlashes.php ├── ParamConverter ├── FlashBagParamConverter.php ├── FormFactoryParamConverter.php ├── ParamConverter.php ├── SecurityContextParamConverter.php └── SessionParamConverter.php ├── Redirect.php ├── Template.php └── View.php /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | paths: [src/*] 3 | checks: 4 | php: true 5 | tools: 6 | external_code_coverage: true 7 | -------------------------------------------------------------------------------- /.sensiolabs.yml: -------------------------------------------------------------------------------- 1 | rules: 2 | php.interface_has_no_interface_suffix: 3 | enabled: false 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Kevin Bond 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ControllerUtil 2 | 3 | [![Build Status](http://img.shields.io/travis/kbond/ControllerUtil.svg?style=flat-square)](https://travis-ci.org/kbond/ControllerUtil) 4 | [![Scrutinizer Code Quality](http://img.shields.io/scrutinizer/g/kbond/ControllerUtil.svg?style=flat-square)](https://scrutinizer-ci.com/g/kbond/ControllerUtil/) 5 | [![Code Coverage](http://img.shields.io/scrutinizer/coverage/g/kbond/ControllerUtil.svg?style=flat-square)](https://scrutinizer-ci.com/g/kbond/ControllerUtil/) 6 | [![SensioLabs Insight](https://img.shields.io/sensiolabs/i/480f9e60-4ae0-46ea-b691-b66e51aa09f4.svg?style=flat-square)](https://insight.sensiolabs.com/projects/480f9e60-4ae0-46ea-b691-b66e51aa09f4) 7 | [![StyleCI](https://styleci.io/repos/17921235/shield)](https://styleci.io/repos/17921235) 8 | [![Latest Stable Version](http://img.shields.io/packagist/v/zenstruck/controller-util.svg?style=flat-square)](https://packagist.org/packages/zenstruck/controller-util) 9 | [![License](http://img.shields.io/packagist/l/zenstruck/controller-util.svg?style=flat-square)](https://packagist.org/packages/zenstruck/controller-util) 10 | 11 | When creating Symfony2 controllers as services, you often require the same dependencies for every controller. 12 | 13 | You often need: 14 | 15 | * The router for generating redirects. 16 | * The session for adding flash messages. 17 | * The templating engine for creating views. 18 | * The kernel for forwarding to another controller. 19 | * If using [jms/serializer](https://github.com/schmittjoh/serializer), the serializer. 20 | 21 | This library aims to remove those common dependencies by enabling your controllers to return small immutable 22 | objects for these tasks. View listeners then take those objects and create the response. 23 | 24 | There is a [Symfony2 Bundle](https://github.com/kbond/ZenstruckControllerUtilBundle) and a 25 | [Silex Service Provider](https://github.com/kbond/ControllerUtilServiceProvider) available to ease integration 26 | into your project. 27 | 28 | ## Usage 29 | 30 | ### Forward 31 | 32 | To forward the request to another controller, return the `Zenstruck\ControllerUtil\Forward` object. 33 | 34 | ```php 35 | use Zenstruck\ControllerUtil\Forward; 36 | 37 | // ... 38 | public function forwardAction() 39 | { 40 | return new Forward('another.controller:anotherAction', array('foo' => 'bar')); 41 | } 42 | // ... 43 | ``` 44 | 45 | Arguments: 46 | 47 | * `$controller`: the controller to forward to (*required*). 48 | * `$parameters`: an array of parameters to pass to the controller (default: `array()`). 49 | 50 | ### Redirect 51 | 52 | To redirect to another route, return the `Zenstruck\ControllerUtil\Redirect` object. 53 | 54 | ```php 55 | use Zenstruck\ControllerUtil\Redirect; 56 | 57 | // ... 58 | public function redirectAction() 59 | { 60 | return new Redirect('my_route'); 61 | 62 | // with parameters 63 | return new Redirect('my_route', array('foo' => 'bar')); 64 | } 65 | // ... 66 | ``` 67 | 68 | Arguments: 69 | 70 | * `$route`: the route to redirect to (*required*). 71 | * `$parameters`: an array of parameters required by the route (default: `array()`). 72 | * `$statusCode`: the status code for the response (default: `302`). 73 | 74 | ### FlashRedirect 75 | 76 | To redirect to another route and add a flash message, return the 77 | `Zenstruck\ControllerUtil\FlashRedirect` object. 78 | 79 | ```php 80 | use Zenstruck\ControllerUtil\FlashRedirect; 81 | 82 | // ... 83 | public function redirectAction() 84 | { 85 | return new FlashRedirect('my_route', array('foo' => 'bar'), array('info' => array('Success!')); 86 | 87 | // factory methods 88 | return FlashRedirect::create('my_route', array('foo' => 'bar'), 'Error', 'error'); 89 | return FlashRedirect::createSimple('my_route', 'Success'); 90 | } 91 | // ... 92 | ``` 93 | 94 | Arguments: 95 | 96 | * `$route`: the route to redirect to (*required*). 97 | * `$parameters`: an array of parameters required by the route (default: `array()`). 98 | * `$flashes`: an array of flash messages (default: `array()`). 99 | * `$statusCode`: the status code for the response (default: `302`). 100 | 101 | **NOTE**: The flashes must be an array in the following format: `array($key => array($message))`. As this 102 | can be cumbersome, there are factory methods. 103 | 104 | Factory Methods: 105 | 106 | * `FlashRedirect::create`: 107 | 108 | Arguments: 109 | 110 | * `$route`: the route to redirect to (*required*). 111 | * `$parameters`: an array of parameters required by the route (*required*). 112 | * `$message`: The flash message (*required*). 113 | * `$type`: The flash type (default: `info`). 114 | * `$statusCode`: the status code for the response (default: `302`). 115 | 116 | * `FlashRedirect::createSimple` (for redirects with no route parameters): 117 | 118 | Arguments: 119 | 120 | * `$route`: the route to redirect to (*required*). 121 | * `$message`: The flash message (*required*). 122 | * `$type`: The flash type (default: `info`). 123 | * `$statusCode`: the status code for the response (default: `302`). 124 | 125 | ### View 126 | 127 | To create a view for your response, return the `Zenstruck\ControllerUtil\View` object. This library has 128 | 3 view listeners: 129 | 130 | * `TemplatingViewListener`: for rendering views with the Symfony2 templating component. 131 | * `TwigViewListener`: for rendering views with Twig. 132 | * `SerializerViewListener`: for rendering non-html views with [jms/serializer](https://github.com/schmittjoh/serializer). 133 | 134 | ```php 135 | use Zenstruck\ControllerUtil\View; 136 | 137 | // ... 138 | public function viewAction() 139 | { 140 | $object = // .. 141 | 142 | return new View($object, 200); 143 | 144 | // with templates 145 | return new View($object, 200, 'my_template.html.twig'); 146 | 147 | // with an array of fallback templates 148 | return new View($object, 200, array('my_template.html.twig', 'fallback_template.html.twig')); 149 | 150 | // factory methods 151 | return View::createCached($object, 86400); 152 | } 153 | // ... 154 | ``` 155 | 156 | Arguments: 157 | 158 | * `$data`: the data to pass to the view. 159 | * `$statusCode`: the status code for the response (default: `200`). 160 | * `$template`: the template or an array of templates (default: `null`). 161 | * `$cache`: an array of cache options for the response (default: `array()`). 162 | * `$headers`: an array of response headers (default: `array()`). 163 | 164 | Factory Methods: 165 | 166 | * `View::createCached`: 167 | 168 | Arguments: 169 | 170 | * `$data`: the data to pass to the view (*required*). 171 | * `$sharedMaxAge`: the shared max age in seconds (*required*). 172 | * `$statusCode`: the status code for the response (default: `200`). 173 | 174 | **NOTES**: 175 | 176 | * When `$template` is an array of templates, the view listener will loop through them and render the first one 177 | that exists. 178 | * If no template is provided, you need to have the `SerializerViewListener` enabled and the request must be 179 | non-html. Otherwise an error will result. 180 | * If `$data` is not an array, a template is provided, the view listener will convert `$data` to 181 | `array('data' => $data)` before passing it to your template. 182 | 183 | #### No Content View 184 | 185 | This library has a `NoContentViewListener` which allows your controllers to return an empty View or (if enabled) 186 | simply null. The view listener will set a no content response (204). 187 | 188 | ```php 189 | use Zenstruck\ControllerUtil\View; 190 | 191 | // ... 192 | public function viewAction() 193 | { 194 | return new View(null); 195 | 196 | return null; // if enabled 197 | } 198 | // ... 199 | ``` 200 | 201 | ### Template 202 | 203 | If your views always have a template, you can use the `Zenstruck\ControllerUtil\Template` object for convenience. 204 | 205 | ```php 206 | use Zenstruck\ControllerUtil\Template; 207 | 208 | // ... 209 | public function viewAction() 210 | { 211 | $object = // .. 212 | 213 | return new Template('my_template.html.twig', array('object' => $object)); 214 | } 215 | // ... 216 | ``` 217 | 218 | Arguments: 219 | 220 | * `$template`: the template or an array of templates (*required*). 221 | * `$parameters`: the parameters to pass to the view (default: `array()`). 222 | * `$statusCode`: the status code for the response (default: `200`). 223 | * `$cache`: an array of cache options for the response (default: `array()`). 224 | * `$headers`: an array of response headers (default: `array()`). 225 | 226 | Factory Methods: 227 | 228 | * `Template::createCached`: 229 | 230 | Arguments: 231 | 232 | * `$template`: the template or an array of templates (*required*). 233 | * `$sharedMaxAge`: the shared max age in seconds (*required*). 234 | * `$parameters`: the parameters to pass to the view (default: `array()`). 235 | * `$statusCode`: the status code for the response (default: `200`). 236 | 237 | ## Manual installation 238 | 239 | It is recommended you use either the [Symfony2 Bundle](https://github.com/kbond/ZenstruckControllerUtilBundle) 240 | or the [Silex Service Provider](https://github.com/kbond/ControllerUtilServiceProvider) for including these 241 | utilities in your project. 242 | 243 | If you are doing something custom using the Symfony2 Event Dispatcher, you can register the listeners manually: 244 | 245 | ```php 246 | // add the HasFlashesListener 247 | $eventDispatcher->addListener( 248 | KernelEvents::VIEW, 249 | array(new HasFlashesListener($flashBag), 'onKernelView'), 250 | 10 // before other events 251 | ); 252 | 253 | // add the RedirectListener 254 | $eventDispatcher->addListener( 255 | KernelEvents::VIEW, 256 | array(new RedirectListener($urlGenerator), 'onKernelView') 257 | ); 258 | 259 | // add the ForwardListener 260 | $eventDispatcher->addListener( 261 | KernelEvents::VIEW, 262 | array(new ForwardListener(), 'onKernelView') 263 | ); 264 | 265 | // add the TwigViewListener 266 | $eventDispatcher->addListener( 267 | KernelEvents::VIEW, 268 | array(new TwigViewListener($twigEnvironment), 'onKernelView') 269 | ); 270 | 271 | // add the TemplatingViewListener 272 | $eventDispatcher->addListener( 273 | KernelEvents::VIEW, 274 | array(new TemplatingViewListener($templating), 'onKernelView') 275 | ); 276 | 277 | // add the NoContentViewListener 278 | $eventDispatcher->addListener( 279 | KernelEvents::VIEW, 280 | array(new NoContentViewListener(true /* false to force an empty view and not allow null */), 'onKernelView'), 281 | 7 // before other events 282 | ); 283 | 284 | // add the SerializerViewListener 285 | $eventDispatcher->addListener( 286 | KernelEvents::VIEW, 287 | array(new SerializerViewListener($serializer), 'onKernelView'), 288 | 5 // before other events 289 | ); 290 | ``` 291 | 292 | **NOTES**: 293 | 294 | * Notice the priority on the `HasFlashesListener`, `NoContentViewListener` and `SerializerViewListener`. These need 295 | to be triggered before the other listeners. 296 | * You should only use either the `TemplatingViewListener` or `TwigViewListener` - not both. 297 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zenstruck/controller-util", 3 | "type": "library", 4 | "description": "Utilities for creating Symfony2 responses without dependencies.", 5 | "keywords": ["symfony", "silex", "symfony2", "controller", "response", "rest", "serializer"], 6 | "homepage": "http://zenstruck.com/projects/ControllerUtil", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Kevin Bond", 11 | "email": "kevinbond@gmail.com" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=5.3.3", 16 | "symfony/http-kernel": "~2.3|~3.0" 17 | }, 18 | "require-dev": { 19 | "symfony/routing": "~2.3|~3.0", 20 | "symfony/templating": "~2.3|~3.0", 21 | "jms/serializer": "~0.16|~1.0", 22 | "symfony/serializer": "~2.3|~3.0" 23 | }, 24 | "suggest": { 25 | "symfony/templating": "For using the TemplatingViewListener", 26 | "symfony/routing": "For using the RedirectListener", 27 | "twig/twig": "For using the TwigViewListener", 28 | "jms/serializer": "For using the SerializerViewListener with JMS Serializer", 29 | "symfony/serializer": "For using the SerializerViewListener with the Symfony Serializer" 30 | }, 31 | "autoload": { 32 | "psr-4": { "Zenstruck\\ControllerUtil\\": "src/" } 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { "Zenstruck\\ControllerUtil\\Tests\\": "tests/" } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/EventListener/ConvertExceptionListener.php: -------------------------------------------------------------------------------- 1 | 13 | * @author Kevin Bond 14 | * 15 | * @see https://github.com/QafooLabs/QafooLabsNoFrameworkBundle 16 | */ 17 | class ConvertExceptionListener 18 | { 19 | private $exceptionClassMap; 20 | 21 | /** 22 | * @param array $exceptionClassMap Class as the key, HTTP status code as the value 23 | */ 24 | public function __construct(array $exceptionClassMap = array()) 25 | { 26 | $this->exceptionClassMap = $exceptionClassMap; 27 | } 28 | 29 | /** 30 | * @param GetResponseForExceptionEvent $event 31 | */ 32 | public function onKernelException(GetResponseForExceptionEvent $event) 33 | { 34 | $exception = $event->getException(); 35 | 36 | if ($exception instanceof HttpExceptionInterface) { 37 | return; 38 | } 39 | 40 | $statusCode = $this->findStatusCode($exception); 41 | 42 | if (null === $statusCode) { 43 | return; 44 | } 45 | 46 | $event->setException(new HttpException($statusCode, null, $exception)); 47 | } 48 | 49 | /** 50 | * @param \Exception $exception 51 | * 52 | * @return int|null 53 | */ 54 | private function findStatusCode(\Exception $exception) 55 | { 56 | $exceptionClass = get_class($exception); 57 | 58 | foreach ($this->exceptionClassMap as $originalExceptionClass => $statusCode) { 59 | if ($exceptionClass === $originalExceptionClass || is_subclass_of($exceptionClass, $originalExceptionClass)) { 60 | return (int) $statusCode; 61 | } 62 | } 63 | 64 | return null; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/EventListener/ForwardListener.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class ForwardListener 13 | { 14 | public function onKernelView(GetResponseForControllerResultEvent $event) 15 | { 16 | $result = $event->getControllerResult(); 17 | 18 | if (!$result instanceof Forward) { 19 | return; 20 | } 21 | 22 | $parameters = $result->getParameters(); 23 | $parameters['_controller'] = $result->getController(); 24 | $subRequest = $event->getRequest()->duplicate(null, null, $parameters); 25 | $response = $event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST); 26 | $event->setResponse($response); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/EventListener/HasFlashesListener.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class HasFlashesListener 13 | { 14 | private $flashBag; 15 | 16 | public function __construct(FlashBagInterface $flashBag) 17 | { 18 | $this->flashBag = $flashBag; 19 | } 20 | 21 | public function onKernelView(GetResponseForControllerResultEvent $event) 22 | { 23 | $result = $event->getControllerResult(); 24 | 25 | if (!$result instanceof HasFlashes) { 26 | return; 27 | } 28 | 29 | foreach ($result->getFlashes() as $type => $messages) { 30 | foreach ($messages as $message) { 31 | $this->flashBag->add($type, $message); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/EventListener/NoContentViewListener.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class NoContentViewListener 13 | { 14 | private $allowNull; 15 | 16 | /** 17 | * @param bool $allowNull 18 | */ 19 | public function __construct($allowNull = true) 20 | { 21 | $this->allowNull = $allowNull; 22 | } 23 | 24 | public function onKernelView(GetResponseForControllerResultEvent $event) 25 | { 26 | $result = $event->getControllerResult(); 27 | 28 | if (null === $result && true === $this->allowNull) { 29 | $event->setResponse($this->createResponse()); 30 | 31 | return; 32 | } 33 | 34 | if (!$result instanceof View) { 35 | return; 36 | } 37 | 38 | if (null === $result->getData() && null === $result->getTemplate()) { 39 | $event->setResponse($this->createResponse($result->getHeaders())); 40 | } 41 | } 42 | 43 | /** 44 | * @param array $headers 45 | * 46 | * @return Response 47 | */ 48 | private function createResponse(array $headers = array()) 49 | { 50 | return new Response('', 204, $headers); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/EventListener/ParamConverterListener.php: -------------------------------------------------------------------------------- 1 | 17 | * @author Kevin Bond 18 | * 19 | * @see https://github.com/QafooLabs/QafooLabsNoFrameworkBundle 20 | * @see https://github.com/sensiolabs/SensioFrameworkExtraBundle 21 | */ 22 | class ParamConverterListener 23 | { 24 | private $paramConverters; 25 | 26 | /** 27 | * @param ParamConverter[] $paramConverters 28 | */ 29 | public function __construct(array $paramConverters = array()) 30 | { 31 | $this->paramConverters = $paramConverters; 32 | } 33 | 34 | /** 35 | * @param ParamConverter $paramConverter 36 | */ 37 | public function addParamConverter(ParamConverter $paramConverter) 38 | { 39 | $this->paramConverters[] = $paramConverter; 40 | } 41 | 42 | /** 43 | * @param FilterControllerEvent $event 44 | */ 45 | public function onKernelController(FilterControllerEvent $event) 46 | { 47 | $controller = $event->getController(); 48 | $request = $event->getRequest(); 49 | 50 | if (is_array($controller)) { 51 | $reflection = new \ReflectionMethod($controller[0], $controller[1]); 52 | } elseif (is_string($controller)) { 53 | $reflection = new \ReflectionFunction($controller); 54 | } else { 55 | // use __invoke 56 | $objReflector = new \ReflectionObject($controller); 57 | $reflection = $objReflector->getMethod('__invoke'); 58 | } 59 | 60 | foreach ($reflection->getParameters() as $param) { 61 | if (!$param->getClass() || $param->getClass()->isInstance($request)) { 62 | continue; 63 | } 64 | 65 | $this->apply($request, $param); 66 | } 67 | } 68 | 69 | /** 70 | * @param Request $request 71 | * @param \ReflectionParameter $param 72 | */ 73 | private function apply(Request $request, \ReflectionParameter $param) 74 | { 75 | $class = $param->getClass()->getName(); 76 | 77 | foreach ($this->paramConverters as $paramConverter) { 78 | if (!$paramConverter->supports($class)) { 79 | continue; 80 | } 81 | 82 | $request->attributes->set($param->getName(), $paramConverter->getObject($request)); 83 | 84 | break; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/EventListener/RedirectListener.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class RedirectListener 14 | { 15 | private $urlGenerator; 16 | 17 | public function __construct(UrlGeneratorInterface $urlGenerator) 18 | { 19 | $this->urlGenerator = $urlGenerator; 20 | } 21 | 22 | public function onKernelView(GetResponseForControllerResultEvent $event) 23 | { 24 | $result = $event->getControllerResult(); 25 | 26 | if (!$result instanceof Redirect) { 27 | return; 28 | } 29 | 30 | $event->setResponse(new RedirectResponse( 31 | $this->urlGenerator->generate($result->getRoute(), $result->getParameters()), 32 | $result->getStatusCode() 33 | )); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/EventListener/SerializerViewListener.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class SerializerViewListener extends ViewListener 13 | { 14 | private $serializer; 15 | 16 | /** 17 | * @param JMSSerializer|SymfonySerializer $serializer 18 | */ 19 | public function __construct($serializer) 20 | { 21 | if (!$serializer instanceof SymfonySerializer && !$serializer instanceof JMSSerializer) { 22 | throw new \InvalidArgumentException('Serializer must be instance of Symfony\Component\Serializer\Serializer or JMS\Serializer\SerializerInterface.'); 23 | } 24 | 25 | $this->serializer = $serializer; 26 | } 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | protected function supports(View $view, $format) 32 | { 33 | return 'html' !== $format; 34 | } 35 | 36 | /** 37 | * {@inheritdoc} 38 | */ 39 | protected function getContent(View $view, $format) 40 | { 41 | return $this->serializer->serialize($view->getData(), $format); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/EventListener/TemplatingViewListener.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class TemplatingViewListener extends ViewListener 12 | { 13 | protected $templating; 14 | 15 | public function __construct(EngineInterface $templating) 16 | { 17 | $this->templating = $templating; 18 | } 19 | 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | protected function getContent(View $view, $format) 24 | { 25 | $template = $view->getTemplate(); 26 | 27 | if (is_array($template)) { 28 | foreach ($template as $t) { 29 | if ($this->templating->exists($t)) { 30 | $template = $t; 31 | break; 32 | } 33 | } 34 | } 35 | 36 | return $this->templating->render($template, $view->getDataAsArray()); 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | protected function supports(View $view, $format) 43 | { 44 | return null !== $view->getTemplate(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/EventListener/TwigViewListener.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class TwigViewListener extends ViewListener 11 | { 12 | protected $twig; 13 | 14 | public function __construct(\Twig_Environment $twig) 15 | { 16 | $this->twig = $twig; 17 | } 18 | 19 | /** 20 | * {@inheritdoc} 21 | */ 22 | protected function getContent(View $view, $format) 23 | { 24 | $template = $this->twig->resolveTemplate($view->getTemplate()); 25 | 26 | return $template->render($view->getDataAsArray()); 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | protected function supports(View $view, $format) 33 | { 34 | return null !== $view->getTemplate(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/EventListener/ViewListener.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | abstract class ViewListener 13 | { 14 | public function onKernelView(GetResponseForControllerResultEvent $event) 15 | { 16 | $result = $event->getControllerResult(); 17 | $format = $event->getRequest()->getRequestFormat(); 18 | 19 | if (!$result instanceof View) { 20 | return; 21 | } 22 | 23 | if (!$this->supports($result, $format)) { 24 | return; 25 | } 26 | 27 | $response = Response::create( 28 | $this->getContent($result, $format), 29 | $result->getStatusCode(), 30 | $result->getHeaders() 31 | ) 32 | ->setCache($result->getCache()) 33 | ; 34 | 35 | $event->setResponse($response); 36 | } 37 | 38 | /** 39 | * @param View $view 40 | * @param string $format 41 | * 42 | * @return bool 43 | */ 44 | abstract protected function supports(View $view, $format); 45 | 46 | /** 47 | * @param View $view 48 | * @param string $format 49 | * 50 | * @return string 51 | */ 52 | abstract protected function getContent(View $view, $format); 53 | } 54 | -------------------------------------------------------------------------------- /src/FlashRedirect.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class FlashRedirect extends Redirect implements HasFlashes 9 | { 10 | private $flashes; 11 | 12 | /** 13 | * @param string $route 14 | * @param array $parameters 15 | * @param string $message 16 | * @param string $type 17 | * @param int $statusCode 18 | * 19 | * @return static 20 | */ 21 | public static function create($route, array $parameters, $message, $type = self::DEFAULT_FLASH_KEY, $statusCode = self::DEFAULT_STATUS_CODE) 22 | { 23 | return new static($route, $parameters, array($type => array($message)), $statusCode); 24 | } 25 | 26 | /** 27 | * @param string $route 28 | * @param string $message 29 | * @param string $type 30 | * @param int $statusCode 31 | * 32 | * @return static 33 | */ 34 | public static function createSimple($route, $message, $type = self::DEFAULT_FLASH_KEY, $statusCode = self::DEFAULT_STATUS_CODE) 35 | { 36 | return new static($route, array(), array($type => array($message)), $statusCode); 37 | } 38 | 39 | /** 40 | * @param string $route 41 | * @param array $parameters 42 | * @param array $flashes 43 | * @param int $statusCode 44 | */ 45 | public function __construct($route, array $parameters = array(), array $flashes = array(), $statusCode = self::DEFAULT_STATUS_CODE) 46 | { 47 | $this->flashes = $flashes; 48 | 49 | parent::__construct($route, $parameters, $statusCode); 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | public function getFlashes() 56 | { 57 | return $this->flashes; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Forward.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class Forward 9 | { 10 | private $controller; 11 | private $parameters; 12 | 13 | /** 14 | * @param string $controller 15 | * @param array $parameters 16 | */ 17 | public function __construct($controller, array $parameters = array()) 18 | { 19 | $this->controller = $controller; 20 | $this->parameters = $parameters; 21 | } 22 | 23 | /** 24 | * @return string 25 | */ 26 | public function getController() 27 | { 28 | return $this->controller; 29 | } 30 | 31 | /** 32 | * @return array 33 | */ 34 | public function getParameters() 35 | { 36 | return $this->parameters; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/HasFlashes.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | interface HasFlashes 9 | { 10 | const DEFAULT_FLASH_KEY = 'info'; 11 | 12 | /** 13 | * Returns the array of flashes. 14 | * 15 | * Example: 16 | * 17 | * array( 18 | * 'info' => array( 19 | * 'Thank you.' 20 | * ) 21 | * ) 22 | * 23 | * @return array 24 | */ 25 | public function getFlashes(); 26 | } 27 | -------------------------------------------------------------------------------- /src/ParamConverter/FlashBagParamConverter.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class FlashBagParamConverter implements ParamConverter 12 | { 13 | private $flashBag; 14 | 15 | public function __construct(FlashBagInterface $flashBag) 16 | { 17 | $this->flashBag = $flashBag; 18 | } 19 | 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | public function getObject(Request $request) 24 | { 25 | return $this->flashBag; 26 | } 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | public function supports($class) 32 | { 33 | return is_subclass_of($class, 'Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface') || 34 | $class === 'Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ParamConverter/FormFactoryParamConverter.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class FormFactoryParamConverter implements ParamConverter 12 | { 13 | private $formFactory; 14 | 15 | public function __construct(FormFactoryInterface $formFactory) 16 | { 17 | $this->formFactory = $formFactory; 18 | } 19 | 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | public function getObject(Request $request) 24 | { 25 | return $this->formFactory; 26 | } 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | public function supports($class) 32 | { 33 | return is_subclass_of($class, 'Symfony\\Component\\Form\\FormFactoryInterface') || 34 | $class === 'Symfony\\Component\\Form\\FormFactoryInterface'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ParamConverter/ParamConverter.php: -------------------------------------------------------------------------------- 1 | 12 | * @author Kevin Bond 13 | * 14 | * @see https://github.com/sensiolabs/SensioFrameworkExtraBundle 15 | */ 16 | interface ParamConverter 17 | { 18 | /** 19 | * Returns the object to add to the request. 20 | * 21 | * @param Request $request The request 22 | * 23 | * @return object 24 | */ 25 | public function getObject(Request $request); 26 | 27 | /** 28 | * Checks if the class is supported. 29 | * 30 | * @param string $class The parameter class 31 | * 32 | * @return bool True if the object is supported, else false 33 | */ 34 | public function supports($class); 35 | } 36 | -------------------------------------------------------------------------------- /src/ParamConverter/SecurityContextParamConverter.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class SecurityContextParamConverter implements ParamConverter 12 | { 13 | private $securityContext; 14 | 15 | public function __construct(SecurityContextInterface $securityContext) 16 | { 17 | $this->securityContext = $securityContext; 18 | } 19 | 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | public function getObject(Request $request) 24 | { 25 | return $this->securityContext; 26 | } 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | public function supports($class) 32 | { 33 | return is_subclass_of($class, 'Symfony\\Component\\Security\\Core\\SecurityContextInterface') || 34 | $class === 'Symfony\\Component\\Security\\Core\\SecurityContextInterface'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ParamConverter/SessionParamConverter.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class SessionParamConverter implements ParamConverter 11 | { 12 | /** 13 | * {@inheritdoc} 14 | */ 15 | public function getObject(Request $request) 16 | { 17 | return $request->getSession(); 18 | } 19 | 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | public function supports($class) 24 | { 25 | return is_subclass_of($class, 'Symfony\\Component\\HttpFoundation\\Session\\SessionInterface') || 26 | $class === 'Symfony\\Component\\HttpFoundation\\Session\\SessionInterface'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Redirect.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class Redirect 9 | { 10 | const DEFAULT_STATUS_CODE = 302; 11 | 12 | private $route; 13 | private $parameters; 14 | private $statusCode; 15 | 16 | /** 17 | * @param string $route 18 | * @param array $parameters 19 | * @param int $statusCode 20 | */ 21 | public function __construct($route, array $parameters = array(), $statusCode = self::DEFAULT_STATUS_CODE) 22 | { 23 | $this->route = $route; 24 | $this->parameters = $parameters; 25 | $this->statusCode = $statusCode; 26 | } 27 | 28 | /** 29 | * @return string 30 | */ 31 | public function getRoute() 32 | { 33 | return $this->route; 34 | } 35 | 36 | /** 37 | * @return array 38 | */ 39 | public function getParameters() 40 | { 41 | return $this->parameters; 42 | } 43 | 44 | /** 45 | * @return int 46 | */ 47 | public function getStatusCode() 48 | { 49 | return $this->statusCode; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Template.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Template extends View 11 | { 12 | /** 13 | * @param string|array $template 14 | * @param int $sharedMaxAge 15 | * @param array $parameters 16 | * @param int $statusCode 17 | * 18 | * @return static 19 | */ 20 | public static function createCached($template, $sharedMaxAge, $parameters = array(), $statusCode = self::DEFAULT_STATUS_CODE) 21 | { 22 | return new static($template, $parameters, $statusCode, array('s_maxage' => $sharedMaxAge)); 23 | } 24 | 25 | /** 26 | * @param string|array $template 27 | * @param mixed $parameters 28 | * @param int $statusCode 29 | * @param array $cache 30 | * @param array $headers 31 | */ 32 | public function __construct( 33 | $template, 34 | $parameters = array(), 35 | $statusCode = self::DEFAULT_STATUS_CODE, 36 | array $cache = array(), 37 | array $headers = array() 38 | ) { 39 | parent::__construct($parameters, $statusCode, $template, $cache, $headers); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/View.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class View 9 | { 10 | const DEFAULT_STATUS_CODE = 200; 11 | 12 | private $data; 13 | private $statusCode; 14 | private $template; 15 | private $cache; 16 | private $headers; 17 | 18 | /** 19 | * @param mixed $data 20 | * @param int $sharedMaxAge 21 | * @param int $statusCode 22 | * 23 | * @return static 24 | */ 25 | public static function createCached($data, $sharedMaxAge, $statusCode = self::DEFAULT_STATUS_CODE) 26 | { 27 | return new static($data, $statusCode, null, array('s_maxage' => $sharedMaxAge)); 28 | } 29 | 30 | /** 31 | * @param mixed|null $data 32 | * @param int $statusCode 33 | * @param string|array $template 34 | * @param array $cache 35 | * @param array $headers 36 | */ 37 | public function __construct( 38 | $data = null, 39 | $statusCode = self::DEFAULT_STATUS_CODE, 40 | $template = null, 41 | array $cache = array(), 42 | array $headers = array() 43 | ) { 44 | $this->data = $data; 45 | $this->statusCode = $statusCode; 46 | $this->template = $template; 47 | $this->cache = $cache; 48 | $this->headers = $headers; 49 | } 50 | 51 | /** 52 | * @return mixed|null 53 | */ 54 | public function getData() 55 | { 56 | return $this->data; 57 | } 58 | 59 | /** 60 | * @param string $key The key to use when creating array if data isn't already an array 61 | * 62 | * @return array 63 | */ 64 | public function getDataAsArray($key = 'data') 65 | { 66 | $data = $this->getData(); 67 | 68 | if (null === $data) { 69 | return array(); 70 | } 71 | 72 | if (!is_array($data)) { 73 | $data = array($key => $data); 74 | } 75 | 76 | return $data; 77 | } 78 | 79 | /** 80 | * @return int 81 | */ 82 | public function getStatusCode() 83 | { 84 | return $this->statusCode; 85 | } 86 | 87 | /** 88 | * Either the template name or an array of templates. 89 | * 90 | * @return string|array|null 91 | */ 92 | public function getTemplate() 93 | { 94 | return $this->template; 95 | } 96 | 97 | /** 98 | * Returns an array of cache options. 99 | * 100 | * @see Symfony\Component\HttpFoundation\Response::setCache 101 | * 102 | * @return array 103 | */ 104 | public function getCache() 105 | { 106 | return $this->cache; 107 | } 108 | 109 | /** 110 | * @return array 111 | */ 112 | public function getHeaders() 113 | { 114 | return $this->headers; 115 | } 116 | } 117 | --------------------------------------------------------------------------------