annotate vendor/symfony-cmf/routing/ContentAwareGenerator.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /*
Chris@0 4 * This file is part of the Symfony CMF package.
Chris@0 5 *
Chris@0 6 * (c) 2011-2015 Symfony CMF
Chris@0 7 *
Chris@0 8 * For the full copyright and license information, please view the LICENSE
Chris@0 9 * file that was distributed with this source code.
Chris@0 10 */
Chris@0 11
Chris@0 12 namespace Symfony\Cmf\Component\Routing;
Chris@0 13
Chris@0 14 use Doctrine\Common\Collections\Collection;
Chris@0 15 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
Chris@0 16 use Symfony\Component\Routing\Route as SymfonyRoute;
Chris@0 17 use Symfony\Component\Routing\Exception\RouteNotFoundException;
Chris@0 18 use Symfony\Component\Routing\RouteCollection;
Chris@0 19
Chris@0 20 /**
Chris@0 21 * A generator that tries to generate routes from object, route names or
Chris@0 22 * content objects or names.
Chris@0 23 *
Chris@0 24 * @author Philippo de Santis
Chris@0 25 * @author David Buchmann
Chris@0 26 * @author Uwe Jäger
Chris@0 27 */
Chris@0 28 class ContentAwareGenerator extends ProviderBasedGenerator
Chris@0 29 {
Chris@0 30 /**
Chris@0 31 * The locale to use when neither the parameters nor the request context
Chris@0 32 * indicate the locale to use.
Chris@0 33 *
Chris@0 34 * @var string
Chris@0 35 */
Chris@0 36 protected $defaultLocale = null;
Chris@0 37
Chris@0 38 /**
Chris@0 39 * The content repository used to find content by it's id
Chris@0 40 * This can be used to specify a parameter content_id when generating urls.
Chris@0 41 *
Chris@0 42 * This is optional and might not be initialized.
Chris@0 43 *
Chris@0 44 * @var ContentRepositoryInterface
Chris@0 45 */
Chris@0 46 protected $contentRepository;
Chris@0 47
Chris@0 48 /**
Chris@0 49 * Set an optional content repository to find content by ids.
Chris@0 50 *
Chris@0 51 * @param ContentRepositoryInterface $contentRepository
Chris@0 52 */
Chris@0 53 public function setContentRepository(ContentRepositoryInterface $contentRepository)
Chris@0 54 {
Chris@0 55 $this->contentRepository = $contentRepository;
Chris@0 56 }
Chris@0 57
Chris@0 58 /**
Chris@0 59 * {@inheritdoc}
Chris@0 60 *
Chris@0 61 * @param string $name ignored.
Chris@0 62 * @param array $parameters must either contain the field 'route' with a
Chris@0 63 * RouteObjectInterface or the field 'content_id'
Chris@0 64 * with the id of a document implementing
Chris@0 65 * RouteReferrersReadInterface.
Chris@0 66 *
Chris@0 67 * @throws RouteNotFoundException If there is no such route in the database
Chris@0 68 */
Chris@0 69 public function generate($name, $parameters = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
Chris@0 70 {
Chris@0 71 if ($name instanceof SymfonyRoute) {
Chris@0 72 $route = $this->getBestLocaleRoute($name, $parameters);
Chris@0 73 } elseif (is_string($name) && $name) {
Chris@0 74 $route = $this->getRouteByName($name, $parameters);
Chris@0 75 } else {
Chris@0 76 $route = $this->getRouteByContent($name, $parameters);
Chris@0 77 }
Chris@0 78
Chris@0 79 if (!$route instanceof SymfonyRoute) {
Chris@0 80 $hint = is_object($route) ? get_class($route) : gettype($route);
Chris@0 81 throw new RouteNotFoundException('Route of this document is not an instance of Symfony\Component\Routing\Route but: '.$hint);
Chris@0 82 }
Chris@0 83
Chris@0 84 $this->unsetLocaleIfNotNeeded($route, $parameters);
Chris@0 85
Chris@0 86 return parent::generate($route, $parameters, $absolute);
Chris@0 87 }
Chris@0 88
Chris@0 89 /**
Chris@0 90 * Get the route by a string name.
Chris@0 91 *
Chris@0 92 * @param string $route
Chris@0 93 * @param array $parameters
Chris@0 94 *
Chris@0 95 * @return SymfonyRoute
Chris@0 96 *
Chris@0 97 * @throws RouteNotFoundException if there is no route found for the provided name
Chris@0 98 */
Chris@0 99 protected function getRouteByName($name, array $parameters)
Chris@0 100 {
Chris@0 101 $route = $this->provider->getRouteByName($name);
Chris@0 102 if (empty($route)) {
Chris@0 103 throw new RouteNotFoundException('No route found for name: '.$name);
Chris@0 104 }
Chris@0 105
Chris@0 106 return $this->getBestLocaleRoute($route, $parameters);
Chris@0 107 }
Chris@0 108
Chris@0 109 /**
Chris@0 110 * Determine if there is a route with matching locale associated with the
Chris@0 111 * given route via associated content.
Chris@0 112 *
Chris@0 113 * @param SymfonyRoute $route
Chris@0 114 * @param array $parameters
Chris@0 115 *
Chris@0 116 * @return SymfonyRoute either the passed route or an alternative with better locale
Chris@0 117 */
Chris@0 118 protected function getBestLocaleRoute(SymfonyRoute $route, $parameters)
Chris@0 119 {
Chris@0 120 if (!$route instanceof RouteObjectInterface) {
Chris@0 121 // this route has no content, we can't get the alternatives
Chris@0 122 return $route;
Chris@0 123 }
Chris@0 124 $locale = $this->getLocale($parameters);
Chris@0 125 if (!$this->checkLocaleRequirement($route, $locale)) {
Chris@0 126 $content = $route->getContent();
Chris@0 127 if ($content instanceof RouteReferrersReadInterface) {
Chris@0 128 $routes = $content->getRoutes();
Chris@0 129 $contentRoute = $this->getRouteByLocale($routes, $locale);
Chris@0 130 if ($contentRoute) {
Chris@0 131 return $contentRoute;
Chris@0 132 }
Chris@0 133 }
Chris@0 134 }
Chris@0 135
Chris@0 136 return $route;
Chris@0 137 }
Chris@0 138
Chris@0 139 /**
Chris@0 140 * Get the route based on the $name that is an object implementing
Chris@0 141 * RouteReferrersReadInterface or a content found in the content repository
Chris@0 142 * with the content_id specified in parameters that is an instance of
Chris@0 143 * RouteReferrersReadInterface.
Chris@0 144 *
Chris@0 145 * Called in generate when there is no route given in the parameters.
Chris@0 146 *
Chris@0 147 * If there is more than one route for the content, tries to find the
Chris@0 148 * first one that matches the _locale (provided in $parameters or otherwise
Chris@0 149 * defaulting to the request locale).
Chris@0 150 *
Chris@0 151 * If no route with matching locale is found, falls back to just return the
Chris@0 152 * first route.
Chris@0 153 *
Chris@0 154 * @param mixed $name
Chris@0 155 * @param array $parameters which should contain a content field containing
Chris@0 156 * a RouteReferrersReadInterface object
Chris@0 157 *
Chris@0 158 * @return SymfonyRoute the route instance
Chris@0 159 *
Chris@0 160 * @throws RouteNotFoundException if no route can be determined
Chris@0 161 */
Chris@0 162 protected function getRouteByContent($name, &$parameters)
Chris@0 163 {
Chris@0 164 if ($name instanceof RouteReferrersReadInterface) {
Chris@0 165 $content = $name;
Chris@0 166 } elseif (isset($parameters['content_id'])
Chris@0 167 && null !== $this->contentRepository
Chris@0 168 ) {
Chris@0 169 $content = $this->contentRepository->findById($parameters['content_id']);
Chris@0 170 if (empty($content)) {
Chris@0 171 throw new RouteNotFoundException('The content repository found nothing at id '.$parameters['content_id']);
Chris@0 172 }
Chris@0 173 if (!$content instanceof RouteReferrersReadInterface) {
Chris@0 174 throw new RouteNotFoundException('Content repository did not return a RouteReferrersReadInterface instance for id '.$parameters['content_id']);
Chris@0 175 }
Chris@0 176 } else {
Chris@0 177 $hint = is_object($name) ? get_class($name) : gettype($name);
Chris@0 178 throw new RouteNotFoundException("The route name argument '$hint' is not RouteReferrersReadInterface instance and there is no 'content_id' parameter");
Chris@0 179 }
Chris@0 180
Chris@0 181 $routes = $content->getRoutes();
Chris@0 182 if (empty($routes)) {
Chris@0 183 $hint = ($this->contentRepository && $this->contentRepository->getContentId($content))
Chris@0 184 ? $this->contentRepository->getContentId($content)
Chris@0 185 : get_class($content);
Chris@0 186 throw new RouteNotFoundException('Content document has no route: '.$hint);
Chris@0 187 }
Chris@0 188
Chris@0 189 unset($parameters['content_id']);
Chris@0 190
Chris@0 191 $route = $this->getRouteByLocale($routes, $this->getLocale($parameters));
Chris@0 192 if ($route) {
Chris@0 193 return $route;
Chris@0 194 }
Chris@0 195
Chris@0 196 // if none matched, randomly return the first one
Chris@0 197 if ($routes instanceof Collection) {
Chris@0 198 return $routes->first();
Chris@0 199 }
Chris@0 200
Chris@0 201 return reset($routes);
Chris@0 202 }
Chris@0 203
Chris@0 204 /**
Chris@0 205 * @param RouteCollection $routes
Chris@0 206 * @param string $locale
Chris@0 207 *
Chris@0 208 * @return bool|SymfonyRoute false if no route requirement matches the provided locale
Chris@0 209 */
Chris@0 210 protected function getRouteByLocale($routes, $locale)
Chris@0 211 {
Chris@0 212 foreach ($routes as $route) {
Chris@0 213 if (!$route instanceof SymfonyRoute) {
Chris@0 214 continue;
Chris@0 215 }
Chris@0 216
Chris@0 217 if ($this->checkLocaleRequirement($route, $locale)) {
Chris@0 218 return $route;
Chris@0 219 }
Chris@0 220 }
Chris@0 221
Chris@0 222 return false;
Chris@0 223 }
Chris@0 224
Chris@0 225 /**
Chris@0 226 * @param SymfonyRoute $route
Chris@0 227 * @param string $locale
Chris@0 228 *
Chris@0 229 * @return bool true if there is either no $locale, no _locale requirement
Chris@0 230 * on the route or if the requirement and the passed $locale
Chris@0 231 * match.
Chris@0 232 */
Chris@0 233 private function checkLocaleRequirement(SymfonyRoute $route, $locale)
Chris@0 234 {
Chris@0 235 return empty($locale)
Chris@0 236 || !$route->getRequirement('_locale')
Chris@0 237 || preg_match('/'.$route->getRequirement('_locale').'/', $locale)
Chris@0 238 ;
Chris@0 239 }
Chris@0 240
Chris@0 241 /**
Chris@0 242 * Determine the locale to be used with this request.
Chris@0 243 *
Chris@0 244 * @param array $parameters the parameters determined by the route
Chris@0 245 *
Chris@0 246 * @return string the locale following of the parameters or any other
Chris@0 247 * information the router has available. defaultLocale if no
Chris@0 248 * other locale can be determined.
Chris@0 249 */
Chris@0 250 protected function getLocale($parameters)
Chris@0 251 {
Chris@0 252 if (isset($parameters['_locale'])) {
Chris@0 253 return $parameters['_locale'];
Chris@0 254 }
Chris@0 255
Chris@0 256 if ($this->getContext()->hasParameter('_locale')) {
Chris@0 257 return $this->getContext()->getParameter('_locale');
Chris@0 258 }
Chris@0 259
Chris@0 260 return $this->defaultLocale;
Chris@0 261 }
Chris@0 262
Chris@0 263 /**
Chris@0 264 * Overwrite the locale to be used by default if there is neither one in
Chris@0 265 * the parameters when building the route nor a request available (i.e. CLI).
Chris@0 266 *
Chris@0 267 * @param string $locale
Chris@0 268 */
Chris@0 269 public function setDefaultLocale($locale)
Chris@0 270 {
Chris@0 271 $this->defaultLocale = $locale;
Chris@0 272 }
Chris@0 273
Chris@0 274 /**
Chris@0 275 * We additionally support empty name and data in parameters and RouteAware content.
Chris@0 276 */
Chris@0 277 public function supports($name)
Chris@0 278 {
Chris@0 279 return !$name || parent::supports($name) || $name instanceof RouteReferrersReadInterface;
Chris@0 280 }
Chris@0 281
Chris@0 282 /**
Chris@0 283 * {@inheritdoc}
Chris@0 284 */
Chris@0 285 public function getRouteDebugMessage($name, array $parameters = array())
Chris@0 286 {
Chris@0 287 if (empty($name) && isset($parameters['content_id'])) {
Chris@0 288 return 'Content id '.$parameters['content_id'];
Chris@0 289 }
Chris@0 290
Chris@0 291 if ($name instanceof RouteReferrersReadInterface) {
Chris@0 292 return 'Route aware content '.parent::getRouteDebugMessage($name, $parameters);
Chris@0 293 }
Chris@0 294
Chris@0 295 return parent::getRouteDebugMessage($name, $parameters);
Chris@0 296 }
Chris@0 297
Chris@0 298 /**
Chris@0 299 * If the _locale parameter is allowed by the requirements of the route
Chris@0 300 * and it is the default locale, remove it from the parameters so that we
Chris@0 301 * do not get an unneeded ?_locale= query string.
Chris@0 302 *
Chris@0 303 * @param SymfonyRoute $route The route being generated.
Chris@0 304 * @param array $parameters The parameters used, will be modified to
Chris@0 305 * remove the _locale field if needed.
Chris@0 306 */
Chris@0 307 protected function unsetLocaleIfNotNeeded(SymfonyRoute $route, array &$parameters)
Chris@0 308 {
Chris@0 309 $locale = $this->getLocale($parameters);
Chris@0 310 if (null !== $locale) {
Chris@0 311 if (preg_match('/'.$route->getRequirement('_locale').'/', $locale)
Chris@0 312 && $locale == $route->getDefault('_locale')
Chris@0 313 ) {
Chris@0 314 $compiledRoute = $route->compile();
Chris@0 315 if (!in_array('_locale', $compiledRoute->getVariables())) {
Chris@0 316 unset($parameters['_locale']);
Chris@0 317 }
Chris@0 318 }
Chris@0 319 }
Chris@0 320 }
Chris@0 321 }