PageRenderTime 41ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/symfony-cmf/routing/ContentAwareGenerator.php

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