From 6c8c545d75ee1946638323b97f13b93635eefc5e Mon Sep 17 00:00:00 2001 From: git <git@102134.no-reply.drupal.org> Date: Mon, 22 Oct 2018 13:37:57 -0500 Subject: [PATCH] Issue #2961431 by ressa: Migrate from local JSON file? --- migrate_json_example/README.md | 22 +++++ migrate_json_example/artifacts/products.json | 16 ++++ ...tity_form_display.node.product.default.yml | 88 +++++++++++++++++++ ...tity_view_display.node.product.default.yml | 49 +++++++++++ ...d.field.node.product.field_description.yml | 18 ++++ .../field.field.node.product.field_price.yml | 22 +++++ .../field.field.node.product.field_upc.yml | 22 +++++ .../field.storage.node.field_description.yml | 20 +++++ .../field.storage.node.field_price.yml | 17 ++++ .../optional/field.storage.node.field_upc.yml | 19 ++++ .../migrate_plus.migration.product.yml | 78 ++++++++++++++++ .../config/optional/node.type.product.yml | 17 ++++ .../migrate_json_example.info.yml | 9 ++ .../migrate_json_example.install | 20 +++++ 14 files changed, 417 insertions(+) create mode 100644 migrate_json_example/README.md create mode 100644 migrate_json_example/artifacts/products.json create mode 100644 migrate_json_example/config/optional/core.entity_form_display.node.product.default.yml create mode 100644 migrate_json_example/config/optional/core.entity_view_display.node.product.default.yml create mode 100644 migrate_json_example/config/optional/field.field.node.product.field_description.yml create mode 100644 migrate_json_example/config/optional/field.field.node.product.field_price.yml create mode 100644 migrate_json_example/config/optional/field.field.node.product.field_upc.yml create mode 100644 migrate_json_example/config/optional/field.storage.node.field_description.yml create mode 100644 migrate_json_example/config/optional/field.storage.node.field_price.yml create mode 100644 migrate_json_example/config/optional/field.storage.node.field_upc.yml create mode 100644 migrate_json_example/config/optional/migrate_plus.migration.product.yml create mode 100644 migrate_json_example/config/optional/node.type.product.yml create mode 100644 migrate_json_example/migrate_json_example.info.yml create mode 100644 migrate_json_example/migrate_json_example.install diff --git a/migrate_json_example/README.md b/migrate_json_example/README.md new file mode 100644 index 00000000..4736b985 --- /dev/null +++ b/migrate_json_example/README.md @@ -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 diff --git a/migrate_json_example/artifacts/products.json b/migrate_json_example/artifacts/products.json new file mode 100644 index 00000000..def2ddf2 --- /dev/null +++ b/migrate_json_example/artifacts/products.json @@ -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" + } + ] +} diff --git a/migrate_json_example/config/optional/core.entity_form_display.node.product.default.yml b/migrate_json_example/config/optional/core.entity_form_display.node.product.default.yml new file mode 100644 index 00000000..7faa1a2b --- /dev/null +++ b/migrate_json_example/config/optional/core.entity_form_display.node.product.default.yml @@ -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: { } diff --git a/migrate_json_example/config/optional/core.entity_view_display.node.product.default.yml b/migrate_json_example/config/optional/core.entity_view_display.node.product.default.yml new file mode 100644 index 00000000..9e7a9f23 --- /dev/null +++ b/migrate_json_example/config/optional/core.entity_view_display.node.product.default.yml @@ -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: { } diff --git a/migrate_json_example/config/optional/field.field.node.product.field_description.yml b/migrate_json_example/config/optional/field.field.node.product.field_description.yml new file mode 100644 index 00000000..7bbbd841 --- /dev/null +++ b/migrate_json_example/config/optional/field.field.node.product.field_description.yml @@ -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 diff --git a/migrate_json_example/config/optional/field.field.node.product.field_price.yml b/migrate_json_example/config/optional/field.field.node.product.field_price.yml new file mode 100644 index 00000000..df23a84a --- /dev/null +++ b/migrate_json_example/config/optional/field.field.node.product.field_price.yml @@ -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 diff --git a/migrate_json_example/config/optional/field.field.node.product.field_upc.yml b/migrate_json_example/config/optional/field.field.node.product.field_upc.yml new file mode 100644 index 00000000..ef0a28ec --- /dev/null +++ b/migrate_json_example/config/optional/field.field.node.product.field_upc.yml @@ -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 diff --git a/migrate_json_example/config/optional/field.storage.node.field_description.yml b/migrate_json_example/config/optional/field.storage.node.field_description.yml new file mode 100644 index 00000000..a5c39580 --- /dev/null +++ b/migrate_json_example/config/optional/field.storage.node.field_description.yml @@ -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 diff --git a/migrate_json_example/config/optional/field.storage.node.field_price.yml b/migrate_json_example/config/optional/field.storage.node.field_price.yml new file mode 100644 index 00000000..090acad3 --- /dev/null +++ b/migrate_json_example/config/optional/field.storage.node.field_price.yml @@ -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 diff --git a/migrate_json_example/config/optional/field.storage.node.field_upc.yml b/migrate_json_example/config/optional/field.storage.node.field_upc.yml new file mode 100644 index 00000000..149b11da --- /dev/null +++ b/migrate_json_example/config/optional/field.storage.node.field_upc.yml @@ -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 diff --git a/migrate_json_example/config/optional/migrate_plus.migration.product.yml b/migrate_json_example/config/optional/migrate_plus.migration.product.yml new file mode 100644 index 00000000..755cfbec --- /dev/null +++ b/migrate_json_example/config/optional/migrate_plus.migration.product.yml @@ -0,0 +1,78 @@ +# This migration demonstrates a simple import from a JSON file. +id: product +label: JSON feed of Products +migration_group: Product +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 diff --git a/migrate_json_example/config/optional/node.type.product.yml b/migrate_json_example/config/optional/node.type.product.yml new file mode 100644 index 00000000..18d8499d --- /dev/null +++ b/migrate_json_example/config/optional/node.type.product.yml @@ -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 diff --git a/migrate_json_example/migrate_json_example.info.yml b/migrate_json_example/migrate_json_example.info.yml new file mode 100644 index 00000000..ed4b8ef4 --- /dev/null +++ b/migrate_json_example/migrate_json_example.info.yml @@ -0,0 +1,9 @@ +type: module +name: Migrate JSON Example +description: 'Simple JSON Migration example' +package: Examples +core: 8.x +dependencies: + - drupal:migrate + - migrate_plus:migrate_plus + - migrate_plus:migrate_tools diff --git a/migrate_json_example/migrate_json_example.install b/migrate_json_example/migrate_json_example.install new file mode 100644 index 00000000..961a0a9e --- /dev/null +++ b/migrate_json_example/migrate_json_example.install @@ -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); +} -- GitLab