TLDR; if you enable content_translation without enabling migrate_drupal, all migrations are broken because of plugin discovery issues.

#2953360: Experimental migrate_drupal_multilingual module introduced a major (critical?) bug on using the migration system with Drupal 8.6, when having content_translation enabled, but migrate_drupal disabled.

This is because d6_entity_reference_translation.yml and d7_entity_reference_translation.yml were moved to content_translation from migrate_drupal, but they use the Drupal\migrate_drupal\Plugin\migrate\EntityReferenceTranslationDeriver class that is only available if migrate_drupal is enabled.

In this probably very common setup (i.e. any new D8 site using content_translation), attempting to use the migration system will throw the following error:

In DerivativeDiscoveryDecorator.php line 218:

  Plugin (d6_entity_reference_translation) deriver "Drupal\migrate_drupal\Plugin\migrate\EntityReferenceTranslationDeriver" does not exist.

Some possible solutions:

1. Move d6_entity_reference_translation.yml and d7_entity_reference_translation.yml back to migrate_drupal - reverts part of the cleanup done in this patch, but is simple to do.
2. Move EntityReferenceTranslationDeriver to content_translation - cleaner solution, but breaks the API, as it would no longer be Drupal\migrate_drupal\Plugin\migrate\EntityReferenceTranslationDeriver and instead Drupal\content_translation\Plugin\migrate\EntityReferenceTranslationDeriver. These migrations are the only references in core to this deriver, and migrate_drupal was an experimental module until now, so maybe we're OK to break the API?

Comments

jcnventura created an issue. See original summary.

jcnventura’s picture

Status: Active » Needs review
StatusFileSize
new2.79 KB

This patch implements option 2 (move EntityReferenceTranslationDeriver from migrate_drupal to content_translation).

catch’s picture

The patch seems OK to me with this small API break (given it's a critical regression due to last minute clean-up in order to mark the module as stable), but could do with a second or third opinion.

heddn’s picture

We still want to leave the old copy in MD and trigger_error on it and point to the new one here. We discussed this at great length in the migrate maintainers meeting today (phenaproxima, heddn, masipila, maxocub) and thought that would work. It isn't the cleanest, but perhaps its a way forward.

heddn’s picture

Status: Needs review » Needs work

Back to NW for the attempt at leaving a copy in MD and trigger_error on the BC nature of things.

jcnventura’s picture

Status: Needs work » Needs review
StatusFileSize
new9.37 KB
new11.25 KB

Left a copy in MD, and added the trigger_error.

This seems to be the only option, as the tests for option 1 (back in the parent issue) didn't pass.

heddn’s picture

Status: Needs review » Needs work
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php
@@ -2,6 +2,8 @@
+@trigger_error(__NAMESPACE__ . '\EntityReferenceTranslationDeriver is deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0. Instead, use \Drupal\content_translation\Plugin\migrate\EntityReferenceTranslationDeriver, see https://www.drupal.org/node/2991710.', E_USER_DEPRECATED);

@@ -13,35 +15,9 @@
+ * See https://www.drupal.org/node/2991710.

This should @see a change record, not a previous issue. Let's add a new one for this. https://www.drupal.org/list-changes/drupal

jcnventura’s picture

phenaproxima’s picture

Status: Needs work » Needs review

Thanks, @jcnventura! I've unpublished the change record, though -- committers publish them when they land the patch :)

heddn’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

I think all is in order here now.

maxocub’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php
@@ -13,35 +15,9 @@
+ * @deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0.
+ * Use \Drupal\content_translation\Plugin\migrate\EntityReferenceTranslationDeriver.
+ * See https://www.drupal.org/node/2991774.

Real nitty, but it should be '@see' with an empty line above. See https://www.drupal.org/core/deprecation#how-class

jcnventura’s picture

Unfortunately, it isn't..

I just tested the patch in a fresh install and found another couple classes that needs to be deprecated and moved to content_translation. I guess the PHP cache was letting me get away with it before for some weird reason:

Drupal\migrate_drupal\Plugin\migrate\source\ContentEntity
Drupal\migrate_drupal\Plugin\migrate\source\ContentEntityDeriver

These are required by the deriver that needs the content_entity plugin.

We should move also the \Drupal\Tests\migrate_drupal\Kernel\Plugin\migrate\source\ContentEntityTest that tests this plugin, and which does not need to be deprecated as it is a test class.

Moving those 3 files, the migration system is able to survive all the cache rebuilds I throw at it...

However, it seems to me that moving all of migrate_drupal into content_translation is not the solution here.. As per #8, the system can now find the deriver.. Perhaps we can somehow make sure that the deriver only works if migrate_drupal is enabled? It honestly makes no sense that now the deriver is listing several d6_entity_reference_translation:node__* and d7_entity_reference_translation:node__* entries on drush ms.

jcnventura’s picture

Status: Needs work » Needs review
StatusFileSize
new2.53 KB
new11.63 KB

I ended up using MigrationDeriverTrait and checking if the content_entity plugin exists..

Seems to be more robust than hardcoding a module name. The trouble is really if that plugin exists or not, so that’s what I tested for.

This way those non-translation classes do not have to be moved, and the deriver doesn't activate if migrate_drupal is not enabled, as that's the module where the 'content_entity' plugin is still located.

phenaproxima’s picture

Another possible approach, which might solve the problem in a more holistic way: why don't we just change the discovery mechanism of the migration plugin manager?

Normally, the plugin system will throw an exception if the deriver doesn't exist -- see \Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator::getDeriverClass() -- but we could easily make the migration plugin manager use a derivative decorator which, if it can't find the deriver class, simply logs an error somewhere and skips that plugin definition (and all of its derivatives).

Framework managers should probably confirm my understanding here, but I don't think this would constitute an API break because the actual deriver discovery mechanisms we use are internal to the plugin manager. As long as we provided some way for developers to know *why* their migration was not discovered (i.e., by logging the error), this might be a very elegant solution indeed, one which works the way the plugin system wants to work, and doesn't require us to duplicate a single line of code.

Status: Needs review » Needs work

The last submitted patch, 13: 2991710-13.patch, failed testing. View results

effulgentsia’s picture

MigrationPluginManager::getDiscovery() already invokes ProviderFilterDecorator. Doesn't that mean that we could solve this by simply adding:

provider:
  - migrate_drupal

to the *_entity_reference_translation.yml files?

effulgentsia’s picture

Or if the module that it's in needs to be explicitly added too, then:

provider:
  - content_translation
  - migrate_drupal

?

jcnventura’s picture

Status: Needs work » Needs review
StatusFileSize
new0 bytes

@effulgentsia, applying #16did get rid of the error. I wonder if the tests pass. No interdiff as this is a completely different approach.

Status: Needs review » Needs work

The last submitted patch, 18: 2991710-18.patch, failed testing. View results

jcnventura’s picture

Status: Needs work » Needs review
StatusFileSize
new1.51 KB

Proper patch file now.

heddn’s picture

+++ b/core/modules/content_translation/migrations/d6_entity_reference_translation.yml
@@ -5,6 +5,8 @@ migration_tags:
+provider:
+  - migrate_drupal

+++ b/core/modules/content_translation/migrations/d7_entity_reference_translation.yml
@@ -5,6 +5,8 @@ migration_tags:
+provider:
+  - migrate_drupal

I think that @effulgentsia is right and we should list both content_translation and migrate_drupal as both need to be enabled.

That said, do we need to log an error for this specific scenario or just silently discard these?

Lastly, still needs tests. Going to try working on this for the next few minutes.

heddn’s picture

StatusFileSize
new1.53 KB

I'm obviously doing something wrong here. This will fail, but it doesn't seem to fail at the right point. At least on local. Going to upload for now and hopefully someone else can move this further along.

maxocub’s picture

Your $definitions array is always empty because, apparently, all migration plugins provided by core should depend on migrate_drupal. I found this by reading this test Drupal\Tests\migrate\Kernel\Plugin\MigrationPluginListTest:

  /**
   * @covers ::getDefinitions
   */
  public function testGetDefinitions() {
    // Make sure retrieving all the core migration plugins does not throw any
    // errors.
    $migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();

    // All the plugins provided by core depend on migrate_drupal.
    $this->assertEmpty($migration_plugins);

    [...]

    // Enable migrate_drupal to test that the plugins can now be discovered.
    $this->enableModules(['migrate_drupal']);

So why does d6_entity_reference_translation do not already depends on migrate_drupal?

maxocub’s picture

Also, the EntityReferenceTranslationDeriver will not derive any migrations unless there is some entity reference fields targeting nodes on the Drupal 8 (destination) site.

Status: Needs review » Needs work

The last submitted patch, 22: 2991710-22_tests-only.patch, failed testing. View results

quietone’s picture

Status: Needs work » Needs review

Some special about the empty source plugin?

grep -ri 'plugin: empty' core/modules/*/migrations/*
core/modules/content_translation/migrations/d6_entity_reference_translation.yml:  plugin: empty
core/modules/content_translation/migrations/d7_entity_reference_translation.yml:  plugin: empty
maxocub’s picture

StatusFileSize
new2.2 KB
new3.76 KB

@quietone: Thanks! That's the reason why those migrations do not depend on migrate_drupal. Do you think adding the provider key to the yaml is a good solution or should we avoid using the empty source plugin?

@heddn: Sorry, I dropped your test and used Drupal\Tests\migrate\Kernel\Plugin\MigrationPluginListTest instead.

Here's a failing patch. No interdiff since this is a small patch and the interdiff would be bigger than the patch.

quietone’s picture

@maxocub, your welcome. I'm very glad it helped. I had been thinking about the problem and reading your comment made me think about requirements. Good team effort.

I think the provider idea is the way to go. However, what we discovered here needs to be documented. I'm thinking in the migrations yml files and in MigationWithFollowUpInterface just after the explanation of the tags. Right now, I even think there could a sub section on the design on the migration yml but I don't know enough of the doc standards.

I'm anxious to see the test results.

The last submitted patch, 27: 2991710-27-test-only.patch, failed testing. View results

quietone’s picture

+++ b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php
@@ -132,6 +141,11 @@ public function testGetDefinitions() {
+    $this->assertArrayHasKey('d6_entity_reference_translation:user__user', $migration_plugins);
...
   }

Let's do an assertArrayNotHasKey before migrate_drupal is enabled. Nevermind, there is an assertion later to confirm that getDefinitions returns an empty array.

The test file has this comment at line 78. I think this would be a good time to add why that migration does not depend on migrate_drupal, especially in light of #23.

    // Enable a module that provides migrations that do not depend on
    // migrate_drupal.
jcnventura’s picture

@maxocub and @quietone: looks way better. Thanks for getting to the bottom of why these migrations did not depend on migrate_drupal, now that they were moved out. I trust this will go in shortly.

We can probably delete the CR node (https://www.drupal.org/node/2991774), now that we're no longer deprecating the deriver in migrate_node.

Even though to be honest, it should be moved to content_translation. Maybe the best solution in OO terms would be to change the migrations as in #27, while doing the move as per #8.

heddn’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php
    @@ -132,6 +141,11 @@ public function testGetDefinitions() {
         // All the plugins provided by core depend on migrate_drupal.
    

    This is not a true statement.

  2. +++ b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php
    @@ -132,6 +141,11 @@ public function testGetDefinitions() {
         $this->assertNotEmpty($migration_plugins);
    ...
    +    $this->assertArrayHasKey('d6_entity_reference_translation:user__user', $migration_plugins);
    +    $this->assertArrayHasKey('d7_entity_reference_translation:user__user', $migration_plugins);
    

    Can we explicitly test the array does not contain these when MD is disabled?

maxocub’s picture

  1. If we just add the word 'migration' does it becomes true: 'All the migration plugins provided by core depend on migrate_drupal.' ?
  2. Yes we can, but it seem silly to test that an array is empty and then test that some keys do not exists in that empty array.
heddn’s picture

Status: Needs work » Reviewed & tested by the community

I take back my comments in #32. That is a true statement and we do have test coverage. Onward to RTBC?

BTW, I re-added a CR to discuss the very specific details of the changes in the parent issue. It probably isn't strictly needed, but we do have a couple pre releases of 8.6 and it would be good to document these things for the poor souls that hit this issue.

jcnventura’s picture

RTBC++

I've been using #27 in development and staging. It does the job.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed 9273076d01 to 8.7.x and 596caa3e51 to 8.6.x. Thanks!

  • alexpott committed 9273076 on 8.7.x
    Issue #2991710 by jcnventura, maxocub, heddn, quietone, phenaproxima,...

  • alexpott committed 596caa3 on 8.6.x
    Issue #2991710 by jcnventura, maxocub, heddn, quietone, phenaproxima,...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

quietone’s picture

Publish change record.