Score:0

How do I migrate a nested paragraph field?

fi flag

I have a wrapper paragraph, Layout (para_layout), which has the following fields.

enter image description here

I created the same paragraphs and field structure in a Drupal 9 website. I created the following custom migration plugin.

id: custom_para1
label: Custom paragraph migration
migration_group: custom
migration_tags:
  - node
source:
  plugin: d7_paragraphs_item
  bundle: para_layout
process:
  field_left_column: field_left_column
  field_right_column: field_right_column
destination:
  plugin: 'entity_reference_revisions:paragraph'
  default_bundle: para_layout

drush mim custom_para1 shows it's processing the items, but I couldn't see any data moved to the respective field tables.

It works fine when the fields are normal text fields instead of paragraph fields.

Kevin avatar
in flag
You have to migrate the paragraph data first, and then migrate their host records that the node refers to in order to connect the two.
ARUN avatar
fi flag
@apaderno, so the host records will be directly inserted through SQL or can it be also migrate through migrate yml?
apaderno avatar
us flag
You migrate the host records in the same way you migrate the paragraph data. I would wait for a comment from @Kevin, since the first comment was posted by him.
ARUN avatar
fi flag
@apaderno I tried to migrate the host entry but, the result is same. Drush show processed something but nothing shows in DB(I have already migrated paragraph contents). Should I change the 'destination plugin' to something else?
Score:3
fr flag

Here is how I was able to achieve it.

Paragraph Migration:

id: fup_balance_csv_import

....
....

process:
  field_upload: field_upload
  field_download: field_download
  field_total: field_total

destination:
  plugin: entity_reference_revisions:paragraph
  default_bundle: fup_balance

Node Migration:

id: subscription_list_csv_import

.............
........

process:
  # Paragraphs field.
  pseudo_field_fup_details:
    -
      plugin: migration_lookup
      migration: fup_balance_csv_import
      source: title # Unique idetifier.
  field_fup_details:
    -
      plugin: sub_process
      source:
        - '@pseudo_field_fup_details'
      process:
        target_id: '0'
        target_revision_id: '1'

This is by using the standard Migration Process. Sometimes it might not be helpful then you need to write your own migration plugin. Below is the way to do it.

# Paragraphs Field.
field_country_time_zones:
  -
    plugin: country_timezones_paragraphs
    source: 
      field_1: source_field_1
      field_2: source_field_2

Migration Plugin :

<?php

namespace Drupal\countries_list_migration\Plugin\migrate\process;

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\paragraphs\Entity\Paragraph;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;

/**
 * Provides a countries_timezones migration plugin.
 *
 * Usage:
 *
 * @code
 * process:
 *   bar:
 *     plugin: country_timezones_paragraphs
 *     source: source_field_name
 * @endcode
 *
 * @MigrateProcessPlugin(
 *   id = "country_timezones_paragraphs",
 *   handle_multiples = TRUE
 * )
 */
class CountryTimezonesParagraphs extends ProcessPluginBase implements ContainerFactoryPluginInterface {

  /**
   * Logger service.
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected $logger;

  /**
   * Constructs a CountriesTimezones plugin.
   *
   * @param array $configuration
   *   The plugin configuration.
   * @param string $plugin_id
   *   The plugin ID.
   * @param mixed $plugin_definition
   *   The plugin definition.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger
   *   The logger service.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerChannelFactoryInterface $logger) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->logger = $logger->get('countries_list_migration');
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('logger.factory')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {

  $paragraphs =[];
  
   if (isset($value)) {
      $paragraphs[] = $this->createCountryTimezonesParagraphsItem($value);
    }

    return $paragraphs;
  }

  /**
   * {@inheritdoc}
   */
  public function multiple(): bool {
    return TRUE;
  }


  protected function createCountryTimezonesParagraphsItem(array $item): array {

    $paragraph = Paragraph::create([

      'type' => 'country_timezones',

      'field_1' => [
        'value'  => $item['field_1'],
      ],
      'field_2' => [
        'value'  => $item['field_2'],
      ],
    ]);

    $paragraph->save();

    return [
      'target_id' => $paragraph->id(),
      'target_revision_id' => $paragraph->getRevisionId(),
    ];
  }

}

Hope its Helpful.

Score:0
in flag

As I said in the comment(s), the plugins currently ensure field collections are migrated to Paragraph bundles and same for d7 Paragraphs. Field data is not moved, this is a gap you currently need to fill yourself. So you have to write YAML migration definitions to do this.

I recently did a d7 to d9 migration that had this requirement (with Field Collections in the mix too that we ported).

You will need to create migration(s) with source plugins (the SQL queries that fetch the data you need to plug in) to do the following:

  1. Migrate all actual paragraphs and their field data
  2. Migrate paragraphs that hold paragraphs (holding the field data)
  3. Migrate the records of the entities that reference the paragraphs holding the paragraphs from step 2 (use migration_lookup to find their new destination ids in D9).
ARUN avatar
fi flag
It would be great if you could provide a sample yaml files to refer. As I already mentioned in the question the yaml that I tried is not working.
SomebodySysop avatar
gb flag
Agreed. Could anyone provide sample YAMLs of these steps just for clarification?
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.