Problem/Motivation

In https://www.drupal.org/project/feeds/issues/2989789 logic was added that will reset the hash of a feed item if an entity reference was not found to force the item to update on the next run. The logic being that if feeds with dependencies are imported in an incorrect order and a referenced item is not present on the intial import, it will still get updated next time (when the reference should be there).

However, sometimes a missing reference field could be a valid situation and we don't want to reset the hash. This is the case for me, where I'm importing feeds in a strict order and some entity references are missing because that's the way the API operates. See Drupal's JSON API documentation -> Missing Resource Identifiers.

Because of this code my purge queue was constantly growing faster than it could be purged, see https://www.drupal.org/project/purge/issues/3132514 because a significant number of entities with missing reference fields were constantly being re-imported (due to having their hash reset each time).

Steps to reproduce

  1. Install drupal with standard profile so you have a page and article content type.
  2. Add an entity reference field called field_article to the page content type, that targets the article content type.
  3. Create an article feed type that uses a csv parser. Under processor settings check the option 'Update existing content items'. Save and add mappings.
  4. Using the data in tests/resources/content.csv as a guide, map source data to target fields.
    1. guid > feeds_item (guid) (unique=TRUE)
    2. title > title
  5. Create a page feed type that uses a csv parser. Under processor settings check the option 'Update existing content items'. Save and add mappings.
  6. Using the data in tests/resources/content-with-reference.csv as a guide, map source data to target fields.
    1. guid > feeds_item (guid) (unique=TRUE)
    2. title > title
    3. article > field_article (referenced by feeds_item guid)
  7. Run the page feed type with the content-with-reference.csv file. There should be two page nodes imported. Both should have an empty field_article entity reference.
  8. Change the title of 'Eodem modo typi' to Page 1 and save.
  9. Change the title of 'Aliquam feugiat diam' to Page 2 and save.
  10. Re-import the page feed with the same file.
  11. The titles should have reverted to 'Eodem modo typi' and 'Aliquam feugiat diam' because the hash was reset.

Proposed resolution

I think if the behaviour in issue 2989789 is desired, then we should make a new option that is opt-in to change this behaviour, preserving existing functionality.

Remaining tasks

  1. Reverse the option: change "Do not reset hash" to "Reset hash".
  2. Default the option to TRUE.
  3. Update tests accordingly.

User interface changes

A new config field (probably boolean).

API changes

N/A

Data model changes

A new configuration setting on the entity reference target plugin.

Manual Testing

To manually test, simply apply the patch and reference Steps to Reproduce. The only different step is when you map the entity reference field in the page feed type, check the new 'Do not reset hash when an entity reference was not found' option. With that change applied the second time you import the feed, the page titles will not revert.

Issue fork feeds-3252730

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

achap created an issue. See original summary.

megachriz’s picture

Good idea to add a configuration option. I think however, that resetting the hash when a reference is not found should remain the default. So it would be an opt-out instead of a opt-in for this behavior.

achap’s picture

Assigned: Unassigned » achap
achap’s picture

Status: Active » Needs review
StatusFileSize
new8.83 KB

Added relevant config to the processor form. Added a new test to ensure correct behaviour. It is opt-in.

I added the new setting to schema.yml. Do I need to write an update hook for existing sites or is it enought that it is set to false inside EntityProcessorBase::defaultConfiguration?

Anything else missing or other comments?

megachriz’s picture

Thanks for the patch!

I would think that an update hook is not necessary and that setting defaults in defaultConfiguration() is enough.

Since the behaviour affects entity reference targets, I think it would be better if the configuration options are on the Entityreference target plugin instead of on the processor.

The test looks good. The code for the creation of the feed types could be shorter, there is a method called createFeedTypeForCsv() in FeedCreationTrait.

achap’s picture

Issue summary: View changes
StatusFileSize
new7.57 KB
achap’s picture

StatusFileSize
new6.82 KB

Ignore patch 6. Had an unused use statement and I guess some sort of automatic code change from PhpStorm which I've reverted. I've moved the configuration to the entity reference target and also updated the test to use the new trait method.

achap’s picture

I noticed after applying this patch to my existing feeds that all my items that belonged to feed types I applied the new setting to were updated again. I guess that's to be expected because the hash would have changed? After that it works as expected on subsequent imports (i.e. no changes detected if it's just a missing entity reference).

irinaz made their first commit to this issue’s fork.

irinaz’s picture

Issue tags: +GlobalSprint2021
irinaz’s picture

Issue tags: -GlobalSprint2021 +GlobalSprint2022
irinaz’s picture

Issue tags: -GlobalSprint2022 +ContributionWeekend2022
kristen pol’s picture

Status: Needs review » Needs work

Thanks for the updated patch. I did not test this but have done a cursory review of the code for readability and noticed some minor nitpicks.

  1. +++ b/src/Feeds/Target/EntityReference.php
    @@ -117,14 +117,16 @@ class EntityReference extends FieldTargetBase implements ConfigurableTargetInter
    +          // The referenced entity is not found. We need to enforce Feeds to try
    +          // to import the same item again on the next import.
    +          // Feeds stores a hash of every imported item in order to make the
    +          // import process more efficient by ignoring items it has already seen.
    +          // In this case we need to destroy the hash in order to be able to
    +          // import the reference on a next import.
    

    Now that this is indented it exceeds 80 characters on one line. While that's being fixed, I'd update the text so it has better wrapping and is reworded slightly. For example, adding a comma after "In this case". Here's a stab at simplifying the wording:

    Feeds stores a hash for every imported item, so that items with hashes can be ignored when re-importing. Since the referenced entity is not found, force Feeds to re-import this item on the next import by resetting the hash.

  2. +++ b/src/Feeds/Target/EntityReference.php
    @@ -432,6 +435,13 @@ class EntityReference extends FieldTargetBase implements ConfigurableTargetInter
    +      '#description' => $this->t('When trying to import values for entity reference fields it is possible that the referenced entity does not exist yet and that the entity in question either appears later in the feed or that it gets imported via a different feed. Resetting the hash will cause the item to be updated on the next import. This is the default behaviour. Checking this option will override that behaviour.'),
    

    A couple things:

    1. The first sentence is very long.

    2. British English is being used.

    Here's a stab at rewording:

    When importing an entity reference, sometimes the referenced entity does not exist yet because it will be imported later. By default, the entity reference's hash is reset in these situations, so the entity reference can be re-imported on the next import. To override this behavior and not allow re-importing, enable this option.

  3. +++ b/tests/src/Kernel/Feeds/Target/EntityReferenceTest.php
    @@ -175,6 +175,108 @@ class EntityReferenceTest extends FeedsKernelTestBase {
    +   * The default behaviour is to re-hash items if they have a missing entity reference.
    +   * @see testUpdatingMissingReferences. When empty_reference_no_hash_reset is checked, the
    +   * item is not re-hashed if it has a missing entity reference. This may be useful if
    +   * your feeds are always imported in a specific order and there is a possibility that
    +   * some referenced items might be missing. Without checking this option, the item with
    +   * a missing entity reference will always be updated, even if nothing changed.
    

    A few things:

    1. Lines exceed 80 characters.

    2. British English is being used.

    3. Feels like this is just rehashing (pun intended!) the form description information. Not sure this is necessary.

    Here's a stab at rewording:

    Tests the `empty_reference_no_hash_reset` option.

    When this option is disabled, which is the default, this is tested by `testUpdatingMissingReferences`. If this option is enabled, test empty entity references are not re-imported when importing again.

  4. +++ b/tests/src/Kernel/Feeds/Target/EntityReferenceTest.php
    @@ -175,6 +175,108 @@ class EntityReferenceTest extends FeedsKernelTestBase {
    +    $this->createFeedTypeForCsv([
    +      'guid' => 'guid',
    +      'title' => 'title',
    +    ], [
    +      'id' => 'article_feed_type',
    

    It appears the formatting is off here.

  5. +++ b/tests/src/Kernel/Feeds/Target/EntityReferenceTest.php
    @@ -175,6 +175,108 @@ class EntityReferenceTest extends FeedsKernelTestBase {
    +    $this->createFeedTypeForCsv([
    +      'guid' => 'guid',
    +      'title' => 'title',
    +      'article' => 'article',
    +    ],
    +    [
    +      'id' => 'page_feed_type',
    

    It appears the formatting is off here.

  6. +++ b/tests/src/Kernel/Feeds/Target/EntityReferenceTest.php
    @@ -175,6 +175,108 @@ class EntityReferenceTest extends FeedsKernelTestBase {
    +    // Check node 2 too.
    

    Extreme nitpick: Odd to read... perhaps reword to just Check other node.

  7. +++ b/tests/src/Kernel/Feeds/Target/EntityReferenceTest.php
    @@ -175,6 +175,108 @@ class EntityReferenceTest extends FeedsKernelTestBase {
    +    // And re-import first feed again.
    

    Extreme nitpick: Odd to read perhaps reword to just Re-import feed.

kristen pol’s picture

Issue tags: +Needs manual testing

Note that the updated patch will need manual testing so tagging now so we don't forget.

achap’s picture

StatusFileSize
new6.42 KB
new4.58 KB

Thanks for the feedback. I'm not that familiar with the intracies of British and American English so I've just used your re-wordings :P

I guess someone other than me should do the manual testing.

achap’s picture

Status: Needs work » Needs review
kristen pol’s picture

Thanks for the updates! Comparing against feedback in #13:

1 - Partially addressed

IMO the following could be removed as the text I suggested above was meant to cover that part as well.

+          // The referenced entity is not found. We need to enforce Feeds to try
+          // to import the same item again on the next import.

2 - Addressed

3 - Addressed

4 - Not addressed

Sorry, I should have been more clear. It seems like the indenting is off for this variable.

+    $this->createFeedTypeForCsv([
+      'guid' => 'guid',
+      'title' => 'title',
+    ],
+    [
+      'id' => 'article_feed_type',

5 - Not addressed

Same as above, i.e. it's related to the indentation.

6 - Addressed

7 - Addressed

kristen pol’s picture

Status: Needs review » Needs work
Issue tags: +Needs steps to reproduce

Tagging for testing steps.

achap’s picture

Status: Needs work » Needs review
StatusFileSize
new6.27 KB
new812 bytes

I've removed that duplicate comment but I'm not sure about the indendation as I can't see anything wrong in my local editor. To my eye it seems to be inline with everything else. Maybe it's a cross OS querk?

I ran the file through codesniffer too and it only detects a violation which is not relevant to my changes:

www-data@5e0f204ef1c0:/app$ vendor/bin/phpcs --standard=Drupal,DrupalPractice --extensions=php,module,inc,install,test,profile,theme,info,txt web/modules/contrib/feeds/tests/src/Kernel/Feeds/Target/EntityReferenceTest.php

FILE: /app/web/modules/contrib/feeds/tests/src/Kernel/Feeds/Target/EntityReferenceTest.php
------------------------------------------------------------------------------------------
FOUND 0 ERRORS AND 1 WARNING AFFECTING 1 LINE
------------------------------------------------------------------------------------------
 437 | WARNING | Unused variable $vocabulary.
------------------------------------------------------------------------------------------

Time: 1.74 secs; Memory: 14MB
kristen pol’s picture

Thanks for the update @achap.

Sounds good regarding the formatting.

Would you be able to add steps for testing this manually?

achap’s picture

Issue summary: View changes

I have added some steps to reproduce for the original issue and a new manual testing section to the issue summary that shows how to test the patch.

kristen pol’s picture

Wow! Amazing work @achap. I'll see if I can find someone to test in the next week.

achap’s picture

Issue summary: View changes
Status: Needs review » Needs work

I have been using the patch at #19 and have come to the conclusion it is not a good solution after all. It helps in that it stops the purge queue being constantly refilled, but it also stops valid cases where referenced items are updated in a different order, which I have encountered since using the patch myself due to a bug in the api we imported from.

The original issue is actually due to the design of Drupal's JSON API and its handling of missing resource identifiers -> https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi.... That was causing everything to keep getting updated and the purge queue to overflow.

I think what is actually needed is some sort of event getting dispatched before the hash is reset that allows the programmer to override the behaviour if that is what they need. For example, if the target references a missing resource I can instruct feeds not to reset the hash, otherwise proceed as normal.

achap’s picture

Status: Needs work » Closed (works as designed)

On more reflection I think this should be closed because it's an issue with Drupal core, not feeds, and solutions are available to get rid of dangling entity references e.g. https://www.drupal.org/project/entity_reference_purger

kristen pol’s picture

Assigned: achap » Unassigned
Issue tags: -Needs manual testing

Cleaning up tags.

andyg5000’s picture

I propose re-opening this issue. We have the following scenario where the hash reset is causing us problems:

* Feed contains a list of terms
* Destination site only uses a handful of the terms in the feed
* On import when a term doesn't exist, the hash is invalidated
* Every subsequent import causes the destination node to be updated even though nothing has changed.

I wrote basically the same patch to solve the problem and @megaChris pointed me here. I realize that #23 says this isn't a good fix for their problem, but in our instance it's exactly what we need. If there were an API around hash validation that could work too, but seems like a much larger effort.

My only feedback for getting #19 added is that it's very verbose and can be trimmed up (e.g $this->configuration['ignore_missing']). I do agree that FALSE should be the default for backwards compatibility.

achap’s picture

Status: Closed (works as designed) » Active

I'm back on the project I had this issue on again (yearly festival site) and have solved my own problem using the Feeds parse event to filter out missing resources. See: https://www.drupal.org/docs/contributed-modules/feeds/feeds-howtos/altering-feed-data

Regarding #26 happy to re-open the issue but I still think my solution isn't the best one because it's either on or off. So it would solve your issue but it would stop entity references that get imported after the main feed from ever getting referenced. So I think we can do better. With that said, it is opt-in...

Would be interested to know how the Migrate API approaches this.

sgalindo2388’s picture

StatusFileSize
new6.29 KB

Re-created patch against latest 3.x-dev

megachriz’s picture

Status: Active » Needs review

Would it be better to reverse the option? Instead of it saying "Do not reset hash", let it say "Reset hash". I'm not sure, but I think it's better UX that enabled options imply to "do" something instead of "do not" something. Opinions?

Also, since there is a patch, the status "Needs review" would apply here. Though it would be better to create a MR from it, because the testbot on drupal.org no longer evaluates patches.

It would be nice indeed to check how Migrate approaches this problem.

megachriz’s picture

Issue summary: View changes

Updated the remaining tasks.

megachriz’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

Needs a reroll (and MR) now that #2973170: Importing new files fails when "Autocreate entity" option is turned on. is in.

Also, I suggested a change, see remaining tasks from the issue summary.

megachriz’s picture

Issue tags: +Novice, +Vienna2025

The Drupal Contribution Mentoring team is triaging issues for DrupalCon Vienna 2025, and we are reserving this issue for Mentored Contribution during the event.

After October 17, this issue returns to being open to all. Thanks!

rory downes’s picture

I am a first time contributor at DrupalCon Vienna and I will take a look at this issue.

rory downes’s picture

StatusFileSize
new6.52 KB

I never got my local working enough to access the fork locally. I did prepare the re-roll against the 8.3-dev branch but not sure how much use that is. I will attach anyway in case. I never got to the final changes. I also did not get to test

rory downes’s picture

I have added the re-rolled patch to the issue branch and created a draft MR.

Next step is to apply the changes and test

benstallings made their first commit to this issue’s fork.

benstallings’s picture

Status: Needs work » Needs review