contrib modules updates
This commit is contained in:
@@ -7,8 +7,8 @@ Extensions to base API
|
||||
migration configuration.
|
||||
* A MigrationGroup configuration entity is provided, which enables migrations to
|
||||
be organized in groups, and to maintain shared configuration in one place.
|
||||
* A MigrateEvents::PREPARE_ROW event is provided to dispatch hook_prepare_row()
|
||||
invocations as events.
|
||||
* A MigrateEvents::PREPARE_ROW event is provided to dispatch
|
||||
hook_migrate_prepare_row() invocations as events.
|
||||
* A SourcePluginExtension class is provided, enabling one to define fields and
|
||||
IDs for a source plugin via configuration rather than requiring PHP code.
|
||||
|
||||
|
||||
+3
@@ -8,6 +8,9 @@ migrate_plus.destination.*:
|
||||
type: boolean
|
||||
label: 'Whether stubbing is allowed.'
|
||||
default: false
|
||||
default_bundle:
|
||||
type: string
|
||||
label: 'The default bundle for content entity destinations.'
|
||||
|
||||
migrate_plus.destination.config:
|
||||
type: migrate_destination
|
||||
|
||||
+2
-2
@@ -29,13 +29,13 @@ migrate_plus.migration.*:
|
||||
type: label
|
||||
label: 'Label'
|
||||
source:
|
||||
type: migrate_plus.source.[plugin]
|
||||
type: ignore
|
||||
label: 'Source'
|
||||
process:
|
||||
type: ignore
|
||||
label: 'Process'
|
||||
destination:
|
||||
type: migrate_plus.destination.[plugin]
|
||||
type: ignore
|
||||
label: 'Destination'
|
||||
migration_dependencies:
|
||||
type: mapping
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Learn to make one for your own drupal.org project:
|
||||
# https://www.drupal.org/drupalorg/docs/drupal-ci/customizing-drupalci-testing
|
||||
build:
|
||||
assessment:
|
||||
validate_codebase:
|
||||
phplint:
|
||||
container_composer:
|
||||
phpcs:
|
||||
# phpcs will use core's specified version of Coder.
|
||||
sniff-all-files: true
|
||||
halt-on-fail: true
|
||||
testing:
|
||||
# run_tests task is executed several times in order of performance speeds.
|
||||
# halt-on-fail can be set on the run_tests tasks in order to fail fast.
|
||||
# suppress-deprecations is false in order to be alerted to usages of
|
||||
# deprecated code.
|
||||
run_tests.standard:
|
||||
types: 'Simpletest,PHPUnit-Unit,PHPUnit-Kernel,PHPUnit-Functional'
|
||||
testgroups: '--all'
|
||||
suppress-deprecations: false
|
||||
+3
-3
@@ -10,8 +10,8 @@ dependencies:
|
||||
- drupal:menu_ui
|
||||
- drupal:path
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-09-06
|
||||
version: '8.x-4.0'
|
||||
# Information added by Drupal.org packaging script on 2019-01-07
|
||||
version: '8.x-4.1'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1536264189
|
||||
datestamp: 1546879116
|
||||
|
||||
+3
-3
@@ -11,8 +11,8 @@ dependencies:
|
||||
- drupal:options
|
||||
- drupal:taxonomy
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-09-06
|
||||
version: '8.x-4.0'
|
||||
# Information added by Drupal.org packaging script on 2019-01-07
|
||||
version: '8.x-4.1'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1536264189
|
||||
datestamp: 1546879116
|
||||
|
||||
+5
-5
@@ -316,7 +316,7 @@ function migrate_example_beer_data_node() {
|
||||
'image_title',
|
||||
'image_description',
|
||||
];
|
||||
$query = db_insert('migrate_example_beer_node')
|
||||
$query = \Drupal::database()->insert('migrate_example_beer_node')
|
||||
->fields($fields);
|
||||
// Use high bid numbers to avoid overwriting an existing node id.
|
||||
$data = [
|
||||
@@ -381,7 +381,7 @@ function migrate_example_beer_data_account() {
|
||||
'sex',
|
||||
'beers',
|
||||
];
|
||||
$query = db_insert('migrate_example_beer_account')
|
||||
$query = \Drupal::database()->insert('migrate_example_beer_account')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[
|
||||
@@ -436,7 +436,7 @@ function migrate_example_beer_data_account() {
|
||||
*/
|
||||
function migrate_example_beer_data_comment() {
|
||||
$fields = ['bid', 'cid_parent', 'subject', 'body', 'name', 'mail', 'aid'];
|
||||
$query = db_insert('migrate_example_beer_comment')
|
||||
$query = \Drupal::database()->insert('migrate_example_beer_comment')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[99999998, NULL, 'im first', 'full body', 'alice', 'alice@example.com', 0],
|
||||
@@ -464,7 +464,7 @@ function migrate_example_beer_data_comment() {
|
||||
*/
|
||||
function migrate_example_beer_data_topic() {
|
||||
$fields = ['style', 'details', 'style_parent', 'region', 'hoppiness'];
|
||||
$query = db_insert('migrate_example_beer_topic')
|
||||
$query = \Drupal::database()->insert('migrate_example_beer_topic')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
['ale', 'traditional', NULL, 'Medieval British Isles', 'Medium'],
|
||||
@@ -488,7 +488,7 @@ function migrate_example_beer_data_topic() {
|
||||
*/
|
||||
function migrate_example_beer_data_topic_node() {
|
||||
$fields = ['bid', 'style'];
|
||||
$query = db_insert('migrate_example_beer_topic_node')
|
||||
$query = \Drupal::database()->insert('migrate_example_beer_topic_node')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[99999999, 'pilsner'],
|
||||
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
# This migration demonstrates importing from an endpoint listing other endpoints
|
||||
# containing individual item data.
|
||||
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
|
||||
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
|
||||
# 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
|
||||
# desired elements.
|
||||
item_selector: /response/variety
|
||||
# Under 'fields', we list the data items to be imported. The first level keys
|
||||
# are the source field names we want to populate (the names to be used as
|
||||
# sources in the process configuration below). For each field we're importing,
|
||||
# we provide a label (optional - this is for display in migration tools) and
|
||||
# an xpath for retrieving that value. It's important to note that this xpath
|
||||
# is relative to the elements retrieved by item_xpath.
|
||||
fields:
|
||||
category_name:
|
||||
label:
|
||||
selector: name
|
||||
category_details:
|
||||
label:
|
||||
selector: details
|
||||
category_parent:
|
||||
label: 'Unique position identifier'
|
||||
selector: parent
|
||||
# Under 'ids', we identify source fields populated above which will uniquely
|
||||
# identify each imported item. The 'type' makes sure the migration map table
|
||||
# uses the proper schema type for stored the IDs.
|
||||
ids:
|
||||
category_name:
|
||||
type: string
|
||||
process:
|
||||
vid:
|
||||
plugin: default_value
|
||||
default_value: migrate_example_wine_varieties
|
||||
name: category_name
|
||||
description: category_details
|
||||
parent:
|
||||
plugin: migration_lookup
|
||||
migration: wine_terms
|
||||
source: category_parent
|
||||
destination:
|
||||
plugin: entity:taxonomy_term
|
||||
migration_dependencies:
|
||||
require:
|
||||
- wine_terms
|
||||
+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-09-06
|
||||
version: '8.x-4.0'
|
||||
# Information added by Drupal.org packaging script on 2019-01-07
|
||||
version: '8.x-4.1'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1536264189
|
||||
datestamp: 1546879116
|
||||
|
||||
+3
-3
@@ -11,8 +11,8 @@ dependencies:
|
||||
- drupal:taxonomy
|
||||
- drupal:rest
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-09-06
|
||||
version: '8.x-4.0'
|
||||
# Information added by Drupal.org packaging script on 2019-01-07
|
||||
version: '8.x-4.1'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1536264189
|
||||
datestamp: 1546879116
|
||||
|
||||
+15
-15
@@ -737,7 +737,7 @@ function migrate_example_advanced_data_wine() {
|
||||
'region',
|
||||
'rating',
|
||||
];
|
||||
$query = db_insert('migrate_example_wine')
|
||||
$query = \Drupal::database()->insert('migrate_example_wine')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[
|
||||
@@ -776,7 +776,7 @@ function migrate_example_advanced_data_wine() {
|
||||
*/
|
||||
function migrate_example_advanced_data_updates() {
|
||||
$fields = ['wineid', 'rating'];
|
||||
$query = db_insert('migrate_example_advanced_updates')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_updates')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[1, 93],
|
||||
@@ -793,7 +793,7 @@ function migrate_example_advanced_data_updates() {
|
||||
*/
|
||||
function migrate_example_advanced_data_producer() {
|
||||
$fields = ['producerid', 'name', 'body', 'excerpt', 'accountid'];
|
||||
$query = db_insert('migrate_example_advanced_producer')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_producer')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[1, 'Montes', 'Fine Chilean winery', 'Great!', 9],
|
||||
@@ -824,7 +824,7 @@ function migrate_example_advanced_data_account() {
|
||||
'imageid',
|
||||
'positions',
|
||||
];
|
||||
$query = db_insert('migrate_example_advanced_account')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_account')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[
|
||||
@@ -884,7 +884,7 @@ function migrate_example_advanced_data_account() {
|
||||
*/
|
||||
function migrate_example_advanced_data_account_updates() {
|
||||
$fields = ['accountid', 'sex'];
|
||||
$query = db_insert('migrate_example_advanced_account_updates')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_account_updates')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[1, NULL],
|
||||
@@ -915,7 +915,7 @@ function migrate_example_advanced_data_comment() {
|
||||
'posted',
|
||||
'lastchanged',
|
||||
];
|
||||
$query = db_insert('migrate_example_advanced_comment')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_comment')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[
|
||||
@@ -1000,7 +1000,7 @@ function migrate_example_advanced_data_comment() {
|
||||
*/
|
||||
function migrate_example_advanced_data_comment_updates() {
|
||||
$fields = ['commentid', 'subject'];
|
||||
$query = db_insert('migrate_example_advanced_comment_updates')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_comment_updates')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[1, 'I am first'],
|
||||
@@ -1027,7 +1027,7 @@ function migrate_example_advanced_data_categories() {
|
||||
'details',
|
||||
'ordering',
|
||||
];
|
||||
$query = db_insert('migrate_example_advanced_categories')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_categories')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[
|
||||
@@ -1079,7 +1079,7 @@ function migrate_example_advanced_data_categories() {
|
||||
*/
|
||||
function migrate_example_advanced_data_vintages() {
|
||||
$fields = ['wineid', 'vintage'];
|
||||
$query = db_insert('migrate_example_advanced_vintages')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_vintages')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[1, 2006],
|
||||
@@ -1097,7 +1097,7 @@ function migrate_example_advanced_data_vintages() {
|
||||
*/
|
||||
function migrate_example_advanced_data_variety_updates() {
|
||||
$fields = ['categoryid', 'details'];
|
||||
$query = db_insert('migrate_example_advanced_variety_updates')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_variety_updates')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[1, 'White wines are simpler and sweeter than red'],
|
||||
@@ -1120,7 +1120,7 @@ function migrate_example_advanced_data_variety_updates() {
|
||||
*/
|
||||
function migrate_example_advanced_data_category_wine() {
|
||||
$fields = ['wineid', 'categoryid'];
|
||||
$query = db_insert('migrate_example_advanced_category_wine')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_category_wine')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[1, 12],
|
||||
@@ -1138,7 +1138,7 @@ function migrate_example_advanced_data_category_wine() {
|
||||
*/
|
||||
function migrate_example_advanced_data_category_producer() {
|
||||
$fields = ['producerid', 'categoryid'];
|
||||
$query = db_insert('migrate_example_advanced_category_producer')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_category_producer')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[1, 17],
|
||||
@@ -1154,7 +1154,7 @@ function migrate_example_advanced_data_category_producer() {
|
||||
*/
|
||||
function migrate_example_advanced_data_files() {
|
||||
$fields = ['imageid', 'url', 'image_alt', 'image_title', 'wineid'];
|
||||
$query = db_insert('migrate_example_advanced_files')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_files')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[
|
||||
@@ -1192,7 +1192,7 @@ function migrate_example_advanced_data_files() {
|
||||
function migrate_example_advanced_data_blobs() {
|
||||
$blob = file_get_contents('core/misc/druplicon.png');
|
||||
$fields = ['imageid', 'imageblob'];
|
||||
$query = db_insert('migrate_example_advanced_blobs')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_blobs')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[1, $blob],
|
||||
@@ -1208,7 +1208,7 @@ function migrate_example_advanced_data_blobs() {
|
||||
*/
|
||||
function migrate_example_advanced_data_table_source() {
|
||||
$fields = ['fooid', 'field1', 'field2'];
|
||||
$query = db_insert('migrate_example_advanced_table_source')
|
||||
$query = \Drupal::database()->insert('migrate_example_advanced_table_source')
|
||||
->fields($fields);
|
||||
$data = [
|
||||
[3, 'Some sample data', 58],
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
A demonstration of a simple import of a JSON file.
|
||||
|
||||
REQUIREMENTS
|
||||
============
|
||||
You need the contrib modules Migrate Plus and Migrate Tools.
|
||||
To make the products.json file available for import, the file will be copied
|
||||
from the artifacts folder to your sites/default/files folder.
|
||||
|
||||
USAGE
|
||||
=====
|
||||
Enable the module, check status, import all products and rollback with Drush
|
||||
drush en migrate_json_example
|
||||
drush migrate-status
|
||||
drush migrate-import product
|
||||
drush migrate-rollback product
|
||||
|
||||
See config/optional/migrate_plus.migration.product.yml for details about the
|
||||
migration.
|
||||
|
||||
Thanks to Jeff Geerling and Christophe for the original code:
|
||||
- https://www.jeffgeerling.com/blog/2016/migrate-custom-json-feed-drupal-8-migrate-source-json
|
||||
- https://colorfield.be/blog/drupal-8-json-custom-migration
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"product": [
|
||||
{
|
||||
"upc": "11111",
|
||||
"name": "Widget",
|
||||
"description": "Helpful for many things.",
|
||||
"price": "14.99"
|
||||
},
|
||||
{
|
||||
"upc": "22222",
|
||||
"name": "Sprocket",
|
||||
"description": "Helpful for things needing sprockets.",
|
||||
"price": "8.99"
|
||||
}
|
||||
]
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.node.product.field_description
|
||||
- field.field.node.product.field_price
|
||||
- field.field.node.product.field_upc
|
||||
- node.type.product
|
||||
module:
|
||||
- path
|
||||
id: node.product.default
|
||||
targetEntityType: node
|
||||
bundle: product
|
||||
mode: default
|
||||
content:
|
||||
created:
|
||||
type: datetime_timestamp
|
||||
weight: 10
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
field_description:
|
||||
weight: 123
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
type: string_textfield
|
||||
region: content
|
||||
field_price:
|
||||
weight: 124
|
||||
settings:
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
type: number
|
||||
region: content
|
||||
field_upc:
|
||||
weight: 122
|
||||
settings:
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
type: number
|
||||
region: content
|
||||
path:
|
||||
type: path
|
||||
weight: 30
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
promote:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
display_label: true
|
||||
weight: 15
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
status:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
display_label: true
|
||||
weight: 120
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
sticky:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
display_label: true
|
||||
weight: 16
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
title:
|
||||
type: string_textfield
|
||||
weight: -5
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
uid:
|
||||
type: entity_reference_autocomplete
|
||||
weight: 5
|
||||
settings:
|
||||
match_operator: CONTAINS
|
||||
size: 60
|
||||
placeholder: ''
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
hidden: { }
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.node.product.field_description
|
||||
- field.field.node.product.field_price
|
||||
- field.field.node.product.field_upc
|
||||
- node.type.product
|
||||
module:
|
||||
- user
|
||||
id: node.product.default
|
||||
targetEntityType: node
|
||||
bundle: product
|
||||
mode: default
|
||||
content:
|
||||
field_description:
|
||||
weight: 103
|
||||
label: above
|
||||
settings:
|
||||
link_to_entity: false
|
||||
third_party_settings: { }
|
||||
type: string
|
||||
region: content
|
||||
field_price:
|
||||
weight: 104
|
||||
label: above
|
||||
settings:
|
||||
thousand_separator: ''
|
||||
decimal_separator: .
|
||||
scale: 2
|
||||
prefix_suffix: true
|
||||
third_party_settings: { }
|
||||
type: number_decimal
|
||||
region: content
|
||||
field_upc:
|
||||
weight: 102
|
||||
label: above
|
||||
settings:
|
||||
thousand_separator: ''
|
||||
prefix_suffix: true
|
||||
third_party_settings: { }
|
||||
type: number_integer
|
||||
region: content
|
||||
links:
|
||||
weight: 100
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
region: content
|
||||
hidden: { }
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.node.field_description
|
||||
- node.type.product
|
||||
id: node.product.field_description
|
||||
field_name: field_description
|
||||
entity_type: node
|
||||
bundle: product
|
||||
label: Description
|
||||
description: ''
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: string
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.node.field_price
|
||||
- node.type.product
|
||||
id: node.product.field_price
|
||||
field_name: field_price
|
||||
entity_type: node
|
||||
bundle: product
|
||||
label: Price
|
||||
description: ''
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
min: null
|
||||
max: null
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
field_type: float
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.node.field_upc
|
||||
- node.type.product
|
||||
id: node.product.field_upc
|
||||
field_name: field_upc
|
||||
entity_type: node
|
||||
bundle: product
|
||||
label: UPC
|
||||
description: ''
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
min: null
|
||||
max: null
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
field_type: integer
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
id: node.field_description
|
||||
field_name: field_description
|
||||
entity_type: node
|
||||
type: string
|
||||
settings:
|
||||
max_length: 255
|
||||
is_ascii: false
|
||||
case_sensitive: false
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
id: node.field_price
|
||||
field_name: field_price
|
||||
entity_type: node
|
||||
type: float
|
||||
settings: { }
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
id: node.field_upc
|
||||
field_name: field_upc
|
||||
entity_type: node
|
||||
type: integer
|
||||
settings:
|
||||
unsigned: false
|
||||
size: normal
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
# This migration demonstrates a simple import from a JSON file.
|
||||
id: product
|
||||
label: JSON feed of Products
|
||||
migration_group: Product
|
||||
migration_tags:
|
||||
- json example
|
||||
source:
|
||||
# We use the JSON source plugin.
|
||||
plugin: url
|
||||
# In this example we get data from a local file, to get data from a URL
|
||||
# define http as data_fetcher_plugin.
|
||||
# data_fetcher_plugin: http
|
||||
data_fetcher_plugin: file
|
||||
data_parser_plugin: json
|
||||
# The data_parser normally limits the fields passed on to the source plugin
|
||||
# to fields configured to be used as part of the migration. To support more
|
||||
# dynamic migrations, the JSON data parser supports including the original
|
||||
# data for the current row. Simply include the 'include_raw_data' flag set
|
||||
# to `true` to enable this. This option is disabled by default to minimize
|
||||
# memory footprint for migrations that do not need this capability.
|
||||
# include_raw_data: true
|
||||
# Flags whether to track changes to incoming data. If TRUE, we will maintain
|
||||
# hashed source rows to determine whether incoming data has changed.
|
||||
# track_changes: true
|
||||
# Copy the example JSON file in artifacts folder to sites/default/files folder.
|
||||
urls:
|
||||
- 'public://migrate_json_example/products.json'
|
||||
# An xpath-like selector corresponding to the items to be imported.
|
||||
item_selector: product
|
||||
# Under 'fields', we list the data items to be imported. The first level keys
|
||||
# are the source field names we want to populate (the names to be used as
|
||||
# sources in the process configuration below). For each field we're importing,
|
||||
# we provide a label (optional - this is for display in migration tools) and
|
||||
# an xpath for retrieving that value. It's important to note that this xpath
|
||||
# is relative to the elements retrieved by item_selector.
|
||||
fields:
|
||||
-
|
||||
name: upc
|
||||
label: 'Unique product identifier'
|
||||
selector: upc
|
||||
-
|
||||
name: name
|
||||
label: 'Product name'
|
||||
selector: name
|
||||
-
|
||||
name: description
|
||||
label: 'Product description'
|
||||
selector: description
|
||||
-
|
||||
name: price
|
||||
label: 'Product price'
|
||||
selector: price
|
||||
# Under 'ids', we identify source fields populated above which will uniquely
|
||||
# identify each imported item. The 'type' makes sure the migration map table
|
||||
# uses the proper schema type for stored the IDs.
|
||||
ids:
|
||||
upc:
|
||||
type: integer
|
||||
process:
|
||||
# Note that the source field names here (name, description and price) were
|
||||
# defined by the 'fields' configuration for the source plugin above.
|
||||
type:
|
||||
plugin: default_value
|
||||
default_value: product
|
||||
title: name
|
||||
field_upc: upc
|
||||
field_description: description
|
||||
field_price: price
|
||||
sticky:
|
||||
plugin: default_value
|
||||
default_value: 0
|
||||
uid:
|
||||
plugin: default_value
|
||||
default_value: 0
|
||||
destination:
|
||||
plugin: 'entity:node'
|
||||
migration_dependencies: { }
|
||||
dependencies:
|
||||
enforced:
|
||||
module:
|
||||
- migrate_json_example
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- menu_ui
|
||||
third_party_settings:
|
||||
menu_ui:
|
||||
available_menus:
|
||||
- main
|
||||
parent: 'main:'
|
||||
name: Product
|
||||
type: product
|
||||
description: ''
|
||||
help: ''
|
||||
new_revision: true
|
||||
preview_mode: 1
|
||||
display_submitted: true
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
type: module
|
||||
name: Migrate JSON Example
|
||||
description: 'Simple JSON Migration example'
|
||||
package: Examples
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
- migrate_plus:migrate_plus
|
||||
- migrate_tools:migrate_tools
|
||||
|
||||
# Information added by Drupal.org packaging script on 2019-01-07
|
||||
version: '8.x-4.1'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1546879116
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update, and uninstall functions for migrate_json_example.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copies the example file to the sites/default/files folder.
|
||||
*/
|
||||
function migrate_json_example_install() {
|
||||
// Create the example file directory and ensure it's writable.
|
||||
$directory = file_default_scheme() . '://migrate_json_example';
|
||||
file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
|
||||
|
||||
// Copy the example file to example directory.
|
||||
$module_path = drupal_get_path('module', 'migrate_json_example');
|
||||
$file_source = $module_path . '/artifacts/products.json';
|
||||
file_unmanaged_copy($file_source, $directory . '/products.json', FILE_EXISTS_REPLACE);
|
||||
}
|
||||
@@ -6,8 +6,8 @@ package: Migration
|
||||
dependencies:
|
||||
- drupal:migrate (>=8.3)
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-09-06
|
||||
version: '8.x-4.0'
|
||||
# Information added by Drupal.org packaging script on 2019-01-07
|
||||
version: '8.x-4.1'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1536264189
|
||||
datestamp: 1546879116
|
||||
|
||||
@@ -18,8 +18,23 @@ use Drupal\Core\Entity\EntityTypeInterface;
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label",
|
||||
* "weight" = "weight"
|
||||
* }
|
||||
* "weight" = "weight",
|
||||
* "status" = "status"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "class",
|
||||
* "field_plugin_method",
|
||||
* "cck_plugin_method",
|
||||
* "migration_tags",
|
||||
* "migration_group",
|
||||
* "status",
|
||||
* "label",
|
||||
* "source",
|
||||
* "process",
|
||||
* "destination",
|
||||
* "migration_dependencies",
|
||||
* },
|
||||
* )
|
||||
*/
|
||||
class Migration extends ConfigEntityBase implements MigrationInterface {
|
||||
|
||||
@@ -19,7 +19,15 @@ use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label"
|
||||
* }
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "label",
|
||||
* "description",
|
||||
* "source_type",
|
||||
* "module",
|
||||
* "shared_configuration",
|
||||
* },
|
||||
* )
|
||||
*/
|
||||
class MigrationGroup extends ConfigEntityBase implements MigrationGroupInterface {
|
||||
|
||||
@@ -20,6 +20,10 @@ class MigrationConfigDeriver extends DeriverBase {
|
||||
$migrations = Migration::loadMultiple();
|
||||
/** @var \Drupal\migrate_plus\Entity\MigrationInterface $migration */
|
||||
foreach ($migrations as $id => $migration) {
|
||||
if (!$migration->status()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->derivatives[$id] = $migration->toArray();
|
||||
}
|
||||
return $this->derivatives;
|
||||
|
||||
+8
-4
@@ -131,8 +131,8 @@ class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginIn
|
||||
$this->entityManager = $entityManager;
|
||||
$this->selectionPluginManager = $selectionPluginManager;
|
||||
$pluginIdParts = explode(':', $this->migration->getDestinationPlugin()->getPluginId());
|
||||
$this->destinationEntityType = empty($pluginIdParts[1]) ?: $pluginIdParts[1];
|
||||
$this->destinationBundleKey = !$this->destinationEntityType ?: $this->entityManager->getDefinition($this->destinationEntityType)->getKey('bundle');
|
||||
$this->destinationEntityType = empty($pluginIdParts[1]) ? NULL : $pluginIdParts[1];
|
||||
$this->destinationBundleKey = $this->destinationEntityType ? $this->entityManager->getDefinition($this->destinationEntityType)->getKey('bundle') : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,8 +223,7 @@ class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginIn
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new MigrateException('Destination field type ' .
|
||||
$fieldConfig->getType() . 'is not a recognized reference type.');
|
||||
throw new MigrateException(sprintf('Destination field type %s is not a recognized reference type.', $fieldConfig->getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,6 +261,11 @@ class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginIn
|
||||
$query = $this->entityManager->getStorage($this->lookupEntityType)
|
||||
->getQuery()
|
||||
->condition($this->lookupValueKey, $value, $multiple ? 'IN' : NULL);
|
||||
// Sqlite and possibly others returns data in a non-deterministic order.
|
||||
// Make it deterministic.
|
||||
if ($multiple) {
|
||||
$query->sort($this->lookupValueKey, 'DESC');
|
||||
}
|
||||
|
||||
if ($this->lookupBundleKey) {
|
||||
$query->condition($this->lookupBundleKey, $this->lookupBundle);
|
||||
|
||||
@@ -56,7 +56,7 @@ class Merge extends ProcessPluginBase {
|
||||
throw new MigrateException(sprintf('Merge process failed for destination property (%s): input is not an array.', $destination_property));
|
||||
}
|
||||
$new_value = [];
|
||||
foreach($value as $i => $item) {
|
||||
foreach ($value as $i => $item) {
|
||||
if (!is_array($item)) {
|
||||
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));
|
||||
}
|
||||
|
||||
+7
-7
@@ -20,10 +20,10 @@ use Drupal\migrate\Row;
|
||||
* - 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.
|
||||
* - method: What to do if the input value equals to value given in
|
||||
* configuration key value. Possible values:
|
||||
* - row: Skips the entire row.
|
||||
* - process: Prevents further processing of the input property
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
@@ -32,12 +32,12 @@ use Drupal\migrate\Row;
|
||||
* type:
|
||||
* plugin: skip_on_value
|
||||
* source: content_type
|
||||
* method: row
|
||||
* method: process
|
||||
* value: blog
|
||||
* @endcode
|
||||
*
|
||||
* The above example will skip processing the input property if the content_type
|
||||
* source field equals "blog".
|
||||
* The above example will skip further processing of the input property if
|
||||
* the content_type source field equals "blog".
|
||||
*
|
||||
* Example usage with full configuration:
|
||||
* @code
|
||||
|
||||
+9
@@ -59,6 +59,15 @@ use Drupal\migrate\Row;
|
||||
* All the rules for
|
||||
* @link http://php.net/manual/function.str-replace.php str_replace @endlink
|
||||
* apply. This means that you can provide arrays as values.
|
||||
*
|
||||
* Multiple values can be matched like this:
|
||||
* @code
|
||||
* field_text:
|
||||
* plugin: str_replace
|
||||
* source: text
|
||||
* search: ["AT", "CH", "DK"]
|
||||
* replace: ["Austria", "Switzerland", "Denmark"]
|
||||
* @endcode
|
||||
*/
|
||||
class StrReplace extends ProcessPluginBase {
|
||||
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ class File extends DataFetcherPluginBase {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getResponse($url) {
|
||||
$response = file_get_contents($url);
|
||||
$response = @file_get_contents($url);
|
||||
if ($response === FALSE) {
|
||||
throw new MigrateException('file parser plugin: could not retrieve data from ' . $url);
|
||||
}
|
||||
|
||||
+2
-2
@@ -114,8 +114,8 @@ class Json extends DataParserPluginBase implements ContainerFactoryPluginInterfa
|
||||
foreach ($field_selectors as $field_selector) {
|
||||
if (is_array($field_data) && array_key_exists($field_selector, $field_data)) {
|
||||
$field_data = $field_data[$field_selector];
|
||||
}
|
||||
else {
|
||||
}
|
||||
else {
|
||||
$field_data = '';
|
||||
}
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: fruit_terms
|
||||
label: Fruit Terms
|
||||
class: null
|
||||
field_plugin_method: null
|
||||
cck_plugin_method: null
|
||||
migration_tags: { }
|
||||
migration_group: default
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
-
|
||||
name: Apple
|
||||
-
|
||||
name: Banana
|
||||
-
|
||||
name: Orange
|
||||
ids:
|
||||
name:
|
||||
type: string
|
||||
constants:
|
||||
vocabulary: fruit
|
||||
process:
|
||||
name: name
|
||||
vid: constants/vocabulary
|
||||
destination:
|
||||
plugin: entity:taxonomy_term
|
||||
migration_dependencies:
|
||||
required: { }
|
||||
optional: { }
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: default
|
||||
label: Default
|
||||
description: ''
|
||||
source_type: ''
|
||||
module: null
|
||||
shared_configuration: null
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
type: module
|
||||
name: Migrate Plus Test
|
||||
description: 'Test module to test Migrate Plus.'
|
||||
package: Testing
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- drupal:migrate (>=8.3)
|
||||
- migrate_plus:migrate_plus
|
||||
|
||||
# Information added by Drupal.org packaging script on 2019-01-07
|
||||
version: '8.x-4.1'
|
||||
core: '8.x'
|
||||
project: 'migrate_plus'
|
||||
datestamp: 1546879116
|
||||
@@ -20,7 +20,10 @@ class LoadTest extends BrowserTestBase {
|
||||
public static $modules = [
|
||||
'migrate_plus',
|
||||
'migrate_example',
|
||||
'migrate_example_setup',
|
||||
'migrate_example_advanced',
|
||||
'migrate_example_advanced_setup',
|
||||
'migrate_json_example',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
+66
-3
@@ -2,17 +2,26 @@
|
||||
|
||||
namespace Drupal\Tests\migrate_plus\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate_plus\Entity\Migration;
|
||||
use Drupal\Tests\migrate\Kernel\MigrateTestBase;
|
||||
|
||||
/**
|
||||
* Test migration config entity discovery.
|
||||
*
|
||||
* @group migrate_plus
|
||||
*/
|
||||
class MigrationConfigEntityTest extends KernelTestBase {
|
||||
class MigrationConfigEntityTest extends MigrateTestBase {
|
||||
|
||||
public static $modules = ['migrate', 'migrate_plus'];
|
||||
public static $modules = [
|
||||
'migrate',
|
||||
'migrate_plus',
|
||||
'migrate_plus_test',
|
||||
'taxonomy',
|
||||
'text',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* The plugin manager.
|
||||
@@ -27,6 +36,9 @@ class MigrationConfigEntityTest extends KernelTestBase {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->pluginManager = \Drupal::service('plugin.manager.migration');
|
||||
$this->installConfig('migrate_plus');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installSchema('system', ['key_value', 'key_value_expire']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,6 +47,7 @@ class MigrationConfigEntityTest extends KernelTestBase {
|
||||
public function testCacheInvalidation() {
|
||||
$config = Migration::create([
|
||||
'id' => 'test',
|
||||
'status' => TRUE,
|
||||
'label' => 'Label A',
|
||||
'migration_tags' => [],
|
||||
'source' => [],
|
||||
@@ -57,4 +70,54 @@ class MigrationConfigEntityTest extends KernelTestBase {
|
||||
$this->assertSame('Label B', $this->pluginManager->getDefinition('test')['label']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration status.
|
||||
*/
|
||||
public function testMigrationStatus() {
|
||||
$configs = [
|
||||
[
|
||||
'id' => 'test_active',
|
||||
'status' => TRUE,
|
||||
'label' => 'Label Active',
|
||||
'migration_tags' => [],
|
||||
'source' => [],
|
||||
'destination' => [],
|
||||
'migration_dependencies' => [],
|
||||
],
|
||||
[
|
||||
'id' => 'test_inactive',
|
||||
'status' => FALSE,
|
||||
'label' => 'Label Inactive',
|
||||
'migration_tags' => [],
|
||||
'source' => [],
|
||||
'destination' => [],
|
||||
'migration_dependencies' => [],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($configs as $config) {
|
||||
Migration::create($config)->save();
|
||||
}
|
||||
|
||||
$definitions = $this->pluginManager->getDefinitions();
|
||||
$this->assertCount(1, $definitions);
|
||||
$this->assertArrayHasKey('test_active', $definitions);
|
||||
|
||||
$this->setExpectedException(PluginNotFoundException::class, 'The "test_inactive" plugin does not exist.');
|
||||
$this->pluginManager->getDefinition('test_inactive');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration from configuration.
|
||||
*/
|
||||
public function testImport() {
|
||||
$this->installConfig('migrate_plus_test');
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->pluginManager->createInstance('fruit_terms');
|
||||
$id_map = $migration->getIdMap();
|
||||
$executable = new MigrateExecutable($migration, $this);
|
||||
$executable->import();
|
||||
$this->assertSame(3, $id_map->importedCount());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-7
@@ -69,10 +69,9 @@ class MigrationGroupTest extends KernelTestBase {
|
||||
$migration->save();
|
||||
|
||||
$expected_config = [
|
||||
'migration_group' => $group_id,
|
||||
'label' => 'Unaffected by the group',
|
||||
'migration_tags' => ['Drupal 7'],
|
||||
'source' => [
|
||||
'getMigrationTags' => ['Drupal 7'],
|
||||
'getSourceConfiguration' => [
|
||||
'plugin' => 'empty',
|
||||
'constants' => [
|
||||
'entity_type' => 'user',
|
||||
@@ -80,13 +79,13 @@ class MigrationGroupTest extends KernelTestBase {
|
||||
'cardinality' => '3',
|
||||
],
|
||||
],
|
||||
'destination' => ['plugin' => 'field_storage_config'],
|
||||
'getDestinationConfiguration' => ['plugin' => 'field_storage_config'],
|
||||
];
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $loaded_migration */
|
||||
/** @var \Drupal\migrate_plus\Plugin\MigrationInterface $loaded_migration */
|
||||
$loaded_migration = $this->container->get('plugin.manager.migration')
|
||||
->createInstance('specific_migration');
|
||||
foreach ($expected_config as $key => $expected_value) {
|
||||
$actual_value = $loaded_migration->get($key);
|
||||
foreach ($expected_config as $method => $expected_value) {
|
||||
$actual_value = call_user_func([$loaded_migration, $method]);
|
||||
$this->assertEquals($expected_value, $actual_value);
|
||||
}
|
||||
}
|
||||
|
||||
+117
-5
@@ -5,12 +5,15 @@ 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;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\MigrateMessageInterface;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* Tests the migration plugin.
|
||||
@@ -107,6 +110,18 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
|
||||
FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
||||
);
|
||||
|
||||
// Create a non-reference field.
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => 'field_integer',
|
||||
'type' => 'integer',
|
||||
'entity_type' => 'node',
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'field_name' => 'field_integer',
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $this->bundle,
|
||||
])->save();
|
||||
|
||||
$this->migrationPluginManager = \Drupal::service('plugin.manager.migration');
|
||||
}
|
||||
|
||||
@@ -156,14 +171,14 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
|
||||
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.");
|
||||
$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.");
|
||||
$this->assertTrue($entity->{$property}->isEmpty(), "Expected value is empty but field $property is not empty.");
|
||||
}
|
||||
}
|
||||
elseif ($entity->{$property}->getValue()) {
|
||||
$this->assertEquals($expectedValue, $entity->{$property}[$valueID]->entity->$key->value);
|
||||
$this->assertEquals($expectedValue, $entity->get($property)->offsetGet($valueID)->entity->{$key}->value);
|
||||
}
|
||||
else {
|
||||
$this->fail("Expected value: $expectedValue does not exist in $property.");
|
||||
@@ -177,7 +192,7 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
|
||||
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.");
|
||||
$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.");
|
||||
@@ -203,6 +218,99 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test lookup without a reference field.
|
||||
*/
|
||||
public function testNonReferenceField() {
|
||||
$values = [
|
||||
'name' => 'Apples',
|
||||
'vid' => $this->vocabulary,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
];
|
||||
$this->createTestData('taxonomy_term', $values);
|
||||
|
||||
// Not enough context is provided for a non reference field, so error out.
|
||||
$definition = [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
'term' => 'Apples',
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'type' => [
|
||||
'plugin' => 'default_value',
|
||||
'default_value' => $this->bundle,
|
||||
],
|
||||
'title' => 'title',
|
||||
'field_integer' => [
|
||||
'plugin' => 'entity_generate',
|
||||
'source' => 'term',
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:node',
|
||||
],
|
||||
];
|
||||
/** @var \Drupal\migrate\Plugin\Migration $migration */
|
||||
$migration = $this->migrationPluginManager->createStubMigration($definition);
|
||||
$migrationExecutable = (new MigrateExecutable($migration, $this));
|
||||
$migrationExecutable->import();
|
||||
$this->assertEquals('Destination field type integer is not a recognized reference type.', $migration->getIdMap()->getMessageIterator()->fetch()->message);
|
||||
$this->assertSame(1, $migration->getIdMap()->messageCount());
|
||||
|
||||
// Enough context is provided so this should work.
|
||||
$definition = [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'title' => 'content item 1',
|
||||
'term' => 'Apples',
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'type' => [
|
||||
'plugin' => 'default_value',
|
||||
'default_value' => $this->bundle,
|
||||
],
|
||||
'title' => 'title',
|
||||
'field_integer' => [
|
||||
'plugin' => 'entity_generate',
|
||||
'source' => 'term',
|
||||
'value_key' => 'name',
|
||||
'bundle_key' => 'vid',
|
||||
'bundle' => $this->vocabulary,
|
||||
'entity_type' => 'taxonomy_term',
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:node',
|
||||
],
|
||||
];
|
||||
/** @var \Drupal\migrate\Plugin\Migration $migration */
|
||||
$migration = $this->migrationPluginManager->createStubMigration($definition);
|
||||
$migrationExecutable = (new MigrateExecutable($migration, $this));
|
||||
$migrationExecutable->import();
|
||||
$this->assertEmpty($migration->getIdMap()->messageCount());
|
||||
$term = Term::load(1);
|
||||
$this->assertEquals('Apples', $term->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides multiple migration definitions for "transform" test.
|
||||
*/
|
||||
@@ -775,6 +883,9 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
|
||||
* The storage manager to create.
|
||||
* @param array $values
|
||||
* The values to use when creating the entity.
|
||||
*
|
||||
* @return string|int
|
||||
* The entity identifier.
|
||||
*/
|
||||
private function createTestData($storageName, array $values) {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
|
||||
@@ -783,6 +894,7 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
|
||||
->getStorage($storageName);
|
||||
$entity = $storage->create($values);
|
||||
$entity->save();
|
||||
return $entity->id();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_plus\Kernel\Plugin\migrate\process;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Plugin\MigrateDestinationInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\Tests\user\Traits\UserCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the entity_lookup plugin.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate_plus\Plugin\migrate\process\EntityLookup
|
||||
* @group migrate_plus
|
||||
*/
|
||||
class EntityLookupTest extends KernelTestBase {
|
||||
|
||||
use UserCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'migrate_plus',
|
||||
'migrate',
|
||||
'user',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', ['sequences']);
|
||||
$this->installEntitySchema('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup an entity without bundles on destination key.
|
||||
*
|
||||
* Using user entity as destination entity without bundles as example for
|
||||
* testing.
|
||||
*
|
||||
* @covers ::transform
|
||||
*/
|
||||
public function testLookupEntityWithoutBundles() {
|
||||
// Create a user.
|
||||
$known_user = $this->createUser([], 'lucuma');
|
||||
// Setup test migration objects.
|
||||
$migration_prophecy = $this->prophesize(MigrationInterface::class);
|
||||
$migrate_destination_prophecy = $this->prophesize(MigrateDestinationInterface::class);
|
||||
$migrate_destination_prophecy->getPluginId()->willReturn('user');
|
||||
$migrate_destination = $migrate_destination_prophecy->reveal();
|
||||
$migration_prophecy->getDestinationPlugin()->willReturn($migrate_destination);
|
||||
$migration_prophecy->getProcess()->willReturn([]);
|
||||
$migration = $migration_prophecy->reveal();
|
||||
$configuration = [
|
||||
'entity_type' => 'user',
|
||||
'value_key' => 'name',
|
||||
];
|
||||
$plugin = \Drupal::service('plugin.manager.migrate.process')
|
||||
->createInstance('entity_lookup', $configuration, $migration);
|
||||
$executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
|
||||
$row = new Row();
|
||||
// Check the known user is found.
|
||||
$value = $plugin->transform('lucuma', $executable, $row, 'name');
|
||||
$this->assertSame($known_user->id(), $value);
|
||||
// Check an unknown user is not found.
|
||||
$value = $plugin->transform('orange', $executable, $row, 'name');
|
||||
$this->assertNull($value);
|
||||
}
|
||||
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_plus\Unit\data_fetcher;
|
||||
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate_plus\Plugin\migrate_plus\data_fetcher\File;
|
||||
use Drupal\Tests\migrate\Unit\MigrateTestCase;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
/**
|
||||
* @file
|
||||
* PHPUnit tests for the Migrate Plus File 'data fetcher' plugin.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate_plus\Plugin\migrate_plus\data_fetcher\File
|
||||
*
|
||||
* @group migrate_plus
|
||||
*/
|
||||
class FileTest extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* Directory where test data will be created.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const BASE_DIRECTORY = 'migration_data';
|
||||
|
||||
/**
|
||||
* Minimal migration configuration data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $specificMigrationConfig = [
|
||||
'source' => 'url',
|
||||
'data_fetcher_plugin' => 'file',
|
||||
'data_parser_plugin' => 'json',
|
||||
'item_selector' => 0,
|
||||
'fields' => [],
|
||||
'ids' => [
|
||||
'id' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The data fetcher plugin ID being tested.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $dataFetcherPluginId = 'file';
|
||||
|
||||
/**
|
||||
* The data fetcher plugin definition.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $pluginDefinition = [
|
||||
'id' => 'file',
|
||||
'title' => 'File',
|
||||
];
|
||||
|
||||
/**
|
||||
* Test data to populate a file with.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $testData = '[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Joe Bloggs"
|
||||
}
|
||||
]';
|
||||
|
||||
/**
|
||||
* Define virtual dir where we'll be creating files in/fetching files from.
|
||||
*
|
||||
* @var \org\bovigo\vfs\vfsStreamDirectory
|
||||
*/
|
||||
private $baseDir;
|
||||
|
||||
/**
|
||||
* Set up test environment.
|
||||
*/
|
||||
public function setUp() {
|
||||
$this->baseDir = vfsStream::setup(self::BASE_DIRECTORY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fetching a valid file.
|
||||
*/
|
||||
public function testFetchFile() {
|
||||
$file_name = 'file.json';
|
||||
$file_path = vfsStream::url(implode(DIRECTORY_SEPARATOR, [self::BASE_DIRECTORY, $file_name]));
|
||||
$migration_config = $this->specificMigrationConfig + [
|
||||
'urls' => [$file_path],
|
||||
];
|
||||
|
||||
$plugin = new File(
|
||||
$migration_config,
|
||||
$this->dataFetcherPluginId,
|
||||
$this->pluginDefinition
|
||||
);
|
||||
|
||||
$tree = [
|
||||
$file_name => $this->testData,
|
||||
];
|
||||
|
||||
vfsStream::create($tree, $this->baseDir);
|
||||
|
||||
$expected = json_decode($this->testData, TRUE);
|
||||
$retrieved = json_decode($plugin->getResponseContent($file_path), TRUE);
|
||||
|
||||
$this->assertEquals($expected, $retrieved);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fetching multiple valid files.
|
||||
*/
|
||||
public function testFetchMultipleFiles() {
|
||||
$number_of_files = 3;
|
||||
$file_paths = [];
|
||||
$file_names = [];
|
||||
|
||||
for ($i = 0; $i < $number_of_files; $i++) {
|
||||
$file_name = 'file_' . $i . '.json';
|
||||
$file_names[] = $file_name;
|
||||
$file_paths[] = vfsStream::url(implode(DIRECTORY_SEPARATOR, [self::BASE_DIRECTORY, $file_name]));
|
||||
}
|
||||
|
||||
$migration_config = $this->specificMigrationConfig + [
|
||||
'urls' => $file_paths,
|
||||
];
|
||||
|
||||
$plugin = new File(
|
||||
$migration_config,
|
||||
$this->dataFetcherPluginId,
|
||||
$this->pluginDefinition
|
||||
);
|
||||
|
||||
for ($i = 0; $i < $number_of_files; $i++) {
|
||||
$file_name = $file_names[$i];
|
||||
$file_path = $file_paths[$i];
|
||||
|
||||
$tree = [
|
||||
$file_name => $this->testData,
|
||||
];
|
||||
|
||||
vfsStream::create($tree, $this->baseDir);
|
||||
|
||||
$expected = json_decode($this->testData);
|
||||
$retrieved = json_decode($plugin->getResponseContent($file_path));
|
||||
|
||||
$this->assertEquals($expected, $retrieved);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test trying to fetch an unreadable file results in exception.
|
||||
*/
|
||||
public function testFetchUnreadableFile() {
|
||||
$file_name = 'file.json';
|
||||
$file_path = vfsStream::url(implode(DIRECTORY_SEPARATOR, [self::BASE_DIRECTORY, $file_name]));
|
||||
$migration_config = $this->specificMigrationConfig + [
|
||||
'urls' => [$file_path],
|
||||
];
|
||||
|
||||
$plugin = new File(
|
||||
$migration_config,
|
||||
$this->dataFetcherPluginId,
|
||||
$this->pluginDefinition
|
||||
);
|
||||
|
||||
// Create an unreadable file.
|
||||
vfsStream::newFile($file_name, 0300)
|
||||
->withContent($this->testData)
|
||||
->at($this->baseDir);
|
||||
|
||||
// Trigger exception trying to read the non-readable file.
|
||||
$this->setExpectedException(MigrateException::class, 'file parser plugin: could not retrieve data from vfs://migration_data/file.json');
|
||||
$plugin->getResponseContent($file_path);
|
||||
}
|
||||
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_plus\Unit\data_fetcher;
|
||||
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate_plus\DataFetcherPluginBase;
|
||||
use Drupal\migrate_plus\Plugin\migrate_plus\authentication\Basic;
|
||||
use Drupal\migrate_plus\Plugin\migrate_plus\data_fetcher\Http;
|
||||
use Drupal\Tests\migrate\Unit\MigrateTestCase;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
|
||||
/**
|
||||
* @file
|
||||
* PHPUnit tests for the Migrate Plus Http 'data fetcher' plugin.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate_plus\Plugin\migrate_plus\data_fetcher\Http
|
||||
*
|
||||
* @group migrate_plus
|
||||
*/
|
||||
class HttpTest extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* Minimal migration configuration data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $specificMigrationConfig = [
|
||||
'source' => 'url',
|
||||
'urls' => ['http://example.org/http_fetcher_test'],
|
||||
'data_fetcher_plugin' => 'http',
|
||||
'data_parser_plugin' => 'json',
|
||||
'item_selector' => 0,
|
||||
'authentication' => [
|
||||
'plugin' => 'basic',
|
||||
'username' => 'testing',
|
||||
'password' => 'password',
|
||||
],
|
||||
'fields' => [],
|
||||
'ids' => [
|
||||
'id' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The data fetcher plugin ID being tested.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $dataFetcherPluginId = 'http';
|
||||
|
||||
/**
|
||||
* The data fetcher plugin definition.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $pluginDefinition = [
|
||||
'id' => 'http',
|
||||
'title' => 'HTTP',
|
||||
];
|
||||
|
||||
/**
|
||||
* Test data to validate an HTTP response against.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $testData = '
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Joe Bloggs"
|
||||
}
|
||||
';
|
||||
|
||||
/**
|
||||
* Mocked up Basic authentication plugin.
|
||||
*
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $basicAuthenticator = NULL;
|
||||
|
||||
/**
|
||||
* Set up test environment.
|
||||
*/
|
||||
public function setUp() {
|
||||
// Mock up a Basic authentication plugin that will be used in requests.
|
||||
$basic_authenticator = $this->getMockBuilder(Basic::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$basic_authenticator->method('getAuthenticationOptions')
|
||||
->will($this->returnValue([
|
||||
'auth' => [
|
||||
'username',
|
||||
'password',
|
||||
],
|
||||
]));
|
||||
|
||||
$this->basicAuthenticator = $basic_authenticator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 'http' data fetcher (with auth) returns an expected response.
|
||||
*/
|
||||
public function testFetchHttpWithAuth() {
|
||||
$migration_config = $this->migrationConfiguration + $this->specificMigrationConfig;
|
||||
|
||||
$plugin = new TestHttp($migration_config, $this->dataFetcherPluginId, $this->pluginDefinition);
|
||||
$plugin->mockHttpClient([[200, 'application/json', $this->testData]], $this->basicAuthenticator);
|
||||
|
||||
// The Guzzle mock returns an instance of StreamInterface.
|
||||
// http://docs.guzzlephp.org/en/latest/psr7.html
|
||||
$stream = $plugin->getResponseContent($migration_config['urls'][0]);
|
||||
|
||||
$body = json_decode((string) $stream, TRUE);
|
||||
|
||||
// Compare what we got back from the parser to what we expected to get.
|
||||
$expected = json_decode($this->testData, TRUE);
|
||||
$this->assertArrayEquals($expected, $body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 'http' data fetcher (without auth) returns an expected response.
|
||||
*/
|
||||
public function testFetchHttpNoAuth() {
|
||||
$migration_config = $this->migrationConfiguration + $this->specificMigrationConfig;
|
||||
unset($migration_config['authentication']);
|
||||
|
||||
$plugin = new TestHttp($migration_config, $this->dataFetcherPluginId, $this->pluginDefinition);
|
||||
$plugin->mockHttpClient([[200, 'application/json', $this->testData]], NULL);
|
||||
|
||||
$stream = $plugin->getResponseContent($migration_config['urls'][0]);
|
||||
|
||||
$body = json_decode((string) $stream, TRUE);
|
||||
|
||||
$expected = json_decode($this->testData, TRUE);
|
||||
$this->assertArrayEquals($expected, $body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 'http' data fetcher (with auth) dies as expected when auth fails.
|
||||
*/
|
||||
public function testFetchHttpAuthFailure() {
|
||||
$migration_config = $this->migrationConfiguration + $this->specificMigrationConfig;
|
||||
|
||||
$plugin = new TestHttp($migration_config, $this->dataFetcherPluginId, $this->pluginDefinition);
|
||||
$plugin->mockHttpClient([[403, 'text/html', 'Forbidden']], $this->basicAuthenticator);
|
||||
|
||||
$this->setExpectedException(MigrateException::class, 'Error message: Client error: `GET http://example.org/http_fetcher_test` resulted in a `403 Forbidden');
|
||||
$plugin->getResponseContent($migration_config['urls'][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 'http' data fetcher (with auth) dies as expected when server down.
|
||||
*/
|
||||
public function testFetchHttp500Error() {
|
||||
$migration_config = $this->migrationConfiguration + $this->specificMigrationConfig;
|
||||
|
||||
$plugin = new TestHttp($migration_config, $this->dataFetcherPluginId, $this->pluginDefinition);
|
||||
$plugin->mockHttpClient([[500, 'text/html', 'Internal Server Error']], $this->basicAuthenticator);
|
||||
|
||||
$this->setExpectedException(MigrateException::class, 'GET http://example.org/http_fetcher_test` resulted in a `500 Internal Server Error');
|
||||
$plugin->getResponseContent($migration_config['urls'][0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test class to mock an HTTP request.
|
||||
*/
|
||||
class TestHttp extends Http {
|
||||
|
||||
/**
|
||||
* Mocked authenticator plugin.
|
||||
*
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
public $authenticator = NULL;
|
||||
|
||||
/**
|
||||
* Mock the HttpClient, so we can control the request/response(s) etc.
|
||||
*
|
||||
* @param array $responses
|
||||
* An array of responses (arrays), with each consisting of properties,
|
||||
* ordered: response code, content-type and response body.
|
||||
* @param \PHPUnit_Framework_MockObject_MockObject $authenticator
|
||||
* Mocked authenticator plugin.
|
||||
*/
|
||||
public function mockHttpClient(array $responses, \PHPUnit_Framework_MockObject_MockObject $authenticator = NULL) {
|
||||
// Set mocked authentication plugin to be used for the request auth plugin.
|
||||
$this->authenticator = $authenticator;
|
||||
|
||||
$handler_responses = [];
|
||||
foreach ($responses as $response) {
|
||||
$handler_responses[] = new Response(
|
||||
$response[0],
|
||||
['Content-Type' => $response[1]],
|
||||
$response[2]
|
||||
);
|
||||
}
|
||||
|
||||
$mock = new MockHandler($handler_responses);
|
||||
$handler = HandlerStack::create($mock);
|
||||
|
||||
$this->httpClient = new Client(['handler' => $handler]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
|
||||
// Skip calling the Http() constructor (that sets the httpClient instance
|
||||
// variable via \Drupal which we don't want to do), but keep the call to its
|
||||
// parent class constructor. @see https://bugs.php.net/bug.php?id=42016
|
||||
DataFetcherPluginBase::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
// This is what the parent class is doing, that we need to override.
|
||||
$this->httpClient = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the parent::getAuthenticationPlugin()
|
||||
*
|
||||
* So we can mock the authentication plugin.
|
||||
*
|
||||
* @return \PHPUnit_Framework_MockObject_MockObject
|
||||
* A mocked authentication plugin.
|
||||
*/
|
||||
public function getAuthenticationPlugin() {
|
||||
if (!isset($this->authenticationPlugin)) {
|
||||
$this->authenticationPlugin = $this->authenticator;
|
||||
}
|
||||
|
||||
return $this->authenticationPlugin;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,13 +8,14 @@
|
||||
"irc": "irc://irc.freenode.org/drupal-migrate",
|
||||
"source": "http://cgit.drupalcode.org/migrate_tools"
|
||||
},
|
||||
"license": "GPL-2.0+",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"require": {
|
||||
"drupal/migrate_plus": "^4"
|
||||
},
|
||||
"require-dev": {
|
||||
"drupal/coder": "^8",
|
||||
"drupal/migrate_source_csv": "^2.2"
|
||||
"drupal/migrate_plus": "4.x-dev",
|
||||
"drupal/migrate_source_csv": "^2.2",
|
||||
"drush/drush": "^9"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Learn to make one for your own drupal.org project:
|
||||
# https://www.drupal.org/drupalorg/docs/drupal-ci/customizing-drupalci-testing
|
||||
build:
|
||||
assessment:
|
||||
validate_codebase:
|
||||
phplint:
|
||||
container_composer:
|
||||
phpcs:
|
||||
# phpcs will use core's specified version of Coder.
|
||||
sniff-all-files: true
|
||||
halt-on-fail: true
|
||||
testing:
|
||||
# run_tests task is executed several times in order of performance speeds.
|
||||
# halt-on-fail can be set on the run_tests tasks in order to fail fast.
|
||||
# suppress-deprecations is false in order to be alerted to usages of
|
||||
# deprecated code.
|
||||
run_tests.standard:
|
||||
types: 'Simpletest,PHPUnit-Unit,PHPUnit-Kernel,PHPUnit-Functional'
|
||||
testgroups: '--all'
|
||||
suppress-deprecations: false
|
||||
run_tests.js:
|
||||
types: 'PHPUnit-FunctionalJavascript'
|
||||
testgroups: '--all'
|
||||
suppress-deprecations: false
|
||||
nightwatchjs: { }
|
||||
@@ -5,13 +5,13 @@
|
||||
* Command-line tools to aid performing and developing migrations.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\RequirementsInterface;
|
||||
use Drupal\migrate_plus\Entity\MigrationGroup;
|
||||
use Drupal\migrate_tools\DrushLogMigrateMessage;
|
||||
use Drupal\migrate_tools\MigrateExecutable;
|
||||
use Drupal\migrate_tools\MigrateTools;
|
||||
|
||||
/**
|
||||
* Implements hook_drush_command().
|
||||
@@ -47,6 +47,7 @@ function migrate_tools_drush_command() {
|
||||
'limit' => 'Limit on the number of items to process in each migration',
|
||||
'feedback' => 'Frequency of progress messages, in items processed',
|
||||
'idlist' => 'Comma-separated list of IDs to import',
|
||||
'idlist-delimiter' => 'The delimiter for records, defaults to \':\'',
|
||||
'update' => ' In addition to processing unprocessed items from the source, update previously-imported items with the current data',
|
||||
'force' => 'Force an operation to run, even if all dependencies are not satisfied',
|
||||
'execute-dependencies' => 'Execute all dependent migrations first.',
|
||||
@@ -62,6 +63,7 @@ function migrate_tools_drush_command() {
|
||||
'migrate-import beer_term,beer_node' => 'Import new terms and nodes',
|
||||
'migrate-import beer_user --limit=2' => 'Import no more than 2 users',
|
||||
'migrate-import beer_user --idlist=5' => 'Import the user record with source ID 5',
|
||||
'migrate-import beer_node_revision --idlist=1:2,2:3,3:5' => "Import the node revision record with source IDs [1,2], [2,3], and [3,5]",
|
||||
],
|
||||
'drupal dependencies' => ['migrate_tools'],
|
||||
'aliases' => ['mi', 'mim'],
|
||||
@@ -74,9 +76,11 @@ function migrate_tools_drush_command() {
|
||||
'group' => 'A comma-separated list of migration groups to rollback',
|
||||
'tag' => 'ID of the migration tag to rollback',
|
||||
'feedback' => 'Frequency of progress messages, in items processed',
|
||||
'idlist' => 'Comma-separated list of IDs to import',
|
||||
],
|
||||
'arguments' => [
|
||||
'migration' => 'Name of migration(s) to rollback. Delimit multiple using commas.',
|
||||
'idlist' => 'Comma-separated list of IDs to import',
|
||||
],
|
||||
'examples' => [
|
||||
'migrate-rollback --all' => 'Perform all migrations',
|
||||
@@ -84,6 +88,7 @@ function migrate_tools_drush_command() {
|
||||
'migrate-rollback --tag=user' => 'Rollback all migrations with the user tag',
|
||||
'migrate-rollback --group=beer --tag=user' => 'Rollback all migrations in the beer group and with the user tag',
|
||||
'migrate-rollback beer_term,beer_node' => 'Rollback imported terms and nodes',
|
||||
'migrate-rollback beer_user --idlist=5' => 'Rollback imported user record with source ID 5',
|
||||
],
|
||||
'drupal dependencies' => ['migrate_tools'],
|
||||
'aliases' => ['mr'],
|
||||
@@ -296,7 +301,16 @@ function _drush_migrate_tools_execute_migration(MigrationInterface $migration, $
|
||||
$migration->set('requirements', []);
|
||||
}
|
||||
if (!empty($options['update'])) {
|
||||
$migration->getIdMap()->prepareUpdate();
|
||||
if (empty($options['idlist'])) {
|
||||
$migration->getIdMap()->prepareUpdate();
|
||||
}
|
||||
else {
|
||||
$source_id_values_list = MigrateTools::buildIdList($options);
|
||||
$keys = array_keys($migration->getSourcePlugin()->getIds());
|
||||
foreach ($source_id_values_list as $source_id_values) {
|
||||
$migration->getIdMap()->setUpdate(array_combine($keys, $source_id_values));
|
||||
}
|
||||
}
|
||||
}
|
||||
$executable = new MigrateExecutable($migration, $log, $options);
|
||||
// Function drush_op() provides --simulate support.
|
||||
@@ -326,8 +340,10 @@ function drush_migrate_tools_migrate_rollback($migration_names = '') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (drush_get_option('feedback')) {
|
||||
$options['feedback'] = drush_get_option('feedback');
|
||||
foreach (['feedback', 'idlist'] as $option) {
|
||||
if (drush_get_option($option)) {
|
||||
$options[$option] = drush_get_option($option);
|
||||
}
|
||||
}
|
||||
|
||||
$log = new DrushLogMigrateMessage();
|
||||
@@ -365,12 +381,15 @@ function drush_migrate_tools_migrate_stop($migration_id = '') {
|
||||
case MigrationInterface::STATUS_IDLE:
|
||||
drush_log(dt('Migration @id is idle', ['@id' => $migration_id]), 'warning');
|
||||
break;
|
||||
|
||||
case MigrationInterface::STATUS_DISABLED:
|
||||
drush_log(dt('Migration @id is disabled', ['@id' => $migration_id]), 'warning');
|
||||
break;
|
||||
|
||||
case MigrationInterface::STATUS_STOPPING:
|
||||
drush_log(dt('Migration @id is already stopping', ['@id' => $migration_id]), 'warning');
|
||||
break;
|
||||
|
||||
default:
|
||||
$migration->interruptMigration(MigrationInterface::RESULT_STOPPED);
|
||||
drush_log(dt('Migration @id requested to stop', ['@id' => $migration_id]), 'success');
|
||||
@@ -505,9 +524,9 @@ function drush_migrate_tools_migration_list($migration_ids = '') {
|
||||
}
|
||||
else {
|
||||
// Get the requested migrations.
|
||||
$migration_ids = explode(',', Unicode::strtolower($migration_ids));
|
||||
$migration_ids = explode(',', mb_strtolower($migration_ids));
|
||||
foreach ($plugins as $id => $migration) {
|
||||
if (in_array(Unicode::strtolower($id), $migration_ids)) {
|
||||
if (in_array(mb_strtolower($id), $migration_ids)) {
|
||||
$matched_migrations[$id] = $migration;
|
||||
}
|
||||
}
|
||||
@@ -539,7 +558,7 @@ function drush_migrate_tools_migration_list($migration_ids = '') {
|
||||
$configured_values = (array) $migration->get($property);
|
||||
$configured_id = (in_array($search_value, $configured_values)) ? $search_value : 'default';
|
||||
if (empty($search_value) || $search_value == $configured_id) {
|
||||
if (empty($migration_ids) || in_array(Unicode::strtolower($id), $migration_ids)) {
|
||||
if (empty($migration_ids) || in_array(mb_strtolower($id), $migration_ids)) {
|
||||
$filtered_migrations[$id] = $migration;
|
||||
}
|
||||
}
|
||||
@@ -553,7 +572,7 @@ function drush_migrate_tools_migration_list($migration_ids = '') {
|
||||
// Sort the matched migrations by group.
|
||||
if (!empty($matched_migrations)) {
|
||||
foreach ($matched_migrations as $id => $migration) {
|
||||
$configured_group_id = empty($migration->get('migration_group')) ? 'default' : $migration->get('migration_group');
|
||||
$configured_group_id = empty($migration->migration_group) ? 'default' : $migration->migration_group;
|
||||
$migrations[$configured_group_id][$id] = $migration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,15 @@ name: Migrate Tools
|
||||
description: 'Tools to assist in developing and running migrations.'
|
||||
package: Migration
|
||||
# core: 8.x
|
||||
configure: entity.migration_group.list
|
||||
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-08-27
|
||||
version: '8.x-4.0'
|
||||
# Information added by Drupal.org packaging script on 2019-01-07
|
||||
version: '8.x-4.1'
|
||||
core: '8.x'
|
||||
project: 'migrate_tools'
|
||||
datestamp: 1535380087
|
||||
datestamp: 1546879109
|
||||
|
||||
@@ -157,7 +157,7 @@ migrate_tools.messages:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/messages'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MessageController::overview'
|
||||
_title: 'Messages'
|
||||
_title_callback: '\Drupal\migrate_tools\Controller\MessageController::title'
|
||||
_migrate_group: true
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="Drupal coding standards">
|
||||
<description>Drupal 8 coding standards</description>
|
||||
|
||||
<file>.</file>
|
||||
<arg name="extensions" value="inc,install,module,php,profile,test,theme"/>
|
||||
|
||||
<!--Exclude third party code.-->
|
||||
<exclude-pattern>./vendor/*</exclude-pattern>
|
||||
<!--Run Drupal standards.-->
|
||||
<rule ref="Drupal.Array"/>
|
||||
<rule ref="Drupal.Classes"/>
|
||||
<rule ref="Drupal.Commenting">
|
||||
<!-- TagsNotGrouped and ParamGroup have false-positives.
|
||||
@see https://www.drupal.org/node/2060925 -->
|
||||
<exclude name="Drupal.Commenting.DocComment.TagsNotGrouped"/>
|
||||
<exclude name="Drupal.Commenting.DocComment.ParamGroup"/>
|
||||
</rule>
|
||||
<rule ref="Drupal.ControlStructures"/>
|
||||
<rule ref="Drupal.CSS"/>
|
||||
<rule ref="Drupal.Files"/>
|
||||
<rule ref="Drupal.Formatting"/>
|
||||
<rule ref="Drupal.Functions"/>
|
||||
<rule ref="Drupal.InfoFiles"/>
|
||||
<rule ref="Drupal.Methods"/>
|
||||
<rule ref="Drupal.NamingConventions"/>
|
||||
<rule ref="Drupal.Scope"/>
|
||||
<rule ref="Drupal.Semantics"/>
|
||||
<rule ref="Drupal.Strings"/>
|
||||
<rule ref="Drupal.WhiteSpace"/>
|
||||
|
||||
<!-- Drupal Practice sniffs -->
|
||||
<rule ref="DrupalPractice.Commenting"/>
|
||||
|
||||
<!-- Generic sniffs -->
|
||||
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
|
||||
<rule ref="Generic.Files.ByteOrderMark"/>
|
||||
<rule ref="Generic.Files.LineEndings"/>
|
||||
<rule ref="Generic.Formatting.SpaceAfterCast"/>
|
||||
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
|
||||
<rule ref="Generic.Functions.OpeningFunctionBraceKernighanRitchie">
|
||||
<properties>
|
||||
<property name="checkClosures" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Generic.NamingConventions.ConstructorName"/>
|
||||
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
|
||||
<rule ref="Generic.PHP.DeprecatedFunctions"/>
|
||||
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
|
||||
<rule ref="Generic.PHP.LowerCaseKeyword"/>
|
||||
<rule ref="Generic.PHP.UpperCaseConstant"/>
|
||||
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
|
||||
|
||||
<!-- MySource sniffs -->
|
||||
<rule ref="MySource.Debug.DebugCode"/>
|
||||
|
||||
<!-- PEAR sniffs -->
|
||||
<rule ref="PEAR.Files.IncludingFile"/>
|
||||
<!-- Disable some error messages that we do not want. -->
|
||||
<rule ref="PEAR.Files.IncludingFile.UseIncludeOnce">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="PEAR.Files.IncludingFile.UseInclude">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="PEAR.Files.IncludingFile.UseRequireOnce">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="PEAR.Files.IncludingFile.UseRequire">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="PEAR.Functions.ValidDefaultValue"/>
|
||||
|
||||
<!-- PEAR sniffs -->
|
||||
<rule ref="PEAR.Functions.FunctionCallSignature"/>
|
||||
<!-- The sniffs inside PEAR.Functions.FunctionCallSignature silenced below are
|
||||
also silenced in Drupal CS' ruleset.xml. The code below is a 1-on-1 copy
|
||||
from that file. -->
|
||||
<!-- Disable some error messages that we already cover. -->
|
||||
<rule ref="PEAR.Functions.FunctionCallSignature.SpaceAfterOpenBracket">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="PEAR.Functions.FunctionCallSignature.SpaceBeforeCloseBracket">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<!-- Disable some error messages that we do not want. -->
|
||||
<rule ref="PEAR.Functions.FunctionCallSignature.Indent">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="PEAR.Functions.FunctionCallSignature.CloseBracketLine">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="PEAR.Functions.FunctionCallSignature.EmptyLine">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
|
||||
<!-- PSR-2 sniffs -->
|
||||
<rule ref="PSR2.Classes.PropertyDeclaration">
|
||||
<exclude name="PSR2.Classes.PropertyDeclaration.Underscore"/>
|
||||
</rule>
|
||||
<rule ref="PSR2.Namespaces.NamespaceDeclaration"/>
|
||||
<rule ref="PSR2.Namespaces.UseDeclaration">
|
||||
<exclude name="PSR2.Namespaces.UseDeclaration.UseAfterNamespace"/>
|
||||
</rule>
|
||||
|
||||
<!-- Squiz sniffs -->
|
||||
<rule ref="Squiz.Arrays.ArrayBracketSpacing"/>
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration">
|
||||
<exclude name="Squiz.Arrays.ArrayDeclaration.NoKeySpecified"/>
|
||||
<exclude name="Squiz.Arrays.ArrayDeclaration.KeySpecified"/>
|
||||
</rule>
|
||||
<!-- Disable some error messages that we do not want. -->
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration.CloseBraceNotAligned">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration.DoubleArrowNotAligned">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration.FirstValueNoNewline">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration.KeyNotAligned">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration.MultiLineNotAllowed">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration.NoComma">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration.NoCommaAfterLast">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration.NotLowerCase">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration.SingleLineNotAllowed">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration.ValueNotAligned">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Arrays.ArrayDeclaration.ValueNoNewline">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration"/>
|
||||
<!-- Disable some error messages that we already cover. -->
|
||||
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.AsNotLower">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.SpaceAfterOpen">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.SpaceBeforeClose">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.ControlStructures.ForLoopDeclaration"/>
|
||||
<!-- Disable some error messages that we already cover. -->
|
||||
<rule ref="Squiz.ControlStructures.ForLoopDeclaration.SpacingAfterOpen">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.ControlStructures.ForLoopDeclaration.SpacingBeforeClose">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration"/>
|
||||
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.BraceOnSameLine">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.ContentAfterBrace">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<!-- Standard yet to be finalized on this (https://www.drupal.org/node/1539712). -->
|
||||
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.FirstParamSpacing">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.Indent">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.CloseBracketLine">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing">
|
||||
<properties>
|
||||
<property name="equalsSpacing" value="1"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.NoSpaceBeforeArg">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="Squiz.PHP.LowercasePHPFunctions"/>
|
||||
<rule ref="Squiz.Strings.ConcatenationSpacing">
|
||||
<properties>
|
||||
<property name="spacing" value="1"/>
|
||||
<property name="ignoreNewlines" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Squiz.WhiteSpace.LanguageConstructSpacing" />
|
||||
<rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
|
||||
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"/>
|
||||
|
||||
<!-- Zend sniffs -->
|
||||
<rule ref="Zend.Files.ClosingTag"/>
|
||||
|
||||
</ruleset>
|
||||
+88
-15
@@ -3,7 +3,6 @@
|
||||
namespace Drupal\migrate_tools\Commands;
|
||||
|
||||
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Datetime\DateFormatter;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||
@@ -12,7 +11,9 @@ use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrationPluginManager;
|
||||
use Drupal\migrate\Plugin\RequirementsInterface;
|
||||
use Drupal\migrate_tools\Drush9LogMigrateMessage;
|
||||
use Drupal\migrate_tools\IdMapFilter;
|
||||
use Drupal\migrate_tools\MigrateExecutable;
|
||||
use Drupal\migrate_tools\MigrateTools;
|
||||
use Drush\Commands\DrushCommands;
|
||||
|
||||
/**
|
||||
@@ -89,6 +90,8 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @option tag Name of the migration tag to list
|
||||
* @option names-only Only return names, not all the details (faster)
|
||||
*
|
||||
* @default $options []
|
||||
*
|
||||
* @usage migrate:status
|
||||
* Retrieve status for all migrations
|
||||
* @usage migrate:status --group=beer
|
||||
@@ -118,7 +121,12 @@ 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 = []) {
|
||||
$options += [
|
||||
'group' => NULL,
|
||||
'tag' => NULL,
|
||||
'names-only' => NULL,
|
||||
];
|
||||
$names_only = $options['names-only'];
|
||||
|
||||
$migrations = $this->migrationsList($migration_names, $options);
|
||||
@@ -231,12 +239,15 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @option limit Limit on the number of items to process in each migration
|
||||
* @option feedback Frequency of progress messages, in items processed
|
||||
* @option idlist Comma-separated list of IDs to import
|
||||
* @option idlist-delimiter The delimiter for records, defaults to ':'
|
||||
* @option update In addition to processing unprocessed items from the
|
||||
* source, update previously-imported items with the current data
|
||||
* @option force Force an operation to run, even if all dependencies are not
|
||||
* satisfied
|
||||
* @option execute-dependencies Execute all dependent migrations first.
|
||||
*
|
||||
* @default $options []
|
||||
*
|
||||
* @usage migrate:import --all
|
||||
* Perform all migrations
|
||||
* @usage migrate:import --group=beer
|
||||
@@ -251,6 +262,8 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* Import no more than 2 users
|
||||
* @usage migrate:import beer_user --idlist=5
|
||||
* Import the user record with source ID 5
|
||||
* @usage migrate:import beer_node_revision --idlist=1:2,2:3,3:5
|
||||
* Import the node revision record with source IDs [1,2], [2,3], and [3,5]
|
||||
*
|
||||
* @validate-module-enabled migrate_tools
|
||||
*
|
||||
@@ -259,7 +272,19 @@ 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 = []) {
|
||||
$options += [
|
||||
'all' => NULL,
|
||||
'group' => NULL,
|
||||
'tag' => NULL,
|
||||
'limit' => NULL,
|
||||
'feedback' => NULL,
|
||||
'idlist' => NULL,
|
||||
'idlist-delimiter' => ':',
|
||||
'update' => NULL,
|
||||
'force' => NULL,
|
||||
'execute-dependencies' => NULL,
|
||||
];
|
||||
$group_names = $options['group'];
|
||||
$tag_names = $options['tag'];
|
||||
$all = $options['all'];
|
||||
@@ -268,7 +293,16 @@ 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', 'execute-dependencies'] as $option) {
|
||||
$possible_options = [
|
||||
'limit',
|
||||
'feedback',
|
||||
'idlist',
|
||||
'idlist-delimiter',
|
||||
'update',
|
||||
'force',
|
||||
'execute-dependencies',
|
||||
];
|
||||
foreach ($possible_options as $option) {
|
||||
if ($options[$option]) {
|
||||
$additional_options[$option] = $options[$option];
|
||||
}
|
||||
@@ -303,6 +337,11 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @option group A comma-separated list of migration groups to rollback
|
||||
* @option tag ID of the migration tag to rollback
|
||||
* @option feedback Frequency of progress messages, in items processed
|
||||
* @option feedback Frequency of progress messages, in items processed
|
||||
* @option idlist Comma-separated list of IDs to rollback
|
||||
* @option idlist-delimiter The delimiter for records, defaults to ':'
|
||||
*
|
||||
* @default $options []
|
||||
*
|
||||
* @usage migrate:rollback --all
|
||||
* Perform all migrations
|
||||
@@ -314,6 +353,8 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* Rollback all migrations in the beer group and with the user tag
|
||||
* @usage migrate:rollback beer_term,beer_node
|
||||
* Rollback imported terms and nodes
|
||||
* @usage migrate:rollback beer_user --idlist=5
|
||||
* Rollback imported user record with source ID 5
|
||||
* @validate-module-enabled migrate_tools
|
||||
*
|
||||
* @aliases mr, migrate-rollback
|
||||
@@ -321,7 +362,15 @@ 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 = []) {
|
||||
$options += [
|
||||
'all' => NULL,
|
||||
'group' => NULL,
|
||||
'tag' => NULL,
|
||||
'feedback' => NULL,
|
||||
'idlist' => NULL,
|
||||
'idlist-delimiter' => ':',
|
||||
];
|
||||
$group_names = $options['group'];
|
||||
$tag_names = $options['tag'];
|
||||
$all = $options['all'];
|
||||
@@ -330,8 +379,10 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
throw new \Exception(dt('You must specify --all, --group, --tag, or one or more migration names separated by commas'));
|
||||
}
|
||||
|
||||
if ($options['feedback']) {
|
||||
$additional_options['feedback'] = $options['feedback'];
|
||||
foreach (['feedback', 'idlist', 'idlist-delimiter'] as $option) {
|
||||
if ($options[$option]) {
|
||||
$additional_options[$option] = $options[$option];
|
||||
}
|
||||
}
|
||||
|
||||
$migrations = $this->migrationsList($migration_names, $options);
|
||||
@@ -456,6 +507,10 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @command migrate:messages
|
||||
*
|
||||
* @option csv Export messages as a CSV
|
||||
* @option idlist Comma-separated list of IDs to import
|
||||
* @option idlist-delimiter The delimiter for records, defaults to ':'
|
||||
*
|
||||
* @default $options []
|
||||
*
|
||||
* @usage migrate:messages MyNode
|
||||
* Show all messages for the MyNode migration
|
||||
@@ -473,7 +528,12 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
|
||||
* Source fields of the given migration formatted as a table.
|
||||
*/
|
||||
public function messages($migration_id, array $options = ['csv' => NULL]) {
|
||||
public function messages($migration_id, array $options = []) {
|
||||
$options += [
|
||||
'csv' => NULL,
|
||||
'idlist' => NULL,
|
||||
'idlist-delimiter' => ':',
|
||||
];
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance(
|
||||
$migration_id
|
||||
@@ -484,8 +544,8 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$map = $migration->getIdMap();
|
||||
$id_list = MigrateTools::buildIdList($options);
|
||||
$map = new IdMapFilter($migration->getIdMap(), $id_list);
|
||||
$table = [];
|
||||
foreach ($map->getMessageIterator() as $row) {
|
||||
unset($row->msgid);
|
||||
@@ -561,6 +621,8 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @param array $options
|
||||
* Command options.
|
||||
*
|
||||
* @default $options []
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrationInterface[][]
|
||||
* An array keyed by migration group, each value containing an array of
|
||||
* migrations or an empty array if no migrations match the input criteria.
|
||||
@@ -586,9 +648,9 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
}
|
||||
else {
|
||||
// Get the requested migrations.
|
||||
$migration_ids = explode(',', Unicode::strtolower($migration_ids));
|
||||
$migration_ids = explode(',', mb_strtolower($migration_ids));
|
||||
foreach ($plugins as $id => $migration) {
|
||||
if (in_array(Unicode::strtolower($id), $migration_ids)) {
|
||||
if (in_array(mb_strtolower($id), $migration_ids)) {
|
||||
$matched_migrations[$id] = $migration;
|
||||
}
|
||||
}
|
||||
@@ -624,7 +686,7 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
)) ? $search_value : 'default';
|
||||
if (empty($search_value) || $search_value == $configured_id) {
|
||||
if (empty($migration_ids) || in_array(
|
||||
Unicode::strtolower($id),
|
||||
mb_strtolower($id),
|
||||
$migration_ids
|
||||
)) {
|
||||
$filtered_migrations[$id] = $migration;
|
||||
@@ -640,7 +702,7 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
// Sort the matched migrations by group.
|
||||
if (!empty($matched_migrations)) {
|
||||
foreach ($matched_migrations as $id => $migration) {
|
||||
$configured_group_id = empty($migration->get('migration_group')) ? 'default' : $migration->get('migration_group');
|
||||
$configured_group_id = empty($migration->migration_group) ? 'default' : $migration->migration_group;
|
||||
$migrations[$configured_group_id][$id] = $migration;
|
||||
}
|
||||
}
|
||||
@@ -660,6 +722,8 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
* @param array $options
|
||||
* Additional options of the command.
|
||||
*
|
||||
* @default $options []
|
||||
*
|
||||
* @throws \Exception
|
||||
* If some migrations failed during execution.
|
||||
*/
|
||||
@@ -686,7 +750,16 @@ class MigrateToolsCommands extends DrushCommands {
|
||||
$migration->set('requirements', []);
|
||||
}
|
||||
if (!empty($options['update'])) {
|
||||
$migration->getIdMap()->prepareUpdate();
|
||||
if (empty($options['idlist'])) {
|
||||
$migration->getIdMap()->prepareUpdate();
|
||||
}
|
||||
else {
|
||||
$source_id_values_list = MigrateTools::buildIdList($options);
|
||||
$keys = array_keys($migration->getSourcePlugin()->getIds());
|
||||
foreach ($source_id_values_list as $source_id_values) {
|
||||
$migration->getIdMap()->setUpdate(array_combine($keys, $source_id_values));
|
||||
}
|
||||
}
|
||||
}
|
||||
$executable = new MigrateExecutable($migration, $this->getMigrateMessage(), $options);
|
||||
// drush_op() provides --simulate support.
|
||||
|
||||
@@ -141,4 +141,22 @@ class MessageController extends ControllerBase {
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of the page.
|
||||
*
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationGroupInterface $migration_group
|
||||
* The migration group.
|
||||
* @param \Drupal\migrate_plus\Entity\MigrationInterface $migration
|
||||
* The $migration.
|
||||
*
|
||||
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
* The translated title.
|
||||
*/
|
||||
public function title(MigrationGroupInterface $migration_group, MigratePlusMigrationInterface $migration) {
|
||||
return $this->t(
|
||||
'Messages of %migration',
|
||||
['%migration' => $migration->label()]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -211,6 +211,9 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
|
||||
$row = [];
|
||||
$row[] = ['data' => Html::escape($destination_id)];
|
||||
if (isset($process_line[0]['source'])) {
|
||||
if (is_array($process_line[0]['source'])) {
|
||||
$process_line[0]['source'] = implode(', ', $process_line[0]['source']);
|
||||
}
|
||||
$row[] = ['data' => Xss::filterAdmin($process_line[0]['source'])];
|
||||
}
|
||||
else {
|
||||
|
||||
+1
-1
@@ -142,7 +142,7 @@ class MigrationListBuilder extends ConfigEntityListBuilder implements EntityHand
|
||||
try {
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance($migration_entity->id());
|
||||
$migration_group = $migration->get('migration_group');
|
||||
$migration_group = $migration_entity->get('migration_group');
|
||||
if (!$migration_group) {
|
||||
$migration_group = 'default';
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class MigrationDeleteForm extends EntityConfirmFormBase {
|
||||
$this->entity->delete();
|
||||
|
||||
// Set a message that the entity was deleted.
|
||||
drupal_set_message(t('Migration %label was deleted.', [
|
||||
$this->messenger()->addStatus($this->t('Migration %label was deleted.', [
|
||||
'%label' => $this->entity->label(),
|
||||
]));
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class MigrationEditForm extends MigrationFormBase {
|
||||
*/
|
||||
public function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = t('Update Migration');
|
||||
$actions['submit']['#value'] = $this->t('Update Migration');
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
@@ -67,24 +67,24 @@ class MigrationExecuteForm extends FormBase {
|
||||
// Build the 'Update options' form.
|
||||
$form = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Operations'),
|
||||
'#title' => $this->t('Operations'),
|
||||
];
|
||||
$options = [
|
||||
'import' => t('Import'),
|
||||
'rollback' => t('Rollback'),
|
||||
'stop' => t('Stop'),
|
||||
'reset' => t('Reset'),
|
||||
'import' => $this->t('Import'),
|
||||
'rollback' => $this->t('Rollback'),
|
||||
'stop' => $this->t('Stop'),
|
||||
'reset' => $this->t('Reset'),
|
||||
];
|
||||
$form['operation'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Choose an operation to run'),
|
||||
'#title' => $this->t('Choose an operation to run'),
|
||||
'#options' => $options,
|
||||
'#default_value' => 'import',
|
||||
'#required' => TRUE,
|
||||
];
|
||||
$form['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Execute'),
|
||||
'#value' => $this->t('Execute'),
|
||||
];
|
||||
$definitions = [];
|
||||
$definitions[] = $this->t('Import: Imports all previously unprocessed records from the source, plus any records marked for update, into destination Drupal objects.');
|
||||
@@ -100,26 +100,31 @@ class MigrationExecuteForm extends FormBase {
|
||||
|
||||
$form['options'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Options'),
|
||||
'#title' => $this->t('Options'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
];
|
||||
$form['options']['update'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Update'),
|
||||
'#description' => t('Check this box to update all previously-imported content
|
||||
'#title' => $this->t('Update'),
|
||||
'#description' => $this->t('Check this box to update all previously-imported content
|
||||
in addition to importing new content. Leave unchecked to only import
|
||||
new content'),
|
||||
];
|
||||
$form['options']['force'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Ignore dependencies'),
|
||||
'#description' => t('Check this box to ignore dependencies when running imports
|
||||
'#title' => $this->t('Ignore dependencies'),
|
||||
'#description' => $this->t('Check this box to ignore dependencies when running imports
|
||||
- all tasks will run whether or not their dependent tasks have
|
||||
completed.'),
|
||||
];
|
||||
// @TODO: Limit is not working. Perhaps because of batch? See
|
||||
// https://www.drupal.org/project/migrate_tools/issues/2924298.
|
||||
$form['options']['limit'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Limit to:'),
|
||||
'#size' => 10,
|
||||
'#description' => $this->t('Set a limit of how many items to process for each migration task.'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ class MigrationFormBase extends EntityForm {
|
||||
foreach ($groups as $group) {
|
||||
$group_options[$group->id()] = $group->label();
|
||||
}
|
||||
if (!$migration->get('migration_group') && isset($group_options['default'])) {
|
||||
if (!$migration->migration_group && isset($group_options['default'])) {
|
||||
$migration->set('migration_group', 'default');
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ class MigrationFormBase extends EntityForm {
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Migration Group'),
|
||||
'#empty_value' => '',
|
||||
'#default_value' => $migration->get('migration_group'),
|
||||
'#default_value' => $migration->migration_group,
|
||||
'#options' => $group_options,
|
||||
'#description' => $this->t('Assign this migration to an existing group.'),
|
||||
];
|
||||
@@ -150,7 +150,7 @@ class MigrationFormBase extends EntityForm {
|
||||
* An array of supported actions for the current entity form.
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
// Get the basic actins from the base class.
|
||||
// Get the basic actions from the base class.
|
||||
$actions = parent::actions($form, $form_state);
|
||||
|
||||
// Change the submit button text.
|
||||
@@ -169,11 +169,11 @@ class MigrationFormBase extends EntityForm {
|
||||
|
||||
if ($status == SAVED_UPDATED) {
|
||||
// If we edited an existing entity...
|
||||
drupal_set_message($this->t('Migration %label has been updated.', ['%label' => $migration->label()]));
|
||||
$this->messenger()->addStatus($this->t('Migration %label has been updated.', ['%label' => $migration->label()]));
|
||||
}
|
||||
else {
|
||||
// If we created a new entity...
|
||||
drupal_set_message($this->t('Migration %label has been added.', ['%label' => $migration->label()]));
|
||||
$this->messenger()->addStatus($this->t('Migration %label has been added.', ['%label' => $migration->label()]));
|
||||
}
|
||||
|
||||
// Redirect the user back to the listing route after the save operation.
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ class MigrationGroupDeleteForm extends EntityConfirmFormBase {
|
||||
$this->entity->delete();
|
||||
|
||||
// Set a message that the entity was deleted.
|
||||
drupal_set_message(t('Migration group %label was deleted.', [
|
||||
$this->messenger()->addStatus($this->t('Migration group %label was deleted.', [
|
||||
'%label' => $this->entity->label(),
|
||||
]));
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class MigrationGroupEditForm extends MigrationGroupFormBase {
|
||||
*/
|
||||
public function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = t('Update Migration Group');
|
||||
$actions['submit']['#value'] = $this->t('Update Migration Group');
|
||||
return $actions;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,11 +158,11 @@ class MigrationGroupFormBase extends EntityForm {
|
||||
|
||||
if ($status == SAVED_UPDATED) {
|
||||
// If we edited an existing entity...
|
||||
drupal_set_message($this->t('Migration group %label has been updated.', ['%label' => $migration_group->label()]));
|
||||
$this->messenger()->addStatus($this->t('Migration group %label has been updated.', ['%label' => $migration_group->label()]));
|
||||
}
|
||||
else {
|
||||
// If we created a new entity...
|
||||
drupal_set_message($this->t('Migration group %label has been added.', ['%label' => $migration_group->label()]));
|
||||
$this->messenger()->addStatus($this->t('Migration group %label has been added.', ['%label' => $migration_group->label()]));
|
||||
}
|
||||
|
||||
// Redirect the user back to the listing route after the save operation.
|
||||
|
||||
@@ -25,11 +25,11 @@ use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
|
||||
* 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
|
||||
* private migrate_tools 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
|
||||
* An additional list of all changed migration id is kept in the store, in the
|
||||
* key 'migrations_changed'
|
||||
*
|
||||
* Private Store Usage:
|
||||
@@ -38,7 +38,6 @@ use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
|
||||
* [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
|
||||
@@ -50,22 +49,19 @@ use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
|
||||
* property 2 => label 2
|
||||
* column_index2
|
||||
* property 1 => label 1
|
||||
* @endcode
|
||||
*
|
||||
* Example source configuration.
|
||||
* @code
|
||||
* Example source configuration:
|
||||
* custom_migration
|
||||
* original
|
||||
* 2
|
||||
* title => title
|
||||
* 3
|
||||
* body => foo
|
||||
* updated
|
||||
* 8
|
||||
* title => new_title
|
||||
* 9
|
||||
* body => new_body
|
||||
* @endcode
|
||||
* original
|
||||
* 2
|
||||
* title => title
|
||||
* 3
|
||||
* body => foo
|
||||
* updated
|
||||
* 8
|
||||
* title => new_title
|
||||
* 9
|
||||
* body => new_body
|
||||
*/
|
||||
class SourceCsvForm extends FormBase {
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
|
||||
/**
|
||||
* Class to filter ID map by an ID list.
|
||||
*/
|
||||
class IdMapFilter extends \FilterIterator {
|
||||
|
||||
/**
|
||||
* List of specific source IDs to import.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $idList;
|
||||
|
||||
/**
|
||||
* IdMapFilter constructor.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map
|
||||
* The ID map.
|
||||
* @param array $id_list
|
||||
* The id list to use in the filter.
|
||||
*/
|
||||
public function __construct(MigrateIdMapInterface $id_map, array $id_list) {
|
||||
parent::__construct($id_map);
|
||||
$this->idList = $id_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function accept() {
|
||||
// Row is included.
|
||||
if (empty($this->idList) || in_array(array_values($this->getInnerIterator()->currentSource()), $this->idList)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,15 +2,16 @@
|
||||
|
||||
namespace Drupal\migrate_tools;
|
||||
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\migrate\MigrateMessage;
|
||||
use Drupal\migrate\MigrateMessageInterface;
|
||||
use Drupal\migrate\Plugin\Migration;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Defines a migrate executable class for batch migrations through UI.
|
||||
*/
|
||||
class MigrateBatchExecutable extends MigrateExecutable {
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Representing a batch import operation.
|
||||
@@ -98,10 +99,10 @@ class MigrateBatchExecutable extends MigrateExecutable {
|
||||
if (count($operations) > 0) {
|
||||
$batch = [
|
||||
'operations' => $operations,
|
||||
'title' => t('Migrating %migrate', ['%migrate' => $this->migration->label()]),
|
||||
'init_message' => t('Start migrating %migrate', ['%migrate' => $this->migration->label()]),
|
||||
'progress_message' => t('Migrating %migrate', ['%migrate' => $this->migration->label()]),
|
||||
'error_message' => t('An error occurred while migrating %migrate.', ['%migrate' => $this->migration->label()]),
|
||||
'title' => $this->t('Migrating %migrate', ['%migrate' => $this->migration->label()]),
|
||||
'init_message' => $this->t('Start migrating %migrate', ['%migrate' => $this->migration->label()]),
|
||||
'progress_message' => $this->t('Migrating %migrate', ['%migrate' => $this->migration->label()]),
|
||||
'error_message' => $this->t('An error occurred while migrating %migrate.', ['%migrate' => $this->migration->label()]),
|
||||
'finished' => '\Drupal\migrate_tools\MigrateBatchExecutable::batchFinishedImport',
|
||||
];
|
||||
|
||||
@@ -181,6 +182,12 @@ class MigrateBatchExecutable extends MigrateExecutable {
|
||||
$message = new MigrateMessage();
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = \Drupal::getContainer()->get('plugin.manager.migration')->createInstance($migration_id);
|
||||
|
||||
// Each batch run we need to reinitialize the counter for the migration.
|
||||
if (!empty($options['limit']) && isset($context['results'][$migration->id()]['@numitems'])) {
|
||||
$options['limit'] = $options['limit'] - $context['results'][$migration->id()]['@numitems'];
|
||||
}
|
||||
|
||||
$executable = new MigrateBatchExecutable($migration, $message, $options);
|
||||
|
||||
if (empty($context['sandbox']['total'])) {
|
||||
@@ -249,7 +256,7 @@ class MigrateBatchExecutable extends MigrateExecutable {
|
||||
foreach ($results as $migration_id => $result) {
|
||||
$singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
|
||||
$plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
|
||||
drupal_set_message(\Drupal::translation()->formatPlural($result['@numitems'],
|
||||
\Drupal::messenger()->addStatus(\Drupal::translation()->formatPlural($result['@numitems'],
|
||||
$singular_message,
|
||||
$plural_message,
|
||||
$result));
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
|
||||
namespace Drupal\migrate_tools;
|
||||
|
||||
use Drupal\migrate\Event\MigrateEvents;
|
||||
use Drupal\migrate\Event\MigrateImportEvent;
|
||||
use Drupal\migrate\Event\MigrateMapDeleteEvent;
|
||||
use Drupal\migrate\Event\MigrateMapSaveEvent;
|
||||
use Drupal\migrate\Event\MigratePreRowSaveEvent;
|
||||
use Drupal\migrate\Event\MigrateRollbackEvent;
|
||||
use Drupal\migrate\Event\MigrateRowDeleteEvent;
|
||||
use Drupal\migrate\MigrateExecutable as MigrateExecutableBase;
|
||||
use Drupal\migrate\MigrateMessageInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\MigrateSkipRowException;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Event\MigrateEvents;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate_plus\Event\MigrateEvents as MigratePlusEvents;
|
||||
use Drupal\migrate\Event\MigrateMapSaveEvent;
|
||||
use Drupal\migrate\Event\MigrateMapDeleteEvent;
|
||||
use Drupal\migrate\Event\MigrateImportEvent;
|
||||
use Drupal\migrate_plus\Event\MigratePrepareRowEvent;
|
||||
|
||||
/**
|
||||
@@ -104,14 +104,7 @@ class MigrateExecutable extends MigrateExecutableBase {
|
||||
if (isset($options['feedback'])) {
|
||||
$this->feedback = $options['feedback'];
|
||||
}
|
||||
if (isset($options['idlist'])) {
|
||||
if (is_string($options['idlist'])) {
|
||||
$this->idlist = explode(',', $options['idlist']);
|
||||
array_walk($this->idlist, function (&$value, $key) {
|
||||
$value = explode(':', $value);
|
||||
});
|
||||
}
|
||||
}
|
||||
$this->idlist = MigrateTools::buildIdList($options);
|
||||
|
||||
$this->listeners[MigrateEvents::MAP_SAVE] = [$this, 'onMapSave'];
|
||||
$this->listeners[MigrateEvents::MAP_DELETE] = [$this, 'onMapDelete'];
|
||||
@@ -294,6 +287,8 @@ class MigrateExecutable extends MigrateExecutableBase {
|
||||
* The map event.
|
||||
*/
|
||||
public function onPostRollback(MigrateRollbackEvent $event) {
|
||||
$migrate_last_imported_store = \Drupal::keyValue('migrate_last_imported');
|
||||
$migrate_last_imported_store->set($event->getMigration()->id(), FALSE);
|
||||
$this->rollbackMessage();
|
||||
$this->removeListeners();
|
||||
}
|
||||
@@ -363,6 +358,8 @@ class MigrateExecutable extends MigrateExecutableBase {
|
||||
* @throws \Drupal\migrate\MigrateSkipRowException
|
||||
*/
|
||||
public function onPrepareRow(MigratePrepareRowEvent $event) {
|
||||
// TODO: remove after 8.6 suppor is sunset.
|
||||
// @see https://www.drupal.org/project/migrate_tools/issues/3008316
|
||||
if (!empty($this->idlist)) {
|
||||
$row = $event->getRow();
|
||||
// TODO: replace for $source_id = $row->getSourceIdValues();
|
||||
@@ -378,7 +375,7 @@ class MigrateExecutable extends MigrateExecutableBase {
|
||||
}
|
||||
}
|
||||
if ($skip) {
|
||||
throw new MigrateSkipRowException(NULL, FALSE);
|
||||
throw new MigrateSkipRowException('Skipped due to idlist.', FALSE);
|
||||
}
|
||||
}
|
||||
if ($this->feedback && ($this->counter) && $this->counter % $this->feedback == 0) {
|
||||
@@ -389,7 +386,20 @@ class MigrateExecutable extends MigrateExecutableBase {
|
||||
if ($this->itemLimit && ($this->itemLimitCounter + 1) >= $this->itemLimit) {
|
||||
$event->getMigration()->interruptMigration(MigrationInterface::RESULT_COMPLETED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSource() {
|
||||
return new SourceFilter(parent::getSource(), $this->idlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getIdMap() {
|
||||
return new IdMapFilter(parent::getIdMap(), $this->idlist);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools;
|
||||
|
||||
/**
|
||||
* Utility functionality for use in migrate_tools.
|
||||
*/
|
||||
class MigrateTools {
|
||||
|
||||
/**
|
||||
* Build the list of specific source IDs to import.
|
||||
*
|
||||
* @param array $options
|
||||
* The migration executable options.
|
||||
*
|
||||
* @return array
|
||||
* The ID list.
|
||||
*/
|
||||
public static function buildIdList(array $options) {
|
||||
$options += [
|
||||
'idlist' => NULL,
|
||||
'idlist-delimiter' => ':',
|
||||
];
|
||||
$id_list = [];
|
||||
if ($options['idlist']) {
|
||||
$id_list = explode(',', $options['idlist']);
|
||||
array_walk($id_list, function (&$value) use ($options) {
|
||||
$value = str_getcsv($value, $options['idlist-delimiter']);
|
||||
});
|
||||
}
|
||||
return $id_list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools;
|
||||
|
||||
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
|
||||
use Drupal\migrate\Plugin\MigrateSourceInterface;
|
||||
|
||||
/**
|
||||
* Class to filter source by an ID list.
|
||||
*/
|
||||
class SourceFilter extends \FilterIterator {
|
||||
|
||||
/**
|
||||
* List of specific source IDs to import.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $idList;
|
||||
|
||||
/**
|
||||
* SourceFilter constructor.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrateSourceInterface $source
|
||||
* The ID map.
|
||||
* @param array $id_list
|
||||
* The id list to use in the filter.
|
||||
*/
|
||||
public function __construct(MigrateSourceInterface $source, array $id_list) {
|
||||
parent::__construct($source);
|
||||
$this->idList = $id_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function accept() {
|
||||
// No idlist filtering, don't filter.
|
||||
if (empty($this->idList)) {
|
||||
return TRUE;
|
||||
}
|
||||
// Some source plugins do not extend SourcePluginBase. These cannot be
|
||||
// filtered so warn and return all values.
|
||||
if (!$this->getInnerIterator() instanceof SourcePluginBase) {
|
||||
trigger_error(sprintf('The source plugin %s is not an instance of %s. Extend from %s to support idlist filtering.', $this->getInnerIterator()->getPluginId(), SourcePluginBase::class, SourcePluginBase::class));
|
||||
return TRUE;
|
||||
}
|
||||
// Row is included.
|
||||
if (in_array(array_values($this->getInnerIterator()->getCurrentIds()), $this->idList)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -8,8 +8,8 @@ dependencies:
|
||||
- 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'
|
||||
# Information added by Drupal.org packaging script on 2019-01-07
|
||||
version: '8.x-4.1'
|
||||
core: '8.x'
|
||||
project: 'migrate_tools'
|
||||
datestamp: 1535380087
|
||||
datestamp: 1546879109
|
||||
|
||||
+3
-3
@@ -7,8 +7,8 @@ dependencies:
|
||||
- drupal:migrate (>=8.3)
|
||||
- migrate_plus:migrate_plus
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-08-27
|
||||
version: '8.x-4.0'
|
||||
# Information added by Drupal.org packaging script on 2019-01-07
|
||||
version: '8.x-4.1'
|
||||
core: '8.x'
|
||||
project: 'migrate_tools'
|
||||
datestamp: 1535380087
|
||||
datestamp: 1546879109
|
||||
|
||||
+5
-3
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\Tests\migrate_tools\Functional;
|
||||
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
@@ -11,6 +12,7 @@ use Drupal\Tests\BrowserTestBase;
|
||||
* @group migrate_tools
|
||||
*/
|
||||
class MigrateExecutionFormTest extends BrowserTestBase {
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -77,21 +79,21 @@ class MigrateExecutionFormTest extends BrowserTestBase {
|
||||
$edit = [
|
||||
'operation' => 'import',
|
||||
];
|
||||
$this->drupalPostForm($urlPath, $edit, t('Execute'));
|
||||
$this->drupalPostForm($urlPath, $edit, $this->t('Execute'));
|
||||
$real_count = $this->vocabularyQuery->count()->execute();
|
||||
$expected_count = 3;
|
||||
$this->assertEquals($expected_count, $real_count);
|
||||
$edit = [
|
||||
'operation' => 'rollback',
|
||||
];
|
||||
$this->drupalPostForm($urlPath, $edit, t('Execute'));
|
||||
$this->drupalPostForm($urlPath, $edit, $this->t('Execute'));
|
||||
$real_count = $this->vocabularyQuery->count()->execute();
|
||||
$expected_count = 0;
|
||||
$this->assertEquals($expected_count, $real_count);
|
||||
$edit = [
|
||||
'operation' => 'import',
|
||||
];
|
||||
$this->drupalPostForm($urlPath, $edit, t('Execute'));
|
||||
$this->drupalPostForm($urlPath, $edit, $this->t('Execute'));
|
||||
$real_count = $this->vocabularyQuery->count()->execute();
|
||||
$expected_count = 3;
|
||||
$this->assertEquals($expected_count, $real_count);
|
||||
|
||||
+9
-7
@@ -4,6 +4,7 @@ namespace Drupal\Tests\migrate_tools\Functional;
|
||||
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\taxonomy\VocabularyInterface;
|
||||
@@ -16,6 +17,7 @@ use Drupal\taxonomy\VocabularyInterface;
|
||||
* @group migrate_tools
|
||||
*/
|
||||
class SourceCsvFormTest extends BrowserTestBase {
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Temporary store for column assignment changes.
|
||||
@@ -134,7 +136,7 @@ EOD;
|
||||
'edit-name' => 1,
|
||||
'edit-description' => 1,
|
||||
];
|
||||
$this->drupalPostForm($editUrlPath, $edit, t('Submit'));
|
||||
$this->drupalPostForm($editUrlPath, $edit, $this->t('Submit'));
|
||||
$session->responseContains('Source properties can not share the same source column.');
|
||||
$this->assertTrue($session->optionExists('edit-vid', 'description')
|
||||
->isSelected());
|
||||
@@ -149,7 +151,7 @@ EOD;
|
||||
'edit-name' => 0,
|
||||
'edit-description' => 1,
|
||||
];
|
||||
$this->drupalPostForm($editUrlPath, $edit, t('Submit'));
|
||||
$this->drupalPostForm($editUrlPath, $edit, $this->t('Submit'));
|
||||
$this->assertTrue($session->optionExists('edit-vid', 'weight')
|
||||
->isSelected());
|
||||
$this->assertTrue($session->optionExists('edit-name', 'vid')
|
||||
@@ -184,22 +186,22 @@ EOD;
|
||||
$edit = [
|
||||
'operation' => 'import',
|
||||
];
|
||||
$this->drupalPostForm($executeUrlPath, $edit, t('Execute'));
|
||||
$this->drupalPostForm($executeUrlPath, $edit, $this->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'));
|
||||
$this->drupalPostForm($executeUrlPath, $edit, $this->t('Execute'));
|
||||
|
||||
// Restore to an order that will succesfully migrate.
|
||||
// Restore to an order that will successfully migrate.
|
||||
$edit = [
|
||||
'edit-vid' => 0,
|
||||
'edit-name' => 1,
|
||||
'edit-description' => 2,
|
||||
];
|
||||
$this->drupalPostForm($editUrlPath, $edit, t('Submit'));
|
||||
$this->drupalPostForm($editUrlPath, $edit, $this->t('Submit'));
|
||||
$this->assertTrue($session->optionExists('edit-vid', 'vid')
|
||||
->isSelected());
|
||||
$this->assertTrue($session->optionExists('edit-name', 'name')
|
||||
@@ -212,7 +214,7 @@ EOD;
|
||||
'operation' => 'import',
|
||||
];
|
||||
drupal_flush_all_caches();
|
||||
$this->drupalPostForm($executeUrlPath, $edit, t('Execute'));
|
||||
$this->drupalPostForm($executeUrlPath, $edit, $this->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');
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_tools\Kernel;
|
||||
|
||||
use Drupal\migrate_tools\Commands\MigrateToolsCommands;
|
||||
use Drupal\Tests\migrate\Kernel\MigrateTestBase;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Tests for the Drush 9 commands.
|
||||
*
|
||||
* @group migrate_tools
|
||||
*/
|
||||
class DrushTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'migrate_tools_test',
|
||||
'migrate_tools',
|
||||
'migrate_plus',
|
||||
'taxonomy',
|
||||
'text',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* Base options array for import.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $importBaseOptions = [
|
||||
'all' => NULL,
|
||||
'group' => NULL,
|
||||
'tag' => NULL,
|
||||
'limit' => NULL,
|
||||
'feedback' => NULL,
|
||||
'idlist' => NULL,
|
||||
'idlist-delimiter' => ':',
|
||||
'update' => NULL,
|
||||
'force' => NULL,
|
||||
'execute-dependencies' => NULL,
|
||||
];
|
||||
|
||||
/**
|
||||
* The Migrate Tools Command drush service.
|
||||
*
|
||||
* @var \Drupal\migrate_tools\Commands\MigrateToolsCommands
|
||||
*/
|
||||
protected $commands;
|
||||
|
||||
/**
|
||||
* The migration plugin manager.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
|
||||
*/
|
||||
protected $migrationPluginManager;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var \Drupal\Core\Logger\LoggerChannelInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig('migrate_plus');
|
||||
$this->installConfig('migrate_tools_test');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installSchema('system', ['key_value', 'key_value_expire']);
|
||||
$this->migrationPluginManager = $this->container->get('plugin.manager.migration');
|
||||
$this->logger = $this->container->get('logger.channel.migrate_tools');
|
||||
$this->commands = new MigrateToolsCommands(
|
||||
$this->migrationPluginManager,
|
||||
$this->container->get('date.formatter'),
|
||||
$this->container->get('entity_type.manager'),
|
||||
$this->container->get('keyvalue'));
|
||||
$this->commands->setLogger($this->logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests drush ms.
|
||||
*/
|
||||
public function testStatus() {
|
||||
$this->executeMigration('fruit_terms');
|
||||
/** @var \Consolidation\OutputFormatters\StructuredData\RowsOfFields $result */
|
||||
$result = $this->commands->status('fruit_terms');
|
||||
$rows = $result->getArrayCopy();
|
||||
$this->assertSame(1, count($rows));
|
||||
$row = reset($rows);
|
||||
$this->assertSame('fruit_terms', $row['id']);
|
||||
$this->assertSame(3, $row['total']);
|
||||
$this->assertSame(3, $row['imported']);
|
||||
$this->assertSame('Idle', $row['status']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests drush mim.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
*/
|
||||
public function testImport() {
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance('fruit_terms');
|
||||
$id_map = $migration->getIdMap();
|
||||
$this->commands->import('fruit_terms', ['idlist' => 'Apple'] + $this->importBaseOptions);
|
||||
$this->assertSame(1, $id_map->importedCount());
|
||||
$this->commands->import('fruit_terms');
|
||||
$this->assertSame(3, $id_map->importedCount());
|
||||
$this->commands->import('fruit_terms', ['idlist' => 'Apple', 'update' => TRUE] + $this->importBaseOptions);
|
||||
$this->assertSame(0, count($id_map->getRowsNeedingUpdate(100)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests drush mmsg.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
*/
|
||||
public function testMessages() {
|
||||
$this->executeMigration('fruit_terms');
|
||||
$message = $this->getRandomGenerator()->string(16);
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance('fruit_terms');
|
||||
$id_map = $migration->getIdMap();
|
||||
$id_map->saveMessage(['name' => 'Apple'], $message);
|
||||
/** @var \Consolidation\OutputFormatters\StructuredData\RowsOfFields $result */
|
||||
$result = $this->commands->messages('fruit_terms');
|
||||
$rows = $result->getArrayCopy();
|
||||
$this->assertSame($message, $rows[0]['message']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests drush mr.
|
||||
*/
|
||||
public function testRollback() {
|
||||
$this->executeMigration('fruit_terms');
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance('fruit_terms');
|
||||
$id_map = $migration->getIdMap();
|
||||
$this->assertSame(3, $id_map->importedCount());
|
||||
$this->commands->rollback('fruit_terms');
|
||||
$this->assertSame(0, $id_map->importedCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests drush mrs.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
*/
|
||||
public function testReset() {
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance('fruit_terms');
|
||||
$migration->setStatus(MigrationInterface::STATUS_IMPORTING);
|
||||
$this->assertSame('Importing', $this->commands->status('fruit_terms')->getArrayCopy()[0]['status']);
|
||||
$this->commands->resetStatus('fruit_terms');
|
||||
$this->assertSame(MigrationInterface::STATUS_IDLE, $migration->getStatus());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests drush mst.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
*/
|
||||
public function testStop() {
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->migrationPluginManager->createInstance('fruit_terms');
|
||||
$migration->setStatus(MigrationInterface::STATUS_IMPORTING);
|
||||
$this->commands->stop('fruit_terms');
|
||||
$this->assertSame(MigrationInterface::STATUS_STOPPING, $migration->getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests drush mfs.
|
||||
*/
|
||||
public function testFieldsSource() {
|
||||
/** @var \Consolidation\OutputFormatters\StructuredData\RowsOfFields $result */
|
||||
$result = $this->commands->fieldsSource('fruit_terms');
|
||||
$rows = $result->getArrayCopy();
|
||||
$this->assertSame(1, count($rows));
|
||||
$this->assertSame('name', $rows[0]['machine_name']);
|
||||
$this->assertSame('name', $rows[0]['description']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Drupal\migrate_tools\Commands;
|
||||
|
||||
/**
|
||||
* Stub for drush_op.
|
||||
*
|
||||
* @param callable $callable
|
||||
* The function to call.
|
||||
*/
|
||||
function drush_op(callable $callable) {
|
||||
$args = func_get_args();
|
||||
array_shift($args);
|
||||
call_user_func_array($callable, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub for dt().
|
||||
*
|
||||
* @param string $text
|
||||
* The text.
|
||||
*
|
||||
* @return string
|
||||
* The text.
|
||||
*/
|
||||
function dt($text) {
|
||||
return $text;
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_tools\Kernel;
|
||||
|
||||
use Drupal\migrate_tools\MigrateExecutable;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\Tests\migrate\Kernel\MigrateTestBase;
|
||||
|
||||
/**
|
||||
* Tests imports.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateImportTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field', 'taxonomy', 'text', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('taxonomy_vocabulary');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installConfig(['taxonomy']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rolling back configuration and content entities.
|
||||
*/
|
||||
public function testImport() {
|
||||
// We use vocabularies to demonstrate importing and rolling back
|
||||
// configuration entities.
|
||||
$vocabulary_data_rows = [
|
||||
['id' => '1', 'name' => 'categories', 'weight' => '2'],
|
||||
['id' => '2', 'name' => 'tags', 'weight' => '1'],
|
||||
];
|
||||
$ids = ['id' => ['type' => 'integer']];
|
||||
$definition = [
|
||||
'id' => 'vocabularies',
|
||||
'migration_tags' => ['Import and rollback test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $vocabulary_data_rows,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [
|
||||
'vid' => 'id',
|
||||
'name' => 'name',
|
||||
'weight' => 'weight',
|
||||
],
|
||||
'destination' => ['plugin' => 'entity:taxonomy_vocabulary'],
|
||||
];
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $vocabulary_migration */
|
||||
$vocabulary_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
$vocabulary_id_map = $vocabulary_migration->getIdMap();
|
||||
|
||||
// Test id list import.
|
||||
$executable = new MigrateExecutable($vocabulary_migration, $this, ['idlist' => 2]);
|
||||
$executable->import();
|
||||
|
||||
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
|
||||
$vocabulary = Vocabulary::load(1);
|
||||
$this->assertFalse($vocabulary);
|
||||
$map_row = $vocabulary_id_map->getRowBySource(['id' => 1]);
|
||||
$this->assertFalse($map_row);
|
||||
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
|
||||
$vocabulary = Vocabulary::load(2);
|
||||
$this->assertTrue($vocabulary);
|
||||
$map_row = $vocabulary_id_map->getRowBySource(['id' => 2]);
|
||||
$this->assertNotNull($map_row['destid1']);
|
||||
}
|
||||
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_tools\Kernel;
|
||||
|
||||
use Drupal\migrate_tools\MigrateExecutable;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\Tests\migrate\Kernel\MigrateTestBase;
|
||||
|
||||
/**
|
||||
* Tests rolling back of imports.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateRollbackTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field', 'taxonomy', 'text', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('taxonomy_vocabulary');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installConfig(['taxonomy']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rolling back configuration and content entities.
|
||||
*/
|
||||
public function testRollback() {
|
||||
// We use vocabularies to demonstrate importing and rolling back
|
||||
// configuration entities.
|
||||
$vocabulary_data_rows = [
|
||||
['id' => '1', 'name' => 'categories', 'weight' => '2'],
|
||||
['id' => '2', 'name' => 'tags', 'weight' => '1'],
|
||||
];
|
||||
$ids = ['id' => ['type' => 'integer']];
|
||||
$definition = [
|
||||
'id' => 'vocabularies',
|
||||
'migration_tags' => ['Import and rollback test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $vocabulary_data_rows,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [
|
||||
'vid' => 'id',
|
||||
'name' => 'name',
|
||||
'weight' => 'weight',
|
||||
],
|
||||
'destination' => ['plugin' => 'entity:taxonomy_vocabulary'],
|
||||
];
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $vocabulary_migration */
|
||||
$vocabulary_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
$vocabulary_id_map = $vocabulary_migration->getIdMap();
|
||||
|
||||
// Import and validate vocabulary config entities were created.
|
||||
$executable = new MigrateExecutable($vocabulary_migration, $this, []);
|
||||
$executable->import();
|
||||
foreach ($vocabulary_data_rows as $row) {
|
||||
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
|
||||
$vocabulary = Vocabulary::load($row['id']);
|
||||
$this->assertTrue($vocabulary);
|
||||
$map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]);
|
||||
$this->assertNotNull($map_row['destid1']);
|
||||
}
|
||||
|
||||
// Test id list rollback.
|
||||
$rollback_executable = new MigrateExecutable($vocabulary_migration, $this, ['idlist' => 1]);
|
||||
$rollback_executable->rollback();
|
||||
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
|
||||
$vocabulary = Vocabulary::load(1);
|
||||
$this->assertFalse($vocabulary);
|
||||
$map_row = $vocabulary_id_map->getRowBySource(['id' => 1]);
|
||||
$this->assertFalse($map_row);
|
||||
|
||||
// TODO: remove after 8.6 is sunset.
|
||||
// @see https://www.drupal.org/project/migrate_tools/issues/3008316
|
||||
include_once $this->root . '/core/includes/install.core.inc';
|
||||
$version = _install_get_version_info(\Drupal::VERSION);
|
||||
if ($version['minor'] == 6) {
|
||||
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
|
||||
$vocabulary = Vocabulary::load(1);
|
||||
$this->assertFalse($vocabulary);
|
||||
$map_row = $vocabulary_id_map->getRowBySource(['id' => 1]);
|
||||
$this->assertFalse($map_row);
|
||||
}
|
||||
else {
|
||||
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
|
||||
$vocabulary = Vocabulary::load(2);
|
||||
$this->assertTrue($vocabulary);
|
||||
$map_row = $vocabulary_id_map->getRowBySource(['id' => 2]);
|
||||
$this->assertNotNull($map_row['destid1']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate_tools\Unit;
|
||||
|
||||
use Drupal\migrate_tools\MigrateTools;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate_tools\MigrateTools
|
||||
* @group migrate_tools
|
||||
*/
|
||||
class MigrateToolsTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::buildIdList
|
||||
*
|
||||
* @dataProvider dataProviderIdList
|
||||
*/
|
||||
public function testBuildIdList(array $options, array $expected) {
|
||||
$results = MigrateTools::buildIdList($options);
|
||||
$this->assertEquals($results, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testBuildIdList.
|
||||
*/
|
||||
public function dataProviderIdList() {
|
||||
$cases = [];
|
||||
$cases[] = [
|
||||
'options' => [],
|
||||
'expected' => [],
|
||||
];
|
||||
$cases['single id'] = [
|
||||
'options' => [
|
||||
'idlist' => 123,
|
||||
],
|
||||
'expected' => [[123]],
|
||||
];
|
||||
$cases['multiple ids'] = [
|
||||
'options' => [
|
||||
'idlist' => '123, 456',
|
||||
],
|
||||
'expected' => [
|
||||
[123], [456],
|
||||
],
|
||||
];
|
||||
$cases['default delimiter, composite key'] = [
|
||||
'options' => [
|
||||
'idlist' => '123:456',
|
||||
],
|
||||
'expected' => [
|
||||
[123, 456],
|
||||
],
|
||||
];
|
||||
$cases['special delimiter, single'] = [
|
||||
'options' => [
|
||||
'idlist' => '123:456',
|
||||
'idlist-delimiter' => '~',
|
||||
],
|
||||
'expected' => [
|
||||
['123:456'],
|
||||
],
|
||||
];
|
||||
$cases['special delimiter, multiple'] = [
|
||||
'options' => [
|
||||
'idlist' => '123:456~987:654',
|
||||
'idlist-delimiter' => '~',
|
||||
],
|
||||
'expected' => [
|
||||
['123:456', '987:654'],
|
||||
],
|
||||
];
|
||||
$cases['space delimiter, multiple'] = [
|
||||
'options' => [
|
||||
'idlist' => '123:456 987:654',
|
||||
'idlist-delimiter' => ' ',
|
||||
],
|
||||
'expected' => [
|
||||
['123:456', '987:654'],
|
||||
],
|
||||
];
|
||||
return $cases;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user