Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
It would be handy to be able to migrate data into multifields. The attached patch is a good start.
Comments
Comment #2
becw CreditAttribution: becw commentedI guess I didn't make the patch against the latest -dev after all. Here's a rerolled patch.
Comment #3
becw CreditAttribution: becw commentedComment #4
Dave ReidThanks becw! How does the patch in #2 account for the non-primary columns in the subfields? For instance, if in the code example, field_label is a filtered text field, how do we handle migrating field_my_multifield:field_label_format? Should those sub-columns be exposed as well?
Comment #5
becw CreditAttribution: becw commentedNon-primary columns in the subfields should be subfieldable the same way they would be if they were not in a multifield, since this code invokes the real migrate field handlers for those fields. This means you would do something like:
We could theoretically go into each of those field's migrate handlers and expose their subfields using those handlers'
fields()
methods...Comment #6
Dave ReidI guess I'm confused if they would actually be exposed in the UI since the fields() method doesn't drill down to that level. It might benefit to provide some examples of the non-primary columns in the sample code as well.
Comment #7
becw CreditAttribution: becw commentedOk, cool, I'll add that. Thanks, Dave!
Comment #8
becw CreditAttribution: becw commentedNow with more sample code.
[edit: gah, I mis-named the interdiff]
Comment #10
wadem CreditAttribution: wadem commentedWhen testing this module I came up with the following error:
PHP Fatal error: Call to undefined method MultifieldMigrateFieldHandler::import()
Let me describe the test scenario.
- created a multifield Property
- created my migration:
$this->destination = new MultifieldMigrateFieldHandler('property');
- set field mappings
Executing drush: drush mi Properties --limit='1 item' gave the error above.
Not sure if this belongs here.
W
Comment #11
Dave Reid@wadem: I don't believe you'd want to use this handler as a destination. You should be using a Node destination or some other kind of entity.
Comment #12
becw CreditAttribution: becw commented@davereid @wadem exactly. This migration handler is used like other field handlers, and doesn't require that you treat multifield values a separate entities. If "Property" is a multifield type, and you have a content type called "Listing" with a property field,
field_property
, your migration might look like this:Comment #13
MixologicAwesomesauce. Im so glad somebody already did this as its exactly what I need right now.
I applied the patch and tried to run some migrations, execept I was running into some issues with the subfield names not being set correctly. The ->fields method was not handling the subfields properly, so some other calls that migrate was performing was not getting that right.
I modifed the fields method to leverage what MigrateFieldsEntityHandler was already doing for subfields, and now everything is working swimmingly. Im able to migrate data directly into my subfields no problem, *and* Im using the migrate ui (in migrate 2.6).
Im not clear on what all the comment documenation on top of the prepare method is for.. is that explaining what the code is doing? or saying something that I need to do to leverage the code. If its just 'heres what happens below' then we probably dont need that comprehensive of docs. Comments and documentation should explain Why, not What.
I've left them in there though as ultimately thats up to the maintainer if they are useful.
Comment #14
MixologicWhoops. Misnamed the patch, also forgot to set the status. These are identical to the comment above.
Comment #15
becw CreditAttribution: becw commentedI was trying to explain what sort of values the prepare method expects--because the field mappings map data directly to subfields, you have to make sure that there are blank values in your field mappings if there is missing data. Perhaps this belongs in the documentation at the top of the file, in a more concise form?
Comment #16
Deciphered CreditAttribution: Deciphered commentedWhile I haven't done a proper review of the code, I have tested it and can confirm it is working. Much, much easier than migrating Field Collections. Huge thanks to all involved.
Comment #17
Dave ReidWorking on this a bit further. Moved hook_migrate_api() into multifield.migrate.inc and used more API functions for interacting with multifield field types, and converting field items to pseudo-entities and back again.
Comment #18
Dave ReidSo I'm experiencing some issues with the migration hander:
When I run the migration my field value ends up like:
Comment #19
Dave ReidRevised patch that uses multifield_field_info() to register itself for field types so that it will be compatible with #2234769: Should multifield types be represented as separate field types, or just one 'Multifield' field type?
Comment #20
Dave ReidRevised version that properly passes the bundle into MigrateFieldsEntityHandler->fields().
Comment #21
Dave ReidComment #22
becw CreditAttribution: becw commentedI found that multifield values were being migrated even when all of the subfields are completely empty; in the screenshot below, you can see how there is one item in the 'field_relateditem' multifield, but none of the subfields have any data:
Here's a version of the patch that doesn't migrate empty multifields.
Comment #23
wonder95 CreditAttribution: wonder95 commentedI'm trying to use this to migrate into a multifield using the patch from #22 and becw's example, but I'm getting nothing in my destination. My source D6 field is field_image and my destination is field_image within the field_images multifield (confusing, I know). Here's the code I'm using in my constructor:
I've verified that the associated images have been migrated in the CNSFile migration and records are there in the migrate_map_cnsfile and file_managed tables. The mappings also show appropriately on the Migrate pages.
After doing some debugging, it looks like the problem is that the default file class - MigrateFileFid in this case - is not available in MigrateFileFieldBaseHandler::prepare():
even though it is specified in my constructor as the default value for the field_images:field_image:file_class subfield. Is there something else I'm missing?
Thanks.
Comment #24
wonder95 CreditAttribution: wonder95 commentedAfter some more painful debugging, I think I've found the issue, and it's in Migration::applyMappings():
Basically, this code is only designed to handle one level of subfields (there are only specific references to $destination[0] and $destination[1], not a loop through $destination), so what's happening is that the defaultValue value is overriding the fid value (in this case) at this line:
So the first time through, $this->destinationValues->field_images['arguments']['field_image'], has the fid value I want, but after going through the file_class subfield, it is replaced by 'MigrateFileFid'.
And then, it goes through the preserve_files subfield (true), and resets to true. This is the "true" value that I am seeing when the code gets to the handler, so this explains everything perfectly.
So with that being the case, is there a way to handle a subfield for a field within a multifield, or will it require changes in migrate itself? Since the value is already set by the time it gets to the handler, I'm guessing it's a change to migrate.
Thanks
Comment #25
wonder95 CreditAttribution: wonder95 commentedAttached is a patch that updates the handler to work in conjunction with a patch to Migrate module that I wrote that handles three levels of subfields. As mentioned above, however, this patch is irrelevant without the Migrate patch, since currently Migration::applyMappings() stomps on the third level subfields, so there's nothing to process when the migration process gets to this handler.
I have a migration to an image field within a multifield working perfectly with these two patches.
Comment #27
wonder95 CreditAttribution: wonder95 commentedAfter multiple hours of debugging and trying to figure out why null values were being passed to file_usage_add() for an image field in my multifield when there were no files in the source, I determined that the cause was that this handler was returning NULL instead of an empty $return array. Simply adding
fixed the problem. An updated patch is attached.
Sigh.
Comment #28
wonder95 CreditAttribution: wonder95 commentedComment #29
oldspot CreditAttribution: oldspot at Zoocha commentedIf migrating a multivalued multifield, the
$subfield_arguments
variable gets overridden on every iteration of the loop.Fixed this by adding the
$key
variable and turning it into an array.Comment #30
oldspot CreditAttribution: oldspot at Zoocha commentedIf migrating a multivalued multifield, the
$subfield_arguments
variable gets overridden on every iteration of the loop.Fixed this by adding the
$key
variable and turning it into an array.I have missed commenting out a line in the previous patch.
If this is set then only the first value of the multivalued field will get imported. So needs removing.
$arguments[$key] = $arguments[$key][0];
Comment #31
t0xicCode CreditAttribution: t0xicCode at OpenConcept Consulting Inc. commentedI'm running a migration, so I'll RTBC the patch ASAP.
Comment #32
t0xicCode CreditAttribution: t0xicCode at OpenConcept Consulting Inc. commentedPatch #30 works great with Migrate 2.8-beta1.
Comment #33
t0xicCode CreditAttribution: t0xicCode at OpenConcept Consulting Inc. commentedI've added support for entity translation to the multifield handler. I've tested it on my part and it works.
Comment #34
t0xicCode CreditAttribution: t0xicCode at OpenConcept Consulting Inc. commentedComment #35
dshields CreditAttribution: dshields at OpenConcept Consulting Inc. commentedlooks and works great!
Comment #36
t0xicCode CreditAttribution: t0xicCode at OpenConcept Consulting Inc. commentedThere are instances where this is not defined. The assignment should be protected with a if statement and an
array_key_exists
. I'll resubmit shortlyComment #37
t0xicCode CreditAttribution: t0xicCode at OpenConcept Consulting Inc. commentedI'm putting it back into RTBC because the changes made are relatively trivial, but feel free to review it again.
Comment #38
heddnThe documentation for how to make this work could use some help. I'm dealing with a unlimited number of multifield entries (each entry still is limited to a single value, per the limitation for multifield). In my prepareRow, what do I need to create and how does that look? The documentation in #13 / #15 isn't helping.
Comment #39
heddnThis improves the documentation. Since it is a documentation only change, marking RTBC again since it was already RTBC previously.
Comment #40
osopolarThanks for the work on this, patch from #39 works great for me.
Comment #41
osopolarI have had problems migrating titles of the link filed. The problem is, that all subfield arguments are passed to the field handler (not sure if I am using the correct terminology), as the delta of the link does not match the delta of the arguments (because delta is always 0).
Attached patch fixed this the way, that only the arguments corresponding to the delta are passed. I have only tested with link title, not with other subfields, and only a few cases. Does the code make sense? Do we need to check first, if
$subfield_arguments[$subfield_name]
is an array, or will this alway be an array?Comment #42
t0xicCode CreditAttribution: t0xicCode at OpenConcept Consulting Inc. commentedChecked and ran well on a migration we have.
Comment #43
ultrabob CreditAttribution: ultrabob commentedI've got migration 7.x-2.8 installed and the the latest patch (41) on this thread, and now I'm trying to migrate multivalue multifield consiting of a taxonomy reference + a text field.
Here is the relevant code in my migration module:
The user in my test case should have 8 entries in this multifield. When I run the migration I get the following in the message log:
$row->technical_skills
has the value105,109,110,135,147,146,145,139
and
$row->technical_remarks
has the value|/|/||/|/||/|/||/|/||/|/||/|/||/|/|
meaning that this user didn't add any text details.Nothing is migrated into the multifield. Am I coding this wrong, or does the handler not know how to handle multiple entries passed in with the separator method, or did I apply the wrong patch? Any help greatly appreciated! I'm pretty new to drupal and the community, so if I'm going about this wrong, please educate me.
Comment #44
t0xicCode CreditAttribution: t0xicCode at OpenConcept Consulting Inc. commentedYou might have to run the code in a debugger and step through it to see how migrate deals with a bunch of empty values. You also might have to add a callback to your mapping in the meantime.
Comment #45
ultrabob CreditAttribution: ultrabob commentedJust as a quick followup, I did some more to try to narrow things down, by commenting out first the code for the text field part of the migration, and then the taxonomy reference part on a different user with remarks in place. When I ran the migration with just the taxonomy reference there was nothing in the messages log, and also nothing in the technical skills multifield. When I ran the migration with just the text field, the message log was populated again and now there were 6 blank fields(8 entries were in the source list).
It seems like I have two seperate issues, both including use of separator. One with the text fields, blank values are coming through. Second, with the use of sourceMigration() nothing comes through. If I run the taxonomy ref migration without sourceMigration() in place one of the two test users gets 2 skills filled out of 8, though only one of them is exactly right, the other is a parent of the selected term. (I suspect I just got lucky with some ids in common, but I don't know)
Comment #46
ultrabob CreditAttribution: ultrabob commentedI don't feel confident enough that my change will not break things for others, because I think the line must be written the way it is to address other cases, perhaps where a fieldMapping has more than just one argument. Also being new to drupal I'm not really sure or the details of how to submit a patch. Still, here is what I changed to make the multifield work for me in my situation with one translatable text field, and one taxonomy reference, coming from a sourceMigration with sourceType tid.
Line 156:
was assigning a value of
t
, theni
, thend
and it was assigning the value one level deeper than it was needed. I changed that line to:and now it works for me, with my set of fields. Could someone look at that and see if it applies more generally, or whether there is a need for a conditional there that checks for whether the value is an array or not?
Comment #47
ultrabob CreditAttribution: ultrabob commentedHere is a replacement for that line that will not change behavior in cases where $subfield_argument is an array as seems to have previously been expected, but will fix the case where it is not an array. If that behavior was not a bug in some cases, this will be better.
Comment #48
bkat CreditAttribution: bkat commentedThanks for this awesome patch! I'm so glad that I didn't have to roll something of my own. However I did have one small problem with it. I have a multifield that among other things contains an integer field. Let's call it score Whenever migrate was a creating or updating a node without a score, I was getting SQL errors stating that an invalid value was being set for scores column in the database.
What I did to work around this was set the scores to an impossible value (-99) in the migrations prepareRow() method if they were null in the source. Then I added a prepare() method that does the following
I suspect that what was originally happening is that $entity->field_teams[LANGUAGE_NONE][$delta]['field_score'][LANGUAGE_NONE] was equal to
instead of
Comment #49
becw CreditAttribution: becw at Palantir.net for Acquia commentedI re-rolled the patch from comment 41, since trailing whitespace caused it to not apply cleanly. This re-roll also includes a minor comment tweak.
Comment #50
becw CreditAttribution: becw at Palantir.net for Acquia commentedIn using the previous patch, I found that the subfield arguments were always being treated as an array with a delta corresponding to the subfield values, which didn't work when passing a default value:
In this case, the argument would always get passed as 'f', 'o', 'r', 'm'... which does not have the intended effect :)
Here's an update to the patch that seems to resolve this issue.
Comment #51
ultrabob CreditAttribution: ultrabob commentedThanks @becw, that is just what I was addressing in #47. I wish I could test this for you, but there has been so little happening with multifield, that I abandoned it, and wrestled with field collection until it worked for me. I can verify that the last part of that patch works, because you fixed it exactly the same way I did.
Comment #52
patrick.thurmond@gmail.comI'm running a migrate against this with multiple values going to it. I am migrating in a taxonomy term to value pairing. The taxonomy term was already migrated previously. And each node has 4 or 5 of these key/value pairings. So the pairings consist of the term as the key and a text field as the value.
I am pumping in an array of these values to two mapped fields and have mapped to the correct locations. However, I am seeing that the migrate script is not getting the data correctly. We have the tid for the term going to the fields.inc in the migrate module. But it does not recognize it because the $arguments['source_type'] returns an array instead of a flat value.
Further, when I use xdebug to examine during a migration I am noticing that the $return variable in this plugin comes back structured in such a way that it does not have a value in the field_attribute for each. Just an empty array.
Example of the $return variable structure (see attached screenshot).
I believe something is wrong with how this is being structured. I am still debugging to figure out how to fix it. I am using the most recent patch from comment #50.
Comment #53
patrick.thurmond@gmail.comOk guys, the problem had to do with how the data is being structures in the $values array. When working with a source_type of 'tid' it expects the source type to be a flat value, not an array.
So to fix it I simply checked for that situation and it solves the problem.
I have attached the interdiff and the new patch that is working for me. Let me know if you have any questions. Also I did some visual cleanup to make the code more readable.
Comment #55
patrick.thurmond@gmail.comSo apparently the first patch I made didn't work so well. Here is attempt number two and the same/previous interdiff should still apply.
Comment #56
heddnComment #59
mcdoolz CreditAttribution: mcdoolz commentedThank you for this!