ReinstallSourceBase.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <?php
  2. namespace Drupal\reinstall\Plugin\migrate\source;
  3. use Drupal\Component\Plugin\ConfigurablePluginInterface;
  4. use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
  5. use Drupal\migrate\MigrateException;
  6. use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
  7. use Drupal\migrate\Plugin\MigrationInterface;
  8. use Drupal\migrate\Row;
  9. use Drupal\reinstall\ReinstallEvents;
  10. use Drupal\reinstall\SourceEvent;
  11. use Symfony\Component\DependencyInjection\ContainerInterface;
  12. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  13. use Symfony\Component\Yaml\Exception\ParseException;
  14. use Symfony\Component\Yaml\Yaml;
  15. /**
  16. * Class SimpleSource provides the basic mechanisms to load a YML entity dump.
  17. *
  18. * @MigrateSource(
  19. * id = "reinstall_base"
  20. * )
  21. */
  22. class ReinstallSourceBase extends SourcePluginBase implements ContainerFactoryPluginInterface, ConfigurablePluginInterface {
  23. use SimpleSourceTrait;
  24. /**
  25. * The event_dispatcher service.
  26. *
  27. * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
  28. */
  29. protected $eventDispatcher;
  30. /**
  31. * The source records.
  32. *
  33. * MAY be altered by subscribing to MigrateEvents::PRE_IMPORT.
  34. *
  35. * @var array
  36. */
  37. public $records;
  38. /**
  39. * ReinstallSourceBase constructor.
  40. *
  41. * @param array $configuration
  42. * The plugin configuration.
  43. * @param string $plugin_id
  44. * The plugin id.
  45. * @param mixed $plugin_definition
  46. * The plugin definition.
  47. * @param \Drupal\migrate\Plugin\MigrationInterface $migration
  48. * The migration on which the plugin is invoked.
  49. * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
  50. * The event_dispatcher service.
  51. */
  52. public function __construct(
  53. array $configuration,
  54. $plugin_id,
  55. $plugin_definition,
  56. MigrationInterface $migration,
  57. EventDispatcherInterface $eventDispatcher
  58. ) {
  59. parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);
  60. $this->eventDispatcher = $eventDispatcher;
  61. $this->records = array_map([$this, 'flattenRecord'], $this->initialParse($configuration));
  62. $eventDispatcher->dispatch(ReinstallEvents::POST_SOURCE_PARSE, new SourceEvent($this));
  63. }
  64. /**
  65. * {@inheritdoc}
  66. */
  67. public static function create(
  68. ContainerInterface $container,
  69. array $configuration,
  70. $pluginId,
  71. $pluginDefinition,
  72. MigrationInterface $migration = NULL
  73. ) {
  74. $importPath = $container->getParameter('reinstall.path');
  75. $configuration['importPath'] = $importPath;
  76. $dispatcher = $container->get('event_dispatcher');
  77. return new static($configuration, $pluginId, $pluginDefinition, $migration, $dispatcher);
  78. }
  79. /**
  80. * {@inheritdoc}
  81. */
  82. public function doCount() {
  83. return count($this->records);
  84. }
  85. /**
  86. * Flatten the field hierarchy. Not correct for all cases.
  87. *
  88. * @param array $record
  89. * The raw source values.
  90. *
  91. * @return array
  92. * The flattened values.
  93. *
  94. * @see \Drupal\reinstall\Plugin\migrate\process\TermParent
  95. */
  96. protected function flattenRecord(array $record) {
  97. $row = new Row($record);
  98. $this->flattenRow($row);
  99. return $row->getSource();
  100. }
  101. /**
  102. * Flatten a typical Drupal 8 field array to a 1-level array.
  103. */
  104. protected function flattenRow(Row $row) {
  105. $source = $row->getSource();
  106. foreach ($source as $key => &$item_list) {
  107. if (is_scalar($item_list)) {
  108. continue;
  109. }
  110. if (count($item_list) > 1) {
  111. $item = $item_list;
  112. }
  113. else {
  114. $item = reset($item_list);
  115. }
  116. // Handle bundle['target_id']
  117. // Exclude image field to keep metadata (alt / title)
  118. if (isset($item['target_id']) && !isset($item['alt']) && !isset($item['title'])) {
  119. $value = $item['target_id'];
  120. }
  121. elseif (is_scalar($item) || (count($item) != 1 && !isset($item['width']) && !isset($item['pid']))) {
  122. $value = $item;
  123. }
  124. elseif (isset($item['value'])) {
  125. $value = $item['value'];
  126. }
  127. elseif (isset($item['pid'])) {
  128. $value = $item['alias'];
  129. }
  130. else {
  131. $value = $item;
  132. }
  133. if (empty($item)) {
  134. $value = NULL;
  135. }
  136. $row->setSourceProperty($key, $value);
  137. }
  138. }
  139. /**
  140. * {@inheritdoc}
  141. */
  142. protected function initializeIterator() {
  143. if (!isset($this->iterator)) {
  144. $this->iterator = new \ArrayIterator($this->records);
  145. }
  146. return $this->iterator;
  147. }
  148. /**
  149. * Load then parse the file requested in configuration and return its records.
  150. *
  151. * @param array $configuration
  152. * The source configuration from the migration source section.
  153. * @param string $key
  154. * Optional. A top-level key for the source document. If empty, items will
  155. * be parsed from the root of the source document.
  156. *
  157. * @return array
  158. * An array of entity descriptions.
  159. *
  160. * @throws \Drupal\migrate\MigrateException
  161. */
  162. protected function initialParse(array $configuration, string $key = NULL) {
  163. $this->sstEntityType = $type = $configuration['type'];
  164. $bundle = $configuration['bundle'];
  165. $baseFilePath = $this->configuration['file'] ?? "${type}/${bundle}.yml";
  166. $importPath = $configuration['importPath'] ?? NULL;
  167. $filePath = "$importPath/$baseFilePath";
  168. $realPath = realpath($filePath);
  169. if (!is_file($realPath) || !is_readable($realPath)) {
  170. throw new MigrateException("${filePath} is not a readable file.");
  171. }
  172. try {
  173. $raw = file_get_contents($filePath);
  174. $data = Yaml::parse($raw);
  175. }
  176. catch (ParseException $e) {
  177. throw new MigrateException("Cannot parse the contents of ${filePath}.");
  178. }
  179. if ($key) {
  180. return $data[$key] ?? [];
  181. }
  182. return $data ?? [];
  183. }
  184. /**
  185. * {@inheritdoc}
  186. */
  187. public function __toString() {
  188. $current = $this->getIterator()->current();
  189. $ret = json_encode($current, JSON_PRETTY_PRINT);
  190. return $ret;
  191. }
  192. /**
  193. * Gets this plugin's configuration.
  194. *
  195. * @return array
  196. * An array of this plugin's configuration.
  197. */
  198. public function getConfiguration() {
  199. return $this->configuration;
  200. }
  201. /**
  202. * Sets the configuration for this plugin instance.
  203. *
  204. * @param array $configuration
  205. * An associative array containing the plugin's configuration.
  206. */
  207. public function setConfiguration(array $configuration) {
  208. $this->configuration = $configuration;
  209. }
  210. /**
  211. * Gets default configuration for this plugin.
  212. *
  213. * @return array
  214. * An associative array with the default configuration.
  215. */
  216. public function defaultConfiguration() {
  217. return [];
  218. }
  219. /**
  220. * Calculates dependencies for the configured plugin.
  221. *
  222. * Dependencies are saved in the plugin's configuration entity and are used to
  223. * determine configuration synchronization order. For example, if the plugin
  224. * integrates with specific user roles, this method should return an array of
  225. * dependencies listing the specified roles.
  226. *
  227. * @return array
  228. * An array of dependencies grouped by type (config, content, module,
  229. * theme). For example:
  230. *
  231. * @code
  232. * array(
  233. * 'config' => array('user.role.anonymous', 'user.role.authenticated'),
  234. * 'content' => array('node:article:f0a189e6-55fb-47fb-8005-5bef81c44d6d'),
  235. * 'module' => array('node', 'user'),
  236. * 'theme' => array('seven'),
  237. * );
  238. * @endcode
  239. *
  240. * @see \Drupal\Core\Config\Entity\ConfigDependencyManager
  241. * @see \Drupal\Core\Entity\EntityInterface::getConfigDependencyName()
  242. */
  243. public function calculateDependencies() {
  244. return [];
  245. }
  246. }