The first issue I ran into is that the migrate plugin looks for rows in the field_group db table which most of the time will not be populated if the Drupal 7 site is using Features. Is there a way around this issue?

Also, when I re-save the "manage fields" tab for the content type it will force data back into the field_group database table which lets the migration run successfully but even after that I don't see a field group on the form display page (not even disabled).

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

drupalninja99 created an issue. See original summary.

drupalninja99’s picture

Issue summary: View changes
bob.hinrichs’s picture

I am kind of surprised this is not a very popular issue, but I've had problems locating people writing about it until now. It is real and I encountered the same, as I'm sure thousands have, no? Field groups are a super-important part of drupal, yet it appears you can't migrate them if you use features, unless you re-save everything. I feel like there has to be a good solution somewhere, especially since this same problem can occur for other "features" stored in features.

drupalfan2’s picture

Is there still no solution for migrations field groups to Drupal 8?

kevinquillen’s picture

I just ran into this, solution incoming...

kevinquillen’s picture

I encountered this same issue. I had about 40 field groups that were only defined by Features and unaware they would not migrate.

I solved it by grabbing them and dumping them into the legacy database before running the Drupal 7 -> 9 upgrade.

First:

  1. Ensure you have your legacy database connection setup in settings.php. In the example below, the key is 'migrate'.
  2. Add all your features to a 'features' directory under /modules/
  3. Make sure you have a copy of the legacy database at the connection from step 1.

With that in place, create a custom module:


use Drupal\Core\Database\Database;
use Drupal\Core\Database\Connection;

/**
 * Implements hook_install().
 *
 * This will scan the old features directory for field_group.inc files. From
 * there we can recreate the structure and insert them into the legacy database.
 *
 * After that, we can run the migration from the UI.
 */
function MYMODULE_install() {
  $connection = \Drupal\Core\Database\Database::getConnection('default', 'migrate');

  $path = DRUPAL_ROOT . '/modules/features';
  $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));

  $files = array_filter(iterator_to_array($iterator), function($file) {
    return preg_match('/(.*)\.field_group\.inc/i', $file->getFilename()) ? $file->isFile() : FALSE;
  });

  foreach ($files as $file) {
    $filename = $file->getFilename();
    $function_name = str_replace('.inc', '_info', $filename);
    $function_name = str_replace('.field_group', '_field_group', $function_name);

    // Need to require the .inc file so the function can be called.
    require $file->getPathname();

    $groups = $function_name();

    foreach ($groups as $group) {
      $connection->merge('field_group')
        ->key(['identifier' => $group->identifier])
        ->fields([
          'identifier' => $group->identifier,
          'group_name' => $group->group_name,
          'entity_type' => $group->entity_type,
          'bundle' => $group->bundle,
          'mode' => $group->mode,
          'parent_name' => $group->parent_name,
          'data' => serialize($group->data)
        ])
        ->execute();
    }
  }
}

The gist of it is, PHP will get an array of .field_group.inc files from all the features and loop them, calling their field_group info hooks that return all the group definitions. Then use a merge query to insert them into the legacy databases "field_group" table. Since 'identifier' is used as a key, overwriting whats in the database should not be an issue since it is a unique key and likely doesn't exist. merge() just makes it easier to iterate or start over - you can use insert() too if you want to start fresh each time.

When installing the module, all the field group info hooks are invoked and their arrays are turned into database records:

field groups that were migrated

With the groups inserted into the database, running the Drupal 7 -> Drupal 9 migration brought all field groups in. Example form:

example form with groups

If you don't want to insert into the legacy database, you can do it in your Drupal 9 database too (but you would need to write a migration plugin later to insert those records after the main migration has executed). Just add a schema hook to your custom module:

/**
 * Implements hook_schema().
 */
function mymodule_schema() {
  $schema['field_group'] = [
    'description' => 'Captures old field groups from Features in Drupal 7.',
    'fields' => [
      'id' => [
        'type' => 'serial',
        'not null' => TRUE,
        'description' => 'Primary Key: Unique field group ID.',
      ],
      'identifier' => [
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => "Field group identifier.",
      ],
      'group_name' => [
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'Field group name.',
      ],
      'entity_type' => [
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'Field group name.',
      ],
      'bundle' => [
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'Field group name.',
      ],
      'mode' => [
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'The form mode of the field group.',
      ],
      'parent_name' => [
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'The parent name of the field group.',
      ],
      'data' => [
        'type' => 'blob',
        'size' => 'big',
        'default' => '',
        'description' => 'The fields on the group.',
      ],
    ],
    'primary key' => ['id'],
    'indexes' => [
      'identifier' => ['identifier'],
      'group_name' => ['group_name'],
      'entity_type' => ['entity_type'],
      'bundle' => ['bundle'],
    ],
  ];

  return $schema;
}

and get a normal database connection in the install function. After that you'd need to wire up a migration and source plugin to read this table into field_group. Either way will get you there.

Reconfiguring all of them would have taken me probably a day, this only took an hour to write.

kevinquillen’s picture

Version: 8.x-3.0-rc1 » 8.x-3.x-dev
Component: Miscellaneous » Code
Category: Bug report » Feature request

This can probably be worked into field_group_migrate somehow as a new feature - leaving it up for discussion!

kevinquillen’s picture

Title: Drupal 7 field groups not migrating successfully » Drupal 7 field groups defined in Features field_group.inc files will not migrate.
alison’s picture

Wow, I was so so so so confused as to why four content types' field groups had migrations and no others! (because, Features were maintained for a while after this site was built but not forever, so there are content types and stuff with outdated or zero features)

Thank you everyone on this thread for sharing, and thank you @kevinquillen for the solution share! -- I haven't tried that part out yet, we're in a discovery phase, but I'm adding this to my notes for when we get to implementation.

kevinquillen’s picture

I've used the above trick a few times since then, YMMV.

alison’s picture

@kevinquillen What's the reason for putting the features modules into the Drupal 9 site codebase? I'm missing where/how that part fits in.

alison’s picture

OH! Never mind, I see it now!!

kevinquillen’s picture

They only exist so PHP can read the definitions of them in, in order to create the database records needed for the migration to pick them up.

alison’s picture

Thank you! -- in case it helps anyone else who stops by this thread, my process for using the generated migrations was (and idk if this is best / correct, but it ended up working for me):

TL;DR: Run the field group migrations after field, field_instance, and field_instance_widget_settings migrations.
^^I missed field_instance_widget_settings at first, and almost all my field groups were "there" but empty! -- now, I see how/why.

More specifically, this was my order of things:

  1. Run some migrations that tend to be dependencies of node/content migrations:
    1. upgrade_d7_user_role
    2. upgrade_d7_filter_format
    3. upgrade_d7_node_type
    4. upgrade_d7_user
    5. upgrade_d7_field
    6. upgrade_d7_view_modes
    7. upgrade_d7_taxonomy_vocabulary
    8. (then specific taxonomy/vocab migrations)
  2. Then...
    1. upgrade_d7_field_instance
    2. upgrade_d7_field_instance_widget_settings
  3. Then, "field group" migrations, i.e. upgrade_d7_field_group_node_news
  4. P.S. It also worked when I ran the field group migrations before the field instance widget settings migrations (accidentally, but it worked).
alison’s picture

I came across a couple other "site building things" that don't migrate smoothly / out-of-the box due to not being in the source site database when they're in Features (image styles, views), and posted about it here:
https://www.drupal.org/forum/support/upgrading-drupal/2024-01-24/upgradi...

Mainly, I'm just sharing in case it's helpful to others, but if anyone's run into other "site building things" that behave like that, I hope you'll comment on the Forum thread!