PageRenderTime 46ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Classes/TYPO3/FLOW3/Mvc/Routing/IdentityRoutePart.php

https://github.com/christianjul/FLOW3-Composer
PHP | 286 lines | 148 code | 25 blank | 113 comment | 26 complexity | e38b9e8d9e8a9aff8db87f0cd1f00702 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0
  1. <?php
  2. namespace TYPO3\FLOW3\Mvc\Routing;
  3. /* *
  4. * This script belongs to the FLOW3 framework. *
  5. * *
  6. * It is free software; you can redistribute it and/or modify it under *
  7. * the terms of the GNU Lesser General Public License, either version 3 *
  8. * of the License, or (at your option) any later version. *
  9. * *
  10. * The TYPO3 project - inspiring people to share! *
  11. * */
  12. use TYPO3\FLOW3\Annotations as FLOW3;
  13. /**
  14. * Identity Route Part
  15. * This route part can be used to create and resolve ObjectPathMappings.
  16. * This handler is used by default, if an objectType is specified for a route part in the routing configuration:
  17. * -
  18. * name: 'Some route for xyz entities'
  19. * uriPattern: '{xyz}'
  20. * routeParts:
  21. * xyz:
  22. * objectType: Some\Package\Domain\Model\Xyz
  23. *
  24. * @see \TYPO3\FLOW3\Mvc\Routing\ObjectPathMapping
  25. * @api
  26. */
  27. class IdentityRoutePart extends \TYPO3\FLOW3\Mvc\Routing\DynamicRoutePart {
  28. /**
  29. * @var \TYPO3\FLOW3\Persistence\PersistenceManagerInterface
  30. * @FLOW3\Inject
  31. */
  32. protected $persistenceManager;
  33. /**
  34. * @var \TYPO3\FLOW3\Reflection\ReflectionService
  35. * @FLOW3\Inject
  36. */
  37. protected $reflectionService;
  38. /**
  39. * @var \TYPO3\FLOW3\Mvc\Routing\ObjectPathMappingRepository
  40. * @FLOW3\Inject
  41. */
  42. protected $objectPathMappingRepository;
  43. /**
  44. * The object type (class name) of the entity this route part belongs to
  45. *
  46. * @var string
  47. */
  48. protected $objectType;
  49. /**
  50. * pattern for the URI representation (for example "{date:Y}/{date:m}/{date.d}/{title}")
  51. *
  52. * @var string
  53. */
  54. protected $uriPattern = NULL;
  55. /**
  56. * @param string $objectType
  57. * @return void
  58. */
  59. public function setObjectType($objectType) {
  60. $this->objectType = $objectType;
  61. }
  62. /**
  63. * @return string
  64. */
  65. public function getObjectType() {
  66. return $this->objectType;
  67. }
  68. /**
  69. * @param string $uriPattern
  70. * @return void
  71. */
  72. public function setUriPattern($uriPattern) {
  73. $this->uriPattern = $uriPattern;
  74. }
  75. /**
  76. * If $this->uriPattern is specified, this will be returned, otherwise identity properties of $this->objectType
  77. * are returned in the format {property1}/{property2}/{property3}.
  78. * If $this->objectType does not contain identity properties, an empty string is returned.
  79. *
  80. * @return string
  81. */
  82. public function getUriPattern() {
  83. if ($this->uriPattern === NULL) {
  84. $classSchema = $this->reflectionService->getClassSchema($this->objectType);
  85. $identityProperties = $classSchema->getIdentityProperties();
  86. if (count($identityProperties) === 0) {
  87. $this->uriPattern = '';
  88. } else {
  89. $this->uriPattern = '{' . implode('}/{', array_keys($identityProperties)) . '}';
  90. }
  91. }
  92. return $this->uriPattern;
  93. }
  94. /**
  95. * Checks, whether given value can be matched.
  96. * If the value is empty, FALSE is returned.
  97. * Otherwise the ObjectPathMappingRepository is asked for a matching ObjectPathMapping.
  98. * If that is found the identifier is stored in $this->value, otherwise this route part does not match.
  99. *
  100. * @param string $value value to match
  101. * @return boolean TRUE if value could be matched successfully, otherwise FALSE.
  102. * @api
  103. * @todo make findOneByObjectTypeUriPatternAndPathSegment case sensitive if lowerCase = FALSE (this is not yet supported by the persistence)
  104. */
  105. protected function matchValue($value) {
  106. if ($value === NULL || $value === '') {
  107. return FALSE;
  108. }
  109. $objectPathMapping = $this->objectPathMappingRepository->findOneByObjectTypeUriPatternAndPathSegment($this->objectType, $this->getUriPattern(), $value);
  110. if ($objectPathMapping === NULL) {
  111. return FALSE;
  112. }
  113. $this->value = array('__identity' => $objectPathMapping->getIdentifier());
  114. return TRUE;
  115. }
  116. /**
  117. * Returns the first part of $routePath that should be evaluated in matchValue().
  118. * If not split string is set (route part is the last in the routes uriPattern), the complete $routePart is returned.
  119. * Otherwise the part is returned that matches the specified uriPattern of this route part.
  120. *
  121. * @param string $routePath The request path to be matched
  122. * @return string value to match, or an empty string if $routePath is empty, split string was not found or uriPattern could not be matched
  123. * @api
  124. */
  125. protected function findValueToMatch($routePath) {
  126. if (!isset($routePath) || $routePath === '' || $routePath[0] === '/') {
  127. return '';
  128. }
  129. $uriPattern = $this->getUriPattern();
  130. if ($uriPattern === '') {
  131. return '';
  132. }
  133. $regexPattern = preg_quote($uriPattern, '/');
  134. $regexPattern = preg_replace('/\\\\{[^}]+\\\\}/', '[^\/]+', $regexPattern);
  135. if ($this->splitString !== '') {
  136. $regexPattern .= '(?=' . preg_quote($this->splitString, '/') . ')';
  137. }
  138. $matches = array();
  139. preg_match('/^' . $regexPattern . '/', trim($routePath, '/'), $matches);
  140. return isset($matches[0]) ? $matches[0] : '';
  141. }
  142. /**
  143. * Resolves the given entity and sets the value to a URI representation (path segment) that matches $this->uriPattern and is unique for the given object.
  144. *
  145. * @param mixed $value
  146. * @return boolean TRUE if the object could be resolved and stored in $this->value, otherwise FALSE.
  147. * @throws \TYPO3\FLOW3\Mvc\Exception\InfiniteLoopException if no unique path segment could be found after 100 iterations
  148. */
  149. protected function resolveValue($value) {
  150. if (is_array($value) && isset($value['__identity'])) {
  151. $identifier = $value['__identity'];
  152. } elseif ($value instanceof $this->objectType) {
  153. $identifier = $this->persistenceManager->getIdentifierByObject($value);
  154. } else {
  155. return FALSE;
  156. }
  157. $objectPathMapping = $this->objectPathMappingRepository->findOneByObjectTypeUriPatternAndIdentifier($this->objectType, $this->getUriPattern(), $identifier);
  158. if ($objectPathMapping !== NULL) {
  159. $this->value = $objectPathMapping->getPathSegment();
  160. return TRUE;
  161. }
  162. $pathSegment = $uniquePathSegment = $this->createPathSegmentForObject($value);
  163. $pathSegmentLoopCount = 0;
  164. do {
  165. if ($pathSegmentLoopCount++ > 99) {
  166. throw new \TYPO3\FLOW3\Mvc\Exception\InfiniteLoopException('No unique path segment could be found after ' . ($pathSegmentLoopCount - 1) . ' iterations.', 1316441798);
  167. }
  168. if ($uniquePathSegment !== '') {
  169. $objectPathMapping = $this->objectPathMappingRepository->findOneByObjectTypeUriPatternAndPathSegment($this->objectType, $this->getUriPattern(), $uniquePathSegment);
  170. if ($objectPathMapping === NULL) {
  171. $this->storeObjectPathMapping($uniquePathSegment, $identifier);
  172. break;
  173. }
  174. }
  175. $uniquePathSegment = sprintf('%s-%d', $pathSegment, $pathSegmentLoopCount);
  176. } while (TRUE);
  177. $this->value = $uniquePathSegment;
  178. return TRUE;
  179. }
  180. /**
  181. * Creates a URI representation (path segment) for the given object matching $this->uriPattern.
  182. *
  183. * @param mixed $object object of type $this->objectType
  184. * @return string URI representation (path segment) of the given object
  185. * @throws \TYPO3\FLOW3\Mvc\Exception\InvalidUriPatternException
  186. */
  187. protected function createPathSegmentForObject($object) {
  188. $uriPattern = $this->getUriPattern();
  189. if ($uriPattern === '') {
  190. return $this->rewriteForUri($this->persistenceManager->getIdentifierByObject($object));
  191. }
  192. $matches = array();
  193. preg_match_all('/(?P<dynamic>{?)(?P<content>[^}{]+)}?/', $uriPattern, $matches, PREG_SET_ORDER);
  194. $pathSegment = '';
  195. foreach ($matches as $match) {
  196. if (empty($match['dynamic'])) {
  197. $pathSegment .= $match['content'];
  198. } else {
  199. $dynamicPathSegmentParts = explode(':', $match['content']);
  200. $propertyPath = $dynamicPathSegmentParts[0];
  201. $dynamicPathSegment = \TYPO3\FLOW3\Reflection\ObjectAccess::getPropertyPath($object, $propertyPath);
  202. if (is_object($dynamicPathSegment)) {
  203. if ($dynamicPathSegment instanceof \DateTime) {
  204. $dateFormat = isset($dynamicPathSegmentParts[1]) ? trim($dynamicPathSegmentParts[1]) : 'Y-m-d';
  205. $pathSegment .= $this->rewriteForUri($dynamicPathSegment->format($dateFormat));
  206. } else {
  207. throw new \TYPO3\FLOW3\Mvc\Exception\InvalidUriPatternException('Invalid uriPattern "' . $uriPattern . '" for route part "' . $this->getName() . '". Property "' . $propertyPath . '" must be of type string or \DateTime. "' . (is_object($dynamicPathSegment) ? get_class($dynamicPathSegment) : gettype($dynamicPathSegment)) . '" given.', 1316442409);
  208. }
  209. } else {
  210. $pathSegment .= $this->rewriteForUri($dynamicPathSegment);
  211. }
  212. }
  213. }
  214. return $pathSegment;
  215. }
  216. /**
  217. * Creates a new ObjectPathMapping and stores it in the repository
  218. *
  219. * @param string $pathSegment
  220. * @param mixed $identifier
  221. * @return void
  222. */
  223. protected function storeObjectPathMapping($pathSegment, $identifier) {
  224. $objectPathMapping = new \TYPO3\FLOW3\Mvc\Routing\ObjectPathMapping();
  225. $objectPathMapping->setObjectType($this->objectType);
  226. $objectPathMapping->setUriPattern($this->getUriPattern());
  227. $objectPathMapping->setPathSegment($pathSegment);
  228. $objectPathMapping->setIdentifier($identifier);
  229. $this->objectPathMappingRepository->add($objectPathMapping);
  230. // TODO can be removed, when persistence manager has some memory cache
  231. $this->persistenceManager->persistAll();
  232. }
  233. /**
  234. * Transforms the given string into a URI compatible format without special characters.
  235. * In the long term this should be done with proper transliteration
  236. *
  237. * @param string $value
  238. * @return string
  239. * @todo use transliteration of the I18n sub package
  240. */
  241. protected function rewriteForUri($value) {
  242. $transliteration = array(
  243. 'ä' => 'ae',
  244. 'Ä' => 'Ae',
  245. 'ö' => 'oe',
  246. 'Ö' => 'Oe',
  247. 'ü' => 'ue',
  248. 'Ü' => 'Ue',
  249. 'ß' => 'ss',
  250. );
  251. $value = strtr($value, $transliteration);
  252. $spaceCharacter = '-';
  253. $value = preg_replace('/[ \-+_]+/', $spaceCharacter, $value);
  254. $value = preg_replace('/[^-a-z0-9.\\' . $spaceCharacter . ']/i', '', $value);
  255. $value = preg_replace('/\\' . $spaceCharacter . '{2,}/', $spaceCharacter, $value);
  256. $value = trim($value, $spaceCharacter);
  257. return $value;
  258. }
  259. }
  260. ?>