The Salesforce Suite module for Drupal synchronizes Salesforce Objects with Drupal Content Types on a one-to-one, field-to-field basis. However, it doesn’t map an Object’s fields to a Content Type’s Taxonomy terms. After laying out a use case, I’ll share my working solution.

Example

Let’s say you want to replicate Salesforce Contact Objects as instances of a Drupal Content Type. The following Object fields map directly to fields in our Content Type.

  • Name
  • LeadSource
  • Email

You could create a Content Type mirroring those as simple text fields. But, I have something more interesting in mind for you!

Drupal Contact Content Type fields screenshot, with LeadSource as a Taxonomy

LeadSource is a Salesforce picklist that might work well as a Drupal Taxonomy. Perhaps you’d like to use it in a faceted search, or as an easy filter for a view, or use some cool module for taxonomies? Sorry, the Salesforce module can’t get you there out of the box.

The Road Taken

In August of 2013, the user mkryemadhi posted a Drupal.org issue titled Mapping drupal vocabulary terms with Salesforce field inquiring whether or not it was possible. A few others replied, also in search of a solution. About a year later, one of the module’s contributors, tauno, replied with a suggestion.

… For pulling, you would need to implement hook_salesforce_pull_entity_value_alter() to use a term name from SF and convert that to a term id in Drupal.

A custom field mapping handler could be implemented to make this all possible from the UI. Moving to the Feature Request queue.

My Solution

Here’s how I used the first part of that advice and implemented hook_salesforce_pull_entity_value_alter() to successfully map Salesforce Object fields to Drupal Taxonomy terms.

To start, I created a custom module with a function implementing the hook. Then, because I had more than one implementation of that kind, I checked the incoming Object type. If it wasn’t Contact, I skipped it. Otherwise, I used the Content Type’s mapped field to obtain the Vocabulary and either create a new term or use the existing one.

Drupal Salesforce Contact mapping screenshot

To use my solution, you’ll need to:

  • Have a Content Type with a field of type Term reference.
  • Create and enable a custom module containing my code, below.
  • Set up a mapping from the Content Type’s field (“Drupal Field”) using Related entities, as above, to the Object’s field (“Salesforce Field”).

But, first: please note the following:

  • Expect some naive Drupal / PHP mistakes. This is an excerpt from my very first Drupal module / PHP code.
  • This solution hijacks the module’s concept of Related Entity; the way I went about it prevents its original, intended usage. (It’s meant more for referencing a Node from another Node.)
  • I should have used a custom mapping handler and created a proper UI for it.

In the near future I hope to resolve the deficiencies in my implementation and contribute my solution back to Drupal’s Salesforce Suite community.

Code

Please note: I accidentally left out the get_drupal_field_name() function, below. Unfortunately, I no longer have access to the original codebase and am unable to provide it. If you use a debugger, or use print statements, to inspect $field_map, you may be able to come up with your own version of get_drupal_field_name().

<?php
/**
* Implementation of hook_salesforce_pull_entity_value_alter()
* @author Mike Christianson <http://codeaweso.me/2015/04/mapping-salesforce-object-fields-to-drupal-taxonomy-terms/>
*/
function codeawesome_salesforce_pull_entity_value_alter(&$value, $field_map, $sf_object) {
$sf_type = $sf_object['attributes']['type'];
if ($sf_type !== 'Contact') {
return;
}
if (is_related_entity($field_map)) {
handle_related_entity($value, $field_map, $sf_object);
}
}
/**
* @param $field_map
* @return bool
*/
function is_related_entity($field_map) {
return $field_map['drupal_field']['fieldmap_type'] == 'related_entity';
}
/**
* @param $value
* @param $field_map
* @param $sf_object
*/
function handle_related_entity(&$value, $field_map, $sf_object) {
$field_info_field = get_drupal_field_info($field_map);
$vocabulary = load_vocabulary($field_info_field);
if (isset($vocabulary)) {
$sf_picklist_str = get_sf_field_value($field_map, $sf_object);
if (!empty($sf_picklist_str)) {
$sf_picklist_values = explode(SALESFORCE_MAPPING_ARRAY_DELIMITER, $sf_picklist_str);
$terms = array();
foreach ($sf_picklist_values as $picklist_value) {
$term = create_or_load_term($picklist_value, $vocabulary);
if (isset($term)) {
$terms[] = $term;
}
}
$value = make_array_of_term_ids($terms);
if ($field_info_field['cardinality'] == 1) {
$value = reset($value);
}
}
}
}
/**
* @param $field_map
* @return bool|mixed|void
*/
function get_drupal_field_info($field_map) {
$druapl_field = get_drupal_field_name($field_map);
return field_info_field($druapl_field);
}
/**
* @param $field_info_field
* @return mixed
*/
function load_vocabulary($field_info_field) {
$vocabulary = null;
$vocabularyName = $field_info_field['settings']['allowed_values'][0]['vocabulary'];
if (isset($vocabularyName)) {
$vocabulary = taxonomy_vocabulary_machine_name_load($vocabularyName);
}
return $vocabulary;
}
function get_sf_field_value($field_map, $sf_object) {
$sf_field = $field_map['salesforce_field']['name'];
$sf_value = $sf_object[$sf_field];
return $sf_value;
}
/**
* @param $picklist_value
* @param $vocabulary
* @return array|mixed|null
*/
function create_or_load_term($picklist_value, $vocabulary) {
$term = null;
if (!empty($picklist_value)) {
$existing_terms = taxonomy_get_term_by_name($picklist_value, $vocabulary->machine_name);
if (empty($existing_terms)) {
$term = create_new_term($vocabulary, $picklist_value);
} else {
$term = reset($existing_terms);
}
}
return $term;
}
/**
* @param $vocabulary
* @param $term_name
* @return array
*/
function create_new_term($vocabulary, $term_name) {
$new_term = new stdClass();
$new_term->vid = $vocabulary->vid;
$new_term->name = $term_name;
$new_term->format = 'plain_text';
$new_term->description = 'This term came directly from Salesforce.';
$result = taxonomy_term_save($new_term);
return $new_term;
}
/**
* @param $terms
* @return array
*/
function make_array_of_term_ids($terms)
{
$term_ids = array();
foreach ($terms as $new_term) {
$term_ids[] = $new_term->tid;
}
return $term_ids;
}