vendor/sulu/sulu/src/Sulu/Component/Content/Metadata/Factory/StructureMetadataFactory.php line 89

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of Sulu.
  4. *
  5. * (c) Sulu GmbH
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this source code in the file LICENSE.
  9. */
  10. namespace Sulu\Component\Content\Metadata\Factory;
  11. use Doctrine\Inflector\Inflector;
  12. use Doctrine\Inflector\InflectorFactory;
  13. use Sulu\Component\Content\Metadata\Factory\Exception\DocumentTypeNotFoundException;
  14. use Sulu\Component\Content\Metadata\Factory\Exception\StructureTypeNotFoundException;
  15. use Symfony\Component\Config\ConfigCache;
  16. use Symfony\Component\Config\FileLocator;
  17. use Symfony\Component\Config\Loader\LoaderInterface;
  18. use Symfony\Component\Config\Resource\FileResource;
  19. /**
  20. * Create new (mapped) structures using the provided loader.
  21. */
  22. class StructureMetadataFactory implements StructureMetadataFactoryInterface
  23. {
  24. /**
  25. * @var array
  26. */
  27. private $cache = [];
  28. /**
  29. * @var Inflector
  30. */
  31. private $inflector;
  32. /**
  33. * @param string $cachePath
  34. * @param bool $debug
  35. * @param array<string, string> $defaultTypes
  36. */
  37. public function __construct(
  38. private LoaderInterface $loader,
  39. private array $typePaths,
  40. private array $defaultTypes,
  41. private $cachePath,
  42. private $debug = false
  43. ) {
  44. $this->inflector = InflectorFactory::create()->build();
  45. }
  46. public function getStructureMetadata($type, $structureType = null)
  47. {
  48. $cacheKey = $type . $structureType;
  49. if (isset($this->cache[$cacheKey])) {
  50. return $this->cache[$cacheKey];
  51. }
  52. $this->assertExists($type);
  53. if (!$structureType) {
  54. $structureType = $this->getDefaultStructureType($type);
  55. }
  56. if (!$structureType) {
  57. return null;
  58. }
  59. $cachePath = \sprintf(
  60. '%s%s%s%s',
  61. $this->cachePath,
  62. \DIRECTORY_SEPARATOR,
  63. $this->inflector->camelize($type),
  64. $this->inflector->camelize($structureType)
  65. );
  66. $cache = new ConfigCache($cachePath, $this->debug);
  67. if ($this->debug || !$cache->isFresh()) {
  68. $paths = $this->getPaths($type);
  69. // reverse paths, so that the last path overrides previous ones
  70. $fileLocator = new FileLocator(\array_reverse($paths));
  71. try {
  72. $filePath = $fileLocator->locate(\sprintf('%s.xml', $structureType));
  73. } catch (\InvalidArgumentException $e) {
  74. throw new StructureTypeNotFoundException(
  75. \sprintf(
  76. 'Could not load structure type "%s" for document type "%s", looked in "%s"',
  77. $structureType,
  78. $type,
  79. \implode('", "', $paths)
  80. ), null, $e
  81. );
  82. }
  83. $metadata = $this->loader->load($filePath, $type);
  84. $resources = [new FileResource($filePath)];
  85. $cache->write(
  86. \serialize($metadata),
  87. $resources
  88. );
  89. }
  90. $structure = \unserialize(\file_get_contents($cachePath));
  91. $this->cache[$cacheKey] = $structure;
  92. return $structure;
  93. }
  94. public function getStructures($type)
  95. {
  96. $structureNames = $this->getStructureNames($type);
  97. $structures = [];
  98. foreach ($structureNames as $structureName) {
  99. $structures[] = $this->getStructureMetadata($type, $structureName);
  100. }
  101. return $structures;
  102. }
  103. public function getStructureTypes(): array
  104. {
  105. return \array_keys($this->typePaths);
  106. }
  107. public function hasStructuresFor($type)
  108. {
  109. return isset($this->typePaths[$type]);
  110. }
  111. /**
  112. * Return the structure names for the given type
  113. * (not necessarily valid).
  114. *
  115. * @param string $type
  116. *
  117. * @return string[]
  118. */
  119. private function getStructureNames($type)
  120. {
  121. $this->assertExists($type);
  122. $structureNames = [];
  123. foreach ($this->typePaths[$type] as $pathConfig) {
  124. $structurePath = $pathConfig['path'];
  125. // Ignore not-existing paths
  126. if (!\file_exists($structurePath)) {
  127. continue;
  128. }
  129. $iterator = new \DirectoryIterator($structurePath);
  130. foreach ($iterator as $file) {
  131. $ext = $file->getExtension();
  132. if ('xml' !== $ext) {
  133. continue;
  134. }
  135. $structureNames[] = $file->getBasename('.' . $ext);
  136. }
  137. }
  138. return $structureNames;
  139. }
  140. /**
  141. * Assert type exists.
  142. *
  143. * @param string $type
  144. *
  145. * @return void
  146. */
  147. private function assertExists($type)
  148. {
  149. if (!isset($this->typePaths[$type])) {
  150. throw new DocumentTypeNotFoundException(
  151. \sprintf(
  152. 'Structure path for document type "%s" is not mapped. Mapped structure types: "%s"',
  153. $type,
  154. \implode('", "', \array_keys($this->typePaths))
  155. )
  156. );
  157. }
  158. }
  159. /**
  160. * Get the paths from the type path configuration.
  161. *
  162. * @param string $type
  163. *
  164. * @return array<string>
  165. */
  166. private function getPaths($type)
  167. {
  168. $typeConfigs = $this->typePaths[$type];
  169. $paths = [];
  170. foreach ($typeConfigs as $typeConfig) {
  171. $paths[] = $typeConfig['path'];
  172. }
  173. return $paths;
  174. }
  175. /**
  176. * Return the default structure type for the the given document type.
  177. *
  178. * @param string $type
  179. *
  180. * @return string|null
  181. */
  182. private function getDefaultStructureType($type)
  183. {
  184. if (!isset($this->defaultTypes[$type])) {
  185. return null;
  186. }
  187. return $this->defaultTypes[$type];
  188. }
  189. }