Problem/Motivation

There are a 4-5 process plugins in core which use the following pattern:

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, MigrateProcessInterface $migration_plugin) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->migration = $migration;
    $this->migrationPlugin = $migration_plugin;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
    $migration_plugin_configuration = $configuration + [
      'migration' => 'd6_filter_format',
    ];

    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $migration,
      $container->get('plugin.manager.migrate.process')->createInstance('migration', $migration_plugin_configuration, $migration)
    );
  }

These process plugins need to look up a previous migration result, and do so by creating a copy of the (deprecated) migration plugin.

Additionally, the migration_lookup plugin is problematic. It has a number of inconsistencies in its output, uses deprecated methods,

Proposed resolution

Let's turn the result lookup and stub creation into injectable services. The services can be directly injected into the process plugins that require them, and the migration lookup plugin can be a wrapper around these services.

Remaining tasks

Define interfaces
Define services
Refactor migration lookup plugin to use these services
Write tests.
Refactor other plugins that use migration_lookup to use the service instead. (follow ups)

User interface changes

None

API changes

Provide new migration lookup service and interface

Data model changes

None

CommentFileSizeAuthor
#59 3004927-59.patch1.85 KBquietone
#51 interdiff.3004927.45-51.txt20.77 KBmikelutz
#51 3004927-51.drupal.Create-Migration-Lookup-service.patch69.8 KBmikelutz
#45 interdiff.3004927.43-45.txt3.34 KBmikelutz
#45 3004927-45.drupal.Create-Migration-Lookup-service.patch65.57 KBmikelutz
#43 interdiff.3004927.41-43.txt3.6 KBmikelutz
#43 3004927-43.drupal.Create-Migration-Lookup-service.patch68.91 KBmikelutz
#41 interdiff.3004927.40-41.txt2.51 KBmikelutz
#41 3004927-41.drupal.Create-Migration-Lookup-service.patch67.25 KBmikelutz
#40 interdiff.3004927.38-40.txt16.31 KBmikelutz
#40 3004927-40.drupal.Create-Migration-Lookup-service.patch66.89 KBmikelutz
#38 interdiff.3004927.37-38.txt14.62 KBmikelutz
#38 3004927-38.drupal.Create-Migration-Lookup-service.patch68.98 KBmikelutz
#37 3004927-37.drupal.Create-Migration-Lookup-service.patch66.08 KBmikelutz
#37 Interdiff.3004927.31-37.txt9.8 KBmikelutz
#31 interdiff.3004927.27-31.txt662 bytesmikelutz
#31 3004927-31.drupal.Create-Migration-Lookup-service.patch67.15 KBmikelutz
#28 interdiff.3004927.26-27.txt13.57 KBmikelutz
#28 3004927-27.drupal.Create-Migration-Lookup-service.patch67.03 KBmikelutz
#26 interdiff.3004927.25-26.txt19.78 KBmikelutz
#26 3004927-26.drupal.Create-Migration-Lookup-service.patch64.63 KBmikelutz
#25 interdiff.3004927.23-25.txt445 bytesmikelutz
#25 3004927-25.drupal.Create-Migration-Lookup-service.patch53.85 KBmikelutz
#23 interdiff.3004927.22-23.txt9.62 KBmikelutz
#23 3004927-23.drupal.Create-Migration-Lookup-service.patch53.83 KBmikelutz
#22 interdiff.3004927.20-22.txt3.1 KBmikelutz
#22 3004927-22.drupal.Create-Migration-Lookup-service.patch45.38 KBmikelutz
#20 interdiff.3004927.18-20.txt7.27 KBmikelutz
#20 3004927-20.drupal.Create-Migration-Lookup-service.patch45.36 KBmikelutz
#18 3004927-18.drupal.Create-Migration-Lookup-service.patch45.3 KBmikelutz
#18 interdiff.3004927.16-18.txt18.79 KBmikelutz
#16 interdiff.3004927.13-16.txt9.61 KBmikelutz
#16 3004927-16.drupal.Create-Migration-Lookup-service.patch39.35 KBmikelutz
#11 interdiff.3004927.10-11.txt20.54 KBmikelutz
#11 3004927-11.drupal.Create-Migration-Lookup-service.patch32.47 KBmikelutz
#10 interdiff.3004927.9-10.txt5.85 KBmikelutz
#10 3004927-10.drupal.Create-Migration-Lookup-service.patch19.17 KBmikelutz
#9 interdiff.3004927.8-9.txt749 bytesmikelutz
#9 3004927-9.drupal.Create-Migration-Lookup-service.patch18.03 KBmikelutz
#8 interdiff.3004927.6-8.txt16.22 KBmikelutz
#8 3004927-8.drupal.Create-Migration-Lookup-service.patch17.96 KBmikelutz
#6 interdiff.3004927.4-6.txt9.82 KBmikelutz
#6 3004927-6.drupal.Create-Migration-Lookup-service.patch19.55 KBmikelutz
#5 3004927-4.drupal.Create-Migration-Lookup-service.patch11.52 KBmikelutz

Comments

mikelutz created an issue. See original summary.

heddn’s picture

Issue tags: +Migrate critical

Triaging issue queue.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

mikelutz’s picture

Assigned: Unassigned » mikelutz
Issue summary: View changes
Status: Active » Needs work

Initial crack at Migration lookup and stubbing services. Decided to make them separate services for clarity

mikelutz’s picture

mikelutz’s picture

StatusFileSize
new19.55 KB
new9.82 KB

Adding updates for the first process plugin for testing.

heddn’s picture

  1. +++ b/core/modules/file/src/Plugin/migrate/process/d6/FieldFile.php
    @@ -35,32 +43,49 @@ class FieldFile extends ProcessPluginBase implements ContainerFactoryPluginInter
    +  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, $migrate_lookup) {
    

    The constructors of plugins are explicitly not part of the BC contract. What is the reason we are doing this complicated BC layer here? Do we have a lot of folks that are extending file field?

  2. +++ b/core/modules/migrate/src/MigrateLookup.php
    @@ -0,0 +1,66 @@
    +  public function lookup($migration, array $source_ids) {
    +    if (!is_array($source_ids)) {
    

    I'm pretty sure this requires $source_ids to be an array based on the type hint.

  3. +++ b/core/modules/migrate/src/MigrateStub.php
    @@ -0,0 +1,108 @@
    +  public function stub($migration, $source_ids, array $default_values = []) {
    ...
    +    if (!is_array($source_ids)) {
    

    Can we type hint an array here?

mikelutz’s picture

StatusFileSize
new17.96 KB
new16.22 KB

Rather broken WIP, posting just so I don't lose progress. Please ignore.

mikelutz’s picture

StatusFileSize
new18.03 KB
new749 bytes

Rerolling, fixing a few tests, planning to start moving on this again.

mikelutz’s picture

StatusFileSize
new19.17 KB
new5.85 KB

A few more test fixes, trying to get this PoC mostly passing before I work on refactoring and tests.

mikelutz’s picture

Status: Needs work » Needs review
StatusFileSize
new32.47 KB
new20.54 KB

More test fixes before cleanup.

Status: Needs review » Needs work

The last submitted patch, 11: 3004927-11.drupal.Create-Migration-Lookup-service.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mikelutz’s picture

Status: Needs work » Needs review
StatusFileSize
new417 bytes
new32.5 KB

oops...

mikelutz’s picture

StatusFileSize
new32.51 KB
new868 bytes

Status: Needs review » Needs work

The last submitted patch, 14: 3004927-13.drupal.Create-Migration-Lookup-service.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mikelutz’s picture

Status: Needs work » Needs review
StatusFileSize
new39.35 KB
new9.61 KB

Hopefully this is the last iteration before cleanup.

andypost’s picture

I suggest stubMigration() as method name

+++ b/core/modules/migrate/src/MigrateStub.php
@@ -0,0 +1,136 @@
+  public function stub($migration_id, array $source_ids, array $default_values = [], $derivative_id = NULL) {
+    try {
+      $migrations = $this->migrationPluginManager->createInstances($migration_id);
...
+    return $this->doStub($migration, $source_ids, $default_values);
...
+    try {
+      $destination_ids = $destination->import($row);
...
+      return $destination_ids;

+++ b/core/modules/migrate/src/MigrateStubInterface.php
@@ -0,0 +1,27 @@
+   * Creates a stub.
...
+   * @return array
+   *   An array of destination ids for the new stub.
+   */
+  public function stub($migration_id, array $source_ids, array $default_values = [], $derivative = NULL);

Something wrong with naming here
- it creates stub just internally to return destination IDs
- also under the hood it importing row and that's what it returns

mikelutz’s picture

StatusFileSize
new18.79 KB
new45.3 KB

Beginning cleanup here.

I suggest stubMigration() as method name

I might change it to createStub(). A stub migration is a completely different thing than a stubbed destination import, which is what we are doing here.

Something wrong with naming here
- it creates stub just internally to return destination IDs
- also under the hood it importing row and that's what it returns

That's what a stub is though. Exactly what we are looking to have happen at the moment. There is a slight discrepency in the documented return value of MigrateDestinationInterface::import() and what is expected by idMap, but that is how the whole system currently functions. I hope to clean it up somewhat here but I'm a bit limited for BC reasons.

heddn’s picture

Status: Needs review » Needs work

+1 on general approach. Some minor feedback below.

  1. +++ b/core/modules/migrate/src/MigrateLookup.php
    @@ -0,0 +1,92 @@
    + * Provides a migration lookup and stubbing service.
    ...
    +   * @var \Drupal\Core\Logger\LoggerChannelInterface
    

    This doesn't stub any more.

  2. +++ b/core/modules/migrate/src/MigrateLookup.php
    @@ -0,0 +1,92 @@
    +      $this->logger->error("Could not create an instance of the migration plugin {migration_id} for lookup. The error was: {message}", ['migration_id' => $migration_id, 'message' => $e->getMessage()]);
    

    Nit, we've been typically using double quotes, '"', around variables in error strings like this.

  3. +++ b/core/modules/migrate/src/MigrateStub.php
    @@ -0,0 +1,127 @@
    +  public function stub(string $migration_id, array $source_ids, array $default_values = []) {
    

    I agree, we need something like "create" as part of this method name.

mikelutz’s picture

Status: Needs work » Needs review
StatusFileSize
new45.36 KB
new7.27 KB

Here's an iteration addressing @heddn's feedback specifically. On to more tests. :-)

Status: Needs review » Needs work

The last submitted patch, 20: 3004927-20.drupal.Create-Migration-Lookup-service.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mikelutz’s picture

Priority: Normal » Major
Status: Needs work » Needs review
StatusFileSize
new45.38 KB
new3.1 KB

Fix existing test and coding standards. bumping to major as this blocks D9 deprecation work around the old Migration plugin.

mikelutz’s picture

StatusFileSize
new53.83 KB
new9.62 KB

Here's a test suite for the lookup service, I just need a test suite for the stub service.

mikelutz’s picture

Status: Needs review » Needs work
mikelutz’s picture

Status: Needs work » Needs review
StatusFileSize
new53.85 KB
new445 bytes

If I had a nickel...

mikelutz’s picture

StatusFileSize
new64.63 KB
new19.78 KB

Hopefully the rest of the tests I need. Need to run it through testbot while I review for any more cleanup I want to do.

Status: Needs review » Needs work
mikelutz’s picture

Status: Needs work » Needs review
StatusFileSize
new67.03 KB
new13.57 KB

Alright, I think this is ready for review now. I'll address in advance my least favorite part of this patch, which is the CreateStubRaw() method on the stub service. I absolutely hate it, but have spent a ton of time trying to come up with a better solution and I'm convinced that this is it.

#2890690: MigrateLookup plugin has inconsistent return values. touches on the problem, but the issue is that the return value from MigrateDestinationInterface::import() is very poorly defined, @return mixed The entity ID or an indication of success. and yet the entire id mapping and lookup systems are based on the assumption that ::import is returning an array of destination identifiers. Thankfully all the destination plugins do return this array, but they are inconsistently keyed. The core destination plugins return a numerically indexed array of destination identifiers, but some rather important contrib destinations (I'm looking at you, entity_reference_revisions....) return an array of identifiers indexed by the destination keys. The id mapping system compensates for this, but migration_lookup returns the value of ::import directly if a stub is created, which may not be consistent with what the id map returns if a stub is not created.

Long term I would like to fix this inconsistency in the destination plugins themselves (and starting by updating the documented return value for ::import(). Short and medium term, It really makes the most sense for these services to consistently return associative arrays. Unfortunatly, the current migration_lookup still needs to return the raw value from ::import, which is why I've added the internal createStubRaw() method. The intention is to have a followup to create a new migrate_lookup plugin that fixes a number of problems with migration_lookup that can't be fixed easily in a BC manner and deprecate migration_lookup and createStubRaw() at the same time.

The other option I considered was to simply not refactor migration_lookup to use the new stub service, and just have it stub on its own as it always has. I could go this route if it's deemed best, but I like the additional implicit test coverage that comes from using the service.

Also of note, the createStub method contains a default_values parameter that isn't currently used in migration_lookup. It's long been a request in the community to have the ability to have less gibberish values for things like node titles when stubs are created. I expect to use the $default_values parameter in a new migrate_lookup plugin to allow people to do this, and it is also available for people to use in custom process plugins so that they can have this ability.

Finally, I introduced a migrate logger channel in this patch, one that I hope to inject throughout the migration system. Another big complaint about the migration system is that there is very little information available when things go wrong. This patch is the first to add logging to the migration system, which should help people track down issues with migration lookups and stubbing.

heddn’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/migrate/migrate.services.yml
    @@ -30,3 +30,12 @@ services:
    +  migrate.lookup:
    ...
    +  migrate.stub:
    

    I like these as 2 different services.

  2. +++ b/core/modules/migrate/src/MigrateStub.php
    @@ -0,0 +1,180 @@
    +  public function createStubRaw(string $migration_id, array $source_ids) {
    

    If we plan to mark this as deprecated, let's mark it as @internal. Would that help in any way?

  3. +++ b/core/modules/migrate/src/MigrateStub.php
    @@ -0,0 +1,180 @@
    +      $stub = array_combine(array_keys($migration->getDestinationPlugin()->getIds()), $stub);
    

    A description and example of what we are doing here would help.

mikelutz’s picture

+++ b/core/modules/migrate/src/MigrateStubInterface.php
@@ -0,0 +1,51 @@
+  /**
+   * Creates a stub, returning the raw destination return value.
+   *
+   * This internal method is provided for backwards compatibility reasons. You
+   * should use ::createStub instead.  the migration_lookup plugin historically
+   * has returned the raw return value of ::import when creating stubs. Core
+   * destination plugins typically return a numerically indexed array, but some
+   * contrib destination plugins return an associatively indexed array. This
+   * method allows migration_lookup to preserve its current behaviour.  When
+   * the migration_lookup plugin is deprecated and replaced, this method can be
+   * deprecated and removed too.
+   *
+   * @param string $migration_id
+   *   The migration to stub.
+   * @param array $source_ids
+   *   An array of source ids.
+   *
+   * @return array|false
+   *   An array of destination ids for the new stub, as returned by the
+   *   destination plugin's ::import method, or false if the stub failed.
+   *
+   * @internal
+   */
+  public function createStubRaw(string $migration_id, array $source_ids);

Oh, it's internal. :-)

mikelutz’s picture

Status: Needs work » Needs review
StatusFileSize
new67.15 KB
new662 bytes
heddn’s picture

Status: Needs review » Reviewed & tested by the community

I like it. Let's just bounce over to RTBC and see how that fares.

Status: Reviewed & tested by the community » Needs work
mikelutz’s picture

Status: Needs work » Reviewed & tested by the community
mikelutz’s picture

Title: Create Migration Lookup service » Create Migration Lookup and Stub services
Issue summary: View changes

Updated IS and CR

larowlan’s picture

Status: Reviewed & tested by the community » Needs work

This looks great, nice cleanup and solid test-coverage.

  1. +++ b/core/modules/migrate/src/MigrateLookup.php
    @@ -0,0 +1,96 @@
    +  public function lookup(string $migration_id, array $source_ids) {
    
    +++ b/core/modules/migrate/src/MigrateLookupInterface.php
    @@ -0,0 +1,26 @@
    +  public function lookup(string $migration_id, array $source_ids);
    

    note to self: we're using a scalar type-hint here so this is solid 8.8 only territory.

    but we don't use a return type-hint...but we have no cases of that in core (yet) so we shouldn't start doing that here in my opinion

  2. +++ b/core/modules/migrate/src/MigrateLookup.php
    @@ -0,0 +1,96 @@
    +        $this->logger->error('Could not create an instance of the migration plugin "{migration_id}" for lookup. The plugin was not found.', ['migration_id' => $migration_id]);
    ...
    +      $this->logger->error('Could not create an instance of the migration plugin "{migration_id}" for lookup. The error was: "{message}"', ['migration_id' => $migration_id, 'message' => $e->getMessage()]);
    

    These are fairly silent errors. Should we return a value object instead that has a ::lookupSucceeded method and a ::getResults so calling code can work out if/what went wrong? Or throw exceptions?

  3. +++ b/core/modules/migrate/src/MigrateLookup.php
    @@ -0,0 +1,96 @@
    +      $this->logger->error('Could not retrieve destination ids for provided source ids.  The error was: "{message}"', ['message' => $e->getMessage()]);
    

    Same here

  4. +++ b/core/modules/migrate/src/MigrateStub.php
    @@ -0,0 +1,182 @@
    +    if (array_keys($stub) === range(0, count($stub) - 1)) {
    +      // If the return from ::import is numerically indexed, index it
    +      // associatively using the destination id keys.
    +      $stub = array_combine(array_keys($migration->getDestinationPlugin()->getIds()), $stub);
    +    }
    +    return $stub;
    

    It seems that the only difference between createStubRaw and createStub is this hunk - should we add a fourth parameter to createStub something like bool $keyByDestinationIds or similar and then just have the one method? Or does that paint us into a corner if/when we remove the issues in the various plugins where the return is not consistent?

  5. +++ b/core/modules/migrate/src/MigrateStub.php
    @@ -0,0 +1,182 @@
    +      $this->logger->error('Cannot stub derivable migration "{migration_id}".  You must specify the id of a specific derivative to stub.', ['migration_id' => $migration_id]);
    +      return FALSE;
    ...
    +      $this->logger->error('Cannot create stub for migration "{migration_id}". Expected and provided source id counts do not match.', ['migration_id' => $migration->id()]);
    +      return FALSE;
    

    Again, these are very silent failures. It is very reminiscent of a lot of the stuff we used to have in file.inc that now (thankfully) throws actual exceptions so you don't need to go trawling through logs to work out why your image styles aren't getting created :)

    Is there are reason why we don't throw exceptions for these things that are clearly wrong. e.g. the 'you must specify the id of a specific derivative' is basically a bad method call or logic exception - shouldn't we convey that? Its not really something an admin/site owner (who logs are generally for) can fix, its something the calling code (developer) should fix - so an exception might be more appropriate?

  6. +++ b/core/modules/migrate/src/MigrateStubInterface.php
    @@ -0,0 +1,51 @@
    +   * @internal
    ...
    +  public function createStubRaw(string $migration_id, array $source_ids);
    

    If this is internal, we shouldn't put it on the interface - as that conveys a mixed message - and once its there, its forever.

  7. +++ b/core/modules/migrate/src/Plugin/migrate/process/MigrationLookup.php
    @@ -158,18 +186,15 @@ public static function create(ContainerInterface $container, array $configuratio
    +    if (!is_array($lookup_migration_ids)) {
    +      $lookup_migration_ids = [$lookup_migration_ids];
         }
    

    can't we just do a hard cast here?

    $lookup_migration_ids = (array) $lookup_migration_ids;
    

    And avoid the if?

mikelutz’s picture

Status: Needs work » Needs review
StatusFileSize
new9.8 KB
new66.08 KB

#1: okay
#2,3,5: I have not addressed these in this patch, but I am working on them. The reason that I have tried very hard to not throw exceptions is that this code runs during a data migration, so If we throw a random exception that the migration executable is not configured to catch and gracefully shutdown from then we can leave the system in an undesirable state. That having been said, perhaps the service is not the place to catch and handle the exceptions, perhaps we need to catch them in the process plugin and either handle them there or rethrow them as MigrateExceptions that the migration system can then catch and shut down the migration safely.
#4: At points during development, I've coded it up both ways, and ultimately picked going with a second method because I thought it was cleaner. Here's a version that implements it as an optional 4th parameter that isn't on the interface. I think it works fine this way too. If the service is replaced, the extra parameter is ignored, and by setting the default to NULL instead of TRUE, we can choose to deprecate that parameter later, if and when we are ready.
#6: we've removed the method, so this is no longer an issue.
#7: I only changed the variable name there, not the logic, but yes, we can cast there. I was originally worried about casting if somehow we got a stringable object there, but the value comes from a migration yml file, so it can only be a string or an array.

mikelutz’s picture

StatusFileSize
new68.98 KB
new14.62 KB

Here's a refactor of the migrate.lookup service to throw exceptions, and then make sure they are caught and rethrown as a MigrateException in the process plugin.

Status: Needs review » Needs work

The last submitted patch, 38: 3004927-38.drupal.Create-Migration-Lookup-service.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mikelutz’s picture

Status: Needs work » Needs review
StatusFileSize
new66.89 KB
new16.31 KB

Refactor on the stub service. This should address all issues, but might still have a failing test and/or coding standard fix that I missed.

mikelutz’s picture

So, throwing an exception on a derived migration is a bc break, those should just not be stubbed in the current process plugin. When we build a new one, we will require derived migrations to have a no_stub:true configuration..

mikelutz’s picture

I also realized that I'm going to need to have the ability to pass an array of ids into the lookup service, so I've updated the typehints and documentation on this.

heddn’s picture

Status: Needs review » Needs work

Not seeing much to comment upon.

  1. +++ b/core/modules/migrate/src/MigrateException.php
    @@ -46,7 +46,7 @@ class MigrateException extends \Exception {
    +    parent::__construct($message, $code, $previous);
    

    This seems out of scope.

  2. +++ b/core/modules/migrate/src/MigrateLookupResult.php
    @@ -0,0 +1,124 @@
    +  public function lookupSucceeded(): bool {
    ...
    +  public function setLookupSucceeded(bool $lookupSucceeded) {
    ...
    +  public function getMessage() {
    ...
    +  public function setMessage(string $message) {
    ...
    +  public function getResults(): array {
    ...
    +  public function setResults(array $results) {
    

    Let's be consistent w/ our type and return type hinting.

  3. +++ b/core/modules/migrate/src/MigrateStub.php
    @@ -0,0 +1,120 @@
    +  public function createStub(string $migration_id, array $source_ids, array $default_values = [], $key_by_destination_ids = NULL) {
    

    Do we need to trigger error (yet) on this as deprecated for keying by destination id?

mikelutz’s picture

Assigned: mikelutz » Unassigned
Status: Needs work » Needs review
StatusFileSize
new65.57 KB
new3.34 KB
  1. I've added some chained exceptions, and noticed they don't actually pass through. Not strictly necessary for this to work, I can correct the MigrateException bug in a follow-up though, so I've removed the change.
  2. Oh my, that MigrateLookupResult value object was something I was playing with but didn't finist and ultimately decided against. It was not supposed to be in the patch..
  3. Not yet, the appropriate time to do that is when we deprecate the migration_lookup process plugin, we can't really do it before then. I may try to introduce a new plugin soon, but don't plan on deprecating migration_lookup before Drupal 9. It's much too heavily used and late in the D8 cycle, IMO.
quietone’s picture

Status: Needs review » Needs work

Some feedback on coding standards and comments.

  1. +++ b/core/modules/migrate/src/MigrateStub.php
    @@ -0,0 +1,120 @@
    +      throw new \LogicException(sprintf('Cannot stub derivable migration "%s".  You must specify the id of a specific derivative to stub.', $migration_id));
    
    +++ b/core/modules/migrate/src/Plugin/migrate/process/MigrationLookup.php
    @@ -130,13 +140,26 @@ class MigrationLookup extends ProcessPluginBase implements ContainerFactoryPlugi
    +      @trigger_error("Not passing the migrate lookup service as the fifth parameter to " . __METHOD__ . " is deprecated in drupal:8.8.0 and will throw a type error in drupal:9.0.0. Pass an instance of \Drupal\migrate\MigrateLookupInterface. See https://www.drupal.org/node/3047268", E_USER_DEPRECATED);
    ...
    +      @trigger_error("Not passing the migrate stub service as the sixth parameter to " . __METHOD__ . " is deprecated in drupal:8.8.0 and will throw a type error in drupal:9.0.0. Pass an instance of \Drupal\migrate\MigrateStubInterface. See https://www.drupal.org/node/3047268", E_USER_DEPRECATED);
    
    @@ -181,10 +200,24 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
    +        throw new MigrateException(sprintf("A %s was thrown while processing this migration lookup", gettype($e)), 0, $e);
    
    @@ -193,45 +226,35 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
    +        throw new MigrateException(sprintf("A(n) %s was thrown while attempting to stub.", 0, $e));
    

    Values should be surrounded by single quotes, not double quotes.

  2. +++ b/core/modules/migrate/src/MigrateLookup.php
    @@ -0,0 +1,74 @@
    +   * Perform a direct lookup.
    

    My first though was 'Is there an indirect lookup?'. Can we explain what direct means here.

  3. +++ b/core/modules/migrate/src/MigrateLookup.php
    @@ -0,0 +1,74 @@
    +   *   Thrown when $source_id_values contains unknown keys, or is the wrong
    +   *   length.
    

    Can we have something clearer than 'wrong length' as it sounds like a problem with string length whereas it is about finding extra ids.

  4. +++ b/core/modules/migrate/src/Plugin/migrate/process/MigrationLookup.php
    @@ -156,20 +180,15 @@ public static function create(ContainerInterface $container, array $configuratio
         /** @var \Drupal\migrate\Plugin\MigrationInterface[] $lookup_migrations */
    

    No longer used.

  5. +++ b/core/modules/migrate/src/Plugin/migrate/process/MigrationLookup.php
    @@ -193,45 +226,35 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
    +        throw new MigrateException(sprintf("A(n) %s was thrown while attempting to stub.", 0, $e));
    

    Seem to have an extra argument, '0'. Can be removed.

  6. +++ b/core/modules/migrate/tests/src/Kernel/MigrateStubTest.php
    @@ -0,0 +1,130 @@
    +   * The migrate stub service..
    

    s/../.

quietone’s picture

  1. +++ b/core/modules/migrate/src/Plugin/migrate/process/MigrationLookup.php
    @@ -193,45 +226,35 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
    -      $stub_row = $this->createStubRow($values + $stub_migration->getSourceConfiguration(), $source_ids);
    

    With this removed the createStubRow method is no longer used. Remove it?

  2. +++ b/core/modules/migrate/src/MigrateStub.php
    @@ -0,0 +1,120 @@
    +    $migrations = $this->migrationPluginManager->createInstances($migration_id);
    

    createInstances expects the parameter to be an array but a string is being passed.

  3. +++ b/core/modules/migrate/tests/src/Unit/MigrateLookupTest.php
    @@ -0,0 +1,63 @@
    +    $lookup = new MigrateLookup($plugin_manager->reveal(), $logger->reveal());
    

    Why pass the logger?

  4. +++ b/core/modules/migrate/tests/src/Unit/MigrateStubTest.php
    @@ -0,0 +1,102 @@
    +    $stub = new MigrateStub($this->migrationPluginManager->reveal(), $this->logger->reveal());
    ...
    +    $stub = new MigrateStub($this->migrationPluginManager->reveal(), $this->logger->reveal());
    ...
    +    $stub = new MigrateStub($this->migrationPluginManager->reveal(), $this->logger->reveal());
    

    Why pass the logger?

  5. +++ b/core/modules/migrate/src/MigrateStub.php
    @@ -0,0 +1,120 @@
    +   * @param bool $key_by_destination_ids
    

    Regarding this parameter, In the comment in #28 paragraph 2 mikelutz states

    Thankfully all the destination plugins do return this array, but they are inconsistently keyed. The core destination plugins return a numerically indexed array of destination identifiers, but some rather important contrib destinations (I'm looking at you, entity_reference_revisions....) return an array of identifiers indexed by the destination keys.

    I feel like this info should be in the comment, that is, that core returns one type of array and that contrib may be different.

quietone’s picture

And finally,

+++ b/core/modules/migrate/tests/modules/migrate_lookup_test/migrate_lookup_test.info.yml
@@ -0,0 +1,8 @@
+name: 'Migration Lookup Test'
...
+description: 'Provides test migrations to test migration lookup service.'

+++ b/core/modules/migrate/tests/modules/migrate_lookup_test/migrations/sample_lookup_migration.yml
@@ -0,0 +1,20 @@
+label: "Sample Lookup Migration"
...
+      title: "Node 1"

Quotes are not needed. They all can be removed. Of course, this applies to the other test module as well.

quietone’s picture

+++ b/core/modules/migrate/tests/src/Kernel/MigrateLookupTest.php
@@ -0,0 +1,162 @@
+class MigrateLookupTest extends MigrateTestBase {

As soon I pressed 'enter' I remembered this one as well. The test coverage in all the tests looks good to me. However, I'd like to see MigrateLookupTest also test with an id that is a string. A common example is a language code.

quietone’s picture

Came back to look at something and noticed a typo.

+++ b/core/modules/migrate/tests/src/Unit/MigrateStubTest.php
@@ -0,0 +1,102 @@
+   * {@inheritDoc}

s/inheritDoc/inheritdoc/

mikelutz’s picture

Status: Needs work » Needs review
StatusFileSize
new69.8 KB
new20.77 KB

I agreed with and addressed all feedback. Specifically:

46.1 - I replaced everything with single quotes.
46.2 - eh, originally that was the internal method that performed a direct lookup against a single migration versus what was going to be a couple wrappers/helpers that used that method to perform lookups by tag, or on multiple migrations or whatever. Regardless, I clarified.
46.3 went with 'wrong number of keys'
46.4 fixed
46.5 fixed to the original intention, also updated a previous chain exception to use the original code rather than a hard-coded zero
46.6 fixed
47.1 I deprecated it, though I want to deprecate the entire plugin at some point..
47.2 I changed to an array, though for the record, createInstances says

   * @param array $id
   *   Either the plugin ID or the base plugin ID of the plugins being
   *   instantiated. Also accepts an array of plugin IDs and an empty array to
   *   load all plugins.

The method logic works for both strings and arrays, and the signature doesn't typehint array, so that should probably be @param string|string[]
47.3 The logger was a leftover from a previous iteration on this service. The tests still passed so I didn't notice. fixed.
47.4 The logger was a leftover from a previous iteration on this service. The tests still passed so I didn't notice. fixed.
47.5 Updated the param documentation
48 I removed the quotes as we don't seem to have them in other test migrations, although most everywhere else in core, values in a yml file with spaces seem to be surrounded by single quotes (not double like I had) should we update them all?
49 I added a test migration with string keys.
50 fixed.

I also removed some php 7 only code that we are not supposed to be using until 8.7.0 is EOL

quietone’s picture

@mikelutz, thanks for the fixes.

There are two issue about quotes in yml files, though specfic to info.yml.
#2116897: Adopt identical YAML formatting for info.yml files
#2994928: Quote consistency in .info.yml files for Core modules

Why don't we make an issue to fix the doc on createInstances? I can do that later today if noone else gets to it.

quietone’s picture

Status: Needs review » Reviewed & tested by the community

And this can go back to RTBC. It was back in #32 and later larowlan reviewed it noting that it " looks great, nice cleanup and solid test-coverage". Since then it has been cleanup up some more and another test added.

larowlan’s picture

larowlan’s picture

Status: Reviewed & tested by the community » Needs work

Just a couple of minor things, but otherwise, this is looking good

  1. +++ b/core/modules/migrate/src/MigrateLookup.php
    @@ -0,0 +1,74 @@
    +      if ($result = $this->doLookup($migration, $source_id_values)) {
    +        $results = array_merge($results, $result);
    +      }
    

    ubernit: array_merge with an empty array is a nil-op, so we probably don't need the if here i.e. we could write this as

    $results = array_merge($results, $this->doLookup(...));
    
  2. +++ b/core/modules/migrate/src/MigrateStub.php
    @@ -0,0 +1,130 @@
    +   * @throws \Drupal\Component\Plugin\Exception\PluginException
    +   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
    +   * @throws \Drupal\migrate\MigrateException
    

    These don't meet PHPCS, we should be detailing the scenarios in which this can happen or similar.

larowlan’s picture

Status: Needs work » Reviewed & tested by the community

  • larowlan committed 63ecb2d on 8.8.x
    Issue #3004927 by mikelutz, quietone, heddn, larowlan, andypost: Create...
larowlan’s picture

Status: Reviewed & tested by the community » Fixed

Fixed on commit

diff --git a/core/modules/migrate/tests/src/Unit/MigrateLookupTest.php b/core/modules/migrate/tests/src/Unit/MigrateLookupTest.php
index f6c7dfe3d7..417b08a864 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateLookupTest.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateLookupTest.php
@@ -3,7 +3,6 @@
 namespace Drupal\Tests\migrate\Unit;

 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
-use Drupal\Core\Logger\LoggerChannelInterface;
 use Drupal\migrate\MigrateLookup;
 use Drupal\migrate\Plugin\MigrateDestinationInterface;
 use Drupal\migrate\Plugin\MigrateIdMapInterface;
diff --git a/core/modules/migrate/tests/src/Unit/MigrateStubTest.php b/core/modules/migrate/tests/src/Unit/MigrateStubTest.php
index 8f3816c5e0..0a026ffc52 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateStubTest.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateStubTest.php
@@ -3,7 +3,6 @@
 namespace Drupal\Tests\migrate\Unit;

 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
-use Drupal\Core\Logger\LoggerChannelInterface;
 use Drupal\migrate\MigrateStub;
 use Drupal\migrate\Plugin\MigrateDestinationInterface;
 use Drupal\migrate\Plugin\MigrateIdMapInterface;
diff --git a/core/modules/migrate/tests/src/Unit/process/LegacyMigrationLookupTest.php b/core/modules/migrate/tests/src/Unit/process/LegacyMigrationLookupTest.php
index a28c9d8a31..96a0685356 100644
--- a/core/modules/migrate/tests/src/Unit/process/LegacyMigrationLookupTest.php
+++ b/core/modules/migrate/tests/src/Unit/process/LegacyMigrationLookupTest.php
@@ -4,7 +4,6 @@

 use Drupal\migrate\Plugin\migrate\process\MigrationLookup;
 use Drupal\migrate\Plugin\MigrationInterface;
-use Drupal\Tests\migrate\Unit\MigrateTestCase;

 /**
  * Tests deprecated features of the migration lookup plugin.
@@ -31,7 +30,7 @@ public function testCreateStubRow() {
     /** @var \Drupal\migrate\Row $row */
     $row = $method->invoke($lookup, [
       'id' => 1,
-      'value' => 'test'
+      'value' => 'test',
     ], ['id' => ['type' => 'integer']]);
     $this->assertTrue($row->isStub());
     $this->assertSame('test', $row->get('value'));

Committed 63ecb2d and pushed to 8.8.x. Thanks!

Published change record

quietone’s picture

Status: Fixed » Needs review
StatusFileSize
new1.85 KB

I'm having trouble with a test in commerce migrate and have tracked it to this issue. I create a new test, MigrateUrlAliasNoTranslationTest.php that does not run d7_node_translation. This test fails on HEAD but passes when the commit in this issue is reverted. This patch has just that new test.

I have learned that when a migration does not exist, in this case d6_node_translation, that migrate lookup with throw a pluginnotfound exception which is caught when processing the pipeline and then import is never run. Before this patch, when a migration was not found the pipeline would store an empty value in the destination property ($row->setEmptyDestinationProperty($destination)) and the pipeline would continue to be processed.

mikelutz’s picture

Status: Needs review » Fixed
quietone’s picture

Oops the test class is incorrectly named. Just a reflection of how well my day is going. I am really looking forward to the long walk into town soon for dinner and the symphony.

But I see that mikelutz has fixed that in the followup. Thanks.

mikelutz’s picture

Yes, I caught that and did a little refactoring. The test itself was valid, and I saw what you were trying to do.

Status: Fixed » Closed (fixed)

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

chicodasilva’s picture

Where can I find an example of the usage of the Lookup service?

Francisco