Initial Drupal 11 with DDEV setup
This commit is contained in:
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines a field plugin annotation object.
|
||||
*
|
||||
* Field plugins are responsible for handling the migration of custom fields
|
||||
* (provided by Field API in Drupal 7) to Drupal 8. They are allowed to alter
|
||||
* fieldable entity migrations when these migrations are being generated, and
|
||||
* can compute destination field types for individual fields during the actual
|
||||
* migration process.
|
||||
*
|
||||
* Plugin Namespace: Plugin\migrate\field
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class MigrateField extends Plugin {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($values) {
|
||||
parent::__construct($values);
|
||||
// Provide default value for core property, in case it's missing.
|
||||
if (empty($this->definition['core'])) {
|
||||
$this->definition['core'] = [6];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* Map of D6 and D7 field types to D8 field type plugin IDs.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $type_map = [];
|
||||
|
||||
/**
|
||||
* The Drupal core version(s) this plugin applies to.
|
||||
*
|
||||
* @var int[]
|
||||
*/
|
||||
public $core;
|
||||
|
||||
/**
|
||||
* Identifies the system providing the data the field plugin will read.
|
||||
*
|
||||
* The source_module is expected to be the name of a Drupal module that must
|
||||
* be installed in the source database.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $source_module;
|
||||
|
||||
/**
|
||||
* Identifies the system handling the data the destination plugin will write.
|
||||
*
|
||||
* The destination_module is expected to be the name of a Drupal module on the
|
||||
* destination site that must be installed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $destination_module;
|
||||
|
||||
/**
|
||||
* The weight of this plugin relative to other plugins.
|
||||
*
|
||||
* The weight of this plugin relative to other plugins servicing the same
|
||||
* field type and core version. The lowest weighted applicable plugin will be
|
||||
* used for each field.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $weight = 0;
|
||||
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_drupal\Attribute;
|
||||
|
||||
use Drupal\Component\Plugin\Attribute\Plugin;
|
||||
|
||||
/**
|
||||
* Defines a field plugin attribute object.
|
||||
*
|
||||
* Field plugins are responsible for handling the migration of custom fields
|
||||
* (provided by Field API in Drupal 7) to Drupal 8+. They are allowed to alter
|
||||
* fieldable entity migrations when these migrations are being generated, and
|
||||
* can compute destination field types for individual fields during the actual
|
||||
* migration process.
|
||||
*
|
||||
* Plugin Namespace: Plugin\migrate\field
|
||||
*
|
||||
* For a working example, see
|
||||
* \Drupal\datetime\Plugin\migrate\field\DateField
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\MigratePluginManager
|
||||
* @see \Drupal\migrate_drupal\Plugin\MigrateFieldInterface;
|
||||
* @see \Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase
|
||||
* @see plugin_api
|
||||
*
|
||||
* @ingroup migration
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class MigrateField extends Plugin {
|
||||
|
||||
/**
|
||||
* The plugin definition.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $definition;
|
||||
|
||||
/**
|
||||
* Constructs a migrate field attribute object.
|
||||
*
|
||||
* @param string $id
|
||||
* A unique identifier for the field plugin.
|
||||
* @param int[] $core
|
||||
* (optional) The Drupal core version(s) this plugin applies to.
|
||||
* @param int $weight
|
||||
* (optional) The weight of this plugin relative to other plugins servicing
|
||||
* the same field type and core version. The lowest weighted applicable
|
||||
* plugin will be used for each field.
|
||||
* @param string[] $type_map
|
||||
* (optional) Map of D6 and D7 field types to D8+ field type plugin IDs.
|
||||
* @param string|null $source_module
|
||||
* (optional) Identifies the system providing the data the field plugin will
|
||||
* read. The source_module is expected to be the name of a Drupal module
|
||||
* that must be installed in the source database.
|
||||
* @param string|null $destination_module
|
||||
* (optional) Identifies the system handling the data the destination plugin
|
||||
* will write. The destination_module is expected to be the name of a Drupal
|
||||
* module on the destination site that must be installed.
|
||||
* @param class-string|null $deriver
|
||||
* (optional) The deriver class.
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $id,
|
||||
public readonly array $core = [6],
|
||||
public readonly int $weight = 0,
|
||||
public readonly array $type_map = [],
|
||||
public readonly ?string $source_module = NULL,
|
||||
public readonly ?string $destination_module = NULL,
|
||||
public readonly ?string $deriver = NULL,
|
||||
) {}
|
||||
|
||||
}
|
||||
344
web/core/modules/migrate_drupal/src/FieldDiscovery.php
Normal file
344
web/core/modules/migrate_drupal/src/FieldDiscovery.php
Normal file
@ -0,0 +1,344 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
|
||||
use Drupal\migrate\Plugin\RequirementsInterface;
|
||||
use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Provides field discovery for Drupal 6 & 7 migrations.
|
||||
*/
|
||||
class FieldDiscovery implements FieldDiscoveryInterface {
|
||||
|
||||
/**
|
||||
* An array of already discovered field plugin information.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldPluginCache;
|
||||
|
||||
/**
|
||||
* The field plugin manager.
|
||||
*
|
||||
* @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface
|
||||
*/
|
||||
protected $fieldPluginManager;
|
||||
|
||||
/**
|
||||
* The migration plugin manager.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
|
||||
*/
|
||||
protected $migrationPluginManager;
|
||||
|
||||
/**
|
||||
* The logger channel service.
|
||||
*
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* A cache of discovered fields.
|
||||
*
|
||||
* It is an array of arrays. If the entity type is bundleable, a third level
|
||||
* of arrays is added to account for fields discovered at the bundle level.
|
||||
*
|
||||
* [{core}][{entity_type}][{bundle}]
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $discoveredFieldsCache = [];
|
||||
|
||||
/**
|
||||
* An array of bundle keys, keyed by drupal core version.
|
||||
*
|
||||
* In Drupal 6, only nodes were fieldable, and the bundles were called
|
||||
* 'type_name'. In Drupal 7, everything became entities, and the more
|
||||
* generic 'bundle' was used.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bundleKeys = [
|
||||
FieldDiscoveryInterface::DRUPAL_6 => 'type_name',
|
||||
FieldDiscoveryInterface::DRUPAL_7 => 'bundle',
|
||||
];
|
||||
|
||||
/**
|
||||
* An array of source plugin ids, keyed by Drupal core version.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $sourcePluginIds = [
|
||||
FieldDiscoveryInterface::DRUPAL_6 => 'd6_field_instance',
|
||||
FieldDiscoveryInterface::DRUPAL_7 => 'd7_field_instance',
|
||||
];
|
||||
|
||||
/**
|
||||
* An array of supported Drupal core versions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $supportedCoreVersions = [
|
||||
FieldDiscoveryInterface::DRUPAL_6,
|
||||
FieldDiscoveryInterface::DRUPAL_7,
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructs a FieldDiscovery object.
|
||||
*
|
||||
* @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $field_plugin_manager
|
||||
* The field plugin manager.
|
||||
* @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager
|
||||
* The migration plugin manager.
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* The logger channel service.
|
||||
*/
|
||||
public function __construct(MigrateFieldPluginManagerInterface $field_plugin_manager, MigrationPluginManagerInterface $migration_plugin_manager, LoggerInterface $logger) {
|
||||
$this->fieldPluginManager = $field_plugin_manager;
|
||||
$this->migrationPluginManager = $migration_plugin_manager;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addAllFieldProcesses(MigrationInterface $migration) {
|
||||
$core = $this->getCoreVersion($migration);
|
||||
$fields = $this->getAllFields($core);
|
||||
foreach ($fields as $entity_type_id => $bundle) {
|
||||
$this->addEntityFieldProcesses($migration, $entity_type_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addEntityFieldProcesses(MigrationInterface $migration, $entity_type_id) {
|
||||
$core = $this->getCoreVersion($migration);
|
||||
$fields = $this->getAllFields($core);
|
||||
if (!empty($fields[$entity_type_id]) && is_array($fields[$entity_type_id])) {
|
||||
foreach ($fields[$entity_type_id] as $bundle => $fields) {
|
||||
$this->addBundleFieldProcesses($migration, $entity_type_id, $bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addBundleFieldProcesses(MigrationInterface $migration, $entity_type_id, $bundle) {
|
||||
$core = $this->getCoreVersion($migration);
|
||||
$fields = $this->getAllFields($core);
|
||||
$plugin_definition = $migration->getPluginDefinition();
|
||||
if (empty($fields[$entity_type_id][$bundle])) {
|
||||
return;
|
||||
}
|
||||
$bundle_fields = $fields[$entity_type_id][$bundle];
|
||||
foreach ($bundle_fields as $field_name => $field_info) {
|
||||
$plugin = $this->getFieldPlugin($field_info['type'], $migration);
|
||||
if ($plugin) {
|
||||
$method = $plugin_definition['field_plugin_method'] ?? 'defineValueProcessPipeline';
|
||||
|
||||
call_user_func_array([
|
||||
$plugin,
|
||||
$method,
|
||||
], [
|
||||
$migration,
|
||||
$field_name,
|
||||
$field_info,
|
||||
]);
|
||||
}
|
||||
else {
|
||||
// Default to a get process plugin if this is a value migration.
|
||||
if ((empty($plugin_definition['field_plugin_method']) || $plugin_definition['field_plugin_method'] === 'defineValueProcessPipeline')) {
|
||||
$migration->setProcessOfProperty($field_name, $field_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate field plugin for a given field type.
|
||||
*
|
||||
* @param string $field_type
|
||||
* The field type.
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration to retrieve the plugin for.
|
||||
*
|
||||
* @return \Drupal\migrate_drupal\Plugin\MigrateFieldInterface|bool
|
||||
* The appropriate field plugin to process this field type.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function getFieldPlugin($field_type, MigrationInterface $migration) {
|
||||
$core = $this->getCoreVersion($migration);
|
||||
if (!isset($this->fieldPluginCache[$core][$field_type])) {
|
||||
try {
|
||||
$plugin_id = $this->fieldPluginManager->getPluginIdFromFieldType($field_type, ['core' => $core], $migration);
|
||||
$plugin = $this->fieldPluginManager->createInstance($plugin_id, ['core' => $core], $migration);
|
||||
}
|
||||
catch (PluginNotFoundException) {
|
||||
$plugin = FALSE;
|
||||
}
|
||||
$this->fieldPluginCache[$core][$field_type] = $plugin;
|
||||
}
|
||||
return $this->fieldPluginCache[$core][$field_type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all field information related to this migration.
|
||||
*
|
||||
* @param string $core
|
||||
* The Drupal core version to get fields for.
|
||||
*
|
||||
* @return array
|
||||
* A multidimensional array of source data from the relevant field instance
|
||||
* migration, keyed first by entity type, then by bundle and finally by
|
||||
* field name.
|
||||
*/
|
||||
protected function getAllFields($core) {
|
||||
if (empty($this->discoveredFieldsCache[$core])) {
|
||||
$this->discoveredFieldsCache[$core] = [];
|
||||
$source_plugin = $this->getSourcePlugin($core);
|
||||
foreach ($source_plugin as $row) {
|
||||
/** @var \Drupal\migrate\Row $row */
|
||||
if ($core === FieldDiscoveryInterface::DRUPAL_7) {
|
||||
$entity_type_id = $row->get('entity_type');
|
||||
}
|
||||
else {
|
||||
$entity_type_id = 'node';
|
||||
}
|
||||
$bundle = $row->getSourceProperty($this->bundleKeys[$core]);
|
||||
$this->discoveredFieldsCache[$core][$entity_type_id][$bundle][$row->getSourceProperty('field_name')] = $row->getSource();
|
||||
}
|
||||
}
|
||||
return $this->discoveredFieldsCache[$core];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all field information for a particular entity type.
|
||||
*
|
||||
* @param string $core
|
||||
* The Drupal core version.
|
||||
* @param string $entity_type_id
|
||||
* The legacy entity type ID.
|
||||
*
|
||||
* @return array
|
||||
* A multidimensional array of source data from the relevant field instance
|
||||
* migration for the entity type, keyed first by bundle and then by field
|
||||
* name.
|
||||
*/
|
||||
protected function getEntityFields($core, $entity_type_id) {
|
||||
$fields = $this->getAllFields($core);
|
||||
if (!empty($fields[$entity_type_id])) {
|
||||
return $fields[$entity_type_id];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all field information for a particular entity type and bundle.
|
||||
*
|
||||
* @param string $core
|
||||
* The Drupal core version.
|
||||
* @param string $entity_type_id
|
||||
* The legacy entity type ID.
|
||||
* @param string $bundle
|
||||
* The legacy bundle (or content_type).
|
||||
*
|
||||
* @return array
|
||||
* An array of source data from the relevant field instance migration for
|
||||
* the bundle, keyed by field name.
|
||||
*/
|
||||
protected function getBundleFields($core, $entity_type_id, $bundle) {
|
||||
$fields = $this->getEntityFields($core, $entity_type_id);
|
||||
if (!empty($fields[$bundle])) {
|
||||
return $fields[$bundle];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source plugin to use to gather field information.
|
||||
*
|
||||
* @param string $core
|
||||
* The Drupal core version.
|
||||
*
|
||||
* @return array|\Drupal\migrate\Plugin\MigrateSourceInterface
|
||||
* The source plugin, or an empty array if none can be found that meets
|
||||
* requirements.
|
||||
*/
|
||||
protected function getSourcePlugin($core) {
|
||||
$definition = $this->getFieldInstanceStubMigrationDefinition($core);
|
||||
$source_plugin = $this->migrationPluginManager
|
||||
->createStubMigration($definition)
|
||||
->getSourcePlugin();
|
||||
if ($source_plugin instanceof RequirementsInterface) {
|
||||
try {
|
||||
$source_plugin->checkRequirements();
|
||||
}
|
||||
catch (RequirementsException $e) {
|
||||
// If checkRequirements() failed, the source database did not support
|
||||
// fields (i.e., Field is not installed in D7). Therefore, $fields will
|
||||
// be empty and below we'll return an empty array. The migration will
|
||||
// proceed without adding fields.
|
||||
$this->logger->notice('Field discovery failed for Drupal core version @core. Did this site have the Field module installed? Error: @message', [
|
||||
'@core' => $core,
|
||||
'@message' => $e->getMessage(),
|
||||
]);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return $source_plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the stub migration definition for a given Drupal core version.
|
||||
*
|
||||
* @param string $core
|
||||
* The Drupal core version.
|
||||
*
|
||||
* @return array
|
||||
* The stub migration definition.
|
||||
*/
|
||||
protected function getFieldInstanceStubMigrationDefinition($core) {
|
||||
return [
|
||||
'destination' => ['plugin' => 'null'],
|
||||
'idMap' => ['plugin' => 'null'],
|
||||
'source' => [
|
||||
'ignore_map' => TRUE,
|
||||
'plugin' => $this->sourcePluginIds[$core],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the core version of a Drupal migration.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration.
|
||||
*
|
||||
* @return string|bool
|
||||
* A string representation of the Drupal version, or FALSE.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function getCoreVersion(MigrationInterface $migration) {
|
||||
$tags = $migration->getMigrationTags();
|
||||
if (in_array('Drupal 7', $tags, TRUE)) {
|
||||
return FieldDiscoveryInterface::DRUPAL_7;
|
||||
}
|
||||
elseif (in_array('Drupal 6', $tags, TRUE)) {
|
||||
return FieldDiscoveryInterface::DRUPAL_6;
|
||||
}
|
||||
throw new \InvalidArgumentException("Drupal Core version not found for this migration");
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Provides field discovery for Drupal 6 & 7 migrations.
|
||||
*/
|
||||
interface FieldDiscoveryInterface {
|
||||
|
||||
const DRUPAL_6 = '6';
|
||||
|
||||
const DRUPAL_7 = '7';
|
||||
|
||||
/**
|
||||
* Adds the field processes to a migration.
|
||||
*
|
||||
* This method is used in field migrations to execute the migration process
|
||||
* alter method specified by the 'field_plugin_method' key of the migration
|
||||
* for all field plugins applicable to this Drupal to Drupal migration. This
|
||||
* method is used internally for field, field instance, widget, and formatter
|
||||
* migrations to allow field plugins to alter the process for these
|
||||
* migrations.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration to add process plugins to.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function addAllFieldProcesses(MigrationInterface $migration);
|
||||
|
||||
/**
|
||||
* Adds the field processes for an entity to a migration.
|
||||
*
|
||||
* This method is used in field migrations to execute the migration process
|
||||
* alter method specified by the 'field_plugin_method' key of the migration
|
||||
* for all field plugins applicable to this Drupal to Drupal migration. This
|
||||
* method is used internally for field, field instance, widget, and formatter
|
||||
* migrations to allow field plugins to alter the process for these
|
||||
* migrations.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration to add processes to.
|
||||
* @param string $entity_type_id
|
||||
* The legacy entity type to add processes for.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addEntityFieldProcesses(MigrationInterface $migration, $entity_type_id);
|
||||
|
||||
/**
|
||||
* Adds the field processes for a bundle to a migration.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration to add processes to.
|
||||
* @param string $entity_type_id
|
||||
* The legacy entity type to add processes for.
|
||||
* @param string $bundle
|
||||
* The legacy bundle (or content_type) to add processes for.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addBundleFieldProcesses(MigrationInterface $migration, $entity_type_id, $bundle);
|
||||
|
||||
}
|
||||
144
web/core/modules/migrate_drupal/src/Hook/MigrateDrupalHooks.php
Normal file
144
web/core/modules/migrate_drupal/src/Hook/MigrateDrupalHooks.php
Normal file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Hook;
|
||||
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\migrate_drupal\NodeMigrateType;
|
||||
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\RequirementsInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Hook\Attribute\Hook;
|
||||
|
||||
/**
|
||||
* Hook implementations for migrate_drupal.
|
||||
*/
|
||||
class MigrateDrupalHooks {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
#[Hook('help')]
|
||||
public function help($route_name, RouteMatchInterface $route_match): ?string {
|
||||
switch ($route_name) {
|
||||
case 'help.page.migrate_drupal':
|
||||
$output = '';
|
||||
$output .= '<h2>' . $this->t('About') . '</h2>';
|
||||
$output .= '<p>' . $this->t('The Migrate Drupal module provides a framework based on the <a href=":migrate">Migrate module</a> to facilitate migration from a Drupal (6, 7, or 8) site to your website. It does not provide a user interface. For more information, see the <a href=":migrate_drupal">online documentation for the Migrate Drupal module</a>.', [
|
||||
':migrate' => Url::fromRoute('help.page', [
|
||||
'name' => 'migrate',
|
||||
])->toString(),
|
||||
':migrate_drupal' => 'https://www.drupal.org/documentation/modules/migrate_drupal',
|
||||
]) . '</p>';
|
||||
return $output;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_migration_plugins_alter().
|
||||
*/
|
||||
#[Hook('migration_plugins_alter')]
|
||||
public function migrationPluginsAlter(array &$definitions): void {
|
||||
$module_handler = \Drupal::service('module_handler');
|
||||
$migration_plugin_manager = \Drupal::service('plugin.manager.migration');
|
||||
// This is why the deriver can't do this: the 'd6_taxonomy_vocabulary'
|
||||
// definition is not available to the deriver as it is running inside
|
||||
// getDefinitions().
|
||||
if (isset($definitions['d6_taxonomy_vocabulary'])) {
|
||||
$vocabulary_migration_definition = [
|
||||
'source' => [
|
||||
'ignore_map' => TRUE,
|
||||
'plugin' => 'd6_taxonomy_vocabulary',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'null',
|
||||
],
|
||||
'idMap' => [
|
||||
'plugin' => 'null',
|
||||
],
|
||||
];
|
||||
$vocabulary_migration = $migration_plugin_manager->createStubMigration($vocabulary_migration_definition);
|
||||
$translation_active = $module_handler->moduleExists('content_translation');
|
||||
try {
|
||||
$source_plugin = $vocabulary_migration->getSourcePlugin();
|
||||
if ($source_plugin instanceof RequirementsInterface) {
|
||||
$source_plugin->checkRequirements();
|
||||
}
|
||||
$executable = new MigrateExecutable($vocabulary_migration);
|
||||
$process = ['vid' => $definitions['d6_taxonomy_vocabulary']['process']['vid']];
|
||||
foreach ($source_plugin as $row) {
|
||||
$executable->processRow($row, $process);
|
||||
$source_vid = $row->getSourceProperty('vid');
|
||||
$plugin_ids = ['d6_term_node:' . $source_vid, 'd6_term_node_revision:' . $source_vid];
|
||||
if ($translation_active) {
|
||||
$plugin_ids[] = 'd6_term_node_translation:' . $source_vid;
|
||||
}
|
||||
foreach (array_intersect($plugin_ids, array_keys($definitions)) as $plugin_id) {
|
||||
// Match the field name derivation in d6_vocabulary_field.yml.
|
||||
$field_name = substr('field_' . $row->getDestinationProperty('vid'), 0, 32);
|
||||
// The Forum module is expecting 'taxonomy_forums' as the field name
|
||||
// for the forum nodes. The 'forum_vocabulary' source property is
|
||||
// evaluated in Drupal\taxonomy\Plugin\migrate\source\d6\Vocabulary
|
||||
// and is set to true if the vocabulary vid being migrated is the
|
||||
// same as the one in the 'forum_nav_vocabulary' variable on the
|
||||
// source site.
|
||||
$destination_vid = $row->getSourceProperty('forum_vocabulary') ? 'taxonomy_forums' : $field_name;
|
||||
$definitions[$plugin_id]['process'][$destination_vid] = 'tid';
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (RequirementsException $e) {
|
||||
// This code currently runs whenever the definitions are being loaded
|
||||
// and if you have a Drupal 7 source site then the requirements will not
|
||||
// be met for the d6_taxonomy_vocabulary migration.
|
||||
}
|
||||
catch (DatabaseExceptionWrapper $e) {
|
||||
// When the definitions are loaded it is possible the tables will not
|
||||
// exist.
|
||||
}
|
||||
}
|
||||
if (!$module_handler->moduleExists('node')) {
|
||||
return;
|
||||
}
|
||||
$connection = \Drupal::database();
|
||||
// We need to get the version of the source database in order to check
|
||||
// if the classic or complete node tables have been used in a migration.
|
||||
if (isset($definitions['system_site'])) {
|
||||
// Use the source plugin of the system_site migration to get the
|
||||
// database connection.
|
||||
$migration = $definitions['system_site'];
|
||||
/** @var \Drupal\migrate\Plugin\migrate\source\SqlBase $source_plugin */
|
||||
$source_plugin = $migration_plugin_manager->createStubMigration($migration)->getSourcePlugin();
|
||||
try {
|
||||
$source_connection = $source_plugin->getDatabase();
|
||||
$version = NodeMigrateType::getLegacyDrupalVersion($source_connection);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
\Drupal::messenger()->addError($this->t('Failed to connect to your database server. The server reports the following message: %error.<ul><li>Is the database server running?</li><li>Does the database exist, and have you entered the correct database name?</li><li>Have you entered the correct username and password?</li><li>Have you entered the correct database hostname?</li></ul>', ['%error' => $e->getMessage()]));
|
||||
}
|
||||
}
|
||||
// If this is a complete node migration then for all migrations, except the
|
||||
// classic node migrations, replace any dependency on a classic node
|
||||
// migration with a dependency on the complete node migration.
|
||||
if (NodeMigrateType::getNodeMigrateType($connection, $version ?? FALSE) === NodeMigrateType::NODE_MIGRATE_TYPE_COMPLETE) {
|
||||
$classic_migration_match = '/d([67])_(node|node_translation|node_revision|node_entity_translation)($|:.*)/';
|
||||
$replace_with_complete_migration = function (&$value, $key, $classic_migration_match) {
|
||||
if (is_string($value)) {
|
||||
$value = preg_replace($classic_migration_match, 'd$1_node_complete$3', $value);
|
||||
}
|
||||
};
|
||||
foreach ($definitions as &$definition) {
|
||||
$is_node_classic_migration = preg_match($classic_migration_match, $definition['id']);
|
||||
if (!$is_node_classic_migration && isset($definition['migration_dependencies'])) {
|
||||
array_walk_recursive($definition['migration_dependencies'], $replace_with_complete_migration, $classic_migration_match);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\DependencyInjection\ServiceProviderBase;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Alters container services.
|
||||
*/
|
||||
class MigrateDrupalServiceProvider extends ServiceProviderBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alter(ContainerBuilder $container) {
|
||||
parent::alter($container);
|
||||
|
||||
$container->getDefinition('plugin.manager.migration')
|
||||
->setClass(MigrationPluginManager::class)
|
||||
->addArgument(new Reference('plugin.manager.migrate.source'))
|
||||
->addArgument(new Reference('config.factory'));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,272 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
||||
use Drupal\Core\Database\Statement\FetchAs;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\migrate\Plugin\RequirementsInterface;
|
||||
|
||||
/**
|
||||
* Configures the appropriate migrations for a given source Drupal database.
|
||||
*/
|
||||
trait MigrationConfigurationTrait {
|
||||
|
||||
/**
|
||||
* The config factory service.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The migration plugin manager service.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
|
||||
*/
|
||||
protected $migrationPluginManager;
|
||||
|
||||
/**
|
||||
* The state service.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The follow-up migration tags.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $followUpMigrationTags;
|
||||
|
||||
/**
|
||||
* Gets the database connection for the source Drupal database.
|
||||
*
|
||||
* @param array $database
|
||||
* Database array representing the source Drupal database.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Connection
|
||||
* The database connection for the source Drupal database.
|
||||
*/
|
||||
protected function getConnection(array $database) {
|
||||
// Set up the connection.
|
||||
Database::addConnectionInfo('upgrade', 'default', $database);
|
||||
$connection = Database::getConnection('default', 'upgrade');
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the system data from the system table of the source Drupal database.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* Database connection to the source Drupal database.
|
||||
*
|
||||
* @return array
|
||||
* The system data from the system table of the source Drupal database.
|
||||
*/
|
||||
protected function getSystemData(Connection $connection) {
|
||||
$system_data = [];
|
||||
try {
|
||||
$results = $connection->select('system', 's', [
|
||||
'fetch' => FetchAs::Associative,
|
||||
])
|
||||
->fields('s')
|
||||
->execute();
|
||||
foreach ($results as $result) {
|
||||
$system_data[$result['type']][$result['name']] = $result;
|
||||
}
|
||||
}
|
||||
catch (DatabaseExceptionWrapper) {
|
||||
// The table might not exist for example in tests.
|
||||
}
|
||||
return $system_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the necessary state entries for SqlBase::getDatabase() to work.
|
||||
*
|
||||
* The state entities created here have to exist before migration plugin
|
||||
* instances are created so that derivers such as
|
||||
* \Drupal\taxonomy\Plugin\migrate\D6TermNodeDeriver can access the source
|
||||
* database.
|
||||
*
|
||||
* @param array $database
|
||||
* The source database settings.
|
||||
* @param string $drupal_version
|
||||
* The Drupal version.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SqlBase::getDatabase()
|
||||
*/
|
||||
protected function createDatabaseStateSettings(array $database, $drupal_version) {
|
||||
$database_state['key'] = 'upgrade';
|
||||
$database_state['database'] = $database;
|
||||
$database_state_key = 'migrate_drupal_' . $drupal_version;
|
||||
$state = $this->getState();
|
||||
$state->set($database_state_key, $database_state);
|
||||
$state->set('migrate.fallback_state_key', $database_state_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the migrations for import.
|
||||
*
|
||||
* @param string $database_state_key
|
||||
* The state key.
|
||||
* @param int $drupal_version
|
||||
* The version of Drupal we're getting the migrations for.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrationInterface[]
|
||||
* The migrations for import.
|
||||
*/
|
||||
protected function getMigrations($database_state_key, $drupal_version) {
|
||||
$version_tag = 'Drupal ' . $drupal_version;
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface[] $all_migrations */
|
||||
$all_migrations = $this->getMigrationPluginManager()->createInstancesByTag($version_tag);
|
||||
|
||||
// Unset the node migrations that should not run based on the type of node
|
||||
// migration. That is, if this is a complete node migration then unset the
|
||||
// classic node migrations and if this is a classic node migration then
|
||||
// unset the complete node migrations.
|
||||
$type = NodeMigrateType::getNodeMigrateType(\Drupal::database(), $drupal_version);
|
||||
switch ($type) {
|
||||
case NodeMigrateType::NODE_MIGRATE_TYPE_COMPLETE:
|
||||
$patterns = '/(d' . $drupal_version . '_node:)|(d' . $drupal_version . '_node_translation:)|(d' . $drupal_version . '_node_revision:)|(d7_node_entity_translation:)/';
|
||||
break;
|
||||
|
||||
case NodeMigrateType::NODE_MIGRATE_TYPE_CLASSIC:
|
||||
$patterns = '/(d' . $drupal_version . '_node_complete:)/';
|
||||
break;
|
||||
}
|
||||
foreach ($all_migrations as $key => $migrations) {
|
||||
if (preg_match($patterns, $key)) {
|
||||
unset($all_migrations[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$migrations = [];
|
||||
foreach ($all_migrations as $migration) {
|
||||
// Skip migrations tagged with any of the follow-up migration tags. They
|
||||
// will be derived and executed after the migrations on which they depend
|
||||
// have been successfully executed.
|
||||
// @see Drupal\migrate_drupal\Plugin\MigrationWithFollowUpInterface
|
||||
if (!empty(array_intersect($migration->getMigrationTags(), $this->getFollowUpMigrationTags()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// @todo https://drupal.org/node/2681867 We should be able to validate
|
||||
// the entire migration at this point.
|
||||
$source_plugin = $migration->getSourcePlugin();
|
||||
if ($source_plugin instanceof RequirementsInterface) {
|
||||
$source_plugin->checkRequirements();
|
||||
}
|
||||
$destination_plugin = $migration->getDestinationPlugin();
|
||||
if ($destination_plugin instanceof RequirementsInterface) {
|
||||
$destination_plugin->checkRequirements();
|
||||
}
|
||||
$migrations[] = $migration;
|
||||
}
|
||||
catch (RequirementsException) {
|
||||
// Migrations which are not applicable given the source and destination
|
||||
// site configurations (e.g., what modules are enabled) will be silently
|
||||
// ignored.
|
||||
}
|
||||
}
|
||||
|
||||
return $migrations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the follow-up migration tags.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of follow-up migration tags.
|
||||
*/
|
||||
protected function getFollowUpMigrationTags() {
|
||||
if ($this->followUpMigrationTags === NULL) {
|
||||
$this->followUpMigrationTags = $this->getConfigFactory()
|
||||
->get('migrate_drupal.settings')
|
||||
->get('follow_up_migration_tags') ?: [];
|
||||
}
|
||||
return $this->followUpMigrationTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines what version of Drupal the source database contains.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection object.
|
||||
*
|
||||
* @return string|false
|
||||
* A string representing the major branch of Drupal core (e.g. '6' for
|
||||
* Drupal 6.x), or FALSE if no valid version is matched.
|
||||
*/
|
||||
public static function getLegacyDrupalVersion(Connection $connection) {
|
||||
// Don't assume because a table of that name exists, that it has the columns
|
||||
// we're querying. Catch exceptions and report that the source database is
|
||||
// not Drupal.
|
||||
// Drupal 5/6/7 can be detected by the schema_version in the system table.
|
||||
if ($connection->schema()->tableExists('system')) {
|
||||
try {
|
||||
$version_string = $connection
|
||||
->query('SELECT [schema_version] FROM {system} WHERE [name] = :module', [':module' => 'system'])
|
||||
->fetchField();
|
||||
}
|
||||
catch (DatabaseExceptionWrapper) {
|
||||
// All database errors return FALSE.
|
||||
}
|
||||
}
|
||||
|
||||
return match (TRUE) {
|
||||
!isset($version_string) => FALSE,
|
||||
(int) $version_string >= 6000 => substr($version_string, 0, 1),
|
||||
(int) $version_string >= 1000 => '5',
|
||||
default => FALSE,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the config factory service.
|
||||
*
|
||||
* @return \Drupal\Core\Config\ConfigFactoryInterface
|
||||
* The config factory service.
|
||||
*/
|
||||
protected function getConfigFactory() {
|
||||
if (!$this->configFactory) {
|
||||
$this->configFactory = \Drupal::service('config.factory');
|
||||
}
|
||||
|
||||
return $this->configFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the migration plugin manager service.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrationPluginManagerInterface
|
||||
* The migration plugin manager service.
|
||||
*/
|
||||
protected function getMigrationPluginManager() {
|
||||
if (!$this->migrationPluginManager) {
|
||||
$this->migrationPluginManager = \Drupal::service('plugin.manager.migration');
|
||||
}
|
||||
|
||||
return $this->migrationPluginManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state service.
|
||||
*
|
||||
* @return \Drupal\Core\State\StateInterface
|
||||
* The state service.
|
||||
*/
|
||||
protected function getState() {
|
||||
if (!$this->state) {
|
||||
$this->state = \Drupal::service('state');
|
||||
}
|
||||
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
}
|
||||
118
web/core/modules/migrate_drupal/src/MigrationPluginManager.php
Normal file
118
web/core/modules/migrate_drupal/src/MigrationPluginManager.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\migrate\Plugin\Exception\BadPluginDefinitionException;
|
||||
use Drupal\migrate\Plugin\MigrateSourcePluginManager;
|
||||
use Drupal\migrate\Plugin\MigrationPluginManager as BaseMigrationPluginManager;
|
||||
|
||||
/**
|
||||
* Manages migration plugins.
|
||||
*
|
||||
* Analyzes migration definitions to ensure that the source plugin of any
|
||||
* migration tagged with particular tags ('Drupal 6' or 'Drupal 7' by default)
|
||||
* defines a source_module property in its plugin annotation. This is done in
|
||||
* order to support the Migrate Drupal UI, which needs to know which modules
|
||||
* "own" the data being migrated into Drupal 8, on both the source and
|
||||
* destination sides.
|
||||
*
|
||||
* @todo Enforce the destination_module property too, in
|
||||
* https://www.drupal.org/project/drupal/issues/2923810.
|
||||
*/
|
||||
class MigrationPluginManager extends BaseMigrationPluginManager {
|
||||
|
||||
/**
|
||||
* The Migrate source plugin manager service.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrateSourcePluginManager
|
||||
*/
|
||||
protected $sourceManager;
|
||||
|
||||
/**
|
||||
* The config factory service.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The migration tags which will trigger source_module enforcement.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $enforcedSourceModuleTags;
|
||||
|
||||
/**
|
||||
* MigrationPluginManager constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler service.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* The cache backend.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager service.
|
||||
* @param \Drupal\migrate\Plugin\MigrateSourcePluginManager $source_manager
|
||||
* The Migrate source plugin manager service.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory service.
|
||||
*/
|
||||
public function __construct(ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, MigrateSourcePluginManager $source_manager, ConfigFactoryInterface $config_factory) {
|
||||
parent::__construct($module_handler, $cache_backend, $language_manager);
|
||||
$this->sourceManager = $source_manager;
|
||||
$this->configFactory = $config_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the migration tags that trigger source_module enforcement.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of migration tags that enforce source_module.
|
||||
*/
|
||||
protected function getEnforcedSourceModuleTags() {
|
||||
if ($this->enforcedSourceModuleTags === NULL) {
|
||||
$this->enforcedSourceModuleTags = $this->configFactory
|
||||
->get('migrate_drupal.settings')
|
||||
->get('enforce_source_module_tags') ?: [];
|
||||
}
|
||||
return $this->enforcedSourceModuleTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processDefinition(&$definition, $plugin_id) {
|
||||
parent::processDefinition($definition, $plugin_id);
|
||||
|
||||
$source_id = $definition['source']['plugin'];
|
||||
$source_definition = $this->sourceManager->getDefinition($source_id);
|
||||
// If the source plugin uses annotations, then the 'provider' key is the
|
||||
// array of providers and the 'providers' key is not defined.
|
||||
$providers = $source_definition['providers'] ?? $source_definition['provider'];
|
||||
|
||||
// Check if the migration has any of the tags that trigger source_module
|
||||
// enforcement.
|
||||
$has_enforced_tags = !empty(array_intersect(
|
||||
$definition['migration_tags'] ?? [],
|
||||
$this->getEnforcedSourceModuleTags(),
|
||||
));
|
||||
|
||||
// If source_module is not defined in the migration, then check for it in
|
||||
// the source plugin.
|
||||
$has_source_module = !empty($definition['source']['source_module'])
|
||||
|| !empty($source_definition['source_module']);
|
||||
|
||||
$requires_migrate_drupal = in_array('migrate_drupal', $providers, TRUE);
|
||||
if ($requires_migrate_drupal && $has_enforced_tags && !$has_source_module) {
|
||||
throw new BadPluginDefinitionException($source_id, 'source_module');
|
||||
}
|
||||
|
||||
if (!$requires_migrate_drupal && !$has_enforced_tags && $has_source_module) {
|
||||
@trigger_error("Setting the source_module property without the expected tags is deprecated in drupal:11.2.0 and will trigger an error in drupal:12.0.0. See https://www.drupal.org/node/3306373", E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
457
web/core/modules/migrate_drupal/src/MigrationState.php
Normal file
457
web/core/modules/migrate_drupal/src/MigrationState.php
Normal file
@ -0,0 +1,457 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal;
|
||||
|
||||
use Drupal\Core\Discovery\YamlDiscovery;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Core\Messenger\MessengerTrait;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface;
|
||||
|
||||
/**
|
||||
* Determines the migrate state for all modules enabled on the source.
|
||||
*
|
||||
* Retrieves migrate info from *.migrate_drupal.yml files.
|
||||
*
|
||||
* Knowing which modules will be upgraded and those that will not is needed by
|
||||
* anyone upgrading a legacy Drupal version. This service provides that
|
||||
* information by analyzing the existing migrations and data in
|
||||
* migrate_drupal.yml files. Modules that are enabled or disabled in the source
|
||||
* are included in the analysis modules that are uninstalled are ignored.
|
||||
*
|
||||
* Deciding the upgrade state of a source module is a complicated task. A
|
||||
* destination module is not limited in any way to the source modules or the
|
||||
* current major version destination modules it is providing migrations for. We
|
||||
* see this in core where the Drupal 6 Menu module is upgraded by having
|
||||
* migrations in three Drupal 8 modules; menu_link_content, menu_ui and system.
|
||||
* If migrations for any of those three modules are not complete or if any of
|
||||
* them are not installed on the destination site then the Drupal 6 Menu module
|
||||
* cannot be listed as upgraded. If any one of the conditions are not met then
|
||||
* it should be listed as will not be upgraded.
|
||||
*
|
||||
* Another challenge is to ensure that legacy source modules that do not need an
|
||||
* upgrade path are handled correctly. These will not have migrations but should
|
||||
* be listed as will be upgraded, which even though there are not migrations
|
||||
* under the hood, it lets a site admin know that upgrading with this module
|
||||
* enabled is safe.
|
||||
*
|
||||
* There is not enough information in the existing system to determine the
|
||||
* correct state of the upgrade path for these, and other scenarios.
|
||||
*
|
||||
* The solution is for every destination module that is the successor to a
|
||||
* module built for a legacy Drupal version to declare the state of the upgrade
|
||||
* path(s) for the module. A module's upgrade path from a previous version may
|
||||
* consist of one or more migrations sets. Each migration set definition
|
||||
* consists of a source module supporting a legacy Drupal version, and one or
|
||||
* more current destination modules. This allows a module to indicate that a
|
||||
* provided migration set requires additional modules to be enabled in the
|
||||
* destination.
|
||||
*
|
||||
* A migration set can be marked 'finished', which indicates that all
|
||||
* migrations that are going to be provided by this destination module for this
|
||||
* migration set have been written and are complete. A migration set may also
|
||||
* be marked 'not_finished' which indicates that the module either has not
|
||||
* provided any migrations for the set, or needs to provide additional
|
||||
* migrations to complete the set. Note that other modules may still provide
|
||||
* additional finished or not_finished migrations for the same migration set.
|
||||
*
|
||||
* Modules inform the upgrade process of the migration sets by adding them to
|
||||
* their <module_name>.migrate_drupal.yml file.
|
||||
*
|
||||
* The <module_name>.migrate_drupal.yml file uses the following structure:
|
||||
*
|
||||
* # (optional) List of the source_module/destination_module(s) for the
|
||||
* # migration sets that this module provides and are complete.
|
||||
* finished:
|
||||
* # One or more Drupal legacy version number mappings (i.e. 6 and/or 7).
|
||||
* 6:
|
||||
* # A mapping of legacy module machine names to either an array of modules
|
||||
* # or a single destination module machine name to define this migration
|
||||
* # set.
|
||||
* <source_module_1>: <destination_module_1>
|
||||
* <source_module_2>:
|
||||
* - <destination_module_1>
|
||||
* - <destination_module_2>
|
||||
* 7:
|
||||
* <source_module_1>: <destination_module_1>
|
||||
* <source_module_2>:
|
||||
* - <destination_module_1>
|
||||
* - <destination_module_2>
|
||||
* # (optional) List of the migration sets that this module provides, or will be
|
||||
* # providing, that are incomplete or do not yet exist.
|
||||
* not_finished:
|
||||
* 6:
|
||||
* <source_module_1>: <destination_module_1>
|
||||
* <source_module_2>:
|
||||
* - <destination_module_1>
|
||||
* - <destination_module_2>
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* @code
|
||||
* finished:
|
||||
* 6:
|
||||
* node: node
|
||||
* 7:
|
||||
* node: node
|
||||
* entity_translation: node
|
||||
* not_finished:
|
||||
* 7:
|
||||
* commerce_product: commerce_product
|
||||
* other_module:
|
||||
* - other_module
|
||||
* - further_module
|
||||
* @endcode
|
||||
*
|
||||
* In this example the module has completed the upgrade path for data from the
|
||||
* Drupal 6 and Drupal 7 Node modules to the Drupal 8 Node module and for data
|
||||
* from the Drupal 7 Entity Translation module to the Drupal 8 Node module.
|
||||
*
|
||||
* @code
|
||||
* finished:
|
||||
* 6:
|
||||
* pirate: pirate
|
||||
* 7:
|
||||
* pirate: pirate
|
||||
* @endcode
|
||||
*
|
||||
* The Pirate module does not require an upgrade path. By declaring the upgrade
|
||||
* finished the Pirate module will be included in the finished list. That is,
|
||||
* as long as no other module has an entry "pirate: <any module name>' in its
|
||||
* not_finished section.
|
||||
*/
|
||||
class MigrationState {
|
||||
|
||||
use MessengerTrait;
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Source module upgrade state when all its migrations are complete.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const FINISHED = 'finished';
|
||||
|
||||
/**
|
||||
* Source module upgrade state when all its migrations are not complete.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const NOT_FINISHED = 'not_finished';
|
||||
|
||||
/**
|
||||
* The field plugin manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandler
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The field plugin manager service.
|
||||
*
|
||||
* @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface
|
||||
*/
|
||||
protected $fieldPluginManager;
|
||||
|
||||
/**
|
||||
* An array of migration states declared for each source migration.
|
||||
*
|
||||
* States are keyed by version. Each value is an array keyed by name of the
|
||||
* source module and the value is an array of all the states declared for this
|
||||
* source module.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $stateBySource;
|
||||
|
||||
/**
|
||||
* An array of destinations declared for each source migration.
|
||||
*
|
||||
* Destinations are keyed by version. Each value is an array keyed by the name
|
||||
* of the source module and the value is an array of the destination modules.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $declaredBySource;
|
||||
|
||||
/**
|
||||
* An array of migration source and destinations derived from migrations.
|
||||
*
|
||||
* The key is the source version and the value is an array where the key is
|
||||
* the source module and the value is an array of destinations derived from
|
||||
* migration plugins.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $discoveredBySource;
|
||||
|
||||
/**
|
||||
* An array of migration source and destinations.
|
||||
*
|
||||
* Values are derived from migration plugins and declared states. The key is
|
||||
* the source version and the value is an array where the key is the source
|
||||
* module and the value is an array of declared or derived destinations.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $destinations = [];
|
||||
|
||||
/**
|
||||
* Array of enabled modules.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $enabledModules = [];
|
||||
|
||||
/**
|
||||
* Construct a new MigrationState object.
|
||||
*
|
||||
* @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $fieldPluginManager
|
||||
* Field plugin manager.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
|
||||
* Module handler.
|
||||
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
|
||||
* Messenger service.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $stringTranslation
|
||||
* String translation service.
|
||||
*/
|
||||
public function __construct(MigrateFieldPluginManagerInterface $fieldPluginManager, ModuleHandlerInterface $moduleHandler, MessengerInterface $messenger, TranslationInterface $stringTranslation) {
|
||||
$this->fieldPluginManager = $fieldPluginManager;
|
||||
$this->moduleHandler = $moduleHandler;
|
||||
$this->enabledModules = array_keys($this->moduleHandler->getModuleList());
|
||||
$this->enabledModules[] = 'core';
|
||||
$this->messenger = $messenger;
|
||||
$this->stringTranslation = $stringTranslation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the upgrade states for all enabled source modules.
|
||||
*
|
||||
* @param string $version
|
||||
* The legacy drupal version.
|
||||
* @param array $source_system_data
|
||||
* The data from the source site system table.
|
||||
* @param array $migrations
|
||||
* An array of migrations.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of data with keys of state, source modules and a
|
||||
* value which is a comma separated list of destination modules.
|
||||
*/
|
||||
public function getUpgradeStates($version, array $source_system_data, array $migrations) {
|
||||
return $this->buildUpgradeState($version, $source_system_data, $migrations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets migration state information from *.migrate_drupal.yml.
|
||||
*
|
||||
* @return array
|
||||
* An association array keyed by module of the finished and not_finished
|
||||
* migrations for each module.
|
||||
* */
|
||||
protected function getMigrationStates() {
|
||||
// Always instantiate a new YamlDiscovery object so that we always search on
|
||||
// the up-to-date list of modules.
|
||||
$discovery = new YamlDiscovery('migrate_drupal', array_map(function ($value) {
|
||||
return $value . '/migrations/state';
|
||||
}, $this->moduleHandler->getModuleDirectories()));
|
||||
return $discovery->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines migration state for each source module enabled on the source.
|
||||
*
|
||||
* If there are no migrations for a module and no declared state the state is
|
||||
* set to NOT_FINISHED. When a module does not need any migrations, such as
|
||||
* Overlay, a state of finished is declared in system.migrate_drupal.yml.
|
||||
*
|
||||
* If there are migrations for a module the following happens. If the
|
||||
* destination module is 'core' the state is set to FINISHED. If there are
|
||||
* any occurrences of 'not_finished' in the *.migrate_drupal.yml information
|
||||
* for this source module then the state is set to NOT_FINISHED. And finally,
|
||||
* if there is an occurrence of 'finished' the state is set to FINISHED.
|
||||
*
|
||||
* @param string $version
|
||||
* The legacy drupal version.
|
||||
* @param array $source_system_data
|
||||
* The data from the source site system table.
|
||||
* @param array $migrations
|
||||
* An array of migrations.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of data with keys of state, source modules and a
|
||||
* value which is a comma separated list of destination modules.
|
||||
* Example.
|
||||
*
|
||||
* @code
|
||||
* [
|
||||
* 'finished' => [
|
||||
* 'menu' => [
|
||||
* 'menu_link_content','menu_ui','system'
|
||||
* ]
|
||||
* ],
|
||||
* ]
|
||||
* @endcode
|
||||
*/
|
||||
protected function buildUpgradeState($version, array $source_system_data, array $migrations) {
|
||||
// Remove core profiles from the system data.
|
||||
unset($source_system_data['module']['standard'], $source_system_data['module']['minimal']);
|
||||
$this->buildDiscoveredDestinationsBySource($version, $migrations, $source_system_data);
|
||||
$this->buildDeclaredStateBySource($version);
|
||||
|
||||
$upgrade_state = [];
|
||||
// Loop through every source module that is enabled on the source site.
|
||||
foreach ($source_system_data['module'] as $module) {
|
||||
// The source plugins check requirements requires that all
|
||||
// source_modules are enabled so do the same here.
|
||||
if ($module['status']) {
|
||||
$source_module = $module['name'];
|
||||
$upgrade_state[$this->getSourceState($version, $source_module)][$source_module] = implode(', ', $this->getDestinationsForSource($version, $source_module));
|
||||
}
|
||||
|
||||
}
|
||||
foreach ($upgrade_state as $key => $value) {
|
||||
ksort($upgrade_state[$key]);
|
||||
}
|
||||
return $upgrade_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds migration source and destination module information.
|
||||
*
|
||||
* @param string $version
|
||||
* The legacy Drupal version.
|
||||
* @param array $migrations
|
||||
* The discovered migrations.
|
||||
* @param array $source_system_data
|
||||
* The data from the source site system table.
|
||||
*/
|
||||
protected function buildDiscoveredDestinationsBySource($version, array $migrations, array $source_system_data) {
|
||||
$discovered_upgrade_paths = [];
|
||||
$table_data = [];
|
||||
foreach ($migrations as $migration) {
|
||||
$migration_id = $migration->getPluginId();
|
||||
$source_module = $migration->getSourcePlugin()->getSourceModule();
|
||||
if (!$source_module) {
|
||||
$this->messenger()
|
||||
->addError($this->t('Source module not found for @migration_id.', ['@migration_id' => $migration_id]));
|
||||
}
|
||||
$destination_module = $migration->getDestinationPlugin()
|
||||
->getDestinationModule();
|
||||
if (!$destination_module) {
|
||||
$this->messenger()
|
||||
->addError($this->t('Destination module not found for @migration_id.', ['@migration_id' => $migration_id]));
|
||||
}
|
||||
|
||||
if ($source_module && $destination_module) {
|
||||
$discovered_upgrade_paths[$source_module][] = $destination_module;
|
||||
$table_data[$source_module][$destination_module][$migration_id] = $migration->label();
|
||||
}
|
||||
}
|
||||
|
||||
// Add entries for the field plugins to discovered_upgrade_paths.
|
||||
$definitions = $this->fieldPluginManager->getDefinitions();
|
||||
foreach ($definitions as $definition) {
|
||||
// This is not strict so that we find field plugins with an annotation
|
||||
// where the Drupal core version is an integer and when it is a string.
|
||||
if (in_array($version, $definition['core'])) {
|
||||
$source_module = $definition['source_module'];
|
||||
$destination_module = $definition['destination_module'];
|
||||
$discovered_upgrade_paths[$source_module][] = $destination_module;
|
||||
$table_data[$source_module][$destination_module][$definition['id']] = $definition['id'];
|
||||
}
|
||||
}
|
||||
ksort($table_data);
|
||||
foreach ($table_data as $source_module => $destination_module_info) {
|
||||
ksort($table_data[$source_module]);
|
||||
}
|
||||
$this->discoveredBySource[$version] = array_map('array_unique', $discovered_upgrade_paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets migration data from *.migrate_drupal.yml sorted by source module.
|
||||
*
|
||||
* @param string $version
|
||||
* The legacy Drupal version.
|
||||
*/
|
||||
protected function buildDeclaredStateBySource($version) {
|
||||
$migration_states = $this->getMigrationStates();
|
||||
|
||||
$state_by_source = [];
|
||||
$dest_by_source = [];
|
||||
$states = [static::FINISHED, static::NOT_FINISHED];
|
||||
foreach ($migration_states as $info) {
|
||||
foreach ($states as $state) {
|
||||
if (isset($info[$state][$version])) {
|
||||
foreach ($info[$state][$version] as $source => $destination) {
|
||||
// Add the state.
|
||||
$state_by_source[$source][] = $state;
|
||||
// Add the destination modules.
|
||||
$dest_by_source += [$source => []];
|
||||
$dest_by_source[$source] = array_merge($dest_by_source[$source], (array) $destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->stateBySource[$version] = array_map('array_unique', $state_by_source);
|
||||
$this->declaredBySource[$version] = array_map('array_unique', $dest_by_source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a destination exists for the given source module.
|
||||
*
|
||||
* @param string $version
|
||||
* Source version of Drupal.
|
||||
* @param string $source_module
|
||||
* Source module.
|
||||
*
|
||||
* @return string
|
||||
* Migration state, either 'finished' or 'not_finished'.
|
||||
*/
|
||||
protected function getSourceState($version, $source_module) {
|
||||
// The state is finished only when no declarations of 'not_finished'
|
||||
// were found and each destination module is enabled.
|
||||
if (!$destinations = $this->getDestinationsForSource($version, $source_module)) {
|
||||
// No discovered or declared state.
|
||||
return MigrationState::NOT_FINISHED;
|
||||
}
|
||||
if (!isset($this->stateBySource[$version][$source_module])) {
|
||||
// No declared state.
|
||||
return MigrationState::NOT_FINISHED;
|
||||
}
|
||||
if (in_array(MigrationState::NOT_FINISHED, $this->stateBySource[$version][$source_module], TRUE) || !in_array(MigrationState::FINISHED, $this->stateBySource[$version][$source_module], TRUE)) {
|
||||
return MigrationState::NOT_FINISHED;
|
||||
}
|
||||
if (array_diff($destinations, $this->enabledModules)) {
|
||||
return MigrationState::NOT_FINISHED;
|
||||
}
|
||||
return MigrationState::FINISHED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get net destinations for source module.
|
||||
*
|
||||
* @param string $version
|
||||
* Source version.
|
||||
* @param string $source_module
|
||||
* Source module.
|
||||
*
|
||||
* @return array
|
||||
* Destination modules either declared by {modulename}.migrate_drupal.yml
|
||||
* files or discovered from migration plugins.
|
||||
*/
|
||||
protected function getDestinationsForSource($version, $source_module) {
|
||||
if (!isset($this->destinations[$version][$source_module])) {
|
||||
$this->discoveredBySource[$version] += [$source_module => []];
|
||||
$this->declaredBySource[$version] += [$source_module => []];
|
||||
$destination = array_unique(array_merge($this->discoveredBySource[$version][$source_module], $this->declaredBySource[$version][$source_module]));
|
||||
sort($destination);
|
||||
$this->destinations[$version][$source_module] = $destination;
|
||||
}
|
||||
return $this->destinations[$version][$source_module];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
88
web/core/modules/migrate_drupal/src/NodeMigrateType.php
Normal file
88
web/core/modules/migrate_drupal/src/NodeMigrateType.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Site\Settings;
|
||||
|
||||
/**
|
||||
* Provides a class to determine the type of migration.
|
||||
*/
|
||||
final class NodeMigrateType {
|
||||
|
||||
use MigrationConfigurationTrait;
|
||||
|
||||
/**
|
||||
* Only the complete node migration map tables are in use.
|
||||
*/
|
||||
const NODE_MIGRATE_TYPE_COMPLETE = 'COMPLETE';
|
||||
|
||||
/**
|
||||
* Only the classic node migration map tables are in use.
|
||||
*/
|
||||
const NODE_MIGRATE_TYPE_CLASSIC = 'CLASSIC';
|
||||
|
||||
/**
|
||||
* Determines the type of node migration to be used.
|
||||
*
|
||||
* The node complete migration is the default. It is not used when there
|
||||
* are existing tables for dN_node.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The connection to the target database.
|
||||
* @param string|false $version
|
||||
* The Drupal version of the source database, FALSE if it cannot be
|
||||
* determined.
|
||||
*
|
||||
* @return string
|
||||
* The migrate type.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function getNodeMigrateType(Connection $connection, $version) {
|
||||
$migrate_node_migrate_type_classic = Settings::get('migrate_node_migrate_type_classic', FALSE);
|
||||
if ($migrate_node_migrate_type_classic) {
|
||||
return static::NODE_MIGRATE_TYPE_CLASSIC;
|
||||
}
|
||||
|
||||
$migrate_type = static::NODE_MIGRATE_TYPE_COMPLETE;
|
||||
if ($version) {
|
||||
// Create the variable name, 'node_has_rows' or 'node_complete_exists' and
|
||||
// set it the default value, FALSE.
|
||||
$node_has_rows = FALSE;
|
||||
$node_complete_has_rows = FALSE;
|
||||
|
||||
// Find out what migrate map tables have rows for the node migrations.
|
||||
// It is either the classic, 'dN_node', or the complete,
|
||||
// 'dN_node_complete', or both. This is used to determine which migrations
|
||||
// are run and if migrations using the node migrations in a
|
||||
// migration_lookup are altered.
|
||||
$bases = ['node', 'node_complete'];
|
||||
$tables = $connection->schema()
|
||||
->findTables('migrate_map_d' . $version . '_node%');
|
||||
foreach ($bases as $base) {
|
||||
$has_rows = $base . '_has_rows';
|
||||
$base_tables = preg_grep('/^migrate_map_d' . $version . '_' . $base . '_{2}.*$/', $tables);
|
||||
// Set the has_rows True when a map table has rows with a positive
|
||||
// count for the matched migration.
|
||||
foreach ($base_tables as $base_table) {
|
||||
if ($connection->schema()->tableExists($base_table)) {
|
||||
$count = $connection->select($base_table)->countQuery()
|
||||
->execute()->fetchField();
|
||||
if ($count > 0) {
|
||||
$$has_rows = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the node migration type to use.
|
||||
if ($node_has_rows && !$node_complete_has_rows) {
|
||||
$migrate_type = static::NODE_MIGRATE_TYPE_CLASSIC;
|
||||
}
|
||||
}
|
||||
return $migrate_type;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Provides an interface for all field type plugins.
|
||||
*/
|
||||
interface MigrateFieldInterface extends PluginInspectionInterface {
|
||||
|
||||
/**
|
||||
* Apply any custom processing to the field migration.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration entity.
|
||||
*/
|
||||
public function alterFieldMigration(MigrationInterface $migration);
|
||||
|
||||
/**
|
||||
* Apply any custom processing to the field instance migration.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration entity.
|
||||
*/
|
||||
public function alterFieldInstanceMigration(MigrationInterface $migration);
|
||||
|
||||
/**
|
||||
* Apply any custom processing to the field widget migration.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration entity.
|
||||
*/
|
||||
public function alterFieldWidgetMigration(MigrationInterface $migration);
|
||||
|
||||
/**
|
||||
* Apply any custom processing to the field formatter migration.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration entity.
|
||||
*/
|
||||
public function alterFieldFormatterMigration(MigrationInterface $migration);
|
||||
|
||||
/**
|
||||
* Get the field formatter type from the source.
|
||||
*
|
||||
* @param \Drupal\migrate\Row $row
|
||||
* The field being migrated.
|
||||
*
|
||||
* @return string
|
||||
* The field formatter type.
|
||||
*/
|
||||
public function getFieldFormatterType(Row $row);
|
||||
|
||||
/**
|
||||
* Get a map between D6 formatters and D8 formatters for this field type.
|
||||
*
|
||||
* This is used by static::alterFieldFormatterMigration() in the base class.
|
||||
*
|
||||
* @return array
|
||||
* The keys are D6 formatters and the values are D8 formatters.
|
||||
*/
|
||||
public function getFieldFormatterMap();
|
||||
|
||||
/**
|
||||
* Get the field widget type from the source.
|
||||
*
|
||||
* @param \Drupal\migrate\Row $row
|
||||
* The field being migrated.
|
||||
*
|
||||
* @return string
|
||||
* The field widget type.
|
||||
*/
|
||||
public function getFieldWidgetType(Row $row);
|
||||
|
||||
/**
|
||||
* Get a map between D6 and D8 widgets for this field type.
|
||||
*
|
||||
* @return array
|
||||
* The keys are D6 field widget types and the values D8 widgets.
|
||||
*/
|
||||
public function getFieldWidgetMap();
|
||||
|
||||
/**
|
||||
* Apply any custom processing to the field bundle migrations.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration entity.
|
||||
* @param string $field_name
|
||||
* The field name we're processing the value for.
|
||||
* @param array $data
|
||||
* The array of field data from FieldValues::fieldData().
|
||||
*/
|
||||
public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data);
|
||||
|
||||
/**
|
||||
* Computes the destination type of a migrated field.
|
||||
*
|
||||
* @param \Drupal\migrate\Row $row
|
||||
* The field being migrated.
|
||||
*
|
||||
* @return string
|
||||
* The destination field type.
|
||||
*/
|
||||
public function getFieldType(Row $row);
|
||||
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\migrate\Plugin\Exception\BadPluginDefinitionException;
|
||||
use Drupal\migrate\Plugin\MigratePluginManager;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Plugin manager for migrate field plugins.
|
||||
*
|
||||
* @see \Drupal\migrate_drupal\Plugin\MigrateFieldInterface
|
||||
* @see \Drupal\migrate\Attribute\MigrateField
|
||||
* @see plugin_api
|
||||
*
|
||||
* @ingroup migration
|
||||
*/
|
||||
class MigrateFieldPluginManager extends MigratePluginManager implements MigrateFieldPluginManagerInterface {
|
||||
|
||||
/**
|
||||
* The default version of core to use for field plugins.
|
||||
*
|
||||
* These plugins were initially only built and used for Drupal 6 fields.
|
||||
* Having been extended for Drupal 7 with a "core" annotation, we fall back to
|
||||
* Drupal 6 where none exists.
|
||||
*/
|
||||
const DEFAULT_CORE_VERSION = 6;
|
||||
|
||||
/**
|
||||
* Get the plugin ID from the field type.
|
||||
*
|
||||
* This method determines which field plugin should be used for a given field
|
||||
* type and Drupal core version, returning the lowest weighted plugin
|
||||
* supporting the provided core version, and which matches the field type
|
||||
* either by plugin ID, or in the type_map annotation keys.
|
||||
*
|
||||
* @param string $field_type
|
||||
* The field type being migrated.
|
||||
* @param array $configuration
|
||||
* (optional) An array of configuration relevant to the plugin instance.
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* (optional) The current migration instance.
|
||||
*
|
||||
* @return string
|
||||
* The ID of the plugin for the field type if available.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
|
||||
* If the plugin cannot be determined, such as if the field type is invalid.
|
||||
*
|
||||
* @see \Drupal\migrate_drupal\Attribute\MigrateField
|
||||
*/
|
||||
public function getPluginIdFromFieldType($field_type, array $configuration = [], ?MigrationInterface $migration = NULL) {
|
||||
$core = static::DEFAULT_CORE_VERSION;
|
||||
if (!empty($configuration['core'])) {
|
||||
$core = $configuration['core'];
|
||||
}
|
||||
elseif (!empty($migration->getPluginDefinition()['migration_tags'])) {
|
||||
foreach ($migration->getPluginDefinition()['migration_tags'] as $tag) {
|
||||
if ($tag == 'Drupal 7') {
|
||||
$core = 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$definitions = $this->getDefinitions();
|
||||
foreach ($definitions as $plugin_id => $definition) {
|
||||
if (in_array($core, $definition['core'])) {
|
||||
if (array_key_exists($field_type, $definition['type_map']) || $field_type === $plugin_id) {
|
||||
return $plugin_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new PluginNotFoundException($field_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processDefinition(&$definition, $plugin_id) {
|
||||
parent::processDefinition($definition, $plugin_id);
|
||||
|
||||
foreach (['core', 'source_module', 'destination_module'] as $required_property) {
|
||||
if (empty($definition[$required_property])) {
|
||||
throw new BadPluginDefinitionException($plugin_id, $required_property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function findDefinitions() {
|
||||
$definitions = parent::findDefinitions();
|
||||
$this->sortDefinitions($definitions);
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts a definitions array.
|
||||
*
|
||||
* This sorts the definitions array first by the weight column, and then by
|
||||
* the plugin ID, ensuring a stable, deterministic, and testable ordering of
|
||||
* plugins.
|
||||
*
|
||||
* @param array $definitions
|
||||
* The definitions array to sort.
|
||||
*/
|
||||
protected function sortDefinitions(array &$definitions) {
|
||||
array_multisort(array_column($definitions, 'weight'), SORT_ASC, SORT_NUMERIC, array_keys($definitions), SORT_ASC, SORT_NATURAL, $definitions);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin;
|
||||
|
||||
use Drupal\migrate\Plugin\MigratePluginManagerInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Interface implemented by plugin manager for migrate field plugins.
|
||||
*/
|
||||
interface MigrateFieldPluginManagerInterface extends MigratePluginManagerInterface {
|
||||
|
||||
/**
|
||||
* Get the plugin ID from the field type.
|
||||
*
|
||||
* @param string $field_type
|
||||
* The field type being migrated.
|
||||
* @param array $configuration
|
||||
* (optional) An array of configuration relevant to the plugin instance.
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface|null $migration
|
||||
* (optional) The current migration instance.
|
||||
*
|
||||
* @return string
|
||||
* The ID of the plugin for the field_type if available.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
|
||||
* If the plugin cannot be determined, such as if the field type is invalid.
|
||||
*/
|
||||
public function getPluginIdFromFieldType($field_type, array $configuration = [], ?MigrationInterface $migration = NULL);
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin;
|
||||
|
||||
/**
|
||||
* Interface for migrations with follow-up migrations.
|
||||
*
|
||||
* Some migrations need to be derived and executed after other migrations have
|
||||
* been successfully executed. For example, a migration might need to be derived
|
||||
* based on previously migrated data. For such a case, the migration dependency
|
||||
* system is not enough since all migrations would still be derived before any
|
||||
* one of them has been executed.
|
||||
*
|
||||
* Those "follow-up" migrations need to be tagged with the "Follow-up migration"
|
||||
* tag (or any tag in the "follow_up_migration_tags" configuration) and thus
|
||||
* they won't be derived with the other migrations.
|
||||
*
|
||||
* To get those follow-up migrations derived at the right time, the migrations
|
||||
* on which they depend must implement this interface and generate them in the
|
||||
* generateFollowUpMigrations() method.
|
||||
*
|
||||
* When the migrations implementing this interface have been successfully
|
||||
* executed, the follow-up migrations will then be derived having access to the
|
||||
* now migrated data.
|
||||
*/
|
||||
interface MigrationWithFollowUpInterface {
|
||||
|
||||
/**
|
||||
* Generates follow-up migrations.
|
||||
*
|
||||
* When the migration implementing this interface has been successfully
|
||||
* executed, this method will be used to generate the follow-up migrations
|
||||
* which depends on the now migrated data.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrationInterface[]
|
||||
* The follow-up migrations.
|
||||
*/
|
||||
public function generateFollowUpMigrations();
|
||||
|
||||
}
|
||||
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Component\Plugin\PluginBase;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Deriver for entity reference field translations.
|
||||
*
|
||||
* A migration will be created for every bundle with at least one entity
|
||||
* reference field that is configured to point to one of the supported target
|
||||
* entity types. The migrations will update the entity reference fields with
|
||||
* values found in the mapping tables of the migrations associated with the
|
||||
* target types.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* @code
|
||||
* id: d7_entity_reference_translation
|
||||
* label: Entity reference translations
|
||||
* migration_tags:
|
||||
* - Drupal 7
|
||||
* - Follow-up migration
|
||||
* deriver: Drupal\migrate_drupal\Plugin\migrate\EntityReferenceTranslationDeriver
|
||||
* target_types:
|
||||
* node:
|
||||
* - d7_node_translation
|
||||
* source:
|
||||
* plugin: empty
|
||||
* key: default
|
||||
* target: default
|
||||
* process: []
|
||||
* destination:
|
||||
* plugin: null
|
||||
* @endcode
|
||||
*
|
||||
* In this example, the only supported target type is 'node' and the associated
|
||||
* migration for the mapping table lookup is 'd7_node_translation'.
|
||||
*/
|
||||
class EntityReferenceTranslationDeriver extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The entity field manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* EntityReferenceTranslationDeriver constructor.
|
||||
*
|
||||
* @param string $base_plugin_id
|
||||
* The base plugin ID.
|
||||
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
|
||||
* The entity field manager.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
*/
|
||||
public function __construct($base_plugin_id, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$base_plugin_id,
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('entity_type.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
// Get all entity reference fields.
|
||||
$field_map = $this->entityFieldManager->getFieldMapByFieldType('entity_reference');
|
||||
|
||||
foreach ($field_map as $entity_type => $fields) {
|
||||
foreach ($fields as $field_name => $field) {
|
||||
foreach ($field['bundles'] as $bundle) {
|
||||
$field_definitions = $this->entityFieldManager->getFieldDefinitions($entity_type, $bundle);
|
||||
$target_type = $field_definitions[$field_name]->getSetting('target_type');
|
||||
|
||||
// If the field's target type is not supported, skip it.
|
||||
if (!array_key_exists($target_type, $base_plugin_definition['target_types'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Key derivatives by entity types and bundles.
|
||||
$derivative_key = $entity_type . '__' . $bundle;
|
||||
|
||||
$derivative = $base_plugin_definition;
|
||||
$entity_type_definition = $this->entityTypeManager->getDefinition($entity_type);
|
||||
|
||||
// Set the migration label.
|
||||
$derivative['label'] = $this->t('@label (@derivative)', [
|
||||
'@label' => $base_plugin_definition['label'],
|
||||
'@derivative' => $derivative_key,
|
||||
]);
|
||||
|
||||
// Set the source plugin.
|
||||
$derivative['source']['plugin'] = 'content_entity' . PluginBase::DERIVATIVE_SEPARATOR . $entity_type;
|
||||
if ($entity_type_definition->hasKey('bundle')) {
|
||||
$derivative['source']['bundle'] = $bundle;
|
||||
}
|
||||
|
||||
// Set the process pipeline.
|
||||
$id_key = $entity_type_definition->getKey('id');
|
||||
$derivative['process'][$id_key] = $id_key;
|
||||
if ($entity_type_definition->isRevisionable()) {
|
||||
$revision_key = $entity_type_definition->getKey('revision');
|
||||
$derivative['process'][$revision_key] = $revision_key;
|
||||
}
|
||||
if ($entity_type_definition->isTranslatable()) {
|
||||
$langcode_key = $entity_type_definition->getKey('langcode');
|
||||
$derivative['process'][$langcode_key] = $langcode_key;
|
||||
}
|
||||
|
||||
// Set the destination plugin.
|
||||
$derivative['destination']['plugin'] = 'entity' . PluginBase::DERIVATIVE_SEPARATOR . $entity_type;
|
||||
if ($entity_type_definition->hasKey('bundle')) {
|
||||
$derivative['destination']['default_bundle'] = $bundle;
|
||||
}
|
||||
if ($entity_type_definition->isTranslatable()) {
|
||||
$derivative['destination']['translations'] = TRUE;
|
||||
}
|
||||
|
||||
// Allow overwriting the entity reference field so we can update its
|
||||
// values with the ones found in the mapping table.
|
||||
$derivative['destination']['overwrite_properties'][$field_name] = $field_name;
|
||||
|
||||
// Add the entity reference field to the process pipeline.
|
||||
$derivative['process'][$field_name] = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => $field_name,
|
||||
'process' => [
|
||||
'translation_target_id' => [
|
||||
[
|
||||
'plugin' => 'migration_lookup',
|
||||
'source' => 'target_id',
|
||||
'migration' => $base_plugin_definition['target_types'][$target_type],
|
||||
'no_stub' => TRUE,
|
||||
],
|
||||
[
|
||||
'plugin' => 'skip_on_empty',
|
||||
'method' => 'process',
|
||||
],
|
||||
[
|
||||
'plugin' => 'extract',
|
||||
'index' => [0],
|
||||
],
|
||||
],
|
||||
'target_id' => [
|
||||
[
|
||||
'plugin' => 'null_coalesce',
|
||||
'source' => [
|
||||
'@translation_target_id',
|
||||
'target_id',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (!isset($this->derivatives[$derivative_key])) {
|
||||
// If this is a new derivative, add it to the returned derivatives.
|
||||
$this->derivatives[$derivative_key] = $derivative;
|
||||
}
|
||||
else {
|
||||
// If this is an existing derivative, it means this bundle has more
|
||||
// than one entity reference field. In that case, we only want to
|
||||
// add the field to the process pipeline and add it to
|
||||
// overwrite_properties so it can be overwritten.
|
||||
$this->derivatives[$derivative_key]['process'] += $derivative['process'];
|
||||
$this->derivatives[$derivative_key]['destination']['overwrite_properties'] += $derivative['destination']['overwrite_properties'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate;
|
||||
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\migrate\Plugin\MigrateDestinationPluginManager;
|
||||
use Drupal\migrate\Plugin\MigratePluginManager;
|
||||
use Drupal\migrate\Plugin\Migration;
|
||||
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
|
||||
use Drupal\migrate_drupal\FieldDiscoveryInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Migration plugin class for migrations dealing with field config and values.
|
||||
*/
|
||||
class FieldMigration extends Migration implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field data has been filled already.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $init = FALSE;
|
||||
|
||||
/**
|
||||
* The migration field discovery service.
|
||||
*
|
||||
* @var \Drupal\migrate_drupal\FieldDiscoveryInterface
|
||||
*/
|
||||
protected $fieldDiscovery;
|
||||
|
||||
/**
|
||||
* Constructs a FieldMigration.
|
||||
*
|
||||
* @param array $configuration
|
||||
* Plugin configuration.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin definition.
|
||||
* @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager
|
||||
* The migration plugin manager.
|
||||
* @param \Drupal\migrate\Plugin\MigratePluginManager $source_plugin_manager
|
||||
* The source migration plugin manager.
|
||||
* @param \Drupal\migrate\Plugin\MigratePluginManager $process_plugin_manager
|
||||
* The process migration plugin manager.
|
||||
* @param \Drupal\migrate\Plugin\MigrateDestinationPluginManager $destination_plugin_manager
|
||||
* The destination migration plugin manager.
|
||||
* @param \Drupal\migrate\Plugin\MigratePluginManager $id_map_plugin_manager
|
||||
* The ID map migration plugin manager.
|
||||
* @param \Drupal\migrate_drupal\FieldDiscoveryInterface $field_discovery
|
||||
* The migration field discovery service.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationPluginManagerInterface $migration_plugin_manager, MigratePluginManager $source_plugin_manager, MigratePluginManager $process_plugin_manager, MigrateDestinationPluginManager $destination_plugin_manager, MigratePluginManager $id_map_plugin_manager, FieldDiscoveryInterface $field_discovery) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration_plugin_manager, $source_plugin_manager, $process_plugin_manager, $destination_plugin_manager, $id_map_plugin_manager);
|
||||
$this->fieldDiscovery = $field_discovery;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('plugin.manager.migration'),
|
||||
$container->get('plugin.manager.migrate.source'),
|
||||
$container->get('plugin.manager.migrate.process'),
|
||||
$container->get('plugin.manager.migrate.destination'),
|
||||
$container->get('plugin.manager.migrate.id_map'),
|
||||
$container->get('migrate_drupal.field_discovery')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProcess() {
|
||||
if (!$this->init) {
|
||||
$this->init = TRUE;
|
||||
$this->fieldDiscovery->addAllFieldProcesses($this);
|
||||
}
|
||||
return parent::getProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\field;
|
||||
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\MigrateFieldInterface;
|
||||
|
||||
/**
|
||||
* The base class for all field plugins.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\MigratePluginManager
|
||||
* @see \Drupal\migrate_drupal\Attribute\MigrateField
|
||||
* @see \Drupal\migrate_drupal\Plugin\MigrateFieldInterface
|
||||
* @see plugin_api
|
||||
*
|
||||
* @ingroup migration
|
||||
*/
|
||||
abstract class FieldPluginBase extends PluginBase implements MigrateFieldInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alterFieldMigration(MigrationInterface $migration) {
|
||||
$process[0]['map'][$this->pluginId][$this->pluginId] = $this->pluginId;
|
||||
$migration->mergeProcessOfProperty('type', $process);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alterFieldInstanceMigration(MigrationInterface $migration) {
|
||||
// Nothing to do by default with field instances.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alterFieldWidgetMigration(MigrationInterface $migration) {
|
||||
$process = [];
|
||||
foreach ($this->getFieldWidgetMap() as $source_widget => $destination_widget) {
|
||||
$process['type']['map'][$source_widget] = $destination_widget;
|
||||
}
|
||||
$migration->mergeProcessOfProperty('options/type', $process);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldFormatterType(Row $row) {
|
||||
return $row->getSourceProperty('formatter/type');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldFormatterMap() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldWidgetType(Row $row) {
|
||||
return $row->getSourceProperty('widget/type');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldWidgetMap() {
|
||||
// By default, use the plugin ID for the widget types.
|
||||
return [
|
||||
$this->pluginId => $this->pluginId . '_default',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alterFieldFormatterMigration(MigrationInterface $migration) {
|
||||
$process = [];
|
||||
// Some migrate field plugin IDs are prefixed with 'd6_' or 'd7_'. Since the
|
||||
// plugin ID is used in the static map as the module name, we have to remove
|
||||
// this prefix from the plugin ID.
|
||||
$plugin_id = preg_replace('/d[67]_/', '', $this->pluginId);
|
||||
foreach ($this->getFieldFormatterMap() as $source_format => $destination_format) {
|
||||
$process[0]['map'][$plugin_id][$source_format] = $destination_format;
|
||||
}
|
||||
$migration->mergeProcessOfProperty('options/type', $process);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data) {
|
||||
$process = [
|
||||
'plugin' => 'get',
|
||||
'source' => $field_name,
|
||||
];
|
||||
$migration->mergeProcessOfProperty($field_name, $process);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldType(Row $row) {
|
||||
$field_type = $row->getSourceProperty('type');
|
||||
|
||||
if (isset($this->pluginDefinition['type_map'][$field_type])) {
|
||||
return $this->pluginDefinition['type_map'][$field_type];
|
||||
}
|
||||
else {
|
||||
return $field_type;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\field;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Base class for Drupal reference fields.
|
||||
*/
|
||||
abstract class ReferenceBase extends FieldPluginBase {
|
||||
|
||||
/**
|
||||
* Gets the plugin ID for the reference type migration.
|
||||
*
|
||||
* The reference type migration will be added as a required dependency.
|
||||
*
|
||||
* @return string
|
||||
* The plugin id.
|
||||
*/
|
||||
abstract protected function getEntityTypeMigrationId();
|
||||
|
||||
/**
|
||||
* Gets the name of the field property which holds the entity ID.
|
||||
*
|
||||
* @return string
|
||||
* The entity id.
|
||||
*/
|
||||
abstract protected function entityId();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alterFieldInstanceMigration(MigrationInterface $migration) {
|
||||
parent::alterFieldInstanceMigration($migration);
|
||||
|
||||
// Add the reference migration as a required dependency to this migration.
|
||||
$migration->addRequiredDependencies([$this->getEntityTypeMigrationId()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data) {
|
||||
$process = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => $field_name,
|
||||
'process' => ['target_id' => $this->entityId()],
|
||||
];
|
||||
$migration->setProcessOfProperty($field_name, $process);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldWidgetMap() {
|
||||
return [
|
||||
$this->pluginId . '_select' => 'options_select',
|
||||
$this->pluginId . '_buttons' => 'options_buttons',
|
||||
$this->pluginId . '_autocomplete' => 'entity_reference_autocomplete_tags',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\field\d6;
|
||||
|
||||
// cspell:ignore nodereference
|
||||
|
||||
use Drupal\migrate_drupal\Attribute\MigrateField;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\field\ReferenceBase;
|
||||
|
||||
/**
|
||||
* MigrateField Plugin for Drupal 6 node reference fields.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[MigrateField(
|
||||
id: 'nodereference',
|
||||
core: [6],
|
||||
type_map: [
|
||||
'nodereference' => 'entity_reference',
|
||||
],
|
||||
source_module: 'nodereference',
|
||||
destination_module: 'core',
|
||||
)]
|
||||
class NodeReference extends ReferenceBase {
|
||||
|
||||
/**
|
||||
* The plugin ID for the reference type migration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $nodeTypeMigration = 'd6_node_type';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEntityTypeMigrationId() {
|
||||
return $this->nodeTypeMigration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function entityId() {
|
||||
return 'nid';
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\field\d6;
|
||||
|
||||
// cspell:ignore userreference
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate_drupal\Attribute\MigrateField;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\field\ReferenceBase;
|
||||
|
||||
/**
|
||||
* MigrateField Plugin for Drupal 6 user reference fields.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[MigrateField(
|
||||
id: 'userreference',
|
||||
core: [6],
|
||||
type_map: [
|
||||
'userreference' => 'entity_reference',
|
||||
],
|
||||
source_module: 'userreference',
|
||||
destination_module: 'core',
|
||||
)]
|
||||
class UserReference extends ReferenceBase {
|
||||
|
||||
/**
|
||||
* The plugin ID for the reference type migration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $userTypeMigration = 'd6_user_role';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEntityTypeMigrationId() {
|
||||
return $this->userTypeMigration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function entityId() {
|
||||
return 'uid';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data) {
|
||||
$process = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => $field_name,
|
||||
'process' => [
|
||||
'target_id' => [
|
||||
'plugin' => 'migration_lookup',
|
||||
'migration' => 'd6_user',
|
||||
'source' => 'uid',
|
||||
],
|
||||
],
|
||||
];
|
||||
$migration->setProcessOfProperty($field_name, $process);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\field\d7;
|
||||
|
||||
use Drupal\migrate_drupal\Attribute\MigrateField;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\field\ReferenceBase;
|
||||
|
||||
/**
|
||||
* MigrateField plugin for Drupal 7 node_reference fields.
|
||||
*/
|
||||
#[MigrateField(
|
||||
id: 'node_reference',
|
||||
core: [7],
|
||||
type_map: [
|
||||
'node_reference' => 'entity_reference',
|
||||
],
|
||||
source_module: 'node_reference',
|
||||
destination_module: 'core',
|
||||
)]
|
||||
class NodeReference extends ReferenceBase {
|
||||
|
||||
/**
|
||||
* The plugin ID for the reference type migration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $nodeTypeMigration = 'd7_node_type';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEntityTypeMigrationId() {
|
||||
return $this->nodeTypeMigration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function entityId() {
|
||||
return 'nid';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldFormatterMap() {
|
||||
return [
|
||||
'node_reference_default' => 'entity_reference_label',
|
||||
'node_reference_plain' => 'entity_reference_label',
|
||||
'node_reference_nid' => 'entity_reference_entity_id',
|
||||
'node_reference_node' => 'entity_reference_entity_view',
|
||||
'node_reference_path' => 'entity_reference_label',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\field\d7;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate_drupal\Attribute\MigrateField;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\field\ReferenceBase;
|
||||
|
||||
/**
|
||||
* MigrateField plugin for Drupal 7 user_reference fields.
|
||||
*/
|
||||
#[MigrateField(
|
||||
id: 'user_reference',
|
||||
core: [7],
|
||||
type_map: [
|
||||
'user_reference' => 'entity_reference',
|
||||
],
|
||||
source_module: 'user_reference',
|
||||
destination_module: 'core',
|
||||
)]
|
||||
class UserReference extends ReferenceBase {
|
||||
|
||||
/**
|
||||
* The plugin ID for the reference type migration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $userTypeMigration = 'd7_user_role';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEntityTypeMigrationId() {
|
||||
return $this->userTypeMigration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function entityId() {
|
||||
return 'uid';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldFormatterMap() {
|
||||
return [
|
||||
'user_reference_default' => 'entity_reference_label',
|
||||
'user_reference_plain' => 'entity_reference_label',
|
||||
'user_reference_uid' => 'entity_reference_entity_id',
|
||||
'user_reference_user' => 'entity_reference_entity_view',
|
||||
'user_reference_path' => 'entity_reference_label',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data) {
|
||||
$process = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => $field_name,
|
||||
'process' => [
|
||||
'target_id' => [
|
||||
'plugin' => 'migration_lookup',
|
||||
'migration' => 'd7_user',
|
||||
'source' => 'uid',
|
||||
],
|
||||
],
|
||||
];
|
||||
$migration->setProcessOfProperty($field_name, $process);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateProcess;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Returns only the nid from migration_lookup on node_complete migration.
|
||||
*
|
||||
* It is possible that migration_lookups that use the classic node migrations
|
||||
* in the migration key have been altered to include the complete node
|
||||
* migration. The classic node migration and complete node migration have a
|
||||
* different number of destination keys. This process plugin will ensure that
|
||||
* when the complete node migration is used in the lookup the nid value is
|
||||
* returned. This keeps the behavior the same as the classic node migration.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\MigrateProcessInterface
|
||||
*/
|
||||
#[MigrateProcess('node_complete_node_lookup')]
|
||||
class NodeCompleteNodeLookup extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
if (is_array($value) && count($value) === 3) {
|
||||
return $value[0];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateProcess;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Returns only the vid from migration_lookup on node_complete migration.
|
||||
*
|
||||
* It is possible that migration_lookups that use the classic node migrations
|
||||
* in the migration key have been altered to include the complete node
|
||||
* migration. The classic node migration and complete node migration have a
|
||||
* different number of destination keys. This process plugin will ensure that
|
||||
* when the complete node migration is used in the lookup the vid value is
|
||||
* returned. This keeps the behavior the same as the classic node migration.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\MigrateProcessInterface
|
||||
*/
|
||||
#[MigrateProcess('node_complete_node_revision_lookup')]
|
||||
class NodeCompleteNodeRevisionLookup extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
if (is_array($value) && count($value) === 3) {
|
||||
return $value[1];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateProcess;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Returns nid and langcode from migration_lookup on node_complete migration.
|
||||
*
|
||||
* It is possible that migration_lookups that use the classic node migrations
|
||||
* in the migration key have been altered to include the complete node
|
||||
* migration. The classic node migration and complete node migration have a
|
||||
* different number of destination keys. This process plugin will ensure that
|
||||
* when the complete node migration is used in the lookup the nid and langcode
|
||||
* values are returned. This keeps the behavior the same as the classic node
|
||||
* migration.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\MigrateProcessInterface
|
||||
*/
|
||||
#[MigrateProcess('node_complete_node_translation_lookup')]
|
||||
class NodeCompleteNodeTranslationLookup extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
if (is_array($value) && count($value) === 3) {
|
||||
// If the language is 'und' then the node was not translated.
|
||||
if ($value[2] === 'und') {
|
||||
return NULL;
|
||||
}
|
||||
unset($value[1]);
|
||||
return array_values($value);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,299 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\migrate\EntityFieldDefinitionTrait;
|
||||
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
|
||||
use Drupal\migrate\Plugin\MigrateSourceInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Source plugin to get content entities from the current version of Drupal.
|
||||
*
|
||||
* @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
|
||||
* \Drupal\migrate\Plugin\migrate\source\ContentEntity instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3498916
|
||||
*
|
||||
* This plugin uses the Entity API to export entity data. If the source entity
|
||||
* type has custom field storage fields or computed fields, this class will need
|
||||
* to be extended and the new class will need to load/calculate the values for
|
||||
* those fields.
|
||||
*
|
||||
* Available configuration keys:
|
||||
* - entity_type: The entity type ID of the entities being exported. This is
|
||||
* calculated dynamically by the deriver so it is only needed if the deriver
|
||||
* is not utilized, i.e., a custom source plugin.
|
||||
* - bundle: (optional) If the entity type is bundleable, only return entities
|
||||
* of this bundle.
|
||||
* - include_translations: (optional) Indicates if the entity translations
|
||||
* should be included, defaults to TRUE.
|
||||
* - add_revision_id: (optional) Indicates if the revision key is added to the
|
||||
* source IDs, defaults to TRUE.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* This will return the default revision for all nodes, from every bundle and
|
||||
* every translation. The revision key is added to the source IDs.
|
||||
* @code
|
||||
* source:
|
||||
* plugin: content_entity:node
|
||||
* @endcode
|
||||
*
|
||||
* This will return the default revision for all nodes, from every bundle and
|
||||
* every translation. The revision key is not added to the source IDs.
|
||||
* @code
|
||||
* source:
|
||||
* plugin: content_entity:node
|
||||
* add_revision_id: false
|
||||
* @endcode
|
||||
*
|
||||
* This will only return nodes of type 'article' in their default language.
|
||||
* @code
|
||||
* source:
|
||||
* plugin: content_entity:node
|
||||
* bundle: article
|
||||
* include_translations: false
|
||||
* @endcode
|
||||
*
|
||||
* For additional configuration keys, refer to the parent class:
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
*/
|
||||
class ContentEntity extends SourcePluginBase implements ContainerFactoryPluginInterface {
|
||||
use EntityFieldDefinitionTrait;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The entity field manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* The entity type bundle info service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* The entity type definition.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeInterface
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* The plugin's default configuration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultConfiguration = [
|
||||
'bundle' => NULL,
|
||||
'include_translations' => TRUE,
|
||||
'add_revision_id' => TRUE,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info) {
|
||||
@trigger_error(__CLASS__ . ' is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use \Drupal\migrate\Plugin\migrate\source\ContentEntity instead. See https://www.drupal.org/node/3498916', E_USER_DEPRECATED);
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);
|
||||
if (empty($plugin_definition['entity_type'])) {
|
||||
throw new InvalidPluginDefinitionException($plugin_id, 'Missing required "entity_type" definition.');
|
||||
}
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
$this->entityType = $this->entityTypeManager->getDefinition($plugin_definition['entity_type']);
|
||||
if (!$this->entityType instanceof ContentEntityTypeInterface) {
|
||||
throw new InvalidPluginDefinitionException($plugin_id, sprintf('The entity type (%s) is not supported. The "content_entity" source plugin only supports content entities.', $plugin_definition['entity_type']));
|
||||
}
|
||||
if (!empty($configuration['bundle'])) {
|
||||
if (!$this->entityType->hasKey('bundle')) {
|
||||
throw new \InvalidArgumentException(sprintf('A bundle was provided but the entity type (%s) is not bundleable.', $plugin_definition['entity_type']));
|
||||
}
|
||||
$bundle_info = array_keys($this->entityTypeBundleInfo->getBundleInfo($this->entityType->id()));
|
||||
if (!in_array($configuration['bundle'], $bundle_info, TRUE)) {
|
||||
throw new \InvalidArgumentException(sprintf('The provided bundle (%s) is not valid for the (%s) entity type.', $configuration['bundle'], $plugin_definition['entity_type']));
|
||||
}
|
||||
}
|
||||
parent::__construct($configuration + $this->defaultConfiguration, $plugin_id, $plugin_definition, $migration);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, ?MigrationInterface $migration = NULL) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$migration,
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('entity_type.bundle.info')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString() {
|
||||
return (string) $this->entityType->getPluralLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the iterator with the source data.
|
||||
*
|
||||
* @return \Generator
|
||||
* A data generator for this source.
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
$ids = $this->query()->execute();
|
||||
return $this->yieldEntities($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and yields entities, one at a time.
|
||||
*
|
||||
* @param array $ids
|
||||
* The entity IDs.
|
||||
*
|
||||
* @return \Generator
|
||||
* An iterable of the loaded entities.
|
||||
*/
|
||||
protected function yieldEntities(array $ids) {
|
||||
$storage = $this->entityTypeManager
|
||||
->getStorage($this->entityType->id());
|
||||
foreach ($ids as $id) {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $storage->load($id);
|
||||
yield $this->toArray($entity);
|
||||
if ($this->configuration['include_translations']) {
|
||||
foreach ($entity->getTranslationLanguages(FALSE) as $language) {
|
||||
yield $this->toArray($entity->getTranslation($language->getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an entity to an array.
|
||||
*
|
||||
* Makes all IDs into flat values. All other values are returned as per
|
||||
* $entity->toArray(), which is a nested array.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
|
||||
* The entity to convert.
|
||||
*
|
||||
* @return array
|
||||
* The entity, represented as an array.
|
||||
*/
|
||||
protected function toArray(ContentEntityInterface $entity) {
|
||||
$return = $entity->toArray();
|
||||
// This is necessary because the IDs must be flat. They cannot be nested for
|
||||
// the ID map.
|
||||
foreach (array_keys($this->getIds()) as $id) {
|
||||
/** @var \Drupal\Core\TypedData\Plugin\DataType\ItemList $value */
|
||||
$value = $entity->get($id);
|
||||
// Force the IDs on top of the previous values.
|
||||
$return[$id] = $value->first()->getString();
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to retrieve the entities.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\Query\QueryInterface
|
||||
* The query.
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->entityTypeManager
|
||||
->getStorage($this->entityType->id())
|
||||
->getQuery()
|
||||
->accessCheck(FALSE);
|
||||
if (!empty($this->configuration['bundle'])) {
|
||||
$query->condition($this->entityType->getKey('bundle'), $this->configuration['bundle']);
|
||||
}
|
||||
// Exclude anonymous user account.
|
||||
if ($this->entityType->id() === 'user' && !empty($this->entityType->getKey('id'))) {
|
||||
$query->condition($this->entityType->getKey('id'), 0, '>');
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count($refresh = FALSE): int {
|
||||
// If no translations are included, then a simple query is possible.
|
||||
if (!$this->configuration['include_translations']) {
|
||||
return parent::count($refresh);
|
||||
}
|
||||
// @todo Determine a better way to retrieve a valid count for translations.
|
||||
// https://www.drupal.org/project/drupal/issues/2937166
|
||||
return MigrateSourceInterface::NOT_COUNTABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doCount() {
|
||||
return $this->query()->count()->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
// Retrieving fields from a non-fieldable content entity will throw a
|
||||
// LogicException. Return an empty list of fields instead.
|
||||
if (!$this->entityType->entityClassImplements('Drupal\Core\Entity\FieldableEntityInterface')) {
|
||||
return [];
|
||||
}
|
||||
$field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($this->entityType->id());
|
||||
if (!empty($this->configuration['bundle'])) {
|
||||
$field_definitions += $this->entityFieldManager->getFieldDefinitions($this->entityType->id(), $this->configuration['bundle']);
|
||||
}
|
||||
$fields = array_map(function ($definition) {
|
||||
return (string) $definition->getLabel();
|
||||
}, $field_definitions);
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$id_key = $this->entityType->getKey('id');
|
||||
$ids[$id_key] = $this->getDefinitionFromEntity($id_key);
|
||||
if ($this->configuration['add_revision_id'] && $this->entityType->isRevisionable()) {
|
||||
$revision_key = $this->entityType->getKey('revision');
|
||||
$ids[$revision_key] = $this->getDefinitionFromEntity($revision_key);
|
||||
}
|
||||
if ($this->entityType->isTranslatable()) {
|
||||
$langcode_key = $this->entityType->getKey('langcode');
|
||||
$ids[$langcode_key] = $this->getDefinitionFromEntity($langcode_key);
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Deriver for content entity source plugins.
|
||||
*
|
||||
* @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
|
||||
* \Drupal\migrate\Plugin\migrate\source\ContentEntityDeriver instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3498916
|
||||
*/
|
||||
class ContentEntityDeriver extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a new ContentEntityDeriver.
|
||||
*
|
||||
* @param string $base_plugin_id
|
||||
* The base plugin ID.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
|
||||
* The entity type manager.
|
||||
*/
|
||||
public function __construct($base_plugin_id, EntityTypeManagerInterface $entityTypeManager) {
|
||||
@trigger_error(__CLASS__ . ' is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use \Drupal\migrate\Plugin\migrate\source\ContentEntity instead. See https://www.drupal.org/node/3498916', E_USER_DEPRECATED);
|
||||
$this->entityTypeManager = $entityTypeManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$base_plugin_id,
|
||||
$container->get('entity_type.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$this->derivatives = [];
|
||||
foreach ($this->entityTypeManager->getDefinitions() as $id => $definition) {
|
||||
if ($definition instanceof ContentEntityTypeInterface) {
|
||||
$this->derivatives[$id] = $base_plugin_definition;
|
||||
// Provide entity_type so the source can be used apart from a deriver.
|
||||
$this->derivatives[$id]['entity_type'] = $id;
|
||||
}
|
||||
}
|
||||
return parent::getDerivativeDefinitions($base_plugin_definition);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Component\Plugin\DependentPluginInterface;
|
||||
use Drupal\Core\Entity\DependencyTrait;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* A base class for source plugins using a Drupal database as a source.
|
||||
*
|
||||
* Provides general purpose helper methods that are commonly needed
|
||||
* when writing source plugins that use a Drupal database as a source, for
|
||||
* example:
|
||||
* - Check if the given module exists in the source database.
|
||||
* - Read Drupal configuration variables from the source database.
|
||||
*
|
||||
* For a full list, refer to the methods of this class.
|
||||
*
|
||||
* For available configuration keys, refer to the parent classes.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SqlBase
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
*/
|
||||
abstract class DrupalSqlBase extends SqlBase implements DependentPluginInterface {
|
||||
|
||||
use DependencyTrait;
|
||||
|
||||
/**
|
||||
* The contents of the system table.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $systemData;
|
||||
|
||||
/**
|
||||
* If the source provider is missing.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $requirements = TRUE;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityTypeManagerInterface $entity_type_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $state);
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all system data information from the source Drupal database.
|
||||
*
|
||||
* @return array
|
||||
* List of system table information keyed by type and name.
|
||||
*/
|
||||
public function getSystemData() {
|
||||
if (!isset($this->systemData)) {
|
||||
$this->systemData = [];
|
||||
try {
|
||||
$results = $this->select('system', 's')
|
||||
->fields('s')
|
||||
->execute();
|
||||
foreach ($results as $result) {
|
||||
$this->systemData[$result['type']][$result['name']] = $result;
|
||||
}
|
||||
}
|
||||
catch (\Exception) {
|
||||
// The table might not exist for example in tests.
|
||||
}
|
||||
}
|
||||
return $this->systemData;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, ?MigrationInterface $migration = NULL) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$migration,
|
||||
$container->get('state'),
|
||||
$container->get('entity_type.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkRequirements() {
|
||||
parent::checkRequirements();
|
||||
if ($this->pluginDefinition['requirements_met'] === TRUE) {
|
||||
if ($source_module = $this->getSourceModule()) {
|
||||
if ($this->moduleExists($source_module)) {
|
||||
if (isset($this->pluginDefinition['minimum_version'])) {
|
||||
$minimum_version = (int) $this->pluginDefinition['minimum_version'];
|
||||
$installed_version = (int) $this->getModuleSchemaVersion($source_module);
|
||||
if ($minimum_version > $installed_version) {
|
||||
throw new RequirementsException('Required minimum version ' . $this->pluginDefinition['minimum_version'], ['minimum_version' => $this->pluginDefinition['minimum_version']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new RequirementsException('The module ' . $source_module . ' is not enabled in the source site.', ['source_module' => $source_module]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a module schema_version from the source Drupal database.
|
||||
*
|
||||
* @param string $module
|
||||
* Name of module.
|
||||
*
|
||||
* @return mixed
|
||||
* The current module schema version on the origin system table or FALSE if
|
||||
* not found.
|
||||
*/
|
||||
protected function getModuleSchemaVersion($module) {
|
||||
$system_data = $this->getSystemData();
|
||||
return $system_data['module'][$module]['schema_version'] ?? FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given module is enabled in the source Drupal database.
|
||||
*
|
||||
* @param string $module
|
||||
* Name of module to check.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if module is enabled on the origin system, FALSE if not.
|
||||
*/
|
||||
protected function moduleExists($module) {
|
||||
$system_data = $this->getSystemData();
|
||||
return !empty($system_data['module'][$module]['status']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a variable from a source Drupal database.
|
||||
*
|
||||
* @param string $name
|
||||
* Name of the variable.
|
||||
* @param mixed $default
|
||||
* The default value.
|
||||
*
|
||||
* @return mixed
|
||||
* The variable value.
|
||||
*/
|
||||
protected function variableGet($name, $default) {
|
||||
try {
|
||||
$result = $this->select('variable', 'v')
|
||||
->fields('v', ['value'])
|
||||
->condition('name', $name)
|
||||
->execute()
|
||||
->fetchField();
|
||||
}
|
||||
// The table might not exist.
|
||||
catch (\Exception) {
|
||||
$result = FALSE;
|
||||
}
|
||||
return $result !== FALSE ? unserialize($result) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
// Generic handling for Drupal source plugin constants.
|
||||
if (isset($this->configuration['constants']['entity_type'])) {
|
||||
$this->addDependency('module', $this->entityTypeManager->getDefinition($this->configuration['constants']['entity_type'])->getProvider());
|
||||
}
|
||||
if (isset($this->configuration['constants']['module'])) {
|
||||
$this->addDependency('module', $this->configuration['constants']['module']);
|
||||
}
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourceModule(): ?string {
|
||||
return parent::getSourceModule() ?? $this->pluginDefinition['source_module'] ?? NULL;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Component\Plugin\DependentPluginInterface;
|
||||
use Drupal\Core\Entity\DependencyTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\migrate\source\EmptySource as BaseEmptySource;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
|
||||
/**
|
||||
* Source returning an empty row with Drupal specific config dependencies.
|
||||
*
|
||||
* For more information and available configuration keys, refer to the parent
|
||||
* classes.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\EmptySource
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "md_empty",
|
||||
* source_module = "system",
|
||||
* )
|
||||
*/
|
||||
class EmptySource extends BaseEmptySource implements ContainerFactoryPluginInterface, DependentPluginInterface {
|
||||
|
||||
use DependencyTrait;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityTypeManagerInterface $entity_type_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, ?MigrationInterface $migration = NULL) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$migration,
|
||||
$container->get('entity_type.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
// The empty source plugin supports the entity_type constant.
|
||||
if (isset($this->configuration['constants']['entity_type'])) {
|
||||
$this->addDependency('module', $this->entityTypeManager->getDefinition($this->configuration['constants']['entity_type'])->getProvider());
|
||||
}
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
// cspell:ignore objectid
|
||||
|
||||
/**
|
||||
* Gets an i18n translation from the source database.
|
||||
*/
|
||||
trait I18nQueryTrait {
|
||||
|
||||
/**
|
||||
* The i18n string table name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $i18nStringTable;
|
||||
|
||||
/**
|
||||
* Gets the translation for the property not already in the row.
|
||||
*
|
||||
* For some i18n migrations there are two translation values, such as a
|
||||
* translated title and a translated description, that need to be retrieved.
|
||||
* Since these values are stored in separate rows of the i18nStringTable
|
||||
* table we get them individually, one in the source plugin query() and the
|
||||
* other in prepareRow(). The names of the properties varies, for example,
|
||||
* in BoxTranslation they are 'body' and 'title' whereas in
|
||||
* MenuLinkTranslation they are 'title' and 'description'. This will save both
|
||||
* translations to the row.
|
||||
*
|
||||
* @param \Drupal\migrate\Row $row
|
||||
* The current migration row which must include both a 'language' property
|
||||
* and an 'objectid' property. The 'objectid' is the value for the
|
||||
* 'objectid' field in the i18n_string table.
|
||||
* @param string $property_not_in_row
|
||||
* The name of the property to get the translation for.
|
||||
* @param string $object_id_name
|
||||
* The value of the objectid in the i18n table.
|
||||
* @param \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map
|
||||
* The ID map.
|
||||
*
|
||||
* @return bool
|
||||
* FALSE if the property has already been migrated.
|
||||
*
|
||||
* @throws \Drupal\migrate\MigrateException
|
||||
*/
|
||||
protected function getPropertyNotInRowTranslation(Row $row, string $property_not_in_row, string $object_id_name, MigrateIdMapInterface $id_map): bool {
|
||||
$language = $row->getSourceProperty('language');
|
||||
if (!$language) {
|
||||
throw new MigrateException('No language found.');
|
||||
}
|
||||
$object_id = $row->getSourceProperty($object_id_name);
|
||||
if (!$object_id) {
|
||||
throw new MigrateException('No objectid found.');
|
||||
}
|
||||
|
||||
// If this row has been migrated it is a duplicate so skip it.
|
||||
if ($id_map->lookupDestinationIds([$object_id_name => $object_id, 'language' => $language])) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Save the translation for the property already in the row.
|
||||
$property_in_row = $row->getSourceProperty('property');
|
||||
$row->setSourceProperty($property_in_row . '_translated', $row->getSourceProperty('translation'));
|
||||
|
||||
// Get the translation, if one exists, for the property not already in the
|
||||
// row.
|
||||
$query = $this->select($this->i18nStringTable, 'i18n')
|
||||
->fields('i18n', ['lid'])
|
||||
->condition('i18n.property', $property_not_in_row)
|
||||
->condition('i18n.objectid', $object_id);
|
||||
$query->leftJoin('locales_target', 'lt', '[i18n].[lid] = [lt].[lid]');
|
||||
$query->condition('lt.language', $language);
|
||||
$query->addField('lt', 'translation');
|
||||
$results = $query->execute()->fetchAssoc();
|
||||
$row->setSourceProperty($property_not_in_row . '_translated', $results['translation'] ?? NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Drupal 6/7 variable source from database.
|
||||
*
|
||||
* This source class fetches variables from the source Drupal database.
|
||||
* Depending on the configuration, this returns zero or a single row and as such
|
||||
* is not a good example for any normal source class returning multiple rows.
|
||||
*
|
||||
* Available configuration keys (one of which must be defined):
|
||||
* - variables: (optional) The list of variables to retrieve from the source
|
||||
* database. Specified variables are retrieved in a single row.
|
||||
* - variables_no_row_if_missing: (optional) The list of variables to retrieve
|
||||
* from the source database. If any of the variables listed here are missing
|
||||
* in the source, then the source will return zero rows.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* With this configuration, the source will return one row even when the
|
||||
* "filter_fallback_format" variable isn't available:
|
||||
* @code
|
||||
* source:
|
||||
* plugin: variable
|
||||
* variables:
|
||||
* - filter_fallback_format
|
||||
* @endcode
|
||||
*
|
||||
* With this configuration, the source will return one row if the variable is
|
||||
* available, and zero if it isn't:
|
||||
* @code
|
||||
* source:
|
||||
* plugin: variable
|
||||
* variables_no_row_if_missing:
|
||||
* - filter_fallback_format
|
||||
* @endcode
|
||||
*
|
||||
* The variables and the variables_no_row_if_missing lists are always merged
|
||||
* together. All of the following configurations are valid:
|
||||
* @code
|
||||
* source:
|
||||
* plugin: variable
|
||||
* variables:
|
||||
* - book_child_type
|
||||
* - book_block_mode
|
||||
* - book_allowed_types
|
||||
* variables_no_row_if_missing:
|
||||
* - book_child_type
|
||||
* - book_block_mode
|
||||
* - book_allowed_types
|
||||
*
|
||||
* source:
|
||||
* plugin: variable
|
||||
* variables:
|
||||
* - book_child_type
|
||||
* - book_block_mode
|
||||
* variables_no_row_if_missing:
|
||||
* - book_allowed_types
|
||||
*
|
||||
* source:
|
||||
* plugin: variable
|
||||
* variables_no_row_if_missing:
|
||||
* - book_child_type
|
||||
* - book_block_mode
|
||||
* - book_allowed_types
|
||||
* @endcode
|
||||
*
|
||||
* For additional configuration keys, refer to the parent classes.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SqlBase
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "variable",
|
||||
* source_module = "system",
|
||||
* )
|
||||
*/
|
||||
class Variable extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* The variable names to fetch.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $variables;
|
||||
|
||||
/**
|
||||
* The variables that result in no row if any are missing from the source.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $variablesNoRowIfMissing;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityTypeManagerInterface $entity_type_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $state, $entity_type_manager);
|
||||
$this->variablesNoRowIfMissing = $this->configuration['variables_no_row_if_missing'] ?? [];
|
||||
$variables = $this->configuration['variables'] ?? [];
|
||||
$this->variables = array_unique(array_merge(array_values($variables), array_values($this->variablesNoRowIfMissing)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
if ($this->count()) {
|
||||
return new \ArrayIterator([$this->values()]);
|
||||
}
|
||||
|
||||
return new \ArrayIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the values of the variables specified in the plugin configuration.
|
||||
*
|
||||
* @return array
|
||||
* An associative array where the keys are the variables specified in the
|
||||
* plugin configuration and the values are the values found in the source.
|
||||
* Only those values are returned that are actually in the database.
|
||||
*/
|
||||
protected function values() {
|
||||
// Create an ID field so we can record migration in the map table.
|
||||
// Arbitrarily, use the first variable name.
|
||||
$values['id'] = reset($this->variables);
|
||||
return $values + array_map('unserialize', $this->prepareQuery()->execute()->fetchAllKeyed());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doCount() {
|
||||
if (empty($this->variablesNoRowIfMissing)) {
|
||||
return 1;
|
||||
}
|
||||
$variable_names = array_keys($this->query()->execute()->fetchAllAssoc('name'));
|
||||
|
||||
if (!empty(array_diff($this->variablesNoRowIfMissing, $variable_names))) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array_combine($this->variables, $this->variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->getDatabase()
|
||||
->select('variable', 'v')
|
||||
->fields('v', ['name', 'value'])
|
||||
->condition('name', $this->variables, 'IN');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['id']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
// cspell:ignore multirow
|
||||
|
||||
/**
|
||||
* Drupal 6/7 multiple variables source from database.
|
||||
*
|
||||
* Unlike the variable source plugin, this one returns one row per
|
||||
* variable.
|
||||
*
|
||||
* Available configuration keys:
|
||||
* - variables: (required) The list of variables to retrieve from the source
|
||||
* database. Each variable is retrieved in a separate row.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* @code
|
||||
* plugin: variable_multirow
|
||||
* variables:
|
||||
* - date_format_long
|
||||
* - date_format_medium
|
||||
* - date_format_short
|
||||
* @endcode
|
||||
*
|
||||
* In this example the specified variables are retrieved from the source
|
||||
* database one row per variable.
|
||||
*
|
||||
* For additional configuration keys, refer to the parent classes.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SqlBase
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "variable_multirow",
|
||||
* source_module = "system",
|
||||
* )
|
||||
*/
|
||||
class VariableMultiRow extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->select('variable', 'v')
|
||||
->fields('v', ['name', 'value'])
|
||||
// Cast scalars to array so we can consistently use an IN condition.
|
||||
->condition('name', (array) $this->configuration['variables'], 'IN');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return [
|
||||
'name' => $this->t('Name'),
|
||||
'value' => $this->t('Value'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
if ($value = $row->getSourceProperty('value')) {
|
||||
$row->setSourceProperty('value', unserialize($value));
|
||||
}
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['name']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Drupal 6 i18n_variable source from database.
|
||||
*
|
||||
* Available configuration keys:
|
||||
* - variables: (required) The list of variable translations to retrieve from
|
||||
* the source database. All translations are retrieved in a single row.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* @code
|
||||
* plugin: d6_variable_translation
|
||||
* variables:
|
||||
* - site_offline_message
|
||||
* @endcode
|
||||
* In this example the translations for site_offline_message variable are
|
||||
* retrieved from the source database.
|
||||
*
|
||||
* For additional configuration keys, refer to the parent classes.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SqlBase
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_variable_translation",
|
||||
* source_module = "i18n",
|
||||
* )
|
||||
*/
|
||||
class VariableTranslation extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* The variable names to fetch.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $variables;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityTypeManagerInterface $entity_type_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $state, $entity_type_manager);
|
||||
$this->variables = $this->configuration['variables'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
return new \ArrayIterator($this->values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the values of the variables specified in the plugin configuration.
|
||||
*
|
||||
* @return array
|
||||
* An associative array where the keys are the variables specified in the
|
||||
* plugin configuration and the values are the values found in the source.
|
||||
* A key/value pair is added for the language code. Only those values are
|
||||
* returned that are actually in the database.
|
||||
*/
|
||||
protected function values() {
|
||||
$values = [];
|
||||
$result = $this->prepareQuery()->execute()->FetchAllAssoc('language');
|
||||
foreach ($result as $i18n_variable) {
|
||||
$values[]['language'] = $i18n_variable->language;
|
||||
}
|
||||
$result = $this->prepareQuery()->execute()->FetchAll();
|
||||
foreach ($result as $i18n_variable) {
|
||||
foreach ($values as $key => $value) {
|
||||
if ($values[$key]['language'] === $i18n_variable->language) {
|
||||
$values[$key][$i18n_variable->name] = unserialize($i18n_variable->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doCount() {
|
||||
return $this->initializeIterator()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array_combine($this->variables, $this->variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->getDatabase()
|
||||
->select('i18n_variable', 'v')
|
||||
->fields('v')
|
||||
->condition('name', (array) $this->configuration['variables'], 'IN');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['language']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkRequirements() {
|
||||
if (!$this->getDatabase()->schema()->tableExists('i18n_variable')) {
|
||||
throw new RequirementsException("Source database table 'i18n_variable' does not exist");
|
||||
}
|
||||
parent::checkRequirements();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Base class for D7 source plugins which need to collect field values.
|
||||
*
|
||||
* Field values are collected from the Field API.
|
||||
*
|
||||
* Refer to the existing implementations for examples:
|
||||
*
|
||||
* @see \Drupal\node\Plugin\migrate\source\d7\Node
|
||||
* @see \Drupal\user\Plugin\migrate\source\d7\User
|
||||
*
|
||||
* For available configuration keys, refer to the parent classes.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SqlBase
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
*/
|
||||
abstract class FieldableEntity extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* Cached field and field instance definitions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldInfo;
|
||||
|
||||
/**
|
||||
* Returns all non-deleted field instances attached to a specific entity type.
|
||||
*
|
||||
* Typically, getFields() is used in the prepareRow method of a source plugin
|
||||
* to get a list of all the field instances of the entity. A source plugin can
|
||||
* then loop through the list of fields to do any other preparation before
|
||||
* processing the row. Typically, a source plugin will use getFieldValues()
|
||||
* to get the values of each field.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type ID.
|
||||
* @param string|null $bundle
|
||||
* (optional) The bundle.
|
||||
*
|
||||
* @return array[]
|
||||
* The field instances, keyed by field name.
|
||||
*/
|
||||
protected function getFields($entity_type, $bundle = NULL) {
|
||||
$cid = $entity_type . ':' . ($bundle ?? '');
|
||||
if (!isset($this->fieldInfo[$cid])) {
|
||||
$query = $this->select('field_config_instance', 'fci')
|
||||
->fields('fci')
|
||||
->condition('fci.entity_type', $entity_type)
|
||||
->condition('fci.bundle', $bundle ?? $entity_type)
|
||||
->condition('fci.deleted', 0);
|
||||
|
||||
// Join the 'field_config' table and add the 'translatable' setting to the
|
||||
// query.
|
||||
$query->leftJoin('field_config', 'fc', '[fci].[field_id] = [fc].[id]');
|
||||
$query->addField('fc', 'translatable');
|
||||
|
||||
$this->fieldInfo[$cid] = $query->execute()->fetchAllAssoc('field_name');
|
||||
}
|
||||
|
||||
return $this->fieldInfo[$cid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves field values for a single field of a single entity.
|
||||
*
|
||||
* Typically, getFieldValues() is used in the prepareRow method of a source
|
||||
* plugin where the return values are placed on the row source.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param string $field
|
||||
* The field name.
|
||||
* @param int $entity_id
|
||||
* The entity ID.
|
||||
* @param int|null $revision_id
|
||||
* (optional) The entity revision ID.
|
||||
* @param string $language
|
||||
* (optional) The field language.
|
||||
*
|
||||
* @return array
|
||||
* The raw field values, keyed and sorted by delta.
|
||||
*/
|
||||
protected function getFieldValues($entity_type, $field, $entity_id, $revision_id = NULL, $language = NULL) {
|
||||
$table = (isset($revision_id) ? 'field_revision_' : 'field_data_') . $field;
|
||||
$query = $this->select($table, 't')
|
||||
->fields('t')
|
||||
->condition('entity_type', $entity_type)
|
||||
->condition('entity_id', $entity_id)
|
||||
->condition('deleted', 0)
|
||||
->orderBy('delta');
|
||||
if (isset($revision_id)) {
|
||||
$query->condition('revision_id', $revision_id);
|
||||
}
|
||||
// Add 'language' as a query condition if it has been defined by Entity
|
||||
// Translation.
|
||||
if ($language) {
|
||||
$query->condition('language', $language);
|
||||
}
|
||||
$values = [];
|
||||
foreach ($query->execute() as $row) {
|
||||
foreach ($row as $key => $value) {
|
||||
$delta = $row['delta'];
|
||||
if (str_starts_with($key, $field)) {
|
||||
$column = substr($key, strlen($field) + 1);
|
||||
$values[$delta][$column] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an entity type uses Entity Translation.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return bool
|
||||
* Whether the entity type uses entity translation.
|
||||
*/
|
||||
protected function isEntityTranslatable($entity_type) {
|
||||
return in_array($entity_type, $this->variableGet('entity_translation_entity_types', []), TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an entity source language from the 'entity_translation' table.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param int $entity_id
|
||||
* The entity ID.
|
||||
*
|
||||
* @return string|bool
|
||||
* The entity source language or FALSE if no source language was found.
|
||||
*/
|
||||
protected function getEntityTranslationSourceLanguage($entity_type, $entity_id) {
|
||||
try {
|
||||
return $this->select('entity_translation', 'et')
|
||||
->fields('et', ['language'])
|
||||
->condition('entity_type', $entity_type)
|
||||
->condition('entity_id', $entity_id)
|
||||
->condition('source', '')
|
||||
->execute()
|
||||
->fetchField();
|
||||
}
|
||||
// The table might not exist.
|
||||
catch (\Exception) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Drupal 7 variable_store source from database.
|
||||
*
|
||||
* Available configuration keys:
|
||||
* - variables: (required) The list of variable translations to retrieve from
|
||||
* the source database. All translations are retrieved in a single row.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* @code
|
||||
* plugin: d7_variable_translation
|
||||
* variables:
|
||||
* - site_name
|
||||
* - site_slogan
|
||||
* @endcode
|
||||
* In this example the translations for site_name and site_slogan variables are
|
||||
* retrieved from the source database.
|
||||
*
|
||||
* For additional configuration keys, refer to the parent classes.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SqlBase
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_variable_translation",
|
||||
* source_module = "i18n_variable",
|
||||
* )
|
||||
*/
|
||||
class VariableTranslation extends DrupalSqlBase {
|
||||
/**
|
||||
* The variable names to fetch.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $variables;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityTypeManagerInterface $entity_type_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $state, $entity_type_manager);
|
||||
$this->variables = $this->configuration['variables'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
return new \ArrayIterator($this->values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the values of the variables specified in the plugin configuration.
|
||||
*
|
||||
* @return array
|
||||
* An associative array where the keys are the variables specified in the
|
||||
* plugin configuration and the values are the values found in the source.
|
||||
* A key/value pair is added for the language code. Only those values are
|
||||
* returned that are actually in the database.
|
||||
*/
|
||||
protected function values() {
|
||||
$values = [];
|
||||
$result = $this->prepareQuery()->execute()->FetchAllAssoc('realm_key');
|
||||
foreach ($result as $variable_store) {
|
||||
$values[]['language'] = $variable_store['realm_key'];
|
||||
}
|
||||
$result = $this->prepareQuery()->execute()->FetchAll();
|
||||
foreach ($result as $variable_store) {
|
||||
foreach ($values as $key => $value) {
|
||||
if ($values[$key]['language'] === $variable_store['realm_key']) {
|
||||
if ($variable_store['serialized']) {
|
||||
$values[$key][$variable_store['name']] = unserialize($variable_store['value']);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
$values[$key][$variable_store['name']] = $variable_store['value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doCount() {
|
||||
return $this->initializeIterator()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array_combine($this->variables, $this->variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['language']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->select('variable_store', 'vs')
|
||||
->fields('vs')
|
||||
->condition('realm', 'language')
|
||||
->condition('name', (array) $this->configuration['variables'], 'IN');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_drupal\Plugin\migrate\source\d8;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Drupal 8+ configuration source from database.
|
||||
*
|
||||
* @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
|
||||
* \Drupal\migrate\Plugin\migrate\source\ConfigEntity instead.
|
||||
* @see https://www.drupal.org/node/3508578
|
||||
*
|
||||
* Available configuration keys:
|
||||
* - collections: (optional) The collection of configuration storage to retrieve
|
||||
* from the source - can be a string or an array. If omitted, configuration
|
||||
* objects of all available collections are retrieved.
|
||||
* - names: (optional) Names of configuration objects to retrieve from the
|
||||
* source - can be a string or an array. If omitted, all available
|
||||
* configuration objects are retrieved.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* @code
|
||||
* source:
|
||||
* plugin: d8_config
|
||||
* names:
|
||||
* - node.type.article
|
||||
* - node.type.page
|
||||
* @endcode
|
||||
*
|
||||
* In this example configuration objects of article and page content types are
|
||||
* retrieved from the source database.
|
||||
*
|
||||
* @code
|
||||
* source:
|
||||
* plugin: d8_config
|
||||
* collections: language.fr
|
||||
* names:
|
||||
* - node.type.article
|
||||
* - node.type.page
|
||||
* @endcode
|
||||
*
|
||||
* In this example configuration objects are filtered by language.fr collection.
|
||||
* As a result, French versions of specified configuration objects are retrieved
|
||||
* from the source database.
|
||||
*
|
||||
* For additional configuration keys, refer to the parent classes.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SqlBase
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d8_config",
|
||||
* source_module = "system",
|
||||
* )
|
||||
*/
|
||||
class Config extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityTypeManagerInterface $entity_type_manager) {
|
||||
@trigger_error(__CLASS__ . ' is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use \Drupal\migrate\Plugin\migrate\source\ContentEntity instead. See https://www.drupal.org/node/3508578', E_USER_DEPRECATED);
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $state, $entity_type_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('config', 'c')
|
||||
->fields('c', ['collection', 'name', 'data']);
|
||||
if (!empty($this->configuration['collections'])) {
|
||||
$query->condition('collection', (array) $this->configuration['collections'], 'IN');
|
||||
}
|
||||
if (!empty($this->configuration['names'])) {
|
||||
$query->condition('name', (array) $this->configuration['names'], 'IN');
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
$row->setSourceProperty('data', unserialize($row->getSourceProperty('data')));
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return [
|
||||
'collection' => $this->t('The config object collection.'),
|
||||
'name' => $this->t('The config object name.'),
|
||||
'data' => $this->t('Serialized configuration object data.'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['collection']['type'] = 'string';
|
||||
$ids['name']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
||||
69
web/core/modules/migrate_drupal/src/Tests/StubTestTrait.php
Normal file
69
web/core/modules/migrate_drupal/src/Tests/StubTestTrait.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_drupal\Tests;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Provides common functionality for testing stubbing.
|
||||
*/
|
||||
trait StubTestTrait {
|
||||
|
||||
/**
|
||||
* Tests that creating a stub of an entity type results in a valid entity.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type we are stubbing.
|
||||
*/
|
||||
protected function performStubTest($entity_type_id) {
|
||||
$entity_id = $this->createEntityStub($entity_type_id);
|
||||
$this->assertNotEmpty($entity_id, 'Stub successfully created');
|
||||
// When validateStub fails, it will return an array with the violations.
|
||||
$this->assertEmpty($this->validateStub($entity_type_id, $entity_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a stub of the given entity type.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type we are stubbing.
|
||||
*
|
||||
* @return int
|
||||
* ID of the created entity.
|
||||
*/
|
||||
protected function createEntityStub($entity_type_id) {
|
||||
// Create a dummy migration to pass to the destination plugin.
|
||||
$definition = [
|
||||
'migration_tags' => ['Stub test'],
|
||||
'source' => ['plugin' => 'empty'],
|
||||
'process' => [],
|
||||
'destination' => ['plugin' => 'entity:' . $entity_type_id],
|
||||
];
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
$destination_plugin = $migration->getDestinationPlugin(TRUE);
|
||||
$stub_row = new Row([], [], TRUE);
|
||||
$destination_ids = $destination_plugin->import($stub_row);
|
||||
return reset($destination_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform validation on a stub entity.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type we are stubbing.
|
||||
* @param string $entity_id
|
||||
* ID of the stubbed entity to validate.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityConstraintViolationListInterface
|
||||
* List of constraint violations identified.
|
||||
*/
|
||||
protected function validateStub($entity_type_id, $entity_id) {
|
||||
$controller = \Drupal::entityTypeManager()->getStorage($entity_type_id);
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $stub_entity */
|
||||
$stub_entity = $controller->load($entity_id);
|
||||
return $stub_entity->validate();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user