Problem/Motivation
I want to be able to round trip Drupal entities such as taxonomy terms and nodes, using uuid as the unique identifier. I can do this with migrate framework but feeds doesn't seem to offer a mapping to target an entity's uuid (as it does with nid or tid).
Proposed resolution
Add a FeedsTarget plugin called "uuid". Make sure that it can be set as unique. Add test coverage in the form of a unit test and a kernel test.
Remaining tasks
- Create the FeedsTarget plugin in src/Feeds/Target.
- Add a unit test to tests/src/Unit/Feeds/Target for this plugin. The unit test could be a simple as StringTargetTest in case the plugin doesn't need to do any modifications on the data.
- Add a kernel test to tests/src/Kernel/Feeds/Target for this plugin. This should cover the following:
- Incoming, correct values are saved on the entity.
- Malformed values (if applicable) generate a warning.
- The UUID can be used as unique identifier.
| Comment | File | Size | Author |
|---|---|---|---|
| #21 | interdiff_18_21.txt | 910 bytes | ericgsmith |
| #21 | feeds-3306725-21.patch | 10.42 KB | ericgsmith |
| #18 | feeds-3306725-15-18-interdiff.txt | 8.61 KB | rosk0 |
| #18 | feeds-3306725-18.patch | 10.26 KB | rosk0 |
| #15 | feeds-3306725-14-15-interdiff.txt | 3.87 KB | rosk0 |
Issue fork feeds-3306725
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
Comment #2
jonathan_hunt commentedComment #3
jonathan_hunt commentedComment #4
megachrizYou are right. A target for the UUID of an entity is missing. Do you want to implement one?
Comment #5
jonathan_hunt commentedI'd like to implement
uuidas a target, or help to make that happen. I'm unfamiliar with the feeds codebase though, so pointers would be welcome. It's not that obvious to me whyuuidisn't already a target option, since it's returned by\Drupal::service('entity_field.manager')->getFieldDefinitions()and all the other properties and fields from that call seem to be available...Comment #6
megachrizI've tried in the past to make a generic FeedsTarget plugin to make all fields available but at the time I ran into the issue that you couldn't override that generic plugin with a specific one. So currently for every field type there needs to be a specific FeedsTarget plugin.
Some FeedsTarget plugins do more than just setting a value. They also try to "correct" the data or provide configuration for how to interpret the incoming data. For example: the number target trims the incoming data. The date target converts the data to a DrupalDateTime object and then to the storage format being used by the date field. So not for every field just only passing a value is sufficient.
I tried to set pointers in the "Remaining tasks" section of the issue summary. Does that help or do you need more information?
Comment #7
rosk0I already have a proof of concept working. Was late in a day yesterday to start on issue on the topic, so thanks Jonathan!
Will finilise that plugin and look at test now.
Comment #8
rosk0Added UUID target. Manually tested on nodes and terms, but I don't see any reason it not working on other entity types as it is field type specific, not entity type.
Implemented only unit tests , but I believe they cover most important bit - validation of the incoming value. Setting the value on destination entity is out of scope for added plugin and so is testing of this functionality.
Leaving "Remaining tasks" unchanged for now. Could be updated later, if proposed changes are acceptable.
Comment #9
megachrizThanks for working on this, @RoSk0
I see that Drupal Core has explicitly set the UUID field to be read only in ContentEntityBase, so I assume that means that Drupal doesn't allow the UUID value to ever change. But I must admit that I don't understand the meaning of a field being declared as "read only" when its value can still be mutated. When searching for
->isReadOnly()in core's code base I don't see it actively being used.I wonder if we should allow empty UUID values. That if empty, the system would fallback on generating the UUID instead.
But maybe you would want to be informed if an UUID value is missing if you meant to set it. Hm...
Well, we could always make it configurable on the target: we could add a setting "Require source to set a UUID value".
I do think we need test coverage for setting the value on the entity. Especially since we now allow the target to change a read only field: we want to ensure that updating a read only field doesn't result into errors.
Comment #10
megachrizAh, I see the issue with the UUID field now. A value gets set immediately upon creation in ContentEntityStorageBase:
And EntityProcessorBase skips it if a value has already been set:
Maybe the
isMutable()override should instead check if the entity is new and in that case allow the UUID to be set?Comment #11
rosk0isMutable()doesn't have access to the $entity in the wayisEmpty()does. We can of course abuseisEmpty()to check that the$entityis new and allow to set the value only in this case.The whole purpose of been able to use UUID is to be a unique identifier as it is. Unique identifiers are not expected to change - another identifier is another item.
I manually tested what would happen if I try to change UUID as it stands. The behaviour is based purely on the feed type configuration. Specifically , if both NID and UUID are present in mapping and both are marked as unique, than the first one on a list wins. So, if the first one in the mapping would be NID than it wins and it's possible to update UUID. It is actually still possible to update UUID if the UUID is first on the mapping list, because new UUID would not be matched buy DB lookup and NID will, but it feels like allowing to change UUID is increasing the surface of user error and works against core indication of UUID field being read only.
Would you be OK to abuse
isEmpty()as suggested above to avoid UUID updates?Comment #12
rosk0Suggested in #11 abuse implemented.
Please review and provide feedback.
Comment #13
rosk0It really looks like more higher level testing is required - the change in #12 actually broke plugin functionality and now one will see "UUID (uuid): UUID: this field cannot hold more than 1 values" trying import content with UUID set.
Will look into fixing it and adding some functional testing.
Comment #14
rosk0Code is fixed. Added additional unit test for the case.
Kernel tests are still in progress, will add when finished.
Comment #15
rosk0Kernel test added.
Comment #16
megachrizThanks for the update! I'm going holiday today, so it could take a few weeks before I can properly review this.
Comment #17
rosk0Additional testing by business indicated there is a bug in the implementation.
However it looks like we all got confused about what is expected and what is not. And that is reflected in the implemented kernel test. Bug report says that where CSV item doesn't have UUID filled in it should be skipped as non-conformant , but in reality this is not true, latest implementation revision in #12 made that possible. UUID could be used as unique identifier and still be optional because it is generated in the new entities.
I'm working on a kernel test update to reflect this.
However, I believe that the biggest problem is possible usage of multiple fields as unique identifiers.
During debugging it I uncovered that if we configure feed type to:
first item with the missing UUID will be stored with empty GUID and next one with the empty UUID will be treated as an update to the previous one, if the updates are allowed.
It feels like mapping page should recommend to have only one definition marked unique. @MegaChriz thoughts?
Comment #18
rosk0Comment #19
megachrizTagging for BADCamp. It would be good to manual test various cases for importing UUID's:
Comment #20
neubreed commentedGetting php 8.1+ deprecation warning with the above patch
Comment #21
ericgsmith commentedFixing php 8.1 deprecation warnings
Comment #24
ptmkenny commentedAdded an MR of the patch in #21 to make this easier to review and keep up to date.
Comment #25
ptmkenny commentedI rewrote some of the comments for clarity, but the code looks good to me. All tests are passing, and I've been using this to import entities while specifying the UUIDs on a site for about a year now.
Although I created the MR, I did not write the original code (the MR was derived from patch #21), so I am setting this to RTBC based on my review and testing.
Comment #27
megachrizI merged the changes! Thanks all for contributing!