eventDispatcher = $eventDispatcher; $this->records = array_map([$this, 'flattenRecord'], $this->initialParse($configuration)); $eventDispatcher->dispatch(ReinstallEvents::POST_SOURCE_PARSE, new SourceEvent($this)); } /** * {@inheritdoc} */ public static function create( ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition, MigrationInterface $migration = NULL ) { $importPath = $container->getParameter('reinstall.path'); $configuration['importPath'] = $importPath; $dispatcher = $container->get('event_dispatcher'); return new static($configuration, $pluginId, $pluginDefinition, $migration, $dispatcher); } /** * {@inheritdoc} */ public function doCount() { return count($this->records); } /** * Flatten the field hierarchy. Not correct for all cases. * * @param array $record * The raw source values. * * @return array * The flattened values. * * @see \Drupal\reinstall\Plugin\migrate\process\TermParent */ protected function flattenRecord(array $record) { $row = new Row($record); $this->flattenRow($row); return $row->getSource(); } /** * Flatten a typical Drupal 8 field array to a 1-level array. */ protected function flattenRow(Row $row) { $source = $row->getSource(); foreach ($source as $key => &$item_list) { if (is_scalar($item_list)) { continue; } if (count($item_list) > 1) { $item = $item_list; } else { $item = reset($item_list); } // Handle bundle['target_id'] // Exclude image field to keep metadata (alt / title) if (isset($item['target_id']) && !isset($item['alt']) && !isset($item['title'])) { $value = $item['target_id']; } elseif (is_scalar($item) || (count($item) != 1 && !isset($item['width']) && !isset($item['pid']))) { $value = $item; } elseif (isset($item['value'])) { $value = $item['value']; } elseif (isset($item['pid'])) { $value = $item['alias']; } else { $value = $item; } if (empty($item)) { $value = NULL; } $row->setSourceProperty($key, $value); } } /** * {@inheritdoc} */ protected function initializeIterator() { if (!isset($this->iterator)) { $this->iterator = new \ArrayIterator($this->records); } return $this->iterator; } /** * Load then parse the file requested in configuration and return its records. * * @param array $configuration * The source configuration from the migration source section. * @param string $key * Optional. A top-level key for the source document. If empty, items will * be parsed from the root of the source document. * * @return array * An array of entity descriptions. * * @throws \Drupal\migrate\MigrateException */ protected function initialParse(array $configuration, string $key = NULL) { $this->sstEntityType = $type = $configuration['type']; $bundle = $configuration['bundle']; $baseFilePath = $this->configuration['file'] ?? "${type}/${bundle}.yml"; $importPath = $configuration['importPath'] ?? NULL; $filePath = "$importPath/$baseFilePath"; $realPath = realpath($filePath); if (!is_file($realPath) || !is_readable($realPath)) { throw new MigrateException("${filePath} is not a readable file."); } try { $raw = file_get_contents($filePath); $data = Yaml::parse($raw); } catch (ParseException $e) { throw new MigrateException("Cannot parse the contents of ${filePath}."); } if ($key) { return $data[$key] ?? []; } return $data ?? []; } /** * {@inheritdoc} */ public function __toString() { $current = $this->getIterator()->current(); $ret = json_encode($current, JSON_PRETTY_PRINT); return $ret; } /** * Gets this plugin's configuration. * * @return array * An array of this plugin's configuration. */ public function getConfiguration() { return $this->configuration; } /** * Sets the configuration for this plugin instance. * * @param array $configuration * An associative array containing the plugin's configuration. */ public function setConfiguration(array $configuration) { $this->configuration = $configuration; } /** * Gets default configuration for this plugin. * * @return array * An associative array with the default configuration. */ public function defaultConfiguration() { return []; } /** * Calculates dependencies for the configured plugin. * * Dependencies are saved in the plugin's configuration entity and are used to * determine configuration synchronization order. For example, if the plugin * integrates with specific user roles, this method should return an array of * dependencies listing the specified roles. * * @return array * An array of dependencies grouped by type (config, content, module, * theme). For example: * * @code * array( * 'config' => array('user.role.anonymous', 'user.role.authenticated'), * 'content' => array('node:article:f0a189e6-55fb-47fb-8005-5bef81c44d6d'), * 'module' => array('node', 'user'), * 'theme' => array('seven'), * ); * @endcode * * @see \Drupal\Core\Config\Entity\ConfigDependencyManager * @see \Drupal\Core\Entity\EntityInterface::getConfigDependencyName() */ public function calculateDependencies() { return []; } }