Version 1.x and 2.x solved nesting of paragraphs through a "container" paragraph types with sub-paragraphs (field_paragraphs_paragraphs). This wasn't perfect and especially had the issue, that you couldn't drag and drop paragraphs over levels. Much of the paragraphs container (and child) behaviour was controlled through the field_paragraph_settings field of the children and the container settings (fields).

In version 3.x we then switched to ERL and with 4.x to layout_paragraphs.
We never prepared an upgrade path.

For version 3.x and 4.x, the container paragraph was kept as general "folder" solution, but isn't required anymore for nesting and shouldn't be used.

For 1.x and 2.x we're now planning to provide an upgrade path to 4.x the following way:
Create a separate submodule which allows to run a conversion of all these "container" paragraphs to layout_paragraphs "layout" paragraphs. This contains mapping the settings from the container and its children to the new layout_paragraphs.
Afterwards, drowl_paragraphs can be upgraded to 4.x
We don't solve this in 4.x with update hooks or the module, as 4.x has a modified settings field and shell get cleanup routines in the upgrade hook to ensure the schema is healthy. This might lead to trouble.

High level plan

Create a separate conversion submodule: drowl_paragraphs_c2lp
Run the following batch actions in the submodule via Batch API: https://api.drupal.org/api/drupal/core%21includes%21form.inc/group/batch...
Suggest to upgrade to 4.x afterwards

Helpers / API

These links may help to solve the task:
- https://www.drupal.org/node/2554097
- https://www.alansaunders.co.uk/blog/custom-actions-drupal-89
- https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Field%21F...
- https://www.drupal.org/node/2549139
- https://www.drupal.org/project/convert_bundles
- #3007446: Introduce a helper for renaming an entity bundle

Detailed plan

From database tables in
wd_paragraphs_item_field_data (or paragraphs_item_revision_field_data):

Source:

paragraph type: container
with several fields and
field_paragraphs_paragraphs which references the sub-paragraphs

Target:

paragraph type: layout
with same fields but
without field_paragraphs_paragraphs

Strategy:

1. Get all entries of field_paragraphs (in nodes, blocks, megamenu, ...)
2. Iterate all items one by one. DO NOT TOUCH ITEMS WITH type=layout!!
  2.1a) IF type != container:
      2.1a.1) Create a new one-column layout paragraph with the same parent_delta as the current paragraphs weight:

      2.1a.2) Set values in behavior_settings of the new layout container:
      ['layout_paragraphs' =>
        [
          'parent_uuid' => '', // Empty. Not needed for base-level.
          'region' => '', // Empty. Not needed for base-level.
          'parent_delta' => TODO, // Set equal to current paragraphs weight.
          'layout' => 'drowl_layouts_1col',
          // Get values from field_paragraph_settings for config:
          'config' => [
            'label'='Layout (auto-wrapper from container migration)'
            'layout_section_width' => 'viewport-width-cp',
            'layout_align_cells_vertical' => 'stretch',
            'layout_align_cells_horizontal' => 'left',
            'layout_remove_grid_gutter' => [], // Leavy empty array.
            'extra_classes' => 'dp-layout-migrated', // Indicate by class
            'layout_variant' => '', // Leave empty string.
          ]
        ]
      ]

      2.1a.3) Assign the paragraph to this layout paragraph by parent_uuid=new-layout-paragraph.uuid and region = main
        array (
          'layout_paragraphs' =>
          array (
            'parent_uuid' => 'TODO', // References the "parent" layout paragraph from paragraphs_id:uuid
            'region' => 'main', // In which parent region
            'parent_delta' => 0, // With which weight in parent region
            'layout' => '', // Always empty for non-layouts!
            'config' => [], // Always empty for non-layouts!
          ),
        )

  2.1b) IF type == container:
    2.1b.1) Change paragraph type from "container" to "layout" (must be changed in _paragraphs_item AND paragraphs_item_field_data!)
    2.1b.2) Set values in behavior_settings of the paragraph:
        ['layout_paragraphs' =>
          [
            'parent_uuid' => '', // Empty. Only needed if layout in layout
            'region' => '', // Empty. Only needed if layout in layout
            'parent_delta' => TODO, // Set equal to current paragraphs weight.
            'layout' => 'TODO' // Determined from the CHILD count with columns setting (e.g. 4=3col)

            // Get values from field_paragraph_settings (and child paragraphs infos) for config:
            'config' => [
              'label'='Layout (migrated from container)'
              'column_widths'=TODO // TODO: Derive from layout type (> col) => Determined from the CHILD count + drowl_layout_col_lg setting
              'layout_section_width' => 'viewport-width-cp', // TODO: From field_paragraph_settings.layout_section_width
              'layout_align_cells_vertical' => 'stretch', // TODO: From field_paragraph_settings.layout_align_children_vertical
              'layout_align_cells_horizontal' => 'left', // TODO: From field_paragraph_settings.layout_align_children_horizontal
              'layout_remove_grid_gutter' => [], // Leavy empty array.
              'extra_classes' => 'dp-layout-migrated', // Indicate by class
              'layout_variant' => '', // Leave empty string.
            ]
          ]
        ]

    2.1b.3) Get all entries of field_paragraphs_paragraphs and iterate all items one by one:
      2.1b.3.1a) IF type != container:
        ['layout_paragraphs' =>
          [
            'region' => 'main', // TODO: Determined from the CHILD count with columns setting (e.g. 4=3col)
            'parent_uuid' => 'TODO', // Get from parent container
            'layout' => '', // Leave empty
            'config' => [], // Leave empty array.
            'parent_delta' => TODO, // Set equal to current paragraphs weight.
          ],
        ]

      2.1b.3.1b) IF type == container:
        2.1b.3.1b.1) Change paragraph type from "container" to "layout" (must be changed in _paragraphs_item AND paragraphs_item_field_data!)
        2.1b.3.1b.2) Set values in behavior_settings of the paragraph:
          ['layout_paragraphs' =>
            [
              'parent_uuid' => 'TODO: Parent uuid', // TODO: Set the parent layout UUID
              'region' => 'TODO', // TODO: Determined from the CHILD count with columns setting (e.g. 4=3col)
              'parent_delta' => TODO, // Set equal to current paragraphs weight.
              'layout' => 'TODO' // Determined from the CHILD count with columns setting (e.g. 4=3col)

              // Get values from field_paragraph_settings (and child paragraphs infos) for config:
              'config' => [
                'label'='Layout (migrated from container)'
                'column_widths'=TODO // TODO: Derive from layout type (> col) => Determined from the CHILD count + drowl_layout_col_lg setting
                'layout_section_width' => 'viewport-width-cp', // TODO: From field_paragraph_settings.layout_section_width
                'layout_align_cells_vertical' => 'stretch', // TODO: From field_paragraph_settings.layout_align_children_vertical
                'layout_align_cells_horizontal' => 'left', // TODO: From field_paragraph_settings.layout_align_children_horizontal
                'layout_remove_grid_gutter' => [], // Leavy empty array.
                'extra_classes' => 'dp-layout-migrated', // Indicate by class
                'layout_variant' => '', // Leave empty string.
              ]
            ]
          ]
        2.1b.3.1b.3) Get all entries of field_paragraphs_paragraphs and iterate all items one by one: GOTO 2.1b.3

--------------------- Example data: -----------------------------

// Countdown:
array (
  'layout_paragraphs' =>
  array (
    'parent_uuid' => 'a0452ae2-5e10-4ebc-80db-0cd7587e6b97', // References the "parent" layout paragraph from paragraphs_id:uuid
    'region' => 'main', // In which parent region
    'parent_delta' => 51, // With which weight in parent region
    'layout' => '', // Always empty for non-layouts!
    'config' => [], // Always empty for non-layouts!
  ),
)


// Layout:
array (
  'layout_paragraphs' =>
  array (
    'parent_uuid' => '', // Only needed if layout in layout
    'region' => '', // Only needed if layout in layout
    'parent_delta' => 0, // Only needed if layout in layout
    'layout' => 'drowl_layouts_3col_stacked',
    'config' =>
    array (
      'label' => '',
      'column_widths' => '33-33-33',
      'layout_section_width' => 'viewport-width-cp',
      'layout_align_cells_vertical' => 'stretch',
      'layout_align_cells_horizontal' => 'left',
      'layout_remove_grid_gutter' =>
      array (
      ),
      'extra_classes' => '',
      'layout_variant' => 'card',
    ),
  ),
)

// Example with 2 nested layouts with texts in each:
// Outer layout:
array (
  'layout_paragraphs' =>
  array (
    'region' => '',
    'parent_uuid' => '',
    'layout' => 'drowl_layouts_1col',
    'config' =>
    array (
      'label' => '',
      'layout_section_width' => 'viewport-width-cp',
      'layout_align_cells_vertical' => 'stretch',
      'layout_align_cells_horizontal' => 'left',
      'layout_remove_grid_gutter' =>
      array (
      ),
      'extra_classes' => '',
    ),
  ),
)

// Text in outer layout:
array (
  'layout_paragraphs' =>
  array (
    'region' => 'main',
    'parent_uuid' => 'e145b2a8-5248-426b-bb04-0c39eca84f86',
    'layout' => '',
    'config' =>
    array (
    ),
    'parent_delta' => 0,
  ),
)

// Inner layout:
array (
  'layout_paragraphs' =>
  array (
    'region' => 'main',
    'parent_uuid' => 'e145b2a8-5248-426b-bb04-0c39eca84f86',
    'layout' => 'drowl_layouts_2col',
    'config' =>
    array (
      'label' => '',
      'column_widths' => '50-50',
      'layout_section_width' => 'viewport-width-cp',
      'layout_align_cells_vertical' => 'stretch',
      'layout_align_cells_horizontal' => 'left',
      'layout_remove_grid_gutter' =>
      array (
      ),
      'extra_classes' => '',
      'layout_variant' => 'card',
    ),
    'parent_delta' => 0,
  ),
)

// Text in left region of inner layout:
array (
  'layout_paragraphs' =>
  array (
    'region' => 'left',
    'parent_uuid' => 'b9a65c46-3f01-4a15-b578-d3b5c922634c',
    'layout' => '',
    'config' =>
    array (
    ),
    'parent_delta' => 2,
  ),
)

// Text in right region of inner layout:
array (
  'layout_paragraphs' =>
  array (
    'region' => 'right',
    'parent_uuid' => 'b9a65c46-3f01-4a15-b578-d3b5c922634c',
    'layout' => '',
    'config' =>
    array (
    ),
    'parent_delta' => 2,
  ),
)

Comments

Anybody created an issue. See original summary.

anybody’s picture

Issue summary: View changes
anybody’s picture

Issue summary: View changes
anybody’s picture

Issue summary: View changes
anybody’s picture

Issue summary: View changes
anybody’s picture

Issue summary: View changes
anybody’s picture

Issue summary: View changes
anybody’s picture

Status: Active » Needs work

In progress!

anybody’s picture

Assigned: Unassigned » anybody

  • 0106cfd committed on 8.x-1.x
    Issue #3261150: [UPGRADE] Create submodule to migrate paragraph type...
anybody’s picture

Status: Needs work » Fixed

Works good enough so far. Added a first release to 8.x-1.20!

anybody’s picture

Version: 8.x-1.x-dev » 8.x-2.x-dev
Status: Fixed » Reviewed & tested by the community

Let's leave this open for 8.x-2.x to copy over once it did a good job for 8.x-1.x

anybody’s picture

Mhm unsure if this was for layout_paragraphs 1.x or 2.x - can't find a clear version anywhere...

Edit: found it! layout_paragraphs:layout_paragraphs(^2)

anybody’s picture

Status: Reviewed & tested by the community » Needs work

Found some minor issues for edge conditions, I'll fix.

  • Anybody committed 2ebc7d3 on 8.x-1.x
    Issue #3261150 by Anybody: [UPGRADE] Create submodule to migrate...
anybody’s picture

Still WIP and will receive final fixes in the largest project (DD) I think...

anybody’s picture

Assigned: anybody » Unassigned
Status: Needs work » Postponed

DD is no more relevant for this because of a complete relaunch including contents. 1.x + 2.x is only used in 5 projects, I guess it's not worth further work here? Let's postpone this, until someone need this or all installations are gone.