updated contrib modules : migrate_tools, migrate_plus, piwik -> matomo, color_field, config_filter, admin_toolbar
This commit is contained in:
+29
-1
@@ -6,7 +6,6 @@ migrate_plugin:
|
||||
plugin:
|
||||
type: string
|
||||
label: 'Plugin'
|
||||
|
||||
migrate_destination:
|
||||
type: migrate_plugin
|
||||
label: 'Destination'
|
||||
@@ -25,6 +24,35 @@ migrate_source:
|
||||
constants:
|
||||
type: ignore
|
||||
label: 'Constants'
|
||||
ids:
|
||||
type: ignore
|
||||
label: 'Source IDs schema definition for migrate mapping table'
|
||||
urls:
|
||||
type: sequence
|
||||
label: 'URLs from which to fetch'
|
||||
sequence:
|
||||
type: string
|
||||
data_fetcher_plugin:
|
||||
type: string
|
||||
label: 'Fetcher plugin'
|
||||
data_parser_plugin:
|
||||
type: string
|
||||
label: 'Parser plugin'
|
||||
fields:
|
||||
type: ignore
|
||||
label: Mapping of field names to selectors
|
||||
function:
|
||||
type: string
|
||||
label: 'Function to call on the service'
|
||||
parameters:
|
||||
type: ignore
|
||||
label: 'Parameters to pass to function on the service'
|
||||
response_type:
|
||||
type: string
|
||||
label: 'Type of response; XML string, object or array'
|
||||
item_selector:
|
||||
type: string
|
||||
label: 'XPath selector'
|
||||
|
||||
migrate_process:
|
||||
type: migrate_plugin
|
||||
|
||||
+2
-2
@@ -71,9 +71,9 @@ migrate_plus.process.get:
|
||||
type: string
|
||||
label: 'Source key'
|
||||
|
||||
migrate_plus.process.iterator:
|
||||
migrate_plus.process.sub_process:
|
||||
type: migrate_process
|
||||
label: 'Iterator process'
|
||||
label: 'Sub process'
|
||||
mapping:
|
||||
process:
|
||||
type: ignore
|
||||
|
||||
+2
@@ -2,6 +2,8 @@
|
||||
id: beer_node
|
||||
label: Beers of the world
|
||||
migration_group: beer
|
||||
migration_tags:
|
||||
- example
|
||||
source:
|
||||
plugin: beer_node
|
||||
destination:
|
||||
|
||||
+4
@@ -12,6 +12,10 @@ label: Migrate style categories from the source database to taxonomy terms
|
||||
# configuration to be merged with our own configuration here).
|
||||
migration_group: beer
|
||||
|
||||
# The category or tag for the migration.
|
||||
migration_tags:
|
||||
- example
|
||||
|
||||
# Every migration must have a source plugin, which controls the delivery of our
|
||||
# source data. In this case, our source plugin has the name "beer_term", which
|
||||
# Drupal resolves to the PHP class defined in
|
||||
|
||||
+2
@@ -4,6 +4,8 @@
|
||||
id: beer_user
|
||||
label: Beer Drinkers of the world
|
||||
migration_group: beer
|
||||
migration_tags:
|
||||
- example
|
||||
source:
|
||||
plugin: beer_user
|
||||
destination:
|
||||
|
||||
+3
-3
@@ -10,8 +10,8 @@ dependencies:
|
||||
- drupal:menu_ui
|
||||
- drupal:path
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-02-23
|
||||
version: '8.x-4.0-beta3'
|
||||
# Information added by Drupal.org packaging script on 2018-09-06
|
||||
version: '8.x-4.0'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1519400598
|
||||
datestamp: 1536264189
|
||||
|
||||
+3
-3
@@ -11,8 +11,8 @@ dependencies:
|
||||
- drupal:options
|
||||
- drupal:taxonomy
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-02-23
|
||||
version: '8.x-4.0-beta3'
|
||||
# Information added by Drupal.org packaging script on 2018-09-06
|
||||
version: '8.x-4.0'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1519400598
|
||||
datestamp: 1536264189
|
||||
|
||||
+7
-1
@@ -2,13 +2,16 @@
|
||||
id: weather_soap
|
||||
label: SOAP service providing weather.
|
||||
migration_group: wine
|
||||
migration_tags:
|
||||
- advanced example
|
||||
source:
|
||||
# We use the SOAP parser source plugin.
|
||||
plugin: url
|
||||
data_fetcher_plugin: http # Ignored - SoapClient does the fetching.
|
||||
data_parser_plugin: soap
|
||||
# URL of a WSDL endpoint.
|
||||
urls: http://www.webservicex.net/globalweather.asmx?WSDL
|
||||
urls:
|
||||
- http://www.webservicex.net/globalweather.asmx?WSDL
|
||||
# The function to call on the service, and the parameters to pass. See
|
||||
# http://www.webservicex.net/New/Home/ServiceDetail/56 for the XML structure
|
||||
# of this feed - how CountryName is passed within the GetCitiesByCountry
|
||||
@@ -48,3 +51,6 @@ process:
|
||||
name: City
|
||||
destination:
|
||||
plugin: entity:taxonomy_term
|
||||
migration_dependencies:
|
||||
required: {}
|
||||
optional: {}
|
||||
|
||||
+4
-1
@@ -2,6 +2,8 @@
|
||||
id: wine_role_json
|
||||
label: JSON feed of roles (positions)
|
||||
migration_group: wine
|
||||
migration_tags:
|
||||
- advanced example
|
||||
source:
|
||||
# We use the JSON source plugin.
|
||||
plugin: url
|
||||
@@ -17,7 +19,8 @@ source:
|
||||
# Normally, this is one or more fully-qualified URLs or file paths. Because
|
||||
# we can't hardcode your local URL, we provide a relative path here which
|
||||
# hook_install() will rewrite to a full URL for the current site.
|
||||
urls: /migrate_example_advanced_position?_format=json
|
||||
urls:
|
||||
- /migrate_example_advanced_position?_format=json
|
||||
# An xpath-like selector corresponding to the items to be imported.
|
||||
item_selector: position
|
||||
# Under 'fields', we list the data items to be imported. The first level keys
|
||||
|
||||
+4
-1
@@ -2,6 +2,8 @@
|
||||
id: wine_role_xml
|
||||
label: XML feed of roles (positions)
|
||||
migration_group: wine
|
||||
migration_tags:
|
||||
- advanced example
|
||||
source:
|
||||
# We use the XML data parser plugin.
|
||||
plugin: url
|
||||
@@ -10,7 +12,8 @@ source:
|
||||
# Normally, this is one or more fully-qualified URLs or file paths. Because
|
||||
# we can't hardcode your local URL, we provide a relative path here which
|
||||
# hook_install() will rewrite to a full URL for the current site.
|
||||
urls: /migrate_example_advanced_position?_format=xml
|
||||
urls:
|
||||
- /migrate_example_advanced_position?_format=xml
|
||||
# Visit the URL above (relative to your site root) and look at it. You can see
|
||||
# that <response> is the outer element, and each item we want to import is a
|
||||
# <position> element. The item_xpath value is the xpath to use to query the
|
||||
|
||||
+2
@@ -1,6 +1,8 @@
|
||||
id: wine_terms
|
||||
label: Migrate all categories into Drupal taxonomy terms
|
||||
migration_group: wine
|
||||
migration_tags:
|
||||
- advanced example
|
||||
source:
|
||||
plugin: wine_term
|
||||
destination:
|
||||
|
||||
+4
-1
@@ -3,13 +3,16 @@
|
||||
id: wine_variety_list
|
||||
label: XML feed of varieties
|
||||
migration_group: wine
|
||||
migration_tags:
|
||||
- advanced example
|
||||
source:
|
||||
# We use the XML source plugin.
|
||||
plugin: xml
|
||||
# Normally, this is one or more fully-qualified URLs or file paths. Because
|
||||
# we can't hardcode your local URL, we provide a relative path here which
|
||||
# hook_install() will rewrite to a full URL for the current site.
|
||||
urls: /migrate_example_advanced_variety_list?_format=xml
|
||||
urls:
|
||||
- /migrate_example_advanced_variety_list?_format=xml
|
||||
item_url: /migrate_example_advanced_variety_list/:id?_format=xml
|
||||
id_selector: /response/items
|
||||
# Visit the URL above (relative to your site root) and look at it. You can see
|
||||
|
||||
+2
@@ -2,6 +2,8 @@
|
||||
id: wine_variety_multi_xml
|
||||
label: XML feed of varieties
|
||||
migration_group: wine
|
||||
migration_tags:
|
||||
- advanced example
|
||||
source:
|
||||
# We use the XML source plugin.
|
||||
plugin: url
|
||||
|
||||
+3
-3
@@ -8,8 +8,8 @@ dependencies:
|
||||
- migrate_plus:migrate_example_advanced_setup
|
||||
- migrate_plus:migrate_plus
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-02-23
|
||||
version: '8.x-4.0-beta3'
|
||||
# Information added by Drupal.org packaging script on 2018-09-06
|
||||
version: '8.x-4.0'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1519400598
|
||||
datestamp: 1536264189
|
||||
|
||||
+10
-2
@@ -21,7 +21,11 @@ function migrate_example_advanced_install() {
|
||||
if ($wine_role_xml_migration) {
|
||||
$source = $wine_role_xml_migration->get('source');
|
||||
$request = \Drupal::request();
|
||||
$source['urls'] = 'http://' . $request->getHttpHost() . $source['urls'];
|
||||
$urls = [];
|
||||
foreach ($source['urls'] as $url) {
|
||||
$urls[] = 'http://' . $request->getHttpHost() . $url;
|
||||
}
|
||||
$source['urls'] = $urls;
|
||||
$wine_role_xml_migration->set('source', $source);
|
||||
$wine_role_xml_migration->save();
|
||||
}
|
||||
@@ -30,7 +34,11 @@ function migrate_example_advanced_install() {
|
||||
if ($wine_role_json_migration) {
|
||||
$source = $wine_role_json_migration->get('source');
|
||||
$request = \Drupal::request();
|
||||
$source['urls'] = 'http://' . $request->getHttpHost() . $source['urls'];
|
||||
$urls = [];
|
||||
foreach ($source['urls'] as $url) {
|
||||
$urls[] = 'http://' . $request->getHttpHost() . $url;
|
||||
}
|
||||
$source['urls'] = $urls;
|
||||
$wine_role_json_migration->set('source', $source);
|
||||
$wine_role_json_migration->save();
|
||||
}
|
||||
|
||||
+3
-3
@@ -11,8 +11,8 @@ dependencies:
|
||||
- drupal:taxonomy
|
||||
- drupal:rest
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-02-23
|
||||
version: '8.x-4.0-beta3'
|
||||
# Information added by Drupal.org packaging script on 2018-09-06
|
||||
version: '8.x-4.0'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1519400598
|
||||
datestamp: 1536264189
|
||||
|
||||
@@ -6,8 +6,8 @@ package: Migration
|
||||
dependencies:
|
||||
- drupal:migrate (>=8.3)
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-02-23
|
||||
version: '8.x-4.0-beta3'
|
||||
# Information added by Drupal.org packaging script on 2018-09-06
|
||||
version: '8.x-4.0'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1519400598
|
||||
datestamp: 1536264189
|
||||
|
||||
+82
-4
@@ -2,8 +2,13 @@
|
||||
|
||||
namespace Drupal\migrate_plus\Plugin\migrate\process;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Plugin\MigratePluginManager;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* This plugin generates entities within the process plugin.
|
||||
@@ -16,10 +21,10 @@ use Drupal\migrate\Row;
|
||||
*
|
||||
* All the configuration from the lookup plugin applies here. In its most
|
||||
* simple form, this plugin needs no configuration. If there are fields on the
|
||||
* generated entity that are required or need some default value, that can be
|
||||
* provided via a default_values configuration option.
|
||||
* generated entity that are required or need some value, their values can be
|
||||
* provided via values and/or default_values configuration options.
|
||||
*
|
||||
* Example usage with default_values configuration:
|
||||
* Example usage with values and default_values configuration:
|
||||
* @code
|
||||
* destination:
|
||||
* plugin: 'entity:node'
|
||||
@@ -27,20 +32,86 @@ use Drupal\migrate\Row;
|
||||
* type:
|
||||
* plugin: default_value
|
||||
* default_value: page
|
||||
* foo: bar
|
||||
* field_tags:
|
||||
* plugin: entity_generate
|
||||
* source: tags
|
||||
* default_values:
|
||||
* description: Default description
|
||||
* field_long_description: Default long description
|
||||
* values:
|
||||
* field_long_description: some_source_field
|
||||
* field_foo: '@foo'
|
||||
* @endcode
|
||||
*/
|
||||
class EntityGenerate extends EntityLookup {
|
||||
|
||||
/**
|
||||
* The row from the source to process.
|
||||
*
|
||||
* @var \Drupal\migrate\Row
|
||||
*/
|
||||
protected $row;
|
||||
|
||||
/**
|
||||
* The MigrateExecutable instance.
|
||||
*
|
||||
* @var \Drupal\migrate\MigrateExecutable
|
||||
*/
|
||||
protected $migrateExecutable;
|
||||
|
||||
/**
|
||||
* The get process plugin instance.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\migrate\process\Get
|
||||
*/
|
||||
protected $getProcessPlugin;
|
||||
|
||||
/**
|
||||
* EntityGenerate constructor.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $pluginId
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $pluginDefinition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entityManager
|
||||
* The $entityManager instance.
|
||||
* @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $selectionPluginManager
|
||||
* The $selectionPluginManager instance.
|
||||
* @param \Drupal\migrate\Plugin\MigratePluginManager $migratePluginManager
|
||||
* The MigratePluginManager instance.
|
||||
*/
|
||||
public function __construct(array $configuration, $pluginId, $pluginDefinition, MigrationInterface $migration, EntityManagerInterface $entityManager, SelectionPluginManagerInterface $selectionPluginManager, MigratePluginManager $migratePluginManager) {
|
||||
parent::__construct($configuration, $pluginId, $pluginDefinition, $migration, $entityManager, $selectionPluginManager);
|
||||
if (isset($configuration['values'])) {
|
||||
$this->getProcessPlugin = $migratePluginManager->createInstance('get', ['source' => $configuration['values']]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition, MigrationInterface $migration = NULL) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$pluginId,
|
||||
$pluginDefinition,
|
||||
$migration,
|
||||
$container->get('entity.manager'),
|
||||
$container->get('plugin.manager.entity_reference_selection'),
|
||||
$container->get('plugin.manager.migrate.process')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrateExecutable, Row $row, $destinationProperty) {
|
||||
$this->row = $row;
|
||||
$this->migrateExecutable = $migrateExecutable;
|
||||
// Creates an entity if the lookup determines it doesn't exist.
|
||||
if (!($result = parent::transform($value, $migrateExecutable, $row, $destinationProperty))) {
|
||||
$result = $this->generateEntity($value);
|
||||
@@ -94,6 +165,13 @@ class EntityGenerate extends EntityLookup {
|
||||
$entity_values[$key] = $value;
|
||||
}
|
||||
}
|
||||
// Gather any additional properties/fields.
|
||||
if (isset($this->configuration['values']) && is_array($this->configuration['values'])) {
|
||||
foreach ($this->configuration['values'] as $key => $property) {
|
||||
$source_value = $this->getProcessPlugin->transform(NULL, $this->migrateExecutable, $this->row, $property);
|
||||
$entity_values[$key] = $source_value;
|
||||
}
|
||||
}
|
||||
|
||||
return $entity_values;
|
||||
}
|
||||
|
||||
+8
-1
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\migrate_plus\Plugin\migrate\process;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
@@ -152,6 +153,11 @@ class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginIn
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrateExecutable, Row $row, $destinationProperty) {
|
||||
// If the source data is an empty array, return the same.
|
||||
if (gettype($value) === 'array' && count($value) === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// In case of subfields ('field_reference/target_id'), extract the field
|
||||
// name only.
|
||||
$parts = explode('/', $destinationProperty);
|
||||
@@ -270,7 +276,8 @@ class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginIn
|
||||
if (!$ignoreCase) {
|
||||
// Returns the entity's identifier.
|
||||
foreach ($results as $k => $identifier) {
|
||||
$result_value = $this->entityManager->getStorage($this->lookupEntityType)->load($identifier)->{$this->lookupValueKey}->value;
|
||||
$entity = $this->entityManager->getStorage($this->lookupEntityType)->load($identifier);
|
||||
$result_value = $entity instanceof ConfigEntityInterface ? $entity->get($this->lookupValueKey) : $entity->get($this->lookupValueKey)->value;
|
||||
if (($multiple && !in_array($result_value, $value, TRUE)) || (!$multiple && $result_value !== $value)) {
|
||||
unset($results[$k]);
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ use Drupal\migrate\Row;
|
||||
* paragraphs_field:
|
||||
* plugin: merge
|
||||
* source:
|
||||
* - @temp_body
|
||||
* - @temp_images
|
||||
* - '@temp_body'
|
||||
* - '@temp_images'
|
||||
* destination:
|
||||
* plugin: 'entity:node'
|
||||
*/
|
||||
@@ -53,12 +53,12 @@ class Merge extends ProcessPluginBase {
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
if (!is_array($value)) {
|
||||
throw new MigrateException('Input should be an array.');
|
||||
throw new MigrateException(sprintf('Merge process failed for destination property (%s): input is not an array.', $destination_property));
|
||||
}
|
||||
$new_value = [];
|
||||
foreach ($value as $item) {
|
||||
foreach($value as $i => $item) {
|
||||
if (!is_array($item)) {
|
||||
throw new MigrateException('One of the items is not an array that can be merged.');
|
||||
throw new MigrateException(sprintf('Merge process failed for destination property (%s): index (%s) in the source value is not an array that can be merged.', $destination_property, $i));
|
||||
}
|
||||
$new_value = array_merge($new_value, $item);
|
||||
}
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_plus\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Treat an array of values as a separate / individual values.
|
||||
*
|
||||
* @code
|
||||
* process:
|
||||
* field_authors:
|
||||
* -
|
||||
* plugin: explode
|
||||
* delimiter: ', '
|
||||
* source: authors
|
||||
* -
|
||||
* plugin: single_value
|
||||
* -
|
||||
* plugin: callback
|
||||
* callable: custom_sort_authors
|
||||
* -
|
||||
* plugin: multiple_values
|
||||
* @endcode
|
||||
*
|
||||
* Assume the "authors" field contains comma separated author names.
|
||||
*
|
||||
* We split the names into multiple values and then use the "single_value"
|
||||
* plugin to treat them as a single array of author names. After that, we
|
||||
* pass the values through a custom sort. Callback multiple setting is false. To
|
||||
* convert from a single value to multiple, use the "multiple_values" plugin. It
|
||||
* will make the next plugin treat the values individually instead of an array
|
||||
* of values.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "multiple_values",
|
||||
* handle_multiples = TRUE
|
||||
* )
|
||||
*/
|
||||
class MultipleValues extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function multiple() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_plus\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Treat an array of values as a single value.
|
||||
*
|
||||
* @code
|
||||
* process:
|
||||
* field_authors:
|
||||
* -
|
||||
* plugin: explode
|
||||
* delimiter: ', '
|
||||
* source: authors
|
||||
* -
|
||||
* plugin: single_value
|
||||
* @endcode
|
||||
*
|
||||
* Assume the "authors" field contains comma separated author names.
|
||||
*
|
||||
* After the explode, we end up with each author name as an individual value.
|
||||
* But if we want to perform a sort on all values using a callback, we will
|
||||
* need to send all the values to a callable together as an array of author
|
||||
* names. Calling the "single_value" plugin in such a case will combine all the
|
||||
* values into a single array for the next plugin.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "single_value",
|
||||
* handle_multiples = TRUE
|
||||
* )
|
||||
*/
|
||||
class SingleValue extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
+54
-6
@@ -15,6 +15,44 @@ use Drupal\migrate\Row;
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "skip_on_value"
|
||||
* )
|
||||
*
|
||||
* Available configuration keys:
|
||||
* - value: An single value or array of values against which the source value
|
||||
* should be compared.
|
||||
* - not_equals: (optional) If set, skipping occurs when values are not equal.
|
||||
* - method: What to do if the input value is empty. Possible values:
|
||||
* - row: Skips the entire row when an empty value is encountered.
|
||||
* - process: Prevents further processing of the input property when the value
|
||||
* is empty.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Example usage with minimal configuration:
|
||||
* @code
|
||||
* type:
|
||||
* plugin: skip_on_value
|
||||
* source: content_type
|
||||
* method: row
|
||||
* value: blog
|
||||
* @endcode
|
||||
*
|
||||
* The above example will skip processing the input property if the content_type
|
||||
* source field equals "blog".
|
||||
*
|
||||
* Example usage with full configuration:
|
||||
* @code
|
||||
* type:
|
||||
* plugin: skip_on_value
|
||||
* not_equals: true
|
||||
* source: content_type
|
||||
* method: row
|
||||
* value:
|
||||
* - article
|
||||
* - testimonial
|
||||
* @endcode
|
||||
*
|
||||
* The above example will skip processing any row for which the source row's
|
||||
* content type field is not "article" or "testimonial".
|
||||
*/
|
||||
class SkipOnValue extends ProcessPluginBase {
|
||||
|
||||
@@ -27,10 +65,15 @@ class SkipOnValue extends ProcessPluginBase {
|
||||
}
|
||||
|
||||
if (is_array($this->configuration['value'])) {
|
||||
$value_in_array = FALSE;
|
||||
$not_equals = isset($this->configuration['not_equals']);
|
||||
|
||||
foreach ($this->configuration['value'] as $skipValue) {
|
||||
if ($this->compareValue($value, $skipValue, !isset($this->configuration['not_equals']))) {
|
||||
throw new MigrateSkipRowException();
|
||||
}
|
||||
$value_in_array |= $this->compareValue($value, $skipValue);
|
||||
}
|
||||
|
||||
if (($not_equals && !$value_in_array) || (!$not_equals && $value_in_array)) {
|
||||
throw new MigrateSkipRowException();
|
||||
}
|
||||
}
|
||||
elseif ($this->compareValue($value, $this->configuration['value'], !isset($this->configuration['not_equals']))) {
|
||||
@@ -49,10 +92,15 @@ class SkipOnValue extends ProcessPluginBase {
|
||||
}
|
||||
|
||||
if (is_array($this->configuration['value'])) {
|
||||
$value_in_array = FALSE;
|
||||
$not_equals = isset($this->configuration['not_equals']);
|
||||
|
||||
foreach ($this->configuration['value'] as $skipValue) {
|
||||
if ($this->compareValue($value, $skipValue, !isset($this->configuration['not_equals']))) {
|
||||
throw new MigrateSkipProcessException();
|
||||
}
|
||||
$value_in_array |= $this->compareValue($value, $skipValue);
|
||||
}
|
||||
|
||||
if (($not_equals && !$value_in_array) || (!$not_equals && $value_in_array)) {
|
||||
throw new MigrateSkipProcessException();
|
||||
}
|
||||
}
|
||||
elseif ($this->compareValue($value, $this->configuration['value'], !isset($this->configuration['not_equals']))) {
|
||||
|
||||
+6
-8
@@ -15,13 +15,6 @@ use Drupal\migrate_plus\DataParserPluginBase;
|
||||
*/
|
||||
class Json extends DataParserPluginBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The request headers passed to the data fetcher.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = [];
|
||||
|
||||
/**
|
||||
* Iterator over the JSON data.
|
||||
*
|
||||
@@ -119,7 +112,12 @@ class Json extends DataParserPluginBase implements ContainerFactoryPluginInterfa
|
||||
$field_data = $current;
|
||||
$field_selectors = explode('/', trim($selector, '/'));
|
||||
foreach ($field_selectors as $field_selector) {
|
||||
$field_data = $field_data[$field_selector];
|
||||
if (is_array($field_data) && array_key_exists($field_selector, $field_data)) {
|
||||
$field_data = $field_data[$field_selector];
|
||||
}
|
||||
else {
|
||||
$field_data = '';
|
||||
}
|
||||
}
|
||||
$this->currentItem[$field_name] = $field_data;
|
||||
}
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ class SimpleXml extends DataParserPluginBase {
|
||||
}
|
||||
// Reduce single-value results to scalars.
|
||||
foreach ($this->currentItem as $field_name => $values) {
|
||||
if (count($values) == 1) {
|
||||
if (is_array($values) && count($values) == 1) {
|
||||
$this->currentItem[$field_name] = reset($values);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"title": "Title",
|
||||
"video": {
|
||||
"title": "Video title",
|
||||
"url": "https://localhost/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"video": {
|
||||
"title": "Video title",
|
||||
"url": "https://localhost/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"title": "Title 3"
|
||||
}
|
||||
]
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<items>
|
||||
<item id="1">
|
||||
<values title="Values">
|
||||
<value>Value 1</value>
|
||||
<value>Value 2</value>
|
||||
</values>
|
||||
</item>
|
||||
<item id="2">
|
||||
<values title="Values">
|
||||
<value>Value 1 (single)</value>
|
||||
</values>
|
||||
</item>
|
||||
</items>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_plus\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Simple test to ensure that main page loads with module enabled.
|
||||
*
|
||||
* @group migrate_plus
|
||||
*/
|
||||
class LoadTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'migrate_plus',
|
||||
'migrate_example',
|
||||
'migrate_example_advanced',
|
||||
];
|
||||
|
||||
/**
|
||||
* A user with permission to administer site configuration.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->user = $this->drupalCreateUser(['administer site configuration']);
|
||||
$this->drupalLogin($this->user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the home page loads with a 200 response.
|
||||
*/
|
||||
public function testLoad() {
|
||||
$this->drupalGet(Url::fromRoute('<front>'));
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
}
|
||||
|
||||
}
|
||||
+474
-13
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\Tests\migrate_plus\Kernel\Plugin\migrate\process;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
|
||||
@@ -119,12 +120,16 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
|
||||
public function testTransform(array $definition, array $expected, array $preSeed = []) {
|
||||
// Pre seed some test data.
|
||||
foreach ($preSeed as $storageName => $values) {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
|
||||
$storage = $this->container
|
||||
->get('entity_type.manager')
|
||||
->getStorage($storageName);
|
||||
$entity = $storage->create($values);
|
||||
$entity->save();
|
||||
// If the first element of $values is a non-empty array, create multiple
|
||||
// entities. Otherwise, create just one entity.
|
||||
if (isset($values[0])) {
|
||||
foreach ($values as $itemValues) {
|
||||
$this->createTestData($storageName, $itemValues);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->createTestData($storageName, $values);
|
||||
}
|
||||
}
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\Migration $migration */
|
||||
@@ -139,15 +144,54 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
|
||||
$properties = array_diff_key($row, array_flip(['id']));
|
||||
foreach ($properties as $property => $value) {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $key => $expectedValue) {
|
||||
if (empty($expectedValue)) {
|
||||
$this->assertEmpty($entity->{$property}->getValue(), "Expected value is empty but field $property is not empty.");
|
||||
}
|
||||
elseif ($entity->{$property}->getValue()) {
|
||||
$this->assertEquals($expectedValue, $entity->{$property}[0]->entity->$key->value);
|
||||
if (empty($value)) {
|
||||
$this->assertEmpty($entity->{$property}->getValue(), "Expected value is 'unset' but field $property is set.");
|
||||
}
|
||||
else {
|
||||
// Check if we're testing multiple values in one field. If so, loop
|
||||
// through them one-by-one and check that they're present in the
|
||||
// $entity.
|
||||
if (isset($value[0])) {
|
||||
foreach ($value as $valueID => $valueToCheck) {
|
||||
foreach ($valueToCheck as $key => $expectedValue) {
|
||||
if (empty($expectedValue)) {
|
||||
if (!$entity->{$property}->isEmpty()) {
|
||||
$this->assertTrue($entity->{$property}[0]->entity->$key->isEmpty(), "Expected value is empty but field $property.$key is not empty.");
|
||||
}
|
||||
else {
|
||||
$this->assertTrue($entity->{$property}->isEmpty(), "FOOBAR Expected value is empty but field $property is not empty.");
|
||||
}
|
||||
}
|
||||
elseif ($entity->{$property}->getValue()) {
|
||||
$this->assertEquals($expectedValue, $entity->{$property}[$valueID]->entity->$key->value);
|
||||
}
|
||||
else {
|
||||
$this->fail("Expected value: $expectedValue does not exist in $property.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we get to this point, we're only checking a
|
||||
// single field value.
|
||||
else {
|
||||
$this->fail("Expected value: $expectedValue does not exist in $property.");
|
||||
foreach ($value as $key => $expectedValue) {
|
||||
if (empty($expectedValue)) {
|
||||
if (!$entity->{$property}->isEmpty()) {
|
||||
$this->assertTrue($entity->{$property}[0]->entity->$key->isEmpty(), "Expected value is empty but field $property.$key is not empty.");
|
||||
}
|
||||
else {
|
||||
$this->assertTrue($entity->{$property}->isEmpty(), "BINBAZ Expected value is empty but field $property is not empty.");
|
||||
}
|
||||
}
|
||||
elseif ($entity->{$property}->getValue()) {
|
||||
$referenced_entity = $entity->{$property}[0]->entity;
|
||||
$result_value = $referenced_entity instanceof ConfigEntityInterface ? $referenced_entity->get($key) : $referenced_entity->get($key)->value;
|
||||
$this->assertEquals($expectedValue, $result_value);
|
||||
}
|
||||
else {
|
||||
$this->fail("Expected value: $expectedValue does not exist in $property.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,6 +358,406 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
|
||||
],
|
||||
],
|
||||
],
|
||||
'provide values' => [
|
||||
'definition' => [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
'term' => 'Apples',
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'title' => 'content item 2',
|
||||
'term' => 'Bananas',
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'title' => 'content item 3',
|
||||
'term' => 'Grapes',
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'type' => [
|
||||
'plugin' => 'default_value',
|
||||
'default_value' => $this->bundle,
|
||||
],
|
||||
'title' => 'title',
|
||||
'term_upper' => [
|
||||
'plugin' => 'callback',
|
||||
'source' => 'term',
|
||||
'callable' => 'strtoupper',
|
||||
],
|
||||
$this->fieldName => [
|
||||
'plugin' => 'entity_generate',
|
||||
'source' => 'term',
|
||||
'values' => [
|
||||
'description' => '@term_upper',
|
||||
],
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:node',
|
||||
],
|
||||
],
|
||||
'expected' => [
|
||||
'row 1' => [
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
$this->fieldName => [
|
||||
'tid' => 2,
|
||||
'name' => 'Apples',
|
||||
'description' => 'APPLES',
|
||||
],
|
||||
],
|
||||
'row 2' => [
|
||||
'id' => 2,
|
||||
'title' => 'content item 2',
|
||||
$this->fieldName => [
|
||||
'tid' => 3,
|
||||
'name' => 'Bananas',
|
||||
'description' => 'BANANAS',
|
||||
],
|
||||
],
|
||||
'row 3' => [
|
||||
'id' => 3,
|
||||
'title' => 'content item 3',
|
||||
$this->fieldName => [
|
||||
'tid' => 1,
|
||||
'name' => 'Grapes',
|
||||
'description' => NULL,
|
||||
],
|
||||
],
|
||||
],
|
||||
'pre seed' => [
|
||||
'taxonomy_term' => [
|
||||
'name' => 'Grapes',
|
||||
'vid' => $this->vocabulary,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
],
|
||||
],
|
||||
],
|
||||
'lookup single existing term returns correct term' => [
|
||||
'definition' => [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
'term' => 'Grapes',
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'type' => [
|
||||
'plugin' => 'default_value',
|
||||
'default_value' => $this->bundle,
|
||||
],
|
||||
'title' => 'title',
|
||||
$this->fieldName => [
|
||||
'plugin' => 'entity_lookup',
|
||||
'source' => 'term',
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:node',
|
||||
],
|
||||
],
|
||||
'expected' => [
|
||||
'row 1' => [
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
$this->fieldName => [
|
||||
'tid' => 1,
|
||||
'name' => 'Grapes',
|
||||
],
|
||||
],
|
||||
],
|
||||
'pre seed' => [
|
||||
'taxonomy_term' => [
|
||||
'name' => 'Grapes',
|
||||
'vid' => $this->vocabulary,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
],
|
||||
],
|
||||
],
|
||||
'lookup single missing term returns null value' => [
|
||||
'definition' => [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
'term' => 'Apple',
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'type' => [
|
||||
'plugin' => 'default_value',
|
||||
'default_value' => $this->bundle,
|
||||
],
|
||||
'title' => 'title',
|
||||
$this->fieldName => [
|
||||
'plugin' => 'entity_lookup',
|
||||
'source' => 'term',
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:node',
|
||||
],
|
||||
],
|
||||
'expected' => [
|
||||
'row 1' => [
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
$this->fieldName => [],
|
||||
],
|
||||
],
|
||||
'pre seed' => [
|
||||
'taxonomy_term' => [
|
||||
'name' => 'Grapes',
|
||||
'vid' => $this->vocabulary,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
],
|
||||
],
|
||||
],
|
||||
'lookup multiple existing terms returns correct terms' => [
|
||||
'definition' => [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
'term' => [
|
||||
'Grapes',
|
||||
'Apples',
|
||||
],
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'title' => 'title',
|
||||
'type' => [
|
||||
'plugin' => 'default_value',
|
||||
'default_value' => $this->bundle,
|
||||
],
|
||||
$this->fieldName => [
|
||||
'plugin' => 'entity_lookup',
|
||||
'source' => 'term',
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:node',
|
||||
],
|
||||
],
|
||||
'expected' => [
|
||||
'row 1' => [
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
$this->fieldName => [
|
||||
[
|
||||
'tid' => 1,
|
||||
'name' => 'Grapes',
|
||||
],
|
||||
[
|
||||
'tid' => 2,
|
||||
'name' => 'Apples',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'pre seed' => [
|
||||
'taxonomy_term' => [
|
||||
[
|
||||
'name' => 'Grapes',
|
||||
'vid' => $this->vocabulary,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
],
|
||||
[
|
||||
'name' => 'Apples',
|
||||
'vid' => $this->vocabulary,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'lookup multiple mixed terms returns correct terms' => [
|
||||
'definition' => [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
'term' => [
|
||||
'Grapes',
|
||||
'Pears',
|
||||
],
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'title' => 'title',
|
||||
'type' => [
|
||||
'plugin' => 'default_value',
|
||||
'default_value' => $this->bundle,
|
||||
],
|
||||
$this->fieldName => [
|
||||
'plugin' => 'entity_lookup',
|
||||
'source' => 'term',
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:node',
|
||||
],
|
||||
],
|
||||
'expected' => [
|
||||
'row 1' => [
|
||||
'id' => '1',
|
||||
'title' => 'content item 1',
|
||||
$this->fieldName => [
|
||||
[
|
||||
'tid' => 1,
|
||||
'name' => 'Grapes',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'pre seed' => [
|
||||
'taxonomy_term' => [
|
||||
[
|
||||
'name' => 'Grapes',
|
||||
'vid' => $this->vocabulary,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
],
|
||||
[
|
||||
'name' => 'Apples',
|
||||
'vid' => $this->vocabulary,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'lookup with empty term value returns no terms' => [
|
||||
'definition' => [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
'term' => [],
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'title' => 'title',
|
||||
'type' => [
|
||||
'plugin' => 'default_value',
|
||||
'default_value' => $this->bundle,
|
||||
],
|
||||
$this->fieldName => [
|
||||
'plugin' => 'entity_lookup',
|
||||
'source' => 'term',
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:node',
|
||||
],
|
||||
],
|
||||
'expected' => [
|
||||
'row 1' => [
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
$this->fieldName => [],
|
||||
],
|
||||
],
|
||||
'pre seed' => [
|
||||
'taxonomy_term' => [
|
||||
'name' => 'Grapes',
|
||||
'vid' => $this->vocabulary,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
],
|
||||
],
|
||||
],
|
||||
'lookup config entity' => [
|
||||
'definition' => [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'name' => 'user 1',
|
||||
'mail' => 'user1@user1.com',
|
||||
'roles' => ['role_1'],
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'name' => 'name',
|
||||
'roles' => [
|
||||
'plugin' => 'entity_lookup',
|
||||
'entity_type' => 'user_role',
|
||||
'value_key' => 'id',
|
||||
'source' => 'roles',
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:user',
|
||||
],
|
||||
],
|
||||
'expected' => [
|
||||
'row 1' => [
|
||||
'id' => 1,
|
||||
'name' => 'user 1',
|
||||
'roles' => [
|
||||
'id' => 'role_1',
|
||||
'label' => 'Role 1',
|
||||
],
|
||||
],
|
||||
],
|
||||
'pre seed' => [
|
||||
'user_role' => [
|
||||
'id' => 'role_1',
|
||||
'label' => 'Role 1',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -324,4 +768,21 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
|
||||
$this->assertTrue($type == 'status', $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create pre-seed test data.
|
||||
*
|
||||
* @param string $storageName
|
||||
* The storage manager to create.
|
||||
* @param array $values
|
||||
* The values to use when creating the entity.
|
||||
*/
|
||||
private function createTestData($storageName, array $values) {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
|
||||
$storage = $this->container
|
||||
->get('entity_type.manager')
|
||||
->getStorage($storageName);
|
||||
$entity = $storage->create($values);
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_plus\Kernel\Plugin\migrate_plus\data_parser;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Test of the data_parser Json migrate_plus plugin.
|
||||
*
|
||||
* @group migrate_plus
|
||||
*/
|
||||
class JsonTest extends KernelTestBase {
|
||||
|
||||
public static $modules = ['migrate', 'migrate_plus'];
|
||||
|
||||
/**
|
||||
* Tests missing properties in json file.
|
||||
*
|
||||
* @param string $file
|
||||
* File name in tests/data/ directory of this module.
|
||||
* @param array $ids
|
||||
* Array of ids to pass to the plugin.
|
||||
* @param array $fields
|
||||
* Array of fields to pass to the plugin.
|
||||
* @param array $expected
|
||||
* Expected array from json decoded file.
|
||||
*
|
||||
* @dataProvider jsonBaseDataProvider
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testMissingProperties($file, array $ids, array $fields, array $expected) {
|
||||
$path = $this->container
|
||||
->get('module_handler')
|
||||
->getModule('migrate_plus')
|
||||
->getPath();
|
||||
$url = $path . '/tests/data/' . $file;
|
||||
|
||||
/** @var \Drupal\migrate_plus\DataParserPluginManager $plugin_manager */
|
||||
$plugin_manager = $this->container
|
||||
->get('plugin.manager.migrate_plus.data_parser');
|
||||
$conf = [
|
||||
'plugin' => 'url',
|
||||
'data_fetcher_plugin' => 'file',
|
||||
'data_parser_plugin' => 'json',
|
||||
'destination' => 'node',
|
||||
'urls' => [$url],
|
||||
'ids' => $ids,
|
||||
'fields' => $fields,
|
||||
'item_selector' => NULL,
|
||||
];
|
||||
$json_parser = $plugin_manager->createInstance('json', $conf);
|
||||
|
||||
$data = [];
|
||||
foreach ($json_parser as $item) {
|
||||
$data[] = $item;
|
||||
}
|
||||
|
||||
$this->assertEquals($expected, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides multiple test cases for the testMissingProperty method.
|
||||
*
|
||||
* @return array
|
||||
* The test cases.
|
||||
*/
|
||||
public function jsonBaseDataProvider() {
|
||||
return [
|
||||
'missing properties' => [
|
||||
'file' => 'missing_properties.json',
|
||||
'ids' => ['id' => ['type' => 'integer']],
|
||||
'fields' => [
|
||||
[
|
||||
'name' => 'id',
|
||||
'label' => 'Id',
|
||||
'selector' => '/id',
|
||||
],
|
||||
[
|
||||
'name' => 'title',
|
||||
'label' => 'Title',
|
||||
'selector' => '/title',
|
||||
],
|
||||
[
|
||||
'name' => 'video_url',
|
||||
'label' => 'Video url',
|
||||
'selector' => '/video/url',
|
||||
],
|
||||
],
|
||||
'expected' => [
|
||||
[
|
||||
'id' => '1',
|
||||
'title' => 'Title',
|
||||
'video_url' => 'https://localhost/',
|
||||
],
|
||||
[
|
||||
'id' => '2',
|
||||
'title' => '',
|
||||
'video_url' => 'https://localhost/',
|
||||
],
|
||||
[
|
||||
'id' => '3',
|
||||
'title' => 'Title 3',
|
||||
'video_url' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_plus\Kernel\Plugin\migrate_plus\data_parser;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Test of the data_parser SimpleXml migrate_plus plugin.
|
||||
*
|
||||
* @group migrate_plus
|
||||
*/
|
||||
class SimpleXmlTest extends KernelTestBase {
|
||||
|
||||
public static $modules = ['migrate', 'migrate_plus'];
|
||||
|
||||
/**
|
||||
* Tests reducing single values.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testReduceSingleValue() {
|
||||
$path = $this->container
|
||||
->get('module_handler')
|
||||
->getModule('migrate_plus')
|
||||
->getPath();
|
||||
$url = $path . '/tests/data/simple_xml_reduce_single_value.xml';
|
||||
|
||||
/** @var \Drupal\migrate_plus\DataParserPluginManager $plugin_manager */
|
||||
$plugin_manager = $this->container
|
||||
->get('plugin.manager.migrate_plus.data_parser');
|
||||
$conf = [
|
||||
'plugin' => 'url',
|
||||
'data_fetcher_plugin' => 'file',
|
||||
'data_parser_plugin' => 'simple_xml',
|
||||
'destination' => 'node',
|
||||
'urls' => [$url],
|
||||
'ids' => ['id' => ['type' => 'integer']],
|
||||
'fields' => [
|
||||
[
|
||||
'name' => 'id',
|
||||
'label' => 'Id',
|
||||
'selector' => '@id',
|
||||
],
|
||||
[
|
||||
'name' => 'values',
|
||||
'label' => 'Values',
|
||||
'selector' => 'values',
|
||||
],
|
||||
],
|
||||
'item_selector' => '/items/item',
|
||||
];
|
||||
$parser = $plugin_manager->createInstance('simple_xml', $conf);
|
||||
|
||||
$data = [];
|
||||
foreach ($parser as $item) {
|
||||
$values = [];
|
||||
foreach ($item['values'] as $value) {
|
||||
$values[] = (string) $value;
|
||||
}
|
||||
$data[] = $values;
|
||||
}
|
||||
|
||||
$expected = [
|
||||
[
|
||||
'Value 1',
|
||||
'Value 2',
|
||||
],
|
||||
[
|
||||
'Value 1 (single)',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $data);
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_plus\Unit\process;
|
||||
|
||||
use Drupal\Tests\migrate\Unit\process\MigrateProcessTestCase;
|
||||
use Drupal\migrate_plus\Plugin\migrate\process\MultipleValues;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate_plus\Plugin\migrate\process\MultipleValues
|
||||
* @group migrate
|
||||
*/
|
||||
class MultipleValuesTest extends MigrateProcessTestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->plugin = new MultipleValues([], 'multiple_values', []);
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test input treated as multiple value output.
|
||||
*/
|
||||
public function testTreatAsMultiple() {
|
||||
$value = ['v1', 'v2', 'v3'];
|
||||
$output = $this->plugin->transform($value, $this->migrateExecutable, $this->row, 'destinationproperty');
|
||||
$this->assertSame($output, $value);
|
||||
$this->assertTrue($this->plugin->multiple());
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_plus\Unit\process;
|
||||
|
||||
use Drupal\Tests\migrate\Unit\process\MigrateProcessTestCase;
|
||||
use Drupal\migrate_plus\Plugin\migrate\process\SingleValue;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate_plus\Plugin\migrate\process\SingleValue
|
||||
* @group migrate
|
||||
*/
|
||||
class SingleValueTest extends MigrateProcessTestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->plugin = new SingleValue([], 'single_value', []);
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test input treated as single value output.
|
||||
*/
|
||||
public function testTreatAsSingle() {
|
||||
$value = ['v1', 'v2', 'v3'];
|
||||
$output = $this->plugin->transform($value, $this->migrateExecutable, $this->row, 'destinationproperty');
|
||||
$this->assertSame($output, $value);
|
||||
$this->assertFalse($this->plugin->multiple());
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -65,6 +65,36 @@ class SkipOnValueTest extends MigrateProcessTestCase {
|
||||
$this->assertEquals($value, '4');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::process
|
||||
*/
|
||||
public function testProcessBypassesOnMultipleNonValue() {
|
||||
$configuration['method'] = 'process';
|
||||
$configuration['value'] = [1, 1, 2, 3, 5, 8];
|
||||
$configuration['not_equals'] = TRUE;
|
||||
$value = (new SkipOnValue($configuration, 'skip_on_value', []))
|
||||
->transform(5, $this->migrateExecutable, $this->row, 'destinationproperty');
|
||||
$this->assertEquals($value, '5');
|
||||
$value = (new SkipOnValue($configuration, 'skip_on_value', []))
|
||||
->transform(1, $this->migrateExecutable, $this->row, 'destinationproperty');
|
||||
$this->assertEquals($value, '1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::row
|
||||
*/
|
||||
public function testRowBypassesOnMultipleNonValue() {
|
||||
$configuration['method'] = 'row';
|
||||
$configuration['value'] = [1, 1, 2, 3, 5, 8];
|
||||
$configuration['not_equals'] = TRUE;
|
||||
$value = (new SkipOnValue($configuration, 'skip_on_value', []))
|
||||
->transform(5, $this->migrateExecutable, $this->row, 'destinationproperty');
|
||||
$this->assertEquals($value, '5');
|
||||
$value = (new SkipOnValue($configuration, 'skip_on_value', []))
|
||||
->transform(1, $this->migrateExecutable, $this->row, 'destinationproperty');
|
||||
$this->assertEquals($value, '1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::row
|
||||
*/
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
{
|
||||
"name": "drupal/migrate_tools",
|
||||
"description": "Tools to assist in developing and running migrations.",
|
||||
"type": "drupal-module",
|
||||
"homepage": "http://drupal.org/project/migrate_tools",
|
||||
"support": {
|
||||
"issues": "http://drupal.org/project/migrate_tools",
|
||||
"irc": "irc://irc.freenode.org/drupal-migrate",
|
||||
"source": "http://cgit.drupalcode.org/migrate_tools"
|
||||
},
|
||||
"license": "GPL-2.0+",
|
||||
"require-dev": {
|
||||
"drupal/coder": "^8"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"drush": {
|
||||
"services": {
|
||||
"drush.services.yml": "^9"
|
||||
}
|
||||
"name": "drupal/migrate_tools",
|
||||
"description": "Tools to assist in developing and running migrations.",
|
||||
"type": "drupal-module",
|
||||
"homepage": "http://drupal.org/project/migrate_tools",
|
||||
"support": {
|
||||
"issues": "http://drupal.org/project/migrate_tools",
|
||||
"irc": "irc://irc.freenode.org/drupal-migrate",
|
||||
"source": "http://cgit.drupalcode.org/migrate_tools"
|
||||
},
|
||||
"license": "GPL-2.0+",
|
||||
"require": {
|
||||
"drupal/migrate_plus": "^4"
|
||||
},
|
||||
"require-dev": {
|
||||
"drupal/coder": "^8",
|
||||
"drupal/migrate_source_csv": "^2.2"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"drush": {
|
||||
"services": {
|
||||
"drush.services.yml": "^9"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ package: Migration
|
||||
dependencies:
|
||||
- drupal:migrate (>=8.3)
|
||||
- migrate_plus:migrate_plus
|
||||
test_dependencies:
|
||||
- migrate_source_csv:migrate_source_csv (>=8.x-2.2)
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-02-23
|
||||
version: '8.x-4.0-beta3'
|
||||
# Information added by Drupal.org packaging script on 2018-08-27
|
||||
version: '8.x-4.0'
|
||||
core: '8.x'
|
||||
project: 'migrate_tools'
|
||||
datestamp: 1519400307
|
||||
datestamp: 1535380087
|
||||
|
||||
@@ -39,3 +39,15 @@ entity.migration.delete_form:
|
||||
base_route: entity.migration.overview
|
||||
title: Delete
|
||||
weight: 10
|
||||
|
||||
|
||||
entity.migration.overview_general_edit:
|
||||
title: Overview
|
||||
route_name: entity.migration.edit_form
|
||||
parent_id: entity.migration.edit_form
|
||||
|
||||
entity.migration.overview_source_edit:
|
||||
title: Source
|
||||
route_name: migrate_tools.source_csv
|
||||
parent_id: entity.migration.edit_form
|
||||
weight: 1
|
||||
|
||||
@@ -28,3 +28,31 @@ function migrate_tools_entity_type_build(array &$entity_types) {
|
||||
->setLinkTemplate('edit-form', '/admin/structure/migrate/manage/{migration_group}')
|
||||
->setLinkTemplate('delete-form', '/admin/structure/migrate/manage/{migration_group}/delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_migration_plugins_alter().
|
||||
*/
|
||||
function migrate_tools_migration_plugins_alter(array &$migrations) {
|
||||
/** @var \Drupal\Core\TempStore\PrivateTempStoreFactory $store */
|
||||
$tempStoreFactory = \Drupal::service('tempstore.private');
|
||||
$store = $tempStoreFactory->get('migrate_tools');
|
||||
// TODO: remove work-around after
|
||||
// https://www.drupal.org/project/drupal/issues/2860341 is fixed.
|
||||
if (!\Drupal::request()->hasSession()) {
|
||||
$session = \Drupal::service('session');
|
||||
\Drupal::request()->setSession($session);
|
||||
$session->start();
|
||||
}
|
||||
// Get the list of changed migrations.
|
||||
$migrationsChanged = $store->get('migrations_changed');
|
||||
if (isset($store) && (is_array($migrationsChanged))) {
|
||||
// Alter the source column names for each changed migration.
|
||||
foreach ($migrationsChanged as $id) {
|
||||
$data = $store->get($id);
|
||||
if (isset($data['changed'])) {
|
||||
$migrations[$id]['source']['column_names'] = $data['changed'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,67 +58,140 @@ entity.migration.overview:
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MigrationController::overview'
|
||||
_title: 'Migration overview'
|
||||
_migrate_group: true
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
options:
|
||||
parameters:
|
||||
migration:
|
||||
type: entity:migration
|
||||
migration_group:
|
||||
type: entity:migration_group
|
||||
entity.migration.source:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/source'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MigrationController::source'
|
||||
_title: 'Source'
|
||||
_migrate_group: true
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
options:
|
||||
parameters:
|
||||
migration:
|
||||
type: entity:migration
|
||||
migration_group:
|
||||
type: entity:migration_group
|
||||
entity.migration.process:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/process'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MigrationController::process'
|
||||
_title: 'Process'
|
||||
_migrate_group: true
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
options:
|
||||
parameters:
|
||||
migration:
|
||||
type: entity:migration
|
||||
migration_group:
|
||||
type: entity:migration_group
|
||||
entity.migration.process.run:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/process/run'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MigrationController::run'
|
||||
_title: 'Run'
|
||||
_migrate_group: true
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
options:
|
||||
parameters:
|
||||
migration:
|
||||
type: entity:migration
|
||||
migration_group:
|
||||
type: entity:migration_group
|
||||
entity.migration.destination:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/destination'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MigrationController::destination'
|
||||
_title: 'Destination'
|
||||
_migrate_group: true
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
|
||||
options:
|
||||
parameters:
|
||||
migration:
|
||||
type: entity:migration
|
||||
migration_group:
|
||||
type: entity:migration_group
|
||||
# This is the router item for editing our migration entity.
|
||||
entity.migration.edit_form:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/edit'
|
||||
defaults:
|
||||
_title: 'Edit migration'
|
||||
_entity_form: migration.edit
|
||||
_migrate_group: true
|
||||
requirements:
|
||||
_entity_access: migration.update
|
||||
|
||||
options:
|
||||
parameters:
|
||||
migration:
|
||||
type: entity:migration
|
||||
migration_group:
|
||||
type: entity:migration_group
|
||||
# This is the router item for deleting an instance of our migration entity.
|
||||
entity.migration.delete_form:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/delete'
|
||||
defaults:
|
||||
_title: 'Delete migration'
|
||||
_entity_form: migration.delete
|
||||
_migrate_group: true
|
||||
requirements:
|
||||
_entity_access: migration.delete
|
||||
|
||||
options:
|
||||
parameters:
|
||||
migration:
|
||||
type: entity:migration
|
||||
migration_group:
|
||||
type: entity:migration_group
|
||||
migrate_tools.messages:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/messages'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MessageController::overview'
|
||||
_title: 'Messages'
|
||||
_migrate_group: true
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
|
||||
options:
|
||||
parameters:
|
||||
migration:
|
||||
type: entity:migration
|
||||
migration_group:
|
||||
type: entity:migration_group
|
||||
migrate_tools.execute:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/execute'
|
||||
defaults:
|
||||
_form: '\Drupal\migrate_tools\Form\MigrationExecuteForm'
|
||||
_title: 'Execute migration'
|
||||
_migrate_group: true
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
options:
|
||||
parameters:
|
||||
migration:
|
||||
type: entity:migration
|
||||
migration_group:
|
||||
type: entity:migration_group
|
||||
migrate_tools.source_csv:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/source/edit'
|
||||
defaults:
|
||||
_form: 'Drupal\migrate_tools\Form\SourceCsvForm'
|
||||
_migrate_group: true
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
_custom_access: 'Drupal\migrate_tools\Form\SourceCsvForm::access'
|
||||
options:
|
||||
parameters:
|
||||
migration:
|
||||
type: entity:migration
|
||||
migration_group:
|
||||
type: entity:migration_group
|
||||
|
||||
@@ -3,3 +3,7 @@ services:
|
||||
class: Drupal\Core\Logger\LoggerChannel
|
||||
factory: logger.factory:get
|
||||
arguments: ['migrate_tools']
|
||||
route_processor.migrate_group:
|
||||
class: Drupal\migrate_tools\Routing\RouteProcessor
|
||||
tags:
|
||||
- { name: route_processor_outbound }
|
||||
|
||||
+64
-84
@@ -67,12 +67,7 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $keyValue
|
||||
* Key-value store service.
|
||||
*/
|
||||
public function __construct(
|
||||
MigrationPluginManager $migrationPluginManager,
|
||||
DateFormatter $dateFormatter,
|
||||
EntityTypeManagerInterface $entityTypeManager,
|
||||
KeyValueFactoryInterface $keyValue
|
||||
) {
|
||||
public function __construct(MigrationPluginManager $migrationPluginManager, DateFormatter $dateFormatter, EntityTypeManagerInterface $entityTypeManager, KeyValueFactoryInterface $keyValue) {
|
||||
parent::__construct();
|
||||
$this->migrationPluginManager = $migrationPluginManager;
|
||||
$this->dateFormatter = $dateFormatter;
|
||||
@@ -123,14 +118,7 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
|
||||
* Migrations status formatted as table.
|
||||
*/
|
||||
public function status(
|
||||
$migration_names = '',
|
||||
array $options = [
|
||||
'group' => NULL,
|
||||
'tag' => NULL,
|
||||
'names-only' => NULL,
|
||||
]
|
||||
) {
|
||||
public function status($migration_names = '', array $options = ['group' => NULL, 'tag' => NULL, 'names-only' => NULL]) {
|
||||
$names_only = $options['names-only'];
|
||||
|
||||
$migrations = $this->migrationsList($migration_names, $options);
|
||||
@@ -271,20 +259,7 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @throws \Exception
|
||||
* If there are not enough parameters to the command.
|
||||
*/
|
||||
public function import(
|
||||
$migration_names = '',
|
||||
array $options = [
|
||||
'all' => NULL,
|
||||
'group' => NULL,
|
||||
'tag' => NULL,
|
||||
'limit' => NULL,
|
||||
'feedback' => NULL,
|
||||
'idlist' => NULL,
|
||||
'update' => NULL,
|
||||
'force' => NULL,
|
||||
'execute-dependencies' => NULL,
|
||||
]
|
||||
) {
|
||||
public function import($migration_names = '', array $options = ['all' => NULL, 'group' => NULL, 'tag' => NULL, 'limit' => NULL, 'feedback' => NULL, 'idlist' => NULL, 'update' => NULL, 'force' => NULL, 'execute-dependencies' => NULL]) {
|
||||
$group_names = $options['group'];
|
||||
$tag_names = $options['tag'];
|
||||
$all = $options['all'];
|
||||
@@ -293,7 +268,7 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
throw new \Exception(dt('You must specify --all, --group, --tag or one or more migration names separated by commas'));
|
||||
}
|
||||
|
||||
foreach (['limit', 'feedback', 'idlist', 'update', 'force'] as $option) {
|
||||
foreach (['limit', 'feedback', 'idlist', 'update', 'force', 'execute-dependencies'] as $option) {
|
||||
if ($options[$option]) {
|
||||
$additional_options[$option] = $options[$option];
|
||||
}
|
||||
@@ -346,15 +321,7 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @throws \Exception
|
||||
* If there are not enough parameters to the command.
|
||||
*/
|
||||
public function rollback(
|
||||
$migration_names = '',
|
||||
array $options = [
|
||||
'all' => NULL,
|
||||
'group' => NULL,
|
||||
'tag' => NULL,
|
||||
'feedback' => NULL,
|
||||
]
|
||||
) {
|
||||
public function rollback($migration_names = '', array $options = ['all' => NULL, 'group' => NULL, 'tag' => NULL, 'feedback' => NULL]) {
|
||||
$group_names = $options['group'];
|
||||
$tag_names = $options['tag'];
|
||||
$all = $options['all'];
|
||||
@@ -496,52 +463,47 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @validate-module-enabled migrate_tools
|
||||
*
|
||||
* @aliases mmsg,migrate-messages
|
||||
*
|
||||
* @field-labels
|
||||
* source_ids_hash: Source IDs Hash
|
||||
* level: Level
|
||||
* message: Message
|
||||
* @default-fields source_ids_hash,level,message
|
||||
*
|
||||
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
|
||||
* Source fields of the given migration formatted as a table.
|
||||
*/
|
||||
public function messages($migration_id, array $options = ['csv' => NULL]) {
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance(
|
||||
$migration_id
|
||||
);
|
||||
if ($migration) {
|
||||
$map = $migration->getIdMap();
|
||||
$first = TRUE;
|
||||
$table = [];
|
||||
foreach ($map->getMessageIterator() as $row) {
|
||||
unset($row->msgid);
|
||||
if ($first) {
|
||||
// @todo: Ideally, replace sourceid* with source key names. Or, should
|
||||
// getMessageIterator() do that?
|
||||
foreach ($row as $column => $value) {
|
||||
$table[0][] = $column;
|
||||
}
|
||||
$first = FALSE;
|
||||
}
|
||||
$table[] = (array) $row;
|
||||
}
|
||||
if (empty($table)) {
|
||||
$this->logger()->notice(dt('No messages for this migration'));
|
||||
}
|
||||
else {
|
||||
if ($options['csv']) {
|
||||
foreach ($table as $row) {
|
||||
fputcsv(STDOUT, $row);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$widths = [];
|
||||
foreach ($table[0] as $header) {
|
||||
$widths[] = strlen($header) + 1;
|
||||
}
|
||||
|
||||
drush_print_table($table);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!$migration) {
|
||||
$this->logger()->error(
|
||||
dt('Migration @id does not exist', ['@id' => $migration_id])
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$map = $migration->getIdMap();
|
||||
$table = [];
|
||||
foreach ($map->getMessageIterator() as $row) {
|
||||
unset($row->msgid);
|
||||
$table[] = (array) $row;
|
||||
}
|
||||
if (empty($table)) {
|
||||
$this->logger()->notice(dt('No messages for this migration'));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ($options['csv']) {
|
||||
fputcsv(STDOUT, array_keys($table[0]));
|
||||
foreach ($table as $row) {
|
||||
fputcsv(STDOUT, $row);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return new RowsOfFields($table);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -558,6 +520,14 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @validate-module-enabled migrate_tools
|
||||
*
|
||||
* @aliases mfs, migrate-fields-source
|
||||
*
|
||||
* @field-labels
|
||||
* machine_name: Machine Name
|
||||
* description: Description
|
||||
* @default-fields machine_name,description
|
||||
*
|
||||
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
|
||||
* Source fields of the given migration formatted as a table.
|
||||
*/
|
||||
public function fieldsSource($migration_id) {
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
@@ -568,9 +538,12 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
$source = $migration->getSourcePlugin();
|
||||
$table = [];
|
||||
foreach ($source->fields() as $machine_name => $description) {
|
||||
$table[] = [strip_tags($description), $machine_name];
|
||||
$table[] = [
|
||||
'machine_name' => $machine_name,
|
||||
'description' => strip_tags($description),
|
||||
];
|
||||
}
|
||||
drush_print_table($table);
|
||||
return new RowsOfFields($table);
|
||||
}
|
||||
else {
|
||||
$this->logger()->error(
|
||||
@@ -690,17 +663,23 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @throws \Exception
|
||||
* If some migrations failed during execution.
|
||||
*/
|
||||
protected function executeMigration(
|
||||
MigrationInterface $migration,
|
||||
$migration_id,
|
||||
array $options = []
|
||||
) {
|
||||
protected function executeMigration(MigrationInterface $migration, $migration_id, array $options = []) {
|
||||
// Keep track of all migrations run during this command so the same
|
||||
// migration is not run multiple times.
|
||||
static $executed_migrations = [];
|
||||
|
||||
if (isset($options['execute-dependencies'])) {
|
||||
if ($required_IDS = $migration->get('requirements')) {
|
||||
$required_migrations = $migration->get('requirements');
|
||||
$required_migrations = array_filter($required_migrations, function ($value) use ($executed_migrations) {
|
||||
return !isset($executed_migrations[$value]);
|
||||
});
|
||||
|
||||
if (!empty($required_migrations)) {
|
||||
$manager = $this->migrationPluginManager;
|
||||
$required_migrations = $manager->createInstances($required_IDS);
|
||||
$required_migrations = $manager->createInstances($required_migrations);
|
||||
$dependency_options = array_merge($options, ['is_dependency' => TRUE]);
|
||||
array_walk($required_migrations, __FUNCTION__, $dependency_options);
|
||||
array_walk($required_migrations, [$this, __FUNCTION__], $dependency_options);
|
||||
$executed_migrations += $required_migrations;
|
||||
}
|
||||
}
|
||||
if (!empty($options['force'])) {
|
||||
@@ -712,6 +691,7 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
$executable = new MigrateExecutable($migration, $this->getMigrateMessage(), $options);
|
||||
// drush_op() provides --simulate support.
|
||||
drush_op([$executable, 'import']);
|
||||
$executed_migrations += [$migration_id => $migration_id];
|
||||
if ($count = $executable->getFailedCount()) {
|
||||
// Nudge Drush to use a non-zero exit code.
|
||||
throw new \Exception(
|
||||
|
||||
+11
-11
@@ -7,6 +7,8 @@ use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
|
||||
use Drupal\migrate_plus\Entity\MigrationGroupInterface;
|
||||
use Drupal\migrate_plus\Entity\MigrationInterface as MigratePlusMigrationInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
@@ -71,21 +73,19 @@ class MessageController extends ControllerBase {
|
||||
*
|
||||
* Messages are truncated at 56 chars.
|
||||
*
|
||||
* @param string $migration_group
|
||||
* Machine name of the migration's group.
|
||||
*
|
||||
* @param string $migration
|
||||
* Machine name of the migration.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationGroupInterface $migration_group
|
||||
* The migration group.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationInterface $migration
|
||||
* The $migration.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_render().
|
||||
*/
|
||||
public function overview($migration_group, $migration) {
|
||||
public function overview(MigrationGroupInterface $migration_group, MigratePlusMigrationInterface $migration) {
|
||||
$rows = [];
|
||||
$classes = static::getLogLevelClassMap();
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance($migration);
|
||||
$source_id_field_names = array_keys($migration->getSourcePlugin()->getIds());
|
||||
$migration_plugin = $this->migrationPluginManager->createInstance($migration->id(), $migration->toArray());
|
||||
$source_id_field_names = array_keys($migration_plugin->getSourcePlugin()->getIds());
|
||||
$column_number = 1;
|
||||
foreach ($source_id_field_names as $source_id_field_name) {
|
||||
$header[] = [
|
||||
@@ -104,8 +104,8 @@ class MessageController extends ControllerBase {
|
||||
'field' => 'message',
|
||||
];
|
||||
|
||||
$message_table = $migration->getIdMap()->messageTableName();
|
||||
$map_table = $migration->getIdMap()->mapTableName();
|
||||
$message_table = $migration_plugin->getIdMap()->messageTableName();
|
||||
$map_table = $migration_plugin->getIdMap()->mapTableName();
|
||||
$query = $this->database->select($message_table, 'msg')
|
||||
->extend('\Drupal\Core\Database\Query\PagerSelectExtender')
|
||||
->extend('\Drupal\Core\Database\Query\TableSortExtender');
|
||||
|
||||
+40
-51
@@ -8,6 +8,8 @@ use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Routing\CurrentRouteMatch;
|
||||
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\migrate_plus\Entity\MigrationGroupInterface;
|
||||
use Drupal\migrate_plus\Entity\MigrationInterface;
|
||||
use Drupal\migrate_tools\MigrateBatchExecutable;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Url;
|
||||
@@ -58,19 +60,15 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
/**
|
||||
* Displays an overview of a migration entity.
|
||||
*
|
||||
* @param string $migration_group
|
||||
* Machine name of the migration's group.
|
||||
* @param string $migration
|
||||
* Machine name of the migration.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationGroupInterface $migration_group
|
||||
* The migration group.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationInterface $migration
|
||||
* The $migration.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_render().
|
||||
*/
|
||||
public function overview($migration_group, $migration) {
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance($migration);
|
||||
|
||||
public function overview(MigrationGroupInterface $migration_group, MigrationInterface $migration) {
|
||||
$build['overview'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Overview'),
|
||||
@@ -78,7 +76,7 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
|
||||
$build['overview']['group'] = [
|
||||
'#title' => $this->t('Group:'),
|
||||
'#markup' => Xss::filterAdmin($migration_group),
|
||||
'#markup' => Xss::filterAdmin($migration_group->label()),
|
||||
'#type' => 'item',
|
||||
];
|
||||
|
||||
@@ -87,8 +85,8 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
'#markup' => Xss::filterAdmin($migration->label()),
|
||||
'#type' => 'item',
|
||||
];
|
||||
|
||||
$migration_dependencies = $migration->getMigrationDependencies();
|
||||
$migration_plugin = $this->migrationPluginManager->createInstance($migration->id(), $migration->toArray());
|
||||
$migration_dependencies = $migration_plugin->getMigrationDependencies();
|
||||
if (!empty($migration_dependencies['required'])) {
|
||||
$build['overview']['dependencies'] = [
|
||||
'#title' => $this->t('Migration Dependencies') ,
|
||||
@@ -110,19 +108,15 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
/**
|
||||
* Display source information of a migration entity.
|
||||
*
|
||||
* @param string $migration_group
|
||||
* Machine name of the migration's group.
|
||||
* @param string $migration
|
||||
* Machine name of the migration.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationGroupInterface $migration_group
|
||||
* The migration group.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationInterface $migration
|
||||
* The $migration.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_render().
|
||||
*/
|
||||
public function source($migration_group, $migration) {
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance($migration);
|
||||
|
||||
public function source(MigrationGroupInterface $migration_group, MigrationInterface $migration) {
|
||||
// Source field information.
|
||||
$build['source'] = [
|
||||
'#type' => 'fieldset',
|
||||
@@ -133,8 +127,8 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
'id' => 'migration-detail-source',
|
||||
],
|
||||
];
|
||||
|
||||
$source = $migration->getSourcePlugin();
|
||||
$migration_plugin = $this->migrationPluginManager->createInstance($migration->id(), $migration->toArray());
|
||||
$source = $migration_plugin->getSourcePlugin();
|
||||
$build['source']['query'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Query'),
|
||||
@@ -142,7 +136,7 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
];
|
||||
$header = [$this->t('Machine name'), $this->t('Description')];
|
||||
$rows = [];
|
||||
foreach ($source->fields($migration) as $machine_name => $description) {
|
||||
foreach ($source->fields($migration_plugin) as $machine_name => $description) {
|
||||
$rows[] = [
|
||||
['data' => Html::escape($machine_name)],
|
||||
['data' => Xss::filterAdmin($description)],
|
||||
@@ -162,22 +156,20 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
/**
|
||||
* Run a migration.
|
||||
*
|
||||
* @param string $migration_group
|
||||
* Machine name of the migration's group.
|
||||
* @param string $migration
|
||||
* Machine name of the migration.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationGroupInterface $migration_group
|
||||
* The migration group.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationInterface $migration
|
||||
* The $migration.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_render().
|
||||
*/
|
||||
public function run($migration_group, $migration) {
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance($migration);
|
||||
|
||||
public function run(MigrationGroupInterface $migration_group, MigrationInterface $migration) {
|
||||
$migrateMessage = new MigrateMessage();
|
||||
$options = [];
|
||||
|
||||
$executable = new MigrateBatchExecutable($migration, $migrateMessage, $options);
|
||||
$migration_plugin = $this->migrationPluginManager->createInstance($migration->id(), $migration->toArray());
|
||||
$executable = new MigrateBatchExecutable($migration_plugin, $migrateMessage, $options);
|
||||
$executable->batchImport();
|
||||
|
||||
$migration_group = $this->currentRouteMatch->getParameter('migration_group');
|
||||
@@ -191,18 +183,16 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
/**
|
||||
* Display process information of a migration entity.
|
||||
*
|
||||
* @param string $migration_group
|
||||
* Machine name of the migration's group.
|
||||
* @param string $migration
|
||||
* Machine name of the migration.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationGroupInterface $migration_group
|
||||
* The migration group.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationInterface $migration
|
||||
* The $migration.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_render().
|
||||
*/
|
||||
public function process($migration_group, $migration) {
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance($migration);
|
||||
public function process(MigrationGroupInterface $migration_group, MigrationInterface $migration) {
|
||||
$migration_plugin = $this->migrationPluginManager->createInstance($migration->id(), $migration->toArray());
|
||||
|
||||
// Process information.
|
||||
$build['process'] = [
|
||||
@@ -217,7 +207,7 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
$this->t('Default'),
|
||||
];
|
||||
$rows = [];
|
||||
foreach ($migration->getProcess() as $destination_id => $process_line) {
|
||||
foreach ($migration_plugin->getProcess() as $destination_id => $process_line) {
|
||||
$row = [];
|
||||
$row[] = ['data' => Html::escape($destination_id)];
|
||||
if (isset($process_line[0]['source'])) {
|
||||
@@ -251,7 +241,7 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
$build['process']['run'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Run'),
|
||||
'#url' => Url::fromRoute('entity.migration.process.run', ['migration_group' => $migration_group, 'migration' => $migration->id()]),
|
||||
'#url' => Url::fromRoute('entity.migration.process.run', ['migration_group' => $migration_group->id(), 'migration' => $migration->id()]),
|
||||
];
|
||||
|
||||
return $build;
|
||||
@@ -260,17 +250,16 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
/**
|
||||
* Displays destination information of a migration entity.
|
||||
*
|
||||
* @param string $migration_group
|
||||
* Machine name of the migration's group.
|
||||
* @param string $migration
|
||||
* Machine name of the migration.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationGroupInterface $migration_group
|
||||
* The migration group.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationInterface $migration
|
||||
* The $migration.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_render().
|
||||
*/
|
||||
public function destination($migration_group, $migration) {
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance($migration);
|
||||
public function destination(MigrationGroupInterface $migration_group, MigrationInterface $migration) {
|
||||
$migration_plugin = $this->migrationPluginManager->createInstance($migration->id(), $migration->toArray());
|
||||
|
||||
// Destination field information.
|
||||
$build['destination'] = [
|
||||
@@ -282,7 +271,7 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
'id' => 'migration-detail-destination',
|
||||
],
|
||||
];
|
||||
$destination = $migration->getDestinationPlugin();
|
||||
$destination = $migration_plugin->getDestinationPlugin();
|
||||
$build['destination']['type'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Type'),
|
||||
|
||||
+1
-1
@@ -161,7 +161,7 @@ class MigrationListBuilder extends ConfigEntityListBuilder implements EntityHand
|
||||
$row['status'] = $migration->getStatusLabel();
|
||||
}
|
||||
catch (PluginException $e) {
|
||||
$this->logger->warning('Migration entity id %id is malformed', ['%id' => $migration_entity->id()]);
|
||||
$this->logger->warning('Migration entity id %id is malformed: %orig', ['%id' => $migration_entity->id(), '%orig' => $e->getMessage()]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ class MigrationExecuteForm extends FormBase {
|
||||
return $form;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Operations.
|
||||
*/
|
||||
@@ -124,7 +123,6 @@ class MigrationExecuteForm extends FormBase {
|
||||
return $form;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -162,12 +160,10 @@ class MigrationExecuteForm extends FormBase {
|
||||
$force = 0;
|
||||
}
|
||||
|
||||
$migration_name = \Drupal::routeMatch()->getParameter('migration');
|
||||
|
||||
if ($migration_name) {
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance($migration_name);
|
||||
$migration = \Drupal::routeMatch()->getParameter('migration');
|
||||
if ($migration) {
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration_plugin */
|
||||
$migration_plugin = $this->migrationPluginManager->createInstance($migration->id(), $migration->toArray());
|
||||
$migrateMessage = new MigrateMessage();
|
||||
|
||||
switch ($operation) {
|
||||
@@ -179,7 +175,7 @@ class MigrationExecuteForm extends FormBase {
|
||||
'force' => $force,
|
||||
];
|
||||
|
||||
$executable = new MigrateBatchExecutable($migration, $migrateMessage, $options);
|
||||
$executable = new MigrateBatchExecutable($migration_plugin, $migrateMessage, $options);
|
||||
$executable->batchImport();
|
||||
|
||||
break;
|
||||
@@ -192,20 +188,20 @@ class MigrationExecuteForm extends FormBase {
|
||||
'force' => $force,
|
||||
];
|
||||
|
||||
$executable = new MigrateBatchExecutable($migration, $migrateMessage, $options);
|
||||
$executable = new MigrateBatchExecutable($migration_plugin, $migrateMessage, $options);
|
||||
$executable->rollback();
|
||||
|
||||
break;
|
||||
|
||||
case 'stop':
|
||||
|
||||
$migration->interruptMigration(MigrationInterface::RESULT_STOPPED);
|
||||
$migration_plugin->interruptMigration(MigrationInterface::RESULT_STOPPED);
|
||||
|
||||
break;
|
||||
|
||||
case 'reset':
|
||||
|
||||
$migration->setStatus(MigrationInterface::STATUS_IDLE);
|
||||
$migration_plugin->setStatus(MigrationInterface::STATUS_IDLE);
|
||||
|
||||
break;
|
||||
|
||||
|
||||
@@ -0,0 +1,453 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Form;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Core\TempStore\PrivateTempStoreFactory;
|
||||
use Drupal\Core\TempStore\TempStoreException;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\migrate_plus\Entity\MigrationInterface;
|
||||
use Drupal\migrate_source_csv\Plugin\migrate\source\CSV;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
|
||||
|
||||
/**
|
||||
* Provides an edit form for CSV source plugin column_names configuration.
|
||||
*
|
||||
* This means you can tell the migration which columns your data is in and no
|
||||
* longer edit the CSV to fit the column order set in the migration or edit the
|
||||
* migration yml itself.
|
||||
*
|
||||
* Changes made to the column configuration, or aliases, are stored in the
|
||||
* private migrate_toools private store keyed by the migration plugin id. The
|
||||
* data stored for each migrations consists of two arrays, the 'original' column
|
||||
* aliases and the 'updated' column aliases.
|
||||
*
|
||||
* An addtional list of all changed migration id is kept in the store, in the
|
||||
* key 'migrations_changed'
|
||||
*
|
||||
* Private Store Usage:
|
||||
* migrations_changed: An array of the ids of the migrations that have been
|
||||
* changed:
|
||||
* [migration_id]: The original and changed values for this column assignments
|
||||
*
|
||||
* Format of the source configuration saved in the store.
|
||||
* @code
|
||||
* migration_id
|
||||
* original
|
||||
* column_index1
|
||||
* property 1 => label 1
|
||||
* column_index2
|
||||
* property 2 => label 2
|
||||
* updated
|
||||
* column_index1
|
||||
* property 2 => label 2
|
||||
* column_index2
|
||||
* property 1 => label 1
|
||||
* @endcode
|
||||
*
|
||||
* Example source configuration.
|
||||
* @code
|
||||
* custom_migration
|
||||
* original
|
||||
* 2
|
||||
* title => title
|
||||
* 3
|
||||
* body => foo
|
||||
* updated
|
||||
* 8
|
||||
* title => new_title
|
||||
* 9
|
||||
* body => new_body
|
||||
* @endcode
|
||||
*/
|
||||
class SourceCsvForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Plugin manager for migration plugins.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
|
||||
*/
|
||||
protected $migrationPluginManager;
|
||||
|
||||
/**
|
||||
* The messenger service.
|
||||
*
|
||||
* @var \Drupal\Core\Messenger\MessengerInterface
|
||||
*/
|
||||
protected $messenger;
|
||||
|
||||
/**
|
||||
* Temporary store for column assignment changes.
|
||||
*
|
||||
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
|
||||
*/
|
||||
protected $store;
|
||||
|
||||
/**
|
||||
* The file object that reads the CSV file.
|
||||
*
|
||||
* @var \SplFileObject
|
||||
*/
|
||||
protected $file = NULL;
|
||||
|
||||
/**
|
||||
* The migration being examined.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* The migration plugin id.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The array of columns names from the CSV source plugin.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $columnNames;
|
||||
|
||||
/**
|
||||
* An array of options for the column select form field..
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* An array of modified and original column_name source plugin configuration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $sourceConfiguration;
|
||||
|
||||
/**
|
||||
* Constructs new SourceCsvForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
* @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager
|
||||
* The plugin manager for config entity-based migrations.
|
||||
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
|
||||
* The messenger service.
|
||||
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $private_store
|
||||
* The private store.
|
||||
*/
|
||||
public function __construct(Connection $connection, MigrationPluginManagerInterface $migration_plugin_manager, MessengerInterface $messenger, PrivateTempStoreFactory $private_store) {
|
||||
$this->connection = $connection;
|
||||
$this->migrationPluginManager = $migration_plugin_manager;
|
||||
$this->messenger = $messenger;
|
||||
$this->store = $private_store->get('migrate_tools');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('database'),
|
||||
$container->get('plugin.manager.migration'),
|
||||
$container->get('messenger'),
|
||||
$container->get('tempstore.private')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom access check.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationInterface $migration
|
||||
* The $migration.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResult
|
||||
* Allowed or forbidden, neutral if tempstore is empty.
|
||||
*/
|
||||
public function access(AccountInterface $account, MigrationInterface $migration) {
|
||||
try {
|
||||
$this->migration = $this->migrationPluginManager->createInstance($migration->id(), $migration->toArray());
|
||||
}
|
||||
catch (PluginException $e) {
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
if ($this->migration) {
|
||||
if ($source = $this->migration->getSourcePlugin()) {
|
||||
if (is_a($source, CSV::class)) {
|
||||
return AccessResult::allowed();
|
||||
}
|
||||
}
|
||||
}
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, MigrationInterface $migration = NULL) {
|
||||
try {
|
||||
// @TODO: remove this horrible config work around after
|
||||
// https://www.drupal.org/project/drupal/issues/2986665 is fixed.
|
||||
$this->migration = $this->migrationPluginManager->createInstance($migration->id(), $migration->toArray());
|
||||
/** @var \Drupal\migrate_source_csv\Plugin\migrate\source\CSV $source */
|
||||
$source = $this->migration->getSourcePlugin();
|
||||
$source->setConfiguration($migration->toArray()['source']);
|
||||
}
|
||||
catch (PluginException $e) {
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
// Get the source file after the properties are initialized.
|
||||
$source->initializeIterator();
|
||||
$this->file = $source->getFile();
|
||||
|
||||
// Set the input field options to the header row values or, if there are
|
||||
// no such values, use an indexed array.
|
||||
if ($this->file->getHeaderRowCount() > 0) {
|
||||
$this->options = $this->getHeaderColumnNames();
|
||||
}
|
||||
else {
|
||||
for ($i = 0; $i < $this->getFileColumnCount(); $i++) {
|
||||
$this->options[$i] = $i;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the store key to the migration id.
|
||||
$this->id = $this->migration->getPluginId();
|
||||
|
||||
// Get the column names from the file or from the store, if updated
|
||||
// values are in the store.
|
||||
$this->sourceConfiguration = $this->store->get($this->id);
|
||||
if (isset($this->sourceConfiguration['changed'])) {
|
||||
if ($config = $this->sourceConfiguration['changed']) {
|
||||
$this->columnNames = $config;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Get the calculated column names. This is either the header rows or
|
||||
// the configuration column_name value.
|
||||
$this->columnNames = $this->file->getColumnNames();
|
||||
if (!isset($this->sourceConfiguration['original'])) {
|
||||
// Save as the original values.
|
||||
$this->sourceConfiguration['original'] = $this->columnNames;
|
||||
$this->store->set($this->id, $this->sourceConfiguration);
|
||||
}
|
||||
}
|
||||
$form['#title'] = $this->t('Column Aliases');
|
||||
|
||||
$form['heading'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t(':label', [':label' => $this->migration->label()]),
|
||||
'#description' => '<p>' . $this->t('You can change the columns to be used by this migration for each source property.') . '</p>',
|
||||
];
|
||||
// Create a form field for each column in this migration.
|
||||
foreach ($this->columnNames as $index => $data) {
|
||||
$property_name = key($data);
|
||||
$default_value = $index;
|
||||
$label = $this->getLabel($this->sourceConfiguration['original'], $property_name);
|
||||
|
||||
$description = $this->t('Select the column where the data for <em>:label</em>, property <em>:property</em>, will be found.', [
|
||||
':label' => $label,
|
||||
':property' => $property_name,
|
||||
]);
|
||||
$form['aliases'][$property_name] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $label,
|
||||
'#description' => $description,
|
||||
'#options' => $this->options,
|
||||
'#default_value' => $default_value,
|
||||
];
|
||||
}
|
||||
$form['actions'] = ['#type' => 'actions'];
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#button_type' => 'primary',
|
||||
'#value' => $this->t('Submit'),
|
||||
];
|
||||
$form['actions']['cancel'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Cancel'),
|
||||
'#submit' => ['::cancel'],
|
||||
'#limit_validation_errors' => [],
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
// Display an error message if two properties have the same source column.
|
||||
$values = [];
|
||||
foreach ($this->columnNames as $index => $data) {
|
||||
$property_name = key($data);
|
||||
$value = $form_state->getValue($property_name);
|
||||
if (in_array($value, $values)) {
|
||||
$form_state->setErrorByName($property_name, $this->t('Source properties can not share the same source column.'));
|
||||
}
|
||||
$values[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// Create a new column_names configuration.
|
||||
$new_column_names = [];
|
||||
foreach ($this->columnNames as $index => $data) {
|
||||
// Keep the property name as it is used in the process pipeline.
|
||||
$property_name = key($data);
|
||||
// Get the new column number from the form alias field for this property.
|
||||
$new_index = $form_state->getValue($property_name);
|
||||
// Get the new label from the options array.
|
||||
$new_label = $this->options[$new_index];
|
||||
// Save using the new column number and new label.
|
||||
$new_column_names[$new_index] = [$property_name => $new_label];
|
||||
}
|
||||
// Update the file columns.
|
||||
$this->file->setColumnNames($new_column_names);
|
||||
// Save as updated in the store.
|
||||
$this->sourceConfiguration['changed'] = $new_column_names;
|
||||
$this->store->set($this->id, $this->sourceConfiguration);
|
||||
|
||||
$changed = ($this->store->get('migrations_changed')) ? $this->store->get('migrations_changed') : [];
|
||||
if (!in_array($this->id, $changed)) {
|
||||
$changed[] = $this->id;
|
||||
$this->store->set('migrations_changed', $changed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for the 'cancel' action.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
public function cancel(array $form, FormStateInterface $form_state) {
|
||||
// Restore the file columns to the original settings.
|
||||
$this->file->setColumnNames($this->sourceConfiguration['original']);
|
||||
// Remove this migration from the store.
|
||||
try {
|
||||
$this->store->delete($this->id);
|
||||
}
|
||||
catch (TempStoreException $e) {
|
||||
$this->messenger->addError($e->getMessage());
|
||||
}
|
||||
|
||||
$migrationsChanged = $this->store->get('migrations_changed');
|
||||
unset($migrationsChanged[$this->id]);
|
||||
try {
|
||||
$this->store->set('migrations_changed', $migrationsChanged);
|
||||
}
|
||||
catch (TempStoreException $e) {
|
||||
$this->messenger->addError($e->getMessage());
|
||||
}
|
||||
$form_state->setRedirect('entity.migration_group.list');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'migrate_tools_source_csv';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.migration_group.list');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the header row.
|
||||
*
|
||||
* Use a new file handle so that CSVFileObject::current() is not executed.
|
||||
*
|
||||
* @return array
|
||||
* The header row.
|
||||
*/
|
||||
public function getHeaderColumnNames() {
|
||||
$row = [];
|
||||
$fname = $this->file->getPathname();
|
||||
$handle = fopen($fname, 'r');
|
||||
if ($handle) {
|
||||
fseek($handle, $this->file->getHeaderRowCount() - 1);
|
||||
$row = fgetcsv($handle);
|
||||
fclose($handle);
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count of fields in the header row.
|
||||
*
|
||||
* Use a new file handle so that CSVFileObject::current() is not executed.
|
||||
*
|
||||
* @return int
|
||||
* The number of fields in the header row.
|
||||
*/
|
||||
public function getFileColumnCount() {
|
||||
$count = 0;
|
||||
$fname = $this->file->getPathname();
|
||||
$handle = fopen($fname, 'r');
|
||||
if ($handle) {
|
||||
$row = fgetcsv($handle);
|
||||
$count = count($row);
|
||||
fclose($handle);
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label for a given property from a column_names array.
|
||||
*
|
||||
* @param array $column_names
|
||||
* An array of column_names.
|
||||
* @param string $property_name
|
||||
* The property name to find a label for.
|
||||
*
|
||||
* @return string
|
||||
* The label for this property.
|
||||
*/
|
||||
protected function getLabel(array $column_names, $property_name) {
|
||||
$label = '';
|
||||
foreach ($column_names as $column) {
|
||||
foreach ($column as $key => $value) {
|
||||
if ($key === $property_name) {
|
||||
$label = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $label;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,6 +35,13 @@ class MigrateExecutable extends MigrateExecutableBase {
|
||||
MigrateIdMapInterface::STATUS_NEEDS_UPDATE => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* Counter of map saves, used to detect the item limit threshold.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $itemLimitCounter = 0;
|
||||
|
||||
/**
|
||||
* Counter of map deletions.
|
||||
*
|
||||
@@ -128,6 +135,7 @@ class MigrateExecutable extends MigrateExecutableBase {
|
||||
// Only count saves for this migration.
|
||||
if ($event->getMap()->getQualifiedMapTableName() == $this->migration->getIdMap()->getQualifiedMapTableName()) {
|
||||
$fields = $event->getFields();
|
||||
$this->itemLimitCounter++;
|
||||
// Distinguish between creation and update.
|
||||
if ($fields['source_row_status'] == MigrateIdMapInterface::STATUS_IMPORTED &&
|
||||
$this->preExistingItem
|
||||
@@ -378,7 +386,7 @@ class MigrateExecutable extends MigrateExecutableBase {
|
||||
$this->resetCounters();
|
||||
}
|
||||
$this->counter++;
|
||||
if ($this->itemLimit && ($this->getProcessedCount() + 1) >= $this->itemLimit) {
|
||||
if ($this->itemLimit && ($this->itemLimitCounter + 1) >= $this->itemLimit) {
|
||||
$event->getMigration()->interruptMigration(MigrationInterface::RESULT_COMPLETED);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Routing;
|
||||
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Route processor to expand migrate_group.
|
||||
*/
|
||||
class RouteProcessor implements OutboundRouteProcessorInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processOutbound($route_name, Route $route, array &$parameters, BubbleableMetadata $bubbleable_metadata = NULL) {
|
||||
if ($route->hasDefault('_migrate_group')) {
|
||||
if ($migration = \Drupal::entityTypeManager()->getStorage('migration')->load($parameters['migration'])) {
|
||||
if ($group = $migration->get('migration_group')) {
|
||||
$parameters['migration_group'] = $group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: csv_source_test
|
||||
label: Test edit of column aliases for CSV source plugin
|
||||
class: null
|
||||
field_plugin_method: null
|
||||
cck_plugin_method: null
|
||||
migration_tags: { }
|
||||
migration_group: csv_test
|
||||
source:
|
||||
plugin: csv
|
||||
path: 'public://test.csv'
|
||||
header_row_count: 1
|
||||
enclosure: '"'
|
||||
keys:
|
||||
- vid
|
||||
column_names:
|
||||
0:
|
||||
vid: 'Vocabulary Id'
|
||||
1:
|
||||
name: 'Name'
|
||||
2:
|
||||
description: 'Description'
|
||||
process:
|
||||
vid: vid
|
||||
name: name
|
||||
description: description
|
||||
destination:
|
||||
plugin: entity:taxonomy_vocabulary
|
||||
migration_dependencies:
|
||||
required: { }
|
||||
optional: { }
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
id: csv_test
|
||||
label: CSV source plugin edit test
|
||||
description: Test editting of source plugin via the UI
|
||||
source_type: CSV
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
type: module
|
||||
name: CSV Source edit test
|
||||
description: 'Test editing of source plugin via the UI.'
|
||||
package: Testing
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- drupal:migrate (>=8.3)
|
||||
- migrate_plus:migrate_plus
|
||||
- migrate_plus:migrate_source_csv
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-08-27
|
||||
version: '8.x-4.0'
|
||||
core: '8.x'
|
||||
project: 'migrate_tools'
|
||||
datestamp: 1535380087
|
||||
Executable → Regular
+3
-3
@@ -7,8 +7,8 @@ dependencies:
|
||||
- drupal:migrate (>=8.3)
|
||||
- migrate_plus:migrate_plus
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-02-23
|
||||
version: '8.x-4.0-beta3'
|
||||
# Information added by Drupal.org packaging script on 2018-08-27
|
||||
version: '8.x-4.0'
|
||||
core: '8.x'
|
||||
project: 'migrate_tools'
|
||||
datestamp: 1519400307
|
||||
datestamp: 1535380087
|
||||
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_tools\Functional;
|
||||
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\taxonomy\VocabularyInterface;
|
||||
|
||||
/**
|
||||
* Test the CSV column alias edit form.
|
||||
*
|
||||
* @requires module migrate_source_csv
|
||||
*
|
||||
* @group migrate_tools
|
||||
*/
|
||||
class SourceCsvFormTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Temporary store for column assignment changes.
|
||||
*
|
||||
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
|
||||
*/
|
||||
protected $store;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'taxonomy',
|
||||
'migrate',
|
||||
'migrate_plus',
|
||||
'migrate_tools',
|
||||
'migrate_source_csv',
|
||||
'csv_source_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $profile = 'testing';
|
||||
|
||||
/**
|
||||
* The migration group for the test migration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group;
|
||||
|
||||
/**
|
||||
* The test migration id.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Log in as user 1. Migrations in the UI can only be performed as user 1.
|
||||
$this->drupalLogin($this->rootUser);
|
||||
|
||||
// Setup the file system so we create the source CSV.
|
||||
$this->container->get('stream_wrapper_manager')->registerWrapper('public', PublicStream::class, StreamWrapperInterface::NORMAL);
|
||||
$fs = \Drupal::service('file_system');
|
||||
$fs->mkdir('public://sites/default/files', NULL, TRUE);
|
||||
|
||||
// The source data for this test.
|
||||
$source_data = <<<'EOD'
|
||||
vid,name,description,hierarchy,weight
|
||||
tags,Tags,Use tags to group articles,0,0
|
||||
forums,Sujet de discussion,Forum navigation vocabulary,1,0
|
||||
test_vocabulary,Test Vocabulary,This is the vocabulary description,1,0
|
||||
genre,Genre,Genre description,1,0
|
||||
EOD;
|
||||
|
||||
// Write the data to the filepath given in the test migration.
|
||||
file_put_contents('public://test.csv', $source_data);
|
||||
|
||||
// Get the store.
|
||||
$tempStoreFactory = \Drupal::service('tempstore.private');
|
||||
$this->store = $tempStoreFactory->get('migrate_tools');
|
||||
|
||||
// Select the group and migration to test.
|
||||
$this->group = 'csv_test';
|
||||
$this->migration = 'csv_source_test';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the form to edit CSV column aliases.
|
||||
*
|
||||
* @throws \Behat\Mink\Exception\ExpectationException
|
||||
*/
|
||||
public function testSourceCsvForm() {
|
||||
// Define the paths to be used.
|
||||
$executeUrlPath = "/admin/structure/migrate/manage/{$this->group}/migrations/{$this->migration}/execute";
|
||||
$editUrlPath = "/admin/structure/migrate/manage/{$this->group}/migrations/{$this->migration}/source/edit";
|
||||
|
||||
// Assert the test migration is listed.
|
||||
$this->drupalGet("/admin/structure/migrate/manage/{$this->group}/migrations");
|
||||
$session = $this->assertSession();
|
||||
$session->responseContains('Test edit of column aliases for CSV source plugin');
|
||||
|
||||
// Proceed to the edit page.
|
||||
$this->drupalGet($editUrlPath);
|
||||
$session->responseContains('You can change the columns to be used by this migration for each source property.');
|
||||
|
||||
// Test that there are 3 select fields available which match the number of
|
||||
// properties in the process pipeline.
|
||||
$this->assertTrue($session->optionExists('edit-vid', 'vid')
|
||||
->isSelected());
|
||||
$this->assertTrue($session->optionExists('edit-name', 'name')
|
||||
->isSelected());
|
||||
$this->assertTrue($session->optionExists('edit-description', 'description')
|
||||
->isSelected());
|
||||
$session->responseNotContains('edit-hierarchy');
|
||||
$session->responseNotContains('edit-weight');
|
||||
|
||||
// Test that all 5 columns in the CSV source are available as options on
|
||||
// one of the select fields.
|
||||
$this->assertTrue($session->optionExists('edit-description', 'vid'));
|
||||
$this->assertTrue($session->optionExists('edit-description', 'name'));
|
||||
$this->assertTrue($session->optionExists('edit-description', 'description'));
|
||||
$this->assertTrue($session->optionExists('edit-description', 'hierarchy'));
|
||||
$this->assertTrue($session->optionExists('edit-description', 'weight'));
|
||||
|
||||
// Test that two aliases can not be the same.
|
||||
$edit = [
|
||||
'edit-vid' => 2,
|
||||
'edit-name' => 1,
|
||||
'edit-description' => 1,
|
||||
];
|
||||
$this->drupalPostForm($editUrlPath, $edit, t('Submit'));
|
||||
$session->responseContains('Source properties can not share the same source column.');
|
||||
$this->assertTrue($session->optionExists('edit-vid', 'description')
|
||||
->isSelected());
|
||||
$this->assertTrue($session->optionExists('edit-name', 'name')
|
||||
->isSelected());
|
||||
$this->assertTrue($session->optionExists('edit-description', 'name')
|
||||
->isSelected());
|
||||
|
||||
// Test that changes to all the column aliases are saved.
|
||||
$edit = [
|
||||
'edit-vid' => 4,
|
||||
'edit-name' => 0,
|
||||
'edit-description' => 1,
|
||||
];
|
||||
$this->drupalPostForm($editUrlPath, $edit, t('Submit'));
|
||||
$this->assertTrue($session->optionExists('edit-vid', 'weight')
|
||||
->isSelected());
|
||||
$this->assertTrue($session->optionExists('edit-name', 'vid')
|
||||
->isSelected());
|
||||
$this->assertTrue($session->optionExists('edit-description', 'name')
|
||||
->isSelected());
|
||||
|
||||
// Test that the changes are saved to store.
|
||||
$columnConfiguration = $this->store->get('csv_source_test');
|
||||
$migrationsChanged = $this->store->get('migrations_changed');
|
||||
$this->assertSame(['csv_source_test'], $migrationsChanged);
|
||||
$expected =
|
||||
[
|
||||
'original' =>
|
||||
[
|
||||
0 => ['vid' => 'Vocabulary Id'],
|
||||
1 => ['name' => 'Name'],
|
||||
2 => ['description' => 'Description'],
|
||||
],
|
||||
'changed' =>
|
||||
[
|
||||
4 => ['vid' => 'weight'],
|
||||
0 => ['name' => 'vid'],
|
||||
1 => ['description' => 'name'],
|
||||
],
|
||||
];
|
||||
$this->assertSame($expected, $columnConfiguration);
|
||||
|
||||
// Test the migration with incorrect column aliases. Flush the cache to
|
||||
// ensure the plugin alter is run.
|
||||
drupal_flush_all_caches();
|
||||
$edit = [
|
||||
'operation' => 'import',
|
||||
];
|
||||
$this->drupalPostForm($executeUrlPath, $edit, t('Execute'));
|
||||
$session->responseContains("Processed 1 item (1 created, 0 updated, 0 failed, 0 ignored) - done with 'csv_source_test'");
|
||||
|
||||
// Rollback.
|
||||
$edit = [
|
||||
'operation' => 'rollback',
|
||||
];
|
||||
$this->drupalPostForm($executeUrlPath, $edit, t('Execute'));
|
||||
|
||||
// Restore to an order that will succesfully migrate.
|
||||
$edit = [
|
||||
'edit-vid' => 0,
|
||||
'edit-name' => 1,
|
||||
'edit-description' => 2,
|
||||
];
|
||||
$this->drupalPostForm($editUrlPath, $edit, t('Submit'));
|
||||
$this->assertTrue($session->optionExists('edit-vid', 'vid')
|
||||
->isSelected());
|
||||
$this->assertTrue($session->optionExists('edit-name', 'name')
|
||||
->isSelected());
|
||||
$this->assertTrue($session->optionExists('edit-description', 'description')
|
||||
->isSelected());
|
||||
|
||||
// Test the vocabulary migration.
|
||||
$edit = [
|
||||
'operation' => 'import',
|
||||
];
|
||||
drupal_flush_all_caches();
|
||||
$this->drupalPostForm($executeUrlPath, $edit, t('Execute'));
|
||||
$session->responseContains("Processed 4 items (4 created, 0 updated, 0 failed, 0 ignored) - done with 'csv_source_test'");
|
||||
$this->assertEntity('tags', 'Tags', 'Use tags to group articles');
|
||||
$this->assertEntity('forums', 'Sujet de discussion', 'Forum navigation vocabulary');
|
||||
$this->assertEntity('test_vocabulary', 'Test Vocabulary', 'This is the vocabulary description');
|
||||
$this->assertEntity('genre', 'Genre', 'Genre description');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a migrated vocabulary contains the expected values.
|
||||
*
|
||||
* @param string $id
|
||||
* Entity ID to load and check.
|
||||
* @param string $expected_name
|
||||
* The name the migrated entity should have.
|
||||
* @param string $expected_description
|
||||
* The description the migrated entity should have.
|
||||
*/
|
||||
protected function assertEntity($id, $expected_name, $expected_description) {
|
||||
/** @var \Drupal\taxonomy\VocabularyInterface $entity */
|
||||
$entity = Vocabulary::load($id);
|
||||
$this->assertTrue($entity instanceof VocabularyInterface);
|
||||
$this->assertSame($expected_name, $entity->label());
|
||||
$this->assertSame($expected_description, $entity->getDescription());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user