For those of you who after reading my last post on the Feed Element Mapper are now interested in the actual works of the module, below is a step by step guide on how to build your own mapper.

But first, three things needed to happen before we were able to create the map-any-element-to-CCK feature:

  • Jess from WETA poked me about it for their TV and Radio feeds at the Drupal Meetup in DC on Tuesday :)

  • FeedAPI’s parser_simplepie now passes on the raw feed item, and with that anything it detects on the feed, on feed_item->options->raw

  • Feed Element Mapper offers a generic CCK mapper

From those three, the one I would like to explain a bit better is the third because it shows how easy it is to write your own mapper so you can map any feed item element identified by FeedAPI to virtually any property of a node. Let me explain how I built this mapper:

1) This is the empty implementation of hook_feedapi_mapper(). It’s just sitting there and doing nothing. In this case, it’ the implementation for the content module, the hook starts with content_.

function content_feedapi_mapper($op, $node, $field_name, 
				$feed_element = array(), $sub_field = '') {

}
  • $op tells us which operation is being executed (‘describe’, ‘list,’ or ‘map’).

  • $node is the node object we’re operating on.

  • $field_name is the name of the field on the node we are being called with (e. g. ‘field_severity’ for $node->field_severity, or ‘taxonomy’ for $node->taxonomy).

  • $feed_element is only set on $op == ‘map’ and contains the exact element that has been mapped to the given field. This element can be a string, a number or an array of string or numbers.

  • $sub_field is advanced stuff — it contains a value only if you offer more than one mapping target on $op == ‘list’, which we won’t do here (e. g. a taxonomy mapper offering a mapping for each vocabulary).

2) Now, this hook is being called for every field Feed Element Mapper finds on a node (it uses the form of the content type of the feed item node configured for the feed for doing this). In this example, I would like to write a generic mapper for any field that starts with ‘field_’, so I need to make sure that I only return stuff if I’m on the right field:

function content_feedapi_mapper($op, $node, $field_name, 
				$feed_element = array(), $sub_field = '') {
  if (strpos($field_name, 'field_') === 0) {

  }
}

Ok, that was easy.

3) Feed Element Mapper calls the hook on three occasions:

  • ‘describe’ — here you can return a string that describes what this mapper does

  • ‘list’ — here you can list sub fields you want to offer mappings to (e. g. a taxonomy implementation would list here all vocabularies) or if you only map to a single variable, return TRUE

  • ‘map’ — this is where the actual mapping happens

This calls for the mighty switch statement (yep, it does, but as yched pointed out in the comments, we just use if/elseif here :), here we go:

function content_feedapi_mapper($op, $node, $field_name, 
				$feed_element = array(), $sub_field = '') {
  if (strpos($field_name, 'field_') === 0) {
    if ($op == 'describe') {

    }
    else if ($op == 'list') {

    }
    else if ($op == 'map') {

    }
  }
}

4) Now let’s add a description to ‘describe’ and return TRUE on ‘list’.

function content_feedapi_mapper($op, $node, $field_name, 
				$feed_element = array(), $sub_field = '') {
  if (strpos($field_name, 'field_') === 0) {
    if ($op == 'describe') {
      return t('Maps a string or a number to this CCK field.');
    }
    else if ($op == 'list') {
      // We just return true here, as any element mapped 
      // to this field will be mapped to field_name[0]['value']
      return TRUE;
    }
    else if ($op == 'map') {

    }
  }
}

5) And here comes the fun part — the mapping. When Feed Element Mapper calls us here, it provides us with the field and the exact feed item element the user has mapped. Also, $node will contain the node object that represents the feed item in Drupal. So all we need to do is grab the feed item element and stick it on the right place on the node. Here is how this plays out for CCK:

function content_feedapi_mapper($op, $node, $field_name, 
				$feed_element = array(), $sub_field = '') {
  if (strpos($field_name, 'field_') === 0) {
    if ($op == 'describe') {
      return t('Maps a string or a number to this CCK field.');
    }
    else if ($op == 'list') {
      return TRUE;
    }
    else if ($op == 'map') {
      if (!is_array($feed_element)) {
        $field = $node->$field_name;
        $field[0]['value'] = $feed_element;
        $node->$field_name = $field;
      }
      return $node;
    }
  }
}

You can look at the original code here.

I hope this little tutorial-style explanation made sense. If you’re all fired up now and can’t wait to get your hands dirty with your own mapper, here is what’s missing: a mapper for the date module ; )

What we're doing.

Latest