The Drupal 7 Location module allows users to store locations in a location field (location_cck submodule), and also directly for node, taxonomy_term and user entities (with location_node, location_taxonomy and location_user submodules). And it does not have a Drupal 8+ release: maintainers decided1 that their users should use address, email and link fields on the most recent core version.

Since it seemed that some customers needed this upgrade path, Acquia decided to found the development of Location Migration. I learned a lot during the development work. 🙂

If you have to migrate a single field type into multiple types of fields, you are alone

The problem is that we have to migrate a single field with location type into address, email, telephone and link fields at the same time. And since this situation does not occur to the migrations in Drupal core, the upstream migrations aren’t extensible in such a way that would work for us. We have to write our own migrations, source and process plugins, and also take care about the location field values being added to the action migration row source properties.

Trailing commas in function calls are only allowed in PHP 7.3+

I made a small mistake while setting up my IDE: I set the PHP level to 7.3 while Drupal 8 is still supported. And the minimum PHP version required by Drupal 8 is 7.0.8. So if you are the maintainer of a contrib module, and you do care about your users, please make sure that your language level config is set on the lowest PHP version of the Drupal core versions you aim to support. (Even if that version of PHP hasn’t been supported for years.)

SQLite is Different from MySQL, MariaDB or PostgreSQL

I ran into this issue on SQLite: Empty location detection goes wrong with SQLite source databases. This happened because I wasn’t fully aware of how SQLite’s type affinity works in conjunction with Drupal.

Well, it does not strictly follow the schema definition: if we store this default value, any other DB backend stores it as 0.000000, so with 6 digits to the right of the decimal point. But SQLite tries to be a bit smarter: it stores only 0.0, exactly the same value that the data schema defines.


Edit on February 17, 2021:

The “merge” word in MigrationInterface::mergeProcessOfProperty() refers to NestedArray::mergeDeepArray(), not array_merge()

A yet another lesson from Wim Leers:

\Drupal\location_migration\Plugin\migrate\field\Location::alterFieldInstanceMigration() does this:

1
2
3
4
5
6
7
8
9
  /**
   * {@inheritdoc}
   */
  public function alterFieldInstanceMigration(MigrationInterface $migration) {
    parent::alterFieldInstanceMigration($migration);
    $migration->mergeProcessOfProperty('settings', [
      'plugin' => 'location_to_address_field_settings',
    ]);
  }

But \Drupal\migrate\Plugin\Migration::mergeProcessOfProperty() uses NestedArray::mergeDeepArray(), which causes [field settings being lost].

What happens under the hood is that the new process being merged is converted to an array of migrate process configurations (so ['plugin' => 'foo'] is normalized to [['plugin' => 'foo']], and since its key is 0, the migrate plugin replaces the first migrate process configuration of the settings destination property with ['plugin' => 'foo']. And this is why every other migrated field instance loses their settings.

I expected this to happen:

1
2
3
4
5
6
7
8
9
10
[
  [
    'plugin' => 'the original plugin',
    'and its' => 'config',
    'I want' => 'to keep',
  ],
  [
    'plugin' => 'location_to_address_field_settings',
  ],
]

But I did this actually:

1
2
3
4
5
[
  [
    'plugin' => 'location_to_address_field_settings',
  ],
]

One of the possible solutions is to use an explicit string array key:

1
2
3
4
5
6
7
8
9
10
11
/**
 * {@inheritdoc}
 */
public function alterFieldInstanceMigration(MigrationInterface $migration) {
  parent::alterFieldInstanceMigration($migration);
  $migration->mergeProcessOfProperty('settings', [
    'a string key' => [
      'plugin' => 'location_to_address_field_settings',
    ],
  ]);
}

But I committed this:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * {@inheritdoc}
 */
public function alterFieldInstanceMigration(MigrationInterface $migration) {
  parent::alterFieldInstanceMigration($migration);

  $current_process = $migration->getProcess()['settings'] ?? [];
  $current_process[] = [
    'plugin' => 'location_to_address_field_settings',
  ];

  $migration->setProcessOfProperty('settings', $current_process);
}

Footnotes:

  1. I don’t blame them at all! On the contrary: I think it’s a lot smarter to join efforts