Problem/Motivation

In Drupal\migrate\Plugin\MigrateMessageInterface, getMessageIterator() is declared as returning \Iterator. According to the PHP page on Iterator, that means the return value should implement five methods, including rewind().

As discussed in #2714529-29: Add source and destination IDs to the data returned by getMessageIterator() , the implementation of getMessageIterator() in Drupal\migrate\Plugin\migrate\id_map\Sql returns an object of class Drupal\Core\Database\Statement, which does not implement rewind().

Proposed resolution

Since this is a public interface, we do not have the option of changing the return type. See the discussion in #5 and #6. Instead, we will add a new method that declares its return value as \Traversable and deprecate the old method.

Completed tasks

  • Add a new method MigrateMessageInterface::getMessages() that duplicates getMessageIterator() except that the doc block reads @return \Traversable. See the patch in #2.
  • In Drupal\migrate\Plugin\migrate\id_map\Sql and Drupal\migrate\Plugin\migrate\id_map\NullIdMap, copy the implementation of getMessageIterator() to implement the new method getMessages().
  • Make both implementations of getMessageIterator() wrappers for getMessages().
  • Add a deprecation notice to getMessageIterator(). See Drupal core deprecation policy.
  • Search for uses of getMessageIterator() and replace them with getMessages(). For example, grep -ri getMessageIterator core.
  • Add a change record. See Contributor task: Write up a change record for a Drupal core issue.
  • Add a release notes snippet to this issue description.
  • Reroll the patch from #18. See Rerolling patches or Contributor task: Re-roll a Drupal core patch.

Remaining tasks

User interface changes

None

API changes

This is an API change, since MigrateMessageInterface::getMessageIterator() is a public function.

Data model changes

None

Release notes snippet

The method getMessageIterator() from Drupal\migrate\Plugin\MigrateMessageInterface is now deprecated. Use getMessages() instead.

Comments

benjifisher created an issue. See original summary.

christinlepson’s picture

StatusFileSize
new762 bytes

Patch to change the doc block to read @return \Traversable and change the return description from "Retrieves an iterator over the message rows." to "Retrieves a traversable object of the message rows."

benjifisher’s picture

Status: Active » Needs review

@clepson:

Thanks for taking this on!

When you add a patch to an issue, you should change the status to "Needs review" (NR) to let people know that the issue is ready for the next step. I will do that for you, and I will give a review in a day or two unless someone else does it first.

benjifisher’s picture

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

The patch in #2 does what the issue suggests. It also does a little more, adjusting the description to match. (Kudos!)

Now we have to decide whether this is the right thing to do. This part is not a novice task, so I am removing that tag.

As far as I can tell, there are two classes that implement this interface, both in the Drupal\migrate\Plugin\migrate\id_map namespace: Sql and NullIdMap.

Sql::getMessageIterator() returns an object of class Drupal\Core\Database\Statement, which implements StatementInterface, which extends \Traversable: so far, so good.

NullIdMap::getMessageIterator() returns new \ArrayIterator([]). That class implements \SeekableIterator, which extends \Iterator, which extends \Traversable.

I think the current approach is the minimal fix to get this working.

While working on #2714529: Add source and destination IDs to the data returned by getMessageIterator() , I also noticed that some of the tests (see MigrateSkipRowTest) assume that Sql::getMessageIterator() returns a Statement object. (They call the fetchAll() method on the result.) I think it is out of scope for this issue, but we could either

  1. Rewrite the test to avoid using fetchAll().
  2. Declare that Sql::getMessageIterator() implements Drupal\Core\Database\StatementInterface

I think that (1) is a terrible idea, but I bring it up for the sake of discussion. I am not sure that (2) is allowed in PHP, but I think it is; if so, then I think it is a good idea. I might even be convinced that it is in scope for this issue.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

So I think we have a problem with getMessageIterator() returning a something other than an iterator. I think we should go a step further here and add a new method getMessages() and that returns a traversable and deprecate getMessageIterator() in favour of getMessages(). I've looked at contrib and there's no one implementing this interface - http://grep.xnddx.ru/search?text=MigrateIdMapInterface&filename=&page=3

benjifisher’s picture

Issue summary: View changes
Issue tags: +Novice

I guess the problem is that this solution violates the backwards compatibility (BC) policy: https://www.drupal.org/core/d8-bc-policy#interfaces.

I will add a new proposed resolution to the issue summary. If I do a good job of that, then I think I can restore the Novice tag.

The BC policy mentions that this change should be mentioned in the release notes, so I am adding a section to the issue summary for a snippet.

heddn’s picture

I'm +1 to deprecateing getMessageIterator and adding a new getMessages method. There are few live custom implementations of ID maps and all that I'm aware of do not focus on messaging but other parts of the ID map.

mglaman’s picture

As someone who has done a few custom ID maps, the work was never around messages but altering the SQL table, see https://glamanate.com/blog/migrationwtf-specified-key-was-too-long-max-k...

christinlepson’s picture

Assigned: Unassigned » christinlepson

Working on it now, just need to write the change record.

christinlepson’s picture

StatusFileSize
new14.03 KB
new13.73 KB

Think I covered everything other than adding release notes to issue description.

heddn’s picture

We still need implicit test coverage of the deprecated method.

christinlepson’s picture

@heddn So would I write unit tests for the two usages of the deprecated methods (in Sql and NullIdMap) to ensure that the correct deprecation message is displayed?

I'm not sure what you mean by implicit test coverage (I'm new, sorry).

heddn’s picture

Thanks for your contributions. Unit tests are perfect, just like you described. There's an @expectedDeprecation annotation on tests that should help here. Search the code for other examples.

benjifisher’s picture

@clepson:

I would use just the interface name and the method in the title of the change record (CR): something like "In MigrateMessageInterface, getMessageIterator() is deprecated in favor of getMessages()". The body of the CR should contain the fully qualified name.

I am not sure whether there are any guidelines on titles for CRs, but when I browse the recently published ones, I see only a few that use fully qualified names. Some, like "\Drupal\Core\Validation\TranslatorInterface no longer extends \Symfony\Component\Translation\TranslatorInterface", would be hard to understand without the full name. I do not think that MigrateMessageInterface has much potential for confusion.

Can you also add some before and after code snippets to the CR? You can use something like

-    $messages = $migration->getIdMap()->getMessageIterator()->fetchAll();
+    $messages = $migration->getIdMap()->getMessage()->fetchAll();

as the source of the example.

I do not have time for a full review now, but while looking for that example, I noticed that the new method is getMessage() instead of getMessages(), as suggested in #5.

christinlepson’s picture

StatusFileSize
new16.39 KB
new16.08 KB
  • Added unit tests for expected deprecation in Drupal\Tests\migrate\Unit\MigrateSqlIdMapTest and Drupal\Tests\migrate\Unit\MigrateNullIdMapTest
  • Updated change record as suggested in #14
  • Changed declaration, calls, and docs from getMessage() to getMessages() as mentioned in #14
  • Updated the getMessages() docs description to read "Retrieves a traversable object of messages related to source records."
christinlepson’s picture

Status: Needs work » Needs review
benjifisher’s picture

Assigned: christinlepson » Unassigned
Issue summary: View changes
Status: Needs review » Needs work

I searched core for getMessageIterator and only found it in the expected places: two new deprecation tests, two IdMap plugins (where it is a wrapper for getMessages()), and the interface. So far, so good!

My only complaint are that the deprecation tests are more complicated than they have to be:

  1. +++ b/core/modules/migrate/tests/src/Unit/MigrateNullIdMapTest.php
    @@ -0,0 +1,26 @@
    +  public function testGetMessageIterator() {
    +    $id_map = new NullIdMap([], 'null', NULL);
    +    $expected = new \ArrayIterator([]);
    +    $actual = $id_map->getMessageIterator();
    +    $this->assertEquals($expected, $actual);
    +  }
        

    The point of this test is to check that we get a deprecation message when we call getMessageIterator(), so I think the first and third lines are all we need. Unless you already tried that and I am wrong.

    Also, the test class extends MigrateTestCase. I did not try it, but I think that $this->getIdMap()->getMessageIterator() should work.

  2. +++ b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php
    @@ -340,6 +340,28 @@ public function testMessageSave() {
    +  public function testGetMessageIterator() {
    +    $message = 'Hello world.';
    +    $original_values = [
    +      5 => ['message' => $message, 'level' => MigrationInterface::MESSAGE_INFORMATIONAL],
    +      6 => ['message' => $message, 'level' => MigrationInterface::MESSAGE_ERROR],
    +    ];
    +    $id_map = $this->getIdMap();
    +
    +    foreach ($original_values as $key => $original_value) {
    +      $id_map->saveMessage(['source_id_property' => $key], $message, $original_value['level']);
    +    }
    +
    +    foreach ($id_map->getMessageIterator() as $message_row) {
    +      $this->assertNotEmpty($message_row);
    +    }
    +  }
        

    Same idea here. I think all we need is $messages = $this->getIdMap()->getMessageIterator();. The rest of this test more or less duplicates testMessageSave().

I edited the change record a bit.

I am updating the issue summary and un-assigning the issue. (You should un-assign yourself at the same time that you mark the issue NR.)

christinlepson’s picture

StatusFileSize
new15.49 KB
new1.83 KB

@benjifisher you are absolutely right. Simplified the tests to only check for deprecation.

christinlepson’s picture

Status: Needs work » Needs review
benjifisher’s picture

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

@clepson:

This looks perfect. Nice work!

I checked https://www.drupal.org/pift-ci-job/1320859, showed all (passing) tests, and verified that the two testGetMessageIterator tests are listed.

I updated the issue summary and added a snippet for the release notes. Back to RTBC.

Status: Reviewed & tested by the community » Needs work

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

alexpott’s picture

Status: Needs work » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

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

mikelutz’s picture

Status: Needs work » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

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

benjifisher’s picture

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

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll
benjifisher’s picture

Status: Needs work » Reviewed & tested by the community
Issue tags: +Novice

This issue needs a reroll because #2714529: Add source and destination IDs to the data returned by getMessageIterator() (the issue I was working on when I created this one---see the issue summary) was fixed.

Doing the reroll is a novice task, so I am adding that label again.

mikelutz’s picture

Status: Reviewed & tested by the community » Needs work
shubham.prakash’s picture

Assigned: Unassigned » shubham.prakash
shubham.prakash’s picture

Assigned: shubham.prakash » Unassigned
Status: Needs work » Needs review
StatusFileSize
new15.73 KB

This patch should fix the issue.

shubham.prakash’s picture

StatusFileSize
new15.62 KB

This patch will fix the issue.

Status: Needs review » Needs work

The last submitted patch, 32: 3054167-32.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

shubham.prakash’s picture

Status: Needs work » Needs review
StatusFileSize
new15.21 KB

Again.

Status: Needs review » Needs work

The last submitted patch, 34: 3054167-34.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

benjifisher’s picture

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

@shubham.prakash:

Thanks for taking this on! I compared your patch to the one in #18.

When I added the Novice tag, I should have added to the issue summary a link to the instructions for rerolling a patch. I will do that now.

The main problem with your patch (probably the reason it fails automated tests) is that you remove the getMessageIterator() method entirely, instead of making it a wrapper for the new getMessages() method. There are some other, more minor problems:

  1. +++ b/core/modules/migrate/src/Plugin/MigrateIdMapInterface.php
    @@ -62,6 +62,21 @@ public function saveIdMapping(Row $row, array $destination_id_values, $status =
        */
       public function saveMessage(array $source_id_values, $message, $level = MigrationInterface::MESSAGE_ERROR);
     
    +    /**
    

    This introduces an indentation error.

  2. @@ -74,7 +89,14 @@ public function saveMessage(array $source_id_values, $message, $level = Migratio
        *
        * @return \Iterator
        *   Retrieves an iterator over the message rows.
    +   *
    +   *
    

    We only need one "blank" line here.

  3. @@ -340,6 +340,16 @@ public function testMessageSave() {
         $this->assertEquals($count, 1);
       }
     
    +  /**
    +   * Tests the SQL ID map get message iterator method.
    +   * @group legacy
    +   * @expectedDeprecation getMessageIterator() is deprecated in drupal:8.8.0 and will be removed from drupal:9.0.0. Use getMessages() instead. See https://www.drupal.org/node/3060969
    +   */
    +  public function testGetMessageIterator() {
    +    $this->getIdMap()->getMessageIterator();
    +  }
    +
    

    This adds an unneeded blank line.

shubham.prakash’s picture

StatusFileSize
new15.6 KB

Thank you @benjifisher
Can you review this patch, I made the required changes.

andypost’s picture

@shubham.prakash please add interdiff of changes https://www.drupal.org/documentation/git/interdiff it's really hard to follow changes you made

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 37: 3054167-37.patch, failed testing. View results

benjifisher’s picture

Issue tags: +@deprecated

Since this issue deprecates an existing method, I think we should add the @deprecated tag.

init90’s picture

Status: Needs work » Needs review
StatusFileSize
new16.78 KB
new4.96 KB

This should fix test failure.

Also, I've changed the deprecation format to adopted in: Adopt consistent deprecation format for core and contrib deprecation messages

And renamed test which was added here: Add source and destination IDs to the data returned by getMessageIterator() from testGetMessageIterator() to testGetMessages().

heddn’s picture

Status: Needs review » Reviewed & tested by the community

This looks good. Full test coverage in place.

benjifisher’s picture

Issue summary: View changes
Issue tags: -Needs reroll, -Novice

@init90:

Thanks for the link to #3024461: Adopt consistent deprecation format for core and contrib deprecation messages. I was not aware of that standard.

Good catch on updating the test added in #2714529: Add source and destination IDs to the data returned by getMessageIterator() . That is the issue that made the patch in #18 fail.

Interdiffs do not work well for rerolls, so I looked at a raw diff between the patches in #18 and #41. (More precisely, I compared them using diff mode in Vim.) +1 for RTBC.

I am updating the "Remaining tasks" section of the issue summary and removing two tags now that the reroll is done.

Tip: use [# 3024461] (without the space character) to generate links to other issues.

ghost of drupal past’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new18.8 KB
new5.18 KB

Couple problems I found:

  1. While fetchAll() works because the default id map is sql, tests serve as examples as well and we should only rely on it being a traversable.
  2. Well, speaking of what can we expect of the return value of getMessages ? Documented as best as I can find in tests.

Status: Needs review » Needs work

The last submitted patch, 44: 3054167_44.patch, failed testing. View results

ghost of drupal past’s picture

Status: Needs work » Needs review
StatusFileSize
new18.82 KB
new801 bytes

Ah, one more iterator_to_array.

heddn’s picture

Status: Needs review » Reviewed & tested by the community

Looks good again.

larowlan’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/modules/language/tests/src/Kernel/Migrate/d6/MigrateDefaultLanguageTest.php
@@ -62,7 +62,7 @@ public function testMigrationWithUnsetVariable() {
+    $messages = $this->migration->getIdMap()->getMessages()->fetchAll();

+++ b/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateDefaultLanguageTest.php
@@ -62,7 +62,7 @@ public function testMigrationWithUnsetVariable() {
+    $messages = $this->migration->getIdMap()->getMessages()->fetchAll();

+++ b/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageContentSettingsTest.php
@@ -53,7 +53,7 @@ public function testLanguageContent() {
+    $messages = $this->migration->getIdMap()->getMessages()->fetchAll();

there's a couple more fetchAll calls here in tests, should we be removing them too as per #44?

Other than that, looks good to me - nice work folks

benjifisher’s picture

It seems to me that the last two patches have expanded the scope of this issue.

Earlier patches aimed to make the minimal changes in order to get an honest @return comment. Given that this issue already involves a new API function and a deprecation, my inclination is to follow that minimalist approach.

The last two patches additionally update existing tests so that they no longer call fetchAll(). Since we are testing an implementation of the interface that actually returns an object of Drupal\Core\Database\Statement, it is not clear to me that we have to do that at all, let alone in this issue. In fact, it seems odd to me to avoid using fetchAll(), but then to assume that the Traversable items are objects with a message property. Let's face it, PHP is not a strongly typed language.

If we are going to expand the scope to removing calls to fetchAll(), then we should say so in the issue summary. If we are not going to expand the scope, then we could go back to the patch in #41, which was already RTBC.

alison’s picture

(taking a look at this issue b/c mentioned in Slack weekly #migration meeting)

I feeeeeel like re-narrowing the scope and opening a separate issue for removing fetchAll() seems like the best path, esp because it looks like there might be a conversation to be had about whether or not to remove fetchAll() anyway?

Just my two cents, and/or, a bump for the thread to nudge prior patch authors to chime in if they wish.

larowlan’s picture

FWIW I'm happy if the answer is 'some of the fetchAll calls in tests are providing implicit coverage for the SQL implementation' I was just posing the question. If that's the case, feel free to put it back to RTBC as is.

I think the changes in #44 provide coverage that the return is indeed an iterator.

heddn’s picture

Status: Needs review » Reviewed & tested by the community

I think the patch is reasonably sized. We've now got implicit coverage of both approaches. Let's just take it as-is.

larowlan’s picture

larowlan’s picture

Thanks folks, agree.

Committed 68c0a25 and pushed to 8.8.x. Thanks!

larowlan’s picture

Status: Reviewed & tested by the community » Fixed

published change record

  • larowlan committed 68c0a25 on 8.8.x
    Issue #3054167 by christinlepson, shubham.prakash, Charlie ChX Negyesi,...

Status: Fixed » Closed (fixed)

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