[UPGRADE] Create submodule to migrate paragraph type "container" to "layout" (layout paragraphs) [1.x, 2.x]

Created on 28 January 2022, about 3 years ago
Updated 25 April 2024, 10 months ago

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,
  ),
)
📌 Task
Status

Postponed

Version

2.0

Component

Code

Created by

🇩🇪Germany Anybody Porta Westfalica

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇩🇪Germany Anybody Porta Westfalica

    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.

Production build 0.71.5 2024