I needed a way to load all the data of a node with multiple nested FC fields. Couldn't find it done before so I modified the code at http://drupal.org/node/1233256#comment-5167316 to not clone but to fully populate (load) an entity with FC fields. It searches for FC reference fields and replaces them with loaded FC entities. Posted here if anyone else finds this useful or perhaps be included in the module?

Usage:
load_all_fc_fields('node', $node);
$node now has the actual data and not just FC references. All FC child fields

<?php
/**
 * Recursively replaces all references to field collection entities with the actual field collection entity
 *
 * @param $entity_type
 * @param $entity
 * @param string $language
 */
function load_all_fc_fields($entity_type, &$entity, $language = LANGUAGE_NONE){
  $field_info_instances = field_info_instances();

  // Search for all fields of $entity that are of the type field_collection_item
  // and operate on them
  foreach($field_info_instances['field_collection_item'] as $field_name => $field_info){
    if(isset($entity->{$field_name})){
      load_fc_field($entity_type, $entity, $field_name, $language = LANGUAGE_NONE);
    }
  }
}

/**
 * Replaces references to field collection entities with the actual field collection entity
 *
 * Recursive for all child fields of $fc_field that are also field collections
 *
 * @param $entity_type Type of $entity
 * @param $entity By reference, the entity being operated on
 * @param $fc_field Name of field to replace references in
 * @param string $language
 */
function load_fc_field($entity_type, &$entity, $fc_field, $language = LANGUAGE_NONE){
  $entity_wrapper = entity_metadata_wrapper($entity_type, $entity);
  $fc_items = $entity_wrapper->{$fc_field}->value();
  if (!is_array($fc_items)) {
    $fc_items = array($fc_items);
  }

  $field_info_instances = field_info_instances();
  $field_names = element_children($field_info_instances['field_collection_item'][$fc_field]);

  // This is where the references to field collection ids is changed to the
  // actual loaded entity
  foreach($entity->{$fc_field}[$language] as $item_key => &$item){
    $item = $fc_items[$item_key];
  }

  foreach ($fc_items as $fc_item) {
    $fc_item_wrapper = entity_metadata_wrapper('field_collection_item', $fc_item);

    // Now check if any of the fields in the newly cloned fc item is a field collection
    // and recursively call this function to properly populate.
    foreach ($field_names as $field_name) {
        if (!empty($fc_item->{$field_name})){
            $field_info = field_info_field($field_name);
            if ($field_info['type'] == 'field_collection'){
              load_fc_field('field_collection_item',$fc_item, $field_name,$language);
            }
        }
    }

  }
}
?>

Comments

jiong_ye’s picture

thank you. it works well. but i did find that if i use node_load to load a node before running it through this function it would give me an error after the first run. i think it was due to the fact that node_load caches the node, so on the second time you run node_load, you dont need to run your function again because it's already populated.

i added a check to your function

if (isset($entity->{$field_name}) && !is_object($entity->{$field_name}[LANGUAGE_NONE][0])) {
load_fc_field($entity_type, $entity, $field_name, $language = LANGUAGE_NONE);
}