Commit a916e4d4 authored by alexpott's avatar alexpott

Issue #2951550 by mikelutz, Jo Fitzgerald, quietone, heddn, alexpott,...

Issue #2951550 by mikelutz, Jo Fitzgerald, quietone, heddn, alexpott, phenaproxima: Make service for field discovery for use in migrate entity derivers
parent 2348afa4
......@@ -13,34 +13,9 @@ class D7Comment extends FieldMigration {
* {@inheritdoc}
*/
public function getProcess() {
if ($this->init) {
return parent::getProcess();
}
$this->init = TRUE;
if (!\Drupal::moduleHandler()->moduleExists('field')) {
return parent::getProcess();
}
$definition['source'] = [
'ignore_map' => TRUE,
] + $this->getSourceConfiguration();
$definition['source']['plugin'] = 'd7_field_instance';
$definition['destination']['plugin'] = 'null';
$definition['idMap']['plugin'] = 'null';
$field_migration = $this->migrationPluginManager->createStubMigration($definition);
foreach ($field_migration->getSourcePlugin() as $row) {
$field_name = $row->getSourceProperty('field_name');
$field_type = $row->getSourceProperty('type');
if ($this->fieldPluginManager->hasDefinition($field_type)) {
if (!isset($this->fieldPluginCache[$field_type])) {
$plugin_id = $this->fieldPluginManager->getPluginIdFromFieldType($field_type, [], $this);
$this->fieldPluginCache[$field_type] = $this->fieldPluginManager->createInstance($plugin_id, [], $this);
}
$info = $row->getSource();
$this->fieldPluginCache[$field_type]->defineValueProcessPipeline($this, $field_name, $info);
}
else {
$this->setProcessOfProperty($field_name, $field_name);
}
if (!$this->init) {
$this->init = TRUE;
$this->fieldDiscovery->addEntityFieldProcesses($this, 'comment');
}
return parent::getProcess();
}
......
......@@ -16,3 +16,12 @@ services:
- '@module_handler'
- '\Drupal\migrate_drupal\Annotation\MigrateCckField'
deprecated: The "%service_id%" service is deprecated. You should use the 'plugin.manager.migrate.field' service instead. See https://www.drupal.org/node/2751897
logger.channel.migrate_drupal:
parent: logger.channel_base
arguments: ['migrate_drupal']
migrate_drupal.field_discovery:
class: Drupal\migrate_drupal\FieldDiscovery
arguments:
- '@plugin.manager.migrate.field'
- '@plugin.manager.migration'
- '@logger.channel.migrate_drupal'
This diff is collapsed.
<?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);
}
......@@ -2,16 +2,12 @@
namespace Drupal\migrate_drupal\Plugin\migrate;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\Plugin\MigrateDestinationPluginManager;
use Drupal\migrate\Plugin\MigratePluginManager;
use Drupal\migrate\Plugin\Migration;
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
use Drupal\migrate\Plugin\RequirementsInterface;
use Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface;
use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface;
use Drupal\migrate_drupal\FieldDiscoveryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -27,6 +23,7 @@ class FieldMigration extends Migration implements ContainerFactoryPluginInterfac
* fallback to the old 'cck_plugin_method'.
*
* @const string
* @deprecated This constant is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use the migrate_drupal.field_discovery service instead. See https://www.drupal.org/node/3006076.
*/
const PLUGIN_METHOD = 'field_plugin_method';
......@@ -38,39 +35,11 @@ class FieldMigration extends Migration implements ContainerFactoryPluginInterfac
protected $init = FALSE;
/**
* List of field plugin IDs which have already run.
* The migration field discovery service.
*
* @var string[]
* @var \Drupal\migrate_drupal\FieldDiscoveryInterface
*/
protected $processedFieldTypes = [];
/**
* Already-instantiated field plugins, keyed by ID.
*
* @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface[]
*/
protected $fieldPluginCache;
/**
* The field plugin manager.
*
* @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface
*/
protected $fieldPluginManager;
/**
* Already-instantiated cckfield plugins, keyed by ID.
*
* @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface[]
*/
protected $cckPluginCache;
/**
* The cckfield plugin manager.
*
* @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface
*/
protected $cckPluginManager;
protected $fieldDiscovery;
/**
* Constructs a FieldMigration.
......@@ -81,10 +50,6 @@ class FieldMigration extends Migration implements ContainerFactoryPluginInterfac
* The plugin ID.
* @param mixed $plugin_definition
* The plugin definition.
* @param \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface $cck_manager
* The cckfield plugin manager.
* @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $field_manager
* The field plugin manager.
* @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager
* The migration plugin manager.
* @param \Drupal\migrate\Plugin\MigratePluginManager $source_plugin_manager
......@@ -95,11 +60,12 @@ class FieldMigration extends Migration implements ContainerFactoryPluginInterfac
* The destination migration plugin manager.
* @param \Drupal\migrate\Plugin\MigratePluginManager $idmap_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, MigrateCckFieldPluginManagerInterface $cck_manager, MigrateFieldPluginManagerInterface $field_manager, MigrationPluginManagerInterface $migration_plugin_manager, MigratePluginManager $source_plugin_manager, MigratePluginManager $process_plugin_manager, MigrateDestinationPluginManager $destination_plugin_manager, MigratePluginManager $idmap_plugin_manager) {
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 $idmap_plugin_manager, FieldDiscoveryInterface $field_discovery) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration_plugin_manager, $source_plugin_manager, $process_plugin_manager, $destination_plugin_manager, $idmap_plugin_manager);
$this->cckPluginManager = $cck_manager;
$this->fieldPluginManager = $field_manager;
$this->fieldDiscovery = $field_discovery;
}
/**
......@@ -110,13 +76,12 @@ public static function create(ContainerInterface $container, array $configuratio
$configuration,
$plugin_id,
$plugin_definition,
$container->get('plugin.manager.migrate.cckfield'),
$container->get('plugin.manager.migrate.field'),
$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('plugin.manager.migrate.id_map'),
$container->get('migrate_drupal.field_discovery')
);
}
......@@ -126,44 +91,7 @@ public static function create(ContainerInterface $container, array $configuratio
public function getProcess() {
if (!$this->init) {
$this->init = TRUE;
$source_plugin = $this->migrationPluginManager->createInstance($this->pluginId)->getSourcePlugin();
if ($source_plugin instanceof RequirementsInterface) {
try {
$source_plugin->checkRequirements();
}
catch (RequirementsException $e) {
// Kill the rest of the method.
$source_plugin = [];
}
}
foreach ($source_plugin as $row) {
$field_type = $row->getSourceProperty('type');
try {
$plugin_id = $this->fieldPluginManager->getPluginIdFromFieldType($field_type, [], $this);
$manager = $this->fieldPluginManager;
}
catch (PluginNotFoundException $ex) {
try {
$plugin_id = $this->cckPluginManager->getPluginIdFromFieldType($field_type, [], $this);
$manager = $this->cckPluginManager;
}
catch (PluginNotFoundException $ex) {
continue;
}
}
if (!isset($this->processedFieldTypes[$field_type]) && $manager->hasDefinition($plugin_id)) {
$this->processedFieldTypes[$field_type] = TRUE;
// Allow the field plugin to alter the migration as necessary so that
// it knows how to handle fields of this type.
if (!isset($this->fieldPluginCache[$field_type])) {
$this->fieldPluginCache[$field_type] = $manager->createInstance($plugin_id, [], $this);
}
}
$method = $this->pluginDefinition[static::PLUGIN_METHOD];
call_user_func([$this->fieldPluginCache[$field_type], $method], $this);
}
$this->fieldDiscovery->addAllFieldProcesses($this);
}
return parent::getProcess();
}
......
name: 'Migrate drupal field discovery tet'
type: module
description: 'Module containing a test class exposing protected field discovery methods'
package: Testing
version: VERSION
core: 8.x
<?php
namespace Drupal\field_discovery_test;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
use Drupal\migrate_drupal\FieldDiscovery;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface;
/**
* A test class to expose protected methods.
*/
class FieldDiscoveryTestClass extends FieldDiscovery {
/**
* An array of test data.
*
* @var array
*/
protected $testData;
/**
* Constructs a FieldDiscoveryTestClass 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 \Drupal\Core\Logger\LoggerChannelInterface $logger
* The logger.
* @param array $test_data
* An array of test data, keyed by method name, for overridden methods to
* return for the purposes of testing other methods.
*/
public function __construct(MigrateFieldPluginManagerInterface $field_plugin_manager, MigrationPluginManagerInterface $migration_plugin_manager, LoggerChannelInterface $logger, array $test_data = []) {
parent::__construct($field_plugin_manager, $migration_plugin_manager, $logger);
$this->testData = $test_data;
}
/**
* {@inheritdoc}
*/
public function getAllFields($core) {
if (!empty($this->testData['getAllFields'][$core])) {
return $this->testData['getAllFields'][$core];
}
return parent::getAllFields($core);
}
/**
* {@inheritdoc}
*/
public function getBundleFields($core, $entity_type_id, $bundle) {
return parent::getBundleFields($core, $entity_type_id, $bundle);
}
/**
* {@inheritdoc}
*/
public function getEntityFields($core, $entity_type_id) {
return parent::getEntityFields($core, $entity_type_id);
}
/**
* {@inheritdoc}
*/
public function getFieldInstanceStubMigrationDefinition($core) {
return parent::getFieldInstanceStubMigrationDefinition($core);
}
/**
* {@inheritdoc}
*/
public function getCoreVersion(MigrationInterface $migration) {
return parent::getCoreVersion($migration);
}
/**
* {@inheritdoc}
*/
public function getFieldPlugin($field_type, MigrationInterface $migration) {
return parent::getFieldPlugin($field_type, $migration);
}
/**
* {@inheritdoc}
*/
public function getSourcePlugin($core) {
return parent::getSourcePlugin($core);
}
}
<?php
namespace Drupal\Tests\migrate_drupal\Kernel\d6;
use Drupal\field\Plugin\migrate\source\d6\FieldInstance;
use Drupal\field_discovery_test\FieldDiscoveryTestClass;
use Drupal\migrate_drupal\FieldDiscoveryInterface;
use Drupal\Tests\migrate_drupal\Traits\FieldDiscoveryTestTrait;
/**
* Tests FieldDiscovery service against Drupal 6.
*
* @group migrate_drupal
* @coversDefaultClass \Drupal\migrate_drupal\FieldDiscovery
*/
class FieldDiscoveryTest extends MigrateDrupal6TestBase {
use FieldDiscoveryTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = [
'menu_ui',
'comment',
'datetime',
'file',
'image',
'link',
'node',
'system',
'taxonomy',
'telephone',
'text',
];
/**
* The Field discovery service.
*
* @var \Drupal\migrate_drupal\FieldDiscoveryInterface
*/
protected $fieldDiscovery;
/**
* 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.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->installConfig(['node']);
$this->executeMigration('d6_node_type');
$this->executeMigration('d6_field');
$this->executeMigration('d6_field_instance');
$this->fieldDiscovery = $this->container->get('migrate_drupal.field_discovery');
$this->migrationPluginManager = $this->container->get('plugin.manager.migration');
$this->fieldPluginManager = $this->container->get('plugin.manager.migrate.field');
$this->logger = $this->container->get('logger.channel.migrate_drupal');
}
/**
* Tests the addAllFieldProcesses method.
*
* @covers ::addAllFieldProcesses
*/
public function testAddAllFieldProcesses() {
$expected_process_keys = [
'field_commander',
'field_company',
'field_company_2',
'field_company_3',
'field_sync',
'field_multivalue',
'field_test_text_single_checkbox',
'field_reference',
'field_reference_2',
'field_test',
'field_test_date',
'field_test_datestamp',
'field_test_datetime',
'field_test_decimal_radio_buttons',
'field_test_email',
'field_test_exclude_unset',
'field_test_filefield',
'field_test_float_single_checkbox',
'field_test_four',
'field_test_identical1',
'field_test_identical2',
'field_test_imagefield',
'field_test_integer_selectlist',
'field_test_link',
'field_test_phone',
'field_test_string_selectlist',
'field_test_text_single_checkbox2',
'field_test_three',
'field_test_two',
];
$this->assertFieldProcessKeys($this->fieldDiscovery, $this->migrationPluginManager, FieldDiscoveryInterface::DRUPAL_6, $expected_process_keys);
}
/**
* Tests the addAllFieldProcesses method for field migrations.
*
* @covers ::addAllFieldProcesses
* @dataProvider addAllFieldProcessesAltersData
*/
public function testAddAllFieldProcessesAlters($field_plugin_method, $expected_process) {
$this->assertFieldProcess($this->fieldDiscovery, $this->migrationPluginManager, FieldDiscoveryInterface::DRUPAL_6, $field_plugin_method, $expected_process);
}
/**
* Provides data for testAddAllFieldProcessesAlters.
*
* @return array
* The data.
*/
public function addAllFieldProcessesAltersData() {
return [
'Field Formatter' => [
'field_plugin_method' => 'alterFieldFormatterMigration',
'expected_process' => [
'options/type' => [
0 => [
'map' => [
'email' => [
'email_formatter_default' => 'email_mailto',
'email_formatter_contact' => 'basic_string',
'email_formatter_plain' => 'basic_string',
'email_formatter_spamspan' => 'basic_string',
'email_default' => 'email_mailto',
'email_contact' => 'basic_string',
'email_plain' => 'basic_string',
'email_spamspan' => 'basic_string',
],
'text' => [
'default' => 'text_default',
'trimmed' => 'text_trimmed',
'plain' => 'basic_string',
],
'datetime' => [
'date_default' => 'datetime_default',
],
'filefield' => [
'default' => 'file_default',
'url_plain' => 'file_url_plain',
'path_plain' => 'file_url_plain',
'image_plain' => 'image',
'image_nodelink' => 'image',
'image_imagelink' => 'image',
],
'link' => [
'default' => 'link',
'plain' => 'link',
'absolute' => 'link',
'title_plain' => 'link',
'url' => 'link',
'short' => 'link',
'label' => 'link',
'separate' => 'link_separate',
],
],
],
],
],
],
'Field Widget' => [
'field_plugin_method' => 'alterFieldWidgetMigration',
'expected_process' => [
'options/type' => [
'type' => [
'map' => [
'userreference' => 'userreference_default',
'nodereference' => 'nodereference_default',
'email_textfield' => 'email_default',
'text_textfield' => 'text_textfield',
'date' => 'datetime_default',
'datetime' => 'datetime_default',
'datestamp' => 'datetime_timestamp',
'filefield_widget' => 'file_generic',
'link' => 'link_default',
],
],
],
],
],
];
}
/**
* Tests the addFields method.
*
* @covers ::addAllFieldProcesses
*/
public function testAddFields() {
$this->migrateFields();
$field_discovery = $this->container->get('migrate_drupal.field_discovery');
$migration_plugin_manager = $this->container->get('plugin.manager.migration');
$definition = [
'migration_tags' => ['Drupal 6'],
];
$migration = $migration_plugin_manager->createStubMigration($definition);
$field_discovery->addBundleFieldProcesses($migration, 'node', 'test_planet');
$actual_process = $migration->getProcess();
$expected_process = [
'field_multivalue' => [
0 => [
'plugin' => 'get',
'source' => 'field_multivalue',
],
],
'field_test_text_single_checkbox' => [
0 => [
'plugin' => 'sub_process',
'source' => 'field_test_text_single_checkbox',
'process' => [
'value' => 'value',
'format' => [
0 => [
'plugin' => 'static_map',
'bypass' => TRUE,
'source' => 'format',
'map' => [
0 => NULL,
],
],
1 => [
'plugin' => 'skip_on_empty',
'method' => 'process',
],
2 => [
'plugin' => 'migration',
'migration' => [
0 => 'd6_filter_format',
1 => 'd7_filter_format',
],
'source' => 'format',
],
],
],
],
],
];
$this->assertEquals($expected_process, $actual_process);
}
/**
* Tests the getAllFields method.
*
* @covers ::getAllFields
*/
public function testGetAllFields() {
$field_discovery_test = new FieldDiscoveryTestClass($this->fieldPluginManager, $this->migrationPluginManager, $this->logger);
$actual_fields = $field_discovery_test->getAllFields('6');
$this->assertSame(['node'], array_keys($actual_fields));
$this->assertSame(['employee', 'test_planet', 'page', 'story', 'test_page'], array_keys($actual_fields['node']));
$this->assertSame(21, count($actual_fields['node']['story']));
foreach ($actual_fields['node'] as $bundle => $fields) {
foreach ($fields as $field_name => $field_info) {
$this->assertArrayHasKey('type', $field_info);
$this->assertSame(22, count($field_info));
$this->assertEquals($bundle, $field_info['type_name']);
}
}
}
/**
* Tests the getSourcePlugin method.
*
* @covers ::getSourcePlugin
*/
public function testGetSourcePlugin() {
$this->assertSourcePlugin('6', FieldInstance::class, [
'requirements_met' => TRUE,
'id' => 'd6_field_instance',
'source_module' => 'content',
'class' => 'Drupal\\field\\Plugin\\migrate\\source\\d6\\FieldInstance',
'provider' => [
0 => 'field',
1 => 'migrate_drupal',
2 => 'migrate',
4 => 'core',
],
]);
}
}
<?php
namespace Drupal\Tests\migrate_drupal\Traits;
use Drupal\field_discovery_test\FieldDiscoveryTestClass;
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
use Drupal\migrate_drupal\FieldDiscoveryInterface;
/**