Just came across the TODO tag "TODO: handle multiple values." in the class doc for MigrateFileFieldHandler in plugins/destinations/fields.inc.
Attached patch integrates multi value support.
The patch integrates a new argument to provide an additional separator. The one for the migration splits up the multiple values, and the new one splits up the image value in it's properties.
Now the input data can look like this:

/tmp/files/image|alternative name|my title|display_name#/tmp/files/2nd_image|2nd alternative name|Title for the 2nd file|display_name

To import this data you have to configure the field like this:

    $arguments = MigrateFileFieldHandler::arguments(NULL, 'file_copy', NULL, NULL, 1, 2, NULL, 3, '|');
    $this->addFieldMapping('field_images', 'images')
      ->separator('#')
      ->arguments($arguments);

Comments

scoff’s picture

patch: **** malformed patch at line 80: $destination_file = file_stream_wrapper_uri_normalize($destination_dir . "/" . basename($full_path));

could you please attath or post a link to a full version of your fields.inc file?

das-peter’s picture

Hi scoff,

I've created a repo on github:
https://github.com/das-peter/migrate/blob/master/plugins/destinations/fi...

Attention: There are other changes besides the file function, maybe you've to change some code. E.g. the stuff from #914440: Enhance multilingual support
$language = $this->getFieldLanguage($entity, $field_info, $arguments);.

scoff’s picture

Thank you!

mikeryan’s picture

Project: Migrate » Migrate Extras
Component: Code » Filefield
Status: Needs review » Patch (to be ported)

Committed for D7, thanks! D6 filefield support is in migrate_extras at the moment, moving to that queue.

philipz’s picture

I can't get this to import multiple files into unlimited filefield - only first file gets imported.
I thought this separator argument is for multiple files but I understand now this is for additional file fields like alt and title.

I'll try to make this work for multiple files but maybe someone already did this or can point me to some information about this?

btmash’s picture

@philipz, you are able to do multiple files with this patch. You would have something like:

    $associated_file_arguments = MigrateFileFieldHandler::arguments(NULL, 'file_copy', FILE_EXISTS_RENAME, NULL, 2, 1, NULL, NULL, '|');
    
    $this->addFieldMapping('field_content_associated_images', 'right_side_images')
      ->separator('##')
      ->arguments($associated_file_arguments);

So you would have 2 separators (in my case, I used '|' and '##'). The first separator is for separation of data related to a file and the second separator is for separation of files (so my data looked like '/path/to/file1|alt stuff|Title stuff##/path/to/file2|more alt stuff|other title'). My understanding is that the separator is built into what migrate does so it technically supports it for any field(? someone who has a better understanding of migrate would be able to correct me on this).

mikeryan’s picture

Project: Migrate Extras » Migrate
Component: Filefield » Code

File field support moved back to Migrate.

markabur’s picture

I was having the same trouble as #5 until I added a non-null value for the new separator, even though I'm not actually using it (I'm just passing filenames, no metadata). Is that actually the intended behavior, to require both separators when doing multi-value file import?

philipz’s picture

The #6 did the trick. I thought I tried something like that but now I don't remember. I had to put that away for a while.
Thanks BTMash!

mikeryan’s picture

Status: Patch (to be ported) » Active

I committed the feature for packing an image's properties into the source field without looking too closely, and now I'm having second thoughts. Passing magic numbers (array indices) as arguments, frankly, grosses me out. Also, this means these arguments have three possible forms - a hard-coded string value, an array('source_field' => 'field_name') to pull the value from the source query, or a 3 to say "the value is in array index 3 of explode($separator, $image_properties)' I'm thinking about JSON as an alternative, but would like some input.

For the people using this technique, obviously it requires you to hack the source data to produce the format above. Exactly how are you introducing the separated properties? Is it in SQL in the source database; are you generating an intermediate table; is there a PHP or other programming step? Would it be difficult to replace the implode('|', $image_properties) (or its equivalent) with json_encode($image_properties)?

Thanks.

markabur’s picture

Good call, yeah, the indices are kind of mysterious.

I bumped into this but didn't end up using the separator/index part of it, so any kind of change here wouldn't affect my stuff (which actually is said and done at this point anyway). But I suppose if I was migrating that kind of metadata I'd expect to assemble the strings in php somehow.

btmash’s picture

Oh, no, I'll have to change up my code! ^_~

I agree that the change into another format is probably a good idea; json works, or passing in an array in a similar way to how the actual file object looks also makes sense to me (so $row->files = array($file_1, $file_2, ...) with each file being an array (or object whichever you specify that has the orig. path, alt name, title name, description, etc) so we don't deal with some other source field(?).

mikeryan’s picture

Version: 7.x-2.x-dev » 6.x-2.x-dev
Status: Active » Patch (to be ported)

OK, done - file values can either be a path (either local or a URL), or a JSON-encoded array with the following members: url, alt, title, description, and display. When file properties are passed in via arguments, any properties present in the JSON override the arguments. See WineWineMigration in migrate_example for an example of how to use this support.

Next, to backport to D6...

mikeryan’s picture

Status: Patch (to be ported) » Fixed

Backport was pretty simple, all done!

Status: Fixed » Closed (fixed)

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

geerlingguy’s picture

Just FYI, to anyone coming in using 7.x-2.x, the method in #6 may not work (at least, I couldn't get it working after a few hours' effort). It turns out that the exact example of best practice is located inside wine.inc, in the WineWineMigration class. It shows you how to build a JSON array of all the file attachments, and pass that to the filefield properly. Much simpler to comprehend, too, considering how many arguments are in the #6 method :-P

btmash’s picture

@geerlingguy, the method in #6 changed into the json method (and building out the attachments array in that manner) with the update from 2.0 to 2.1. And agreed, it is now *much* simpler to understand :)