I'm working on an addressfield handler for #1149890: Support for Addressfield and found myself writing code like:

$arguments = array(
  'thoroughfare' => array('source_field' => 'profile_address'),
  'locality' => array('source_field' => 'profile_city'),
  'administrative_area' => array('source_field' => 'profile_state'),
);
$this->addFieldMapping('field_user_address', 'profile_country')->arguments($arguments);
// Since the excerpt is mapped via an argument, add a null mapping so it's
// not flagged as unmapped.
$this->addFieldMapping(NULL, 'profile_address');
$this->addFieldMapping(NULL, 'profile_city');
$this->addFieldMapping(NULL, 'profile_state');

But wish I could have a helper function so simplify it:

$arguments = MigrateAddressFieldHandler::setupArgumentMapping($this, array(
  'thoroughfare' => 'profile_address',
  'locality' => 'profile_city',
  'administrative_area' => 'profile_state',
));
$this->addFieldMapping('field_user_address', 'profile_country')->arguments($arguments);

So I wrote one:

  static function setupArgumentMapping($migration, $fields) {
    $arguments = array();
    foreach ($fields as $destination_field => $source_field) {
      // Put the field into the arguments array as a source field
      $arguments[$destination_field] = array('source_field' => $source_field);
      // And add the null mapping
      $migration->addFieldMapping(NULL, 'profile_address1');
    }
    return $arguments;
  }

But when I tried to run it ran into the following issue:

PHP Fatal error:  Call to protected method Migration::addFieldMapping() from context 'MigrateAddressFieldHandler' in migrate_extras/addressfield.inc on line 76

Any thoughts on how we could improve this?

CommentFileSizeAuthor
#14 arguments-1279778-14.patch10.41 KBmikeryan
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

drewish’s picture

Actually I'm coming to the conclusion that this is really asking for a faster horse instead of a car. I think we should really look at allowing multiple source fields to be mapped through a single destination field handler. The mapping would need an additional parameter for the role it (what we've been doing via arguments). I can think of several syntaxes that might work.

Use the colon to delimit the field from the role within it:

$this->addFieldMapping('field_user_address:thoroughfare', 'profile_address');
$this->addFieldMapping('field_user_address:locality', 'profile_city');
$this->addFieldMapping('field_user_address:administrative_area', 'profile_state');

Stick the role in as a 3rd parameter. I don't really like separating the destination field from the role:

$this->addFieldMapping('field_user_address', 'profile_address', 'thoroughfare');
$this->addFieldMapping('field_user_address', 'profile_city', 'locality');
$this->addFieldMapping('field_user_address', 'profile_state', 'administrative_area');

Chain a function call similar to defaultValue(), arguments(), etc. I'm not set on the function name it's all that came to mind at 3am:

$this->addFieldMapping('field_user_address', 'profile_address')->subField('thoroughfare');
$this->addFieldMapping('field_user_address', 'profile_city'->subField('locality');
$this->addFieldMapping('field_user_address', 'profile_state'->subField('administrative_area');

I don't think any of these preclude us from preserving the current behavior of passing in a field as array('source_field' => 'foo) or a fixed string 'my custom alt text'.

mikeryan’s picture

I like the idea, should definitely simplify both the field handlers and the mapping calls. I like #1 best - too late for V2.2, but let's get this into V2.3.

mikeryan’s picture

Title: We should make it easier to pass field sources into field handlers via arguments » Improve argument passing to fields
Status: Active » Postponed
Issue tags: +Migrate 2.4
mikeryan’s picture

Status: Postponed » Active
nerdcore’s picture

I am working on an Address Field myself, and am excited to see that Migrate 7.x-2.3 was released 2012-Mar-13.

What is the status of this?

mikeryan’s picture

The main initiative for Migrate 2.4 is improving the file handling, and this is the first step. With Drupalcon followed by a short vacation, I won't have much time in the next couple of weeks, but in the meantime let's discuss how this should be approached here. What would be the best DX for migrating to multi-component fields like files? As I said previously, I prefer @drewish's first proposal, but I'm open to other arguments or new suggestions.

nerdcore’s picture

Is it possible to use default values for arguments, like defaultValue() for fields?

mikeryan’s picture

Not with the current argument-based method, no. But the nice thing about drewish's proposal in #1 is that each subfield would have its own field mapping, so all the field mapping methods like defaultValue and sourceMigration would be available to them.

nerdcore’s picture

I've added an Address Field example to the docs:

http://drupal.org/node/1412126

Mainly because it is a more in-depth example than body_summary (more attributes to consider, and the use of Country as the base field is perhaps confusing to some).

puddyglum’s picture

Assigned: mikeryan » Unassigned
Status: Fixed » Active
drewish’s picture

jmonkfish, Why are you doing the addExpression()? Just use addField() if you need to get an alias. Also why are you querying the same table twice?

Actually don't answer that here. Open up a new support request since it's really not related to this issue.

puddyglum’s picture

Thanks drewish :) I simply don't know how to solve the problem at all currently without some sort of change to how to pass in arguments.

The problem I'm having is how to migrate into a multi-value file field, while keeping the Description for each one? In my example above I show how I can do either multi-value without the description, and also single value and keep the description. How can I do both?

Hopefully 2.4 provides a good solution for this.

Started a new issue for this: #1509750: Trying to map subfields (file field description) to a multi-value file field

drewish’s picture

jmonkfish, I'd be happy to talk about it further if you open up a new issue. I don't want to get further off topic here. I think you should be able to make it work, you'll just have to change your approach.

Update: that said, I now understand the issue you're raising, how do you account for not just sub-values but multi-value fields with sub-values. Sorry to be so slow on the uptake.

mikeryan’s picture

Status: Active » Needs work
FileSize
10.41 KB

OK, the simplest way to implement this in an upwardly-compatible way was to populate the arguments array. This doesn't address multiple values yet - the field handlers will need to change to handle that, but it's a start...

mikeryan’s picture

Assigned: Unassigned » mikeryan
Status: Needs work » Fixed

Committed to D6 and D7. I modified the text field handler to be able to handle arrays, which it couldn't before. E.g.

  $this->addFieldMapping('field_multi_text', 'multi_body');
  $this->addFieldMapping('field_multi_text:summary', 'multi_teaser');
...
  public function prepareRow($row) {
    $result = db_select('source_table', 't')
                   ->fields('t', array('body', 'teaser'))
                   ->condition('id', $row->id)
                   ->execute();
    foreach ($result as $body_row) {
      $row->multi_body[] = $body_row->body;
      $row->multi_teaser[] = $body_row->teaser;
    }
  }

Should ensure that each instance of the text field gets its own summary.

I did not modify the file field handler to deal with arrays for alt/title/etc. - I figure that stuff is going to get completely refactored anyway...

Assigned: Unassigned » mikeryan
Status: Active » Closed (fixed)

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

dgtlmoon’s picture

So if i was doing something like

    // Copy an image file, write DB record to files table, and save in Field storage.
    $arguments = MigrateFileFieldHandler::arguments(IMPORT_FEED_BASE_URL,
      'file_copy', FILE_EXISTS_RENAME, NULL, array());
    $this->addFieldMapping('field_op_main_image', 'image')
         ->arguments($arguments)
         ->xpath('/article/image');

What should the arguments be now?

I get Call to undefined method MigrateFileFieldHandler::arguments()

dgtlmoon’s picture

Status: Closed (fixed) » Needs review

Can we please document and give a little more information to us unfortunate noobs that find functions we were using mysteriously missing

mikeryan’s picture

Status: Needs review » Closed (fixed)

See http://drupal.org/node/1540106 - when you upgraded and ran update.php, it should have given you a link to the documentation.

dgtlmoon’s picture

OK, looks like (correct me if i'm wrong) but you can pass your arguments like this

    $this->addFieldMapping('field_op_main_image', 'image')
         ->xpath('/article/image');

    $this->addFieldMapping('field_op_main_image:file_replace')
         ->defaultValue(FILE_EXISTS_REPLACE);