Problem/Motivation

EntityConfigBase migrations cannot update existing configs due to the lack of an appropriate getEntity implementation.

It defaults to Entity:getEntity which only takes into account the first destination id, however configs typically have multiple IDs which should be concatenated by a ..

This means that in every destination plugin which overrides EntityConfigBase (and do not override the import method), if the Drupal\migrate\Plugin::import() method is called with a non-empty $old_destination_ids param, the import process will fail, because instead of operating on a preexisting entity, the destination plugin tries to create a new one (with the same ID which was calculated based on the destination IDs of the current migration Row).

Affected destination plugins are e.g.

  • entity:field_config
  • entity:field_storage_config
  • entity:base_field_override
  • entity:block

Proposed resolution

Implement appropriate getEntity which takes into account the full config ID.

Remaining tasks

  • Add a test for the abstract EntityConfigBase migrate destination plugin class
  • Implement a BC-safe fix.

User interface changes

N/A

API changes

N/A

Side effects

The current EntityConfigBase makes it impossible to track changes of migrations with multi-ID config entity destinations, but this is not evaluated as a bug.

Fixing the issue will make change tracking possible in these scenarios.

CommentFileSizeAuthor
#61 3118262-61-10.6.patch13.56 KBduaelfr
#61 3118262-61-11.x.patch13.57 KBduaelfr
#52 core-fix_entityconfigbase-3118262-52--complete.patch12.73 KBnarendrar
#46 interdiff-3118262-42-46.txt13.89 KBhuzooka
#46 core-fix_entityconfigbase-3118262-46--complete.patch12.42 KBhuzooka
#46 core-fix_entityconfigbase-3118262-46--test-only.patch6.69 KBhuzooka
#42 interdiff_3118262_38-42.txt3.82 KBankithashetty
#42 3118262-42.patch25.15 KBankithashetty
#38 core-fix_config_entity_migrate_destination_update_entity-3118262-38--complete.patch25.56 KBduaelfr
#38 core-fix_config_entity_migrate_destination_update_entity-3118262-38--tests-only.patch19.76 KBduaelfr
#38 interdiff.3118262.37.38.txt1.65 KBduaelfr
#38 interdiff.3118262.27.38.txt5.94 KBduaelfr
#37 core-fix_config_entity_migrate_destination_update_entity-3118262-37--complete.patch24.26 KBduaelfr
#37 core-fix_config_entity_migrate_destination_update_entity-3118262-37--test-only.patch19.76 KBduaelfr
#37 interdiff.3118262.23.37.txt6.04 KBduaelfr
#23 interdiff-3118262-20-23.txt9.21 KBhuzooka
#23 core-fix_config_entity_migrate_destination_update_entity-3118262-23--fix-only--do-not-test.patch5.4 KBhuzooka
#23 core-fix_config_entity_migrate_destination_update_entity-3118262-23--complete.patch25.2 KBhuzooka
#23 core-fix_config_entity_migrate_destination_update_entity-3118262-23--test-only.patch19.8 KBhuzooka
#20 interdiff-3118262-19-20.txt2.8 KBhuzooka
#20 core-fix_config_entity_migrate_destination_update_entity-3118262-20--fix-only--do-not-test.patch5.54 KBhuzooka
#20 core-fix_config_entity_migrate_destination_update_entity-3118262-20--complete.patch18.64 KBhuzooka
#19 interdiff-3118262-15-19.txt1.19 KBhuzooka
#19 core-fix_config_entity_migrate_destination_update_entity-3118262-19--fix-only--do-not-test.patch5.4 KBhuzooka
#19 core-fix_config_entity_migrate_destination_update_entity-3118262-19--complete.patch18.5 KBhuzooka
#19 core-fix_config_entity_migrate_destination_update_entity-3118262-19--test-only.patch13.1 KBhuzooka
#17 core-fix_config_entity_migrate_destination_update_entity-3118262-15--fix-only--do-not-test.patch5.39 KBhuzooka
#15 interdiff-3118262-8-15.txt24.36 KBhuzooka
#15 core-fix_config_entity_migrate_destination_update_entity-3118262-15--complete.patch18.49 KBhuzooka
#15 core-fix_config_entity_migrate_destination_update_entity-3118262-15--test-only.patch13.1 KBhuzooka
#8 3118262-8.patch6.21 KBchandrashekhar_srijan
#3 get-destination-config-entity-3118262-3.patch1.37 KBcodebymikey
#2 get-destination-config-entity-3118262-2.patch1.35 KBcodebymikey

Issue fork drupal-3118262

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

codebymikey created an issue. See original summary.

codebymikey’s picture

Status: Active » Needs review
StatusFileSize
new1.35 KB
codebymikey’s picture

StatusFileSize
new1.37 KB
mikelutz’s picture

Version: 8.9.x-dev » 9.1.x-dev
Category: Bug report » Feature request
Status: Needs review » Needs work
Issue tags: +Needs tests

The ability to rerun and update migrations using the EntityConfigBase destination is not something currently supported by drupal core, but I think it would be useful to add in this ability if we can do so. We would need to add a good number of tests to make sure that this works, so I'm setting to NW for tests. Additionally, the getEntity method in the patch appears to just be a copy of the parent getEntity() method, which is incorrect for reasons I've noted below. I like the idea, but we need to prove that it works.

codebymikey’s picture

Hi Mike,

It's not an exact copy, the main difference being the code used to fetch the entity ID, the rest of the code are fine as they previously were:

$entity_id = $old_destination_id_values ? implode('.', $old_destination_id_values) : $this->getEntityId($row);

vs.

$entity_id = reset($old_destination_id_values) ?: $this->getEntityId($row);

which is used to load against the first destination id value (which is typically the node ID, or revision ID).

And yes, you're definitely right that it needs more testing, especially with languages. I just wanted to raise the issue so that it was at least public, and could be taken further by other interested parties.

mikelutz’s picture

Wow, total fail to copy my notes on that last comment.. My apologies.

What I meant to say was that it appeared the parent method was copied with just the line changed that you needed changed. My point was that if you are going to have a config specific getEntity method, then the rest of the method should be written specifically for config entities. Specifically these notes (which I really thought I posted above)

  1. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -162,6 +162,32 @@ class EntityConfigBase extends Entity {
    +      // Attempt to ensure we always have a bundle.
    +      if ($bundle = $this->getBundle($row)) {
    +        $row->setDestinationProperty($this->getKey('bundle'), $bundle);
    +      }
    

    Config entities don't have bundles. This is unnecessary

  2. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -162,6 +162,32 @@ class EntityConfigBase extends Entity {
    +      // Stubs might need some required fields filled in.
    +      if ($row->isStub()) {
    +        $this->processStubRow($row);
    +      }
    

    Config entities can't be stubbed. This is unnecessary.

  3. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -162,6 +162,32 @@ class EntityConfigBase extends Entity {
    +      $entity->enforceIsNew();
    

    I don't think we need 'enforceIsNew' with config entities. enforceIsNew lets us set a id on a content entity and save it without the system thinking it's an update, config entities are defined by their id, so this shouldn't be necessary.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

chandrashekhar_srijan’s picture

StatusFileSize
new6.21 KB

rerolled for 9.x. Also applied suggestions made in #6.

codebymikey’s picture

Hi @chandrashekhar_srijan,

Thanks for the patch, but I think it also includes changes for a different functionality.
The only thing left for this issue after incorporating #6 are test cases.

chandrashekhar_srijan’s picture

Regarding #9: I have rerolled the patch for 9.2.x and addressed the suggestion in #6. You are right in pointing that Test cases are still to be done. Will try to take it up if time allows me.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

huzooka’s picture

Assigned: Unassigned » huzooka

Imho we only have to override the inherited getEntityId() method, basically repeating what's already done for the initial migration (when the original destination IDs array is empty on import).

huzooka’s picture

Category: Feature request » Bug report
Issue tags: +migrate-d6-d7

This actually breaks change tracking of migrations with config entity destination.

huzooka’s picture

The ability to rerun and update migrations using the EntityConfigBase destination is not something currently supported by drupal core,

That's true. Although, why is core testing track_changes? I guess because it should be supported.

At least this suggests that: https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugi...

huzooka’s picture

Added a test, but it isn't explicitly testing \Drupal\migrate\Plugin\migrate\destination\EntityContentBase, Drupal\migrate\Plugin\migrate\destination\EntityFieldStorageConfig or Drupal\migrate\Plugin\migrate\destination\EntityFieldInstance: Its a functional testing of track_changes for field storage and field instance migrations.

I hope that it works for all of us, or at least it's a good starting point for a further improvement.

huzooka’s picture

Cancelled the Mysql tests (let's first test with SQLite, it's much faster)!

huzooka’s picture

huzooka’s picture

Assigned: Unassigned » huzooka
Status: Needs review » Needs work
huzooka’s picture

Assigned: Unassigned » huzooka
Status: Needs review » Needs work
huzooka’s picture

I was able to clean up the language override import as well (detecting of a translation was broken – the condition was wrong).

But the major update is that I added a test for EntityContentBase, which shows why this issue should be classified as bug.

huzooka’s picture

Title: Config migrations (EntityConfigBase) can't be be re-ran to update existing config entities due to missing getEntity implementation » Migrate destination plugins extending EntityConfigBase cannot find existing config entities due to broken getEntity implementation (re-run fails, change tracking is impossible)
huzooka’s picture

Title: Migrate destination plugins extending EntityConfigBase cannot find existing config entities due to broken getEntity implementation (re-run fails, change tracking is impossible) » Migrate destination plugins with multiple destinatin IDs extending EntityConfigBase cannot find existing config entities due to broken getEntity implementation (re-run fails, change tracking is impossible)
huzooka’s picture

Title: Migrate destination plugins with multiple destinatin IDs extending EntityConfigBase cannot find existing config entities due to broken getEntity implementation (re-run fails, change tracking is impossible) » Migrate destination plugins with multiple destination IDs extending EntityConfigBase cannot find existing config entities due to broken getEntity implementation (re-run fails, change tracking is impossible)
huzooka’s picture

Explaining the fix:

  1. +++ b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
    @@ -153,7 +153,7 @@ public function fields() {
    -    $entity_id = reset($old_destination_id_values) ?: $this->getEntityId($row);
    +    $entity_id = $this->getEntityIdFromRowOrDestination($row, $old_destination_id_values);
    

    This was the root of the issue: If there are more than one destination IDs, every subclass tried to load the entity with the very first one. Apparently was fine in 80% of the situations, but newer worked in case of field_config and field_config_storage destinations.

  2. +++ b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
    @@ -174,6 +174,21 @@ protected function getEntity(Row $row, array $old_destination_id_values) {
    +  /**
    +   * Gets the entity ID of the row.
    +   *
    +   * @param \Drupal\migrate\Row $row
    +   *   The row of data.
    +   * @param array $old_destination_id_values
    +   *   The previous destination ID values, if any.
    +   *
    +   * @return string
    +   *   The entity ID for the row that we are importing.
    +   */
    +  protected function getEntityIdFromRowOrDestination(Row $row, array $old_destination_id_values) {
    +    return reset($old_destination_id_values) ?: $this->getEntityId($row);
    +  }
    +
    

    ...so I moved the original $entity_id value assignment into this new protected method.

  3. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -282,9 +295,38 @@ public function rollback(array $destination_identifier) {
    +  /**
    +   * {@inheritdoc}
    +   */
    +  protected function getEntityIdFromRowOrDestination(Row $row, array $old_destination_id_values) {
    +    return $old_destination_id_values
    +      ? static::joinIds($old_destination_id_values)
    +      : $this->getEntityId($row);
    +  }
    

    ...and I override it in EntityConfigBase.

  4. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -282,9 +295,38 @@ public function rollback(array $destination_identifier) {
    +  /**
    +   * {@inheritdoc}
    +   */
    +  protected function getEntityId(Row $row) {
    +    $destination_id_key = $this->getKey('id');
    +    $destination_id = $row->hasDestinationProperty($destination_id_key)
    +      ? $row->getDestinationProperty($this->getKey('id'))
    +      : NULL;
    +
    +    if (!empty($destination_id)) {
    +      return $destination_id;
    +    }
    +
    +    $ids = $this->getIds();
    +    // Ids is keyed by the key name so grab the keys.
    +    $id_keys = array_keys($ids);
    +    // Set the ID into the destination in for form "val1.val2.val3".
    +    return $this->generateId($row, $id_keys);
    +  }
    

    I also "fix" the getEntityId() method – this ensures that we will have the right destination entity ID even when we have a preexisting configuration entity. Why this should be possible? Let me give an example!

    If you have a source Drupal 7 site with e.g. a field_image field with cardinality -1 (unlimited), then you are not able to migrate the data into your destination site: Standard profile provides its own field_image storage, but with cardinality set to 1. The most inconvenient part of the problem is that you only notice that something went wrong when you already migrated your data as well (if-you-ever-notice-it).

    If we ensure that every config entity destination is able to find a preexisting entity (please keep in mind that this is the case for node types, vocabularies, etc), then we can also prevent these cases!

  5. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -195,18 +195,18 @@ protected function updateEntity(EntityInterface $entity, Row $row) {
         // This is a translation if the language in the active config does not
         // match the language of this row.
         $translation = FALSE;
    -    if ($row->hasDestinationProperty('langcode') && $this->languageManager instanceof ConfigurableLanguageManager) {
    -      $config = $entity->getConfigDependencyName();
    -      $langcode = $this->configFactory->get('langcode');
    -      if ($langcode != $row->getDestinationProperty('langcode')) {
    -        $translation = TRUE;
    -      }
    +    if (
    +      $row->hasDestinationProperty('langcode') &&
    +      $this->languageManager instanceof ConfigurableLanguageManager &&
    +      $row->getDestinationProperty('langcode') !== $entity->language()->getId()
    +    ) {
    +      $translation = TRUE;
         }
    

    I wasn't able to find out what should be done here, but the comment above helped A LOT.

    Notice the $langcode = $this->configFactory->get('langcode'); line! This was actually loading an immutable config. Do you know which config is expected here? I do not, I think core never had a configuration entity with langcode ID.

    But anyway, comparing this object never matches the value of the langcode destination property – which means that without fixing this logic, we are not able to fix this bug.

    So I went ahead and changed this condition. From now on, it will work what its comment describes.

huzooka’s picture

The reason why you cannot see the first test's failure message in #23 is #3197324: Exception trace cannot be serialized because of closure.

But!
The second tests failure message clearly reflects the bug:

Expectation failed for method name is "load" when invoked zero or more times
Parameter 0 for invocation Drupal\Core\Entity\EntityStorageInterface::load('id1') does not match expected value.
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'id1.id2'
+'id1'

I think I can remove the needs tests tag.

huzooka’s picture

Patches in #23 can be applied on 9.2.x and on 9.1.x as well.

codebymikey’s picture

With regards to the failing test on the test-only patch on #23:

Testing Drupal\Tests\migrate\Kernel\TrackFieldConfigChangesTest
The specified database connection is not defined: migrate

It's related to #3197324-7: Exception trace cannot be serialized because of closure and PHPUnit, and it's intercepting what the original error should be.

And can be bypassed with the following (I think that's the most appropriate fix I can think of, but it still feels a little hacky):

Index: core/lib/Drupal/Core/Database/Query/Query.php
--- a/core/lib/Drupal/Core/Database/Query/Query.php
+++ b/core/lib/Drupal/Core/Database/Query/Query.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Core\Database\Query;
 
+use Drupal\Core\Database\ConnectionNotDefinedException;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\Connection;
 
@@ -93,7 +94,16 @@
    * Implements the magic __wakeup function to reconnect to the database.
    */
   public function __wakeup() {
-    $this->connection = Database::getConnection($this->connectionTarget, $this->connectionKey);
+    try {
+      $this->connection = Database::getConnection($this->connectionTarget, $this->connectionKey);
+    } catch (ConnectionNotDefinedException $e) {
+      // The database might not be accessible at the moment.
+      if (!defined('PHPUNIT_COMPOSER_INSTALL') && !defined('__PHPUNIT_PHAR__')) {
+        // Log if the deserialization was not triggered in a PHPUnit context.
+        // https://www.drupal.org/project/drupal/issues/3197324
+        watchdog_exception('system', $e, 'Unable to unserialize a query object.');
+      }
+    }
   }
 
   /**
huzooka’s picture

Re #30:

I'm able to get a meaningful message even without changing anything out of the kernel test: https://git.drupalcode.org/project/media_migration/-/blob/8.x-1.x/tests/...

But these are out of scope (and core won't solve that issue class by class imho).

(And I already mentioned it in #28)

wim leers’s picture

#15: wow, I literally have nothing to remark about your test-only patch!

A few questions and nits on the complete patch:

  1. +++ b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
    @@ -179,12 +179,14 @@ protected function getEntity(Row $row, array $old_destination_id_values) {
    -  protected function getEntityId(Row $row) {
    -    return $row->getDestinationProperty($this->getKey('id'));
    +  protected function getEntityId(Row $row, array $old_destination_id_values) {
    +    return reset($old_destination_id_values) ?: $row->getDestinationProperty($this->getKey('id'));
    

    This is technically a BC break … 🙈
    Already fixed in #19 👍🥳

  2. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -196,15 +197,19 @@ protected function updateEntity(EntityInterface $entity, Row $row) {
    -      $langcode = $this->configFactory->get('langcode');
    -      if ($langcode != $row->getDestinationProperty('langcode')) {
    ...
    +      $default_langcode = $this->languageManager->getDefaultLanguage()->getId();
    +      $langcodes_representing_default_translation = [
    +        $default_langcode,
    +        LanguageInterface::LANGCODE_NOT_SPECIFIED,
    +      ];
    +      if (!in_array($row->getDestinationProperty('langcode'), $langcodes_representing_default_translation, TRUE)) {
    

    This improves the config translation handling I think? 🤔

    I think to be able to change this we'll need explicit test coverage for this too. And arguably it is out of scope here? 🤔

    I can easily be convinced that this is necessary though, because core doesn't have a langcode config nor does there seem to be anything else trying to get it…

    Confirmed by #27.5 👏👏👏

  3. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -282,9 +300,33 @@ public function rollback(array $destination_identifier) {
    +    // Set the ID into the destination in for form "val1.val2.val3".
    

    Nit: s/in for form/in the form/


#19: nice — updating my review of #15 now :P


#23: This test also looks superb. Only a nit:

+++ b/core/modules/migrate/tests/src/Unit/destination/EntityConfigBaseTest.php
@@ -0,0 +1,211 @@
+    // followings are good to go.

🤔 Nit: not sure what "followings" means?


@huzooka I don't think I've ever seen a core patch of this test complexity be done so well on the first iteration. Impeccable work. Thank you for your excellent work!

I'd RTBC this, but I think this kind of change needs migration system maintainer review.

codebymikey’s picture

Re #31

I had drafted my findings a couple minutes before you made yours, but didn't submit it in time since I had to tend to something else.

The try catch is a much clever workaround for the test case. I just thought it was worth documenting my particular workaround and solving it in core (to avoid other third party test cases from failing in the future).

Core already has a similar fix in DependencySerializationTrait when it attempts to unserialize other complex objects.

There's about 7 calls to __wakeup() in core so should be a bit more manageable.

But yeah, you're right, discussions on this are out of scope for the current issue. The comment was just as an FYI.

huzooka’s picture

@codebymikey, no problem :)

Asking for subsystem maintainer review according to #32.

wim leers’s picture

#34: d'oh — I forgot about that tag!

quietone’s picture

Status: Needs review » Needs work

Time to start looking at this.

I started with the title. Can the title be changed to state what this is doing 'Allow EntityConfig migrations to track changes' or something. (It is rather harsh to claim this is broken when, at the time and as far as I recall, the migration of configuration was considered a once off).

Then I read the IS. The steps to reproduce says to install Migrate Tools, which is true. But it is also true that using Migrate Run or Drush 10.4+ will also allow one to reproduce the problem.

I then went to review the code.

  1. +++ b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
    @@ -174,6 +174,21 @@ protected function getEntity(Row $row, array $old_destination_id_values) {
    +   * Gets the entity ID of the row.
    

    This is the same summary as an existing method in Entity. I suggest adding an explanation of when and why to use or the other.

  2. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -195,18 +195,18 @@ protected function updateEntity(EntityInterface $entity, Row $row) {
    +    if (
    +      $row->hasDestinationProperty('langcode') &&
    +      $this->languageManager instanceof ConfigurableLanguageManager &&
    +      $row->getDestinationProperty('langcode') !== $entity->language()->getId()
    +    ) {
    

    The reformatting here makes it difficult to find the change. Plus the operators are at the end of the line when, typically, they are at the beginning of the line as has been done for the ternary operations elsewhere in the patch. The change is to use $entity->language()->getId() instead of $this->configFactory->get('langcode'). Why is this change needed?

  3. +      $language_override = $this->languageManager->getLanguageConfigOverride($row->getDestinationProperty('langcode'), $entity->getConfigDependencyName());
    +      $language_override->set(str_replace(Row::PROPERTY_SEPARATOR, '.', $row->getDestinationProperty('property')), $row->getDestinationProperty('translation'));
    +      $language_override->save();
    

    If the name is not changed then there is only one line changed here and makes this is easier to review.

  4. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -257,6 +257,19 @@ protected function generateId(Row $row, array $ids) {
    +   * Generates an entity ID.
    

    The summary and the return description are identical to the summary for method generateId. I think at least the summary heres needs to be expanded to explain the difference.

  5. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -257,6 +257,19 @@ protected function generateId(Row $row, array $ids) {
    +  protected static function joinIds(array $id_values) {
    

    Not a fan of the name, JoinIds. Yes, it is descriptive but makes me think of SQL joins. I couldn't think of a better name so browsed the config system and I saw buildCacheId. How about buildId?

  6. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -282,9 +295,38 @@ public function rollback(array $destination_identifier) {
    +    // Ids is keyed by the key name so grab the keys.
    

    s/Ids is/IDs are/

I took a brief look at the tests and I did not see any testing for translated sources, which I looked for because of the changes to the if blocks at the beginning of \Drupal\migrate\Plugin\migrate\destination\EntityConfigBase::updateEntity. Did I miss something?

I hope to continue during this week.

duaelfr’s picture

Title: Migrate destination plugins with multiple destination IDs extending EntityConfigBase cannot find existing config entities due to broken getEntity implementation (re-run fails, change tracking is impossible) » Allow EntityConfig migrations to track changes
Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new6.04 KB
new19.76 KB
new24.26 KB

Per #32 and #36

  1. Changed the issue title as suggested
  2. Updated the IS as suggested
  3. Fixed the code as follow
    1. Did all asked naming and documentation changes
    2. Reverted the change about the langcode as it was not causing the issue in my case (I hope the testbot will agree). If I'm right, it might be moved in a follow-up.
duaelfr’s picture

My bad, those changes that I reverted were useful. Now migrate tells me that it updated the entities but it didn't...
I brought them back and made the minore changes asked in #36.

I did not expand tests to cover the translated source case (and I don't plan to, given my limited skills).

huzooka’s picture

Since you bumped this issue back on my dashboard, there is a better chance of me addrssing it :)

quietone’s picture

Status: Needs review » Needs work

I looked at the tests now. They look fine and appear to test what is needed (but it is late in the day for me). Just a few suggestions.

  1. +++ b/core/modules/migrate/tests/src/Kernel/TrackFieldConfigChangesTest.php
    @@ -0,0 +1,402 @@
    +    'system',
    

    We normally alphabetize the $modules list.

  2. +++ b/core/modules/migrate/tests/src/Kernel/TrackFieldConfigChangesTest.php
    @@ -0,0 +1,402 @@
    +    // Verify that when there are no changes on the source, nothing gets
    

    Expand the comment to make it clear that the destination is being changed.
    "Verify the when the destination configuration changes but the source does not change, that nothing is migrated."

  3. +++ b/core/modules/migrate/tests/src/Kernel/TrackFieldConfigChangesTest.php
    @@ -0,0 +1,402 @@
    +    // Rerun migrations.
    

    Add a blank line here to separate the rerun from the initial migration.

  4. +++ b/core/modules/migrate/tests/src/Kernel/TrackFieldConfigChangesTest.php
    @@ -0,0 +1,402 @@
    +   * Executes the given migrations with track_changes set to TRUE.
    

    s/migrations/migration/
    Because this only does one migration.

  5. +++ b/core/modules/migrate/tests/src/Unit/destination/EntityConfigBaseTest.php
    @@ -0,0 +1,210 @@
    +use Drupal\Core\Config\Entity\ConfigEntityBase;
    

    We normally sort the list of use statements.

  6. +++ b/core/modules/migrate/tests/src/Unit/destination/EntityConfigBaseTest.php
    @@ -0,0 +1,210 @@
    +        return $this->getMockForAbstractClass(ConfigEntityBase::class, [$values, self::ENTITY_TYPE_ID]);
    

    This will be easier to read if the array spans lines.

  7. +++ b/core/modules/migrate/tests/src/Unit/destination/EntityConfigBaseTest.php
    @@ -0,0 +1,210 @@
    +    // Ensure that the preexisting config entity  has the expected initial
    +    // property value. (In this test, the source and destination IDs are the
    +    // same!)
    

    s/entity has/entity has/. The last sentence is difficult. I understand parentheses are used for extra information that may be useful but then the exclamation mark implies that this is import. All together it is confusing. So, remove the parentheses around the last sentence and change the exclamation mark to a period.

  8. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -133,7 +133,7 @@ public function import(Row $row, array $old_destination_id_values = []) {
    +        // Set the ID into the destination in the form "val1.val2.val3".
    

    Out of scope.

ankithashetty’s picture

Status: Needs work » Needs review
StatusFileSize
new25.15 KB
new3.82 KB

Updated the patch in #38 to address the changes suggested in #41, thanks!

mikelutz’s picture

Title: Allow EntityConfig migrations to track changes » Add support for rerunning and updating migrations using the EntityConfig destination.
Category: Bug report » Feature request

Track changes is a completely different thing in migrate world. Additionally, this isn't a bug. The config destination is currently specifically documented to not support updates through the migration system. Adding that functionality is a feature request, not a bug.

volker23’s picture

I have used the patch from #42 on Drupal 9.2.2 to update my migration 'upgrade_d7_field' and it processed right away instead of throwing lots of ''field_storage_config' entity with ID 'xyz' already exists errors. Although the specific update i was trying, didn't work for other reasons, the migration update worked!

Thanks!

huzooka’s picture

Title: Add support for rerunning and updating migrations using the EntityConfig destination. » Calling EntityConfig::import() with multiple destination IDs fails
Issue summary: View changes

@mikelutz,

What if I keep only EntityConfigBaseTest (so remove the extra change tracking test)? I strongly believe that this issue is about a bug (it is obvious for me).

But fixing the bug of EntityConfigBase has a side effect: change tracking will be possible for migrations using destination plugins with multiple destination IDs (which are extending EntityConfigBase).

wim leers’s picture

Seems like quite a few people/migrations would benefit from this. Lots of collaboration on this issue. Bumping for another round of review 🤞 (I can't RTBC this I think.)

quietone’s picture

Status: Needs review » Needs work

Took a look again and found a few things.

  1. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -282,9 +293,38 @@ public function rollback(array $destination_identifier) {
    +    $destination_id_key = $this->getKey('id');
    +    $destination_id = $row->hasDestinationProperty($destination_id_key)
    +      ? $row->getDestinationProperty($this->getKey('id'))
    +      : NULL;
    

    Why not just $row->getDestinationProperty($this->getKey('id'))? It will return NULL if the property does not exist.

    If I am incorrect on that point then if $row->getDestinationProperty($this->getKey('id')) is changed to use use $destination_id_key it will be easier to read.

  2. +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityConfigBase.php
    @@ -282,9 +293,38 @@ public function rollback(array $destination_identifier) {
    +    $ids = $this->getIds();
    

    When is this block of code starting here code executed? The new test does not reach this.

  3. 
    +++ b/core/modules/migrate/tests/src/Unit/destination/EntityConfigBaseTest.php
    @@ -0,0 +1,213 @@
    +   * Tests the initial and subsequent import of a config entity.
    ...
    +    // Test a subsequent import.
    
  4. s/subsequent/update/
    Because subsequent does not convey that what is being done is an update, not just running say 'drush mim' again.

    I'd probably change the variables to use update instead of subsequent as well.

From #27,

If you have a source Drupal 7 site with e.g. a field_image field with cardinality -1 (unlimited), then you are not able to migrate the data into your destination site: Standard profile provides its own field_image storage, but with cardinality set to 1. The most inconvenient part of the problem is that you only notice that something went wrong when you already migrated your data as well (if-you-ever-notice-it).

The first thing that I notice here is that this in migrating to a Drupal destination site with the standard profile but migrations are to be run on a minimal install. However, I did try this on HEAD. I made a D7 site, standard install and change the image field of the article to be unlimited. Then I made a node that had three unique images. I then made a D9 sites, standard install, enabled migrate_drupal_ui and ran the migrations. The image field was correctly changed to unlimited and 3 images were correctly migrated to the node.

And finally what about a testing of a rollback?

quietone’s picture

Issue tags: -migrate-d6-d7

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

narendrar’s picture

Patch rerolled for 9.3.x

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

duaelfr’s picture

Rerolled patches in a new MR
Addressed some comments from #49 (points 1 and 3)

duaelfr’s picture

Hiding files now there is a MR

duaelfr’s picture

Just rerolled on latest 11.x.
This still needs point 2 from #49 to be fixed.

duaelfr’s picture

StatusFileSize
new13.57 KB
new13.56 KB

Patches for composer users <3

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.