Field Collections Clone Field Structure

Specs

Version
Drupal 7
Tools
Batch Processes
Drush
Field Collection
Created
12 Jun 2015

Summary

Use a Drush command to clone data from field collections to new field collections. This snippet loops through all field collection items of a given field collection and copies its data to new field collection items.

Code

1. Drush Command with Batch Process

MODULE_NAME.drush.inc
MODULE_NAME.drush.inc will be autodiscovered by Drush after putting it in your module directory. MODULE_NAME_drush_command() implements hook_drush_command() and defines the Drush command as "drush update1". After putting this in your module directory and flushing the cache, navigate to your docroot using a terminal and run "drush update1". Because it could be a long process and could possibly time out, MODULE_NAME_drush_update_1() breaks the process up in to multiple batch operations.
          
/**
 * Implements hook_drush_command().
 */
function MODULE_NAME_drush_command() {

  $items['update1'] = array(
    'description' => t('Process update 1'),
    'aliases' => array('updr'),
    'callback' => 'MODULE_NAME_drush_update_1',
    'options' => array(
      'number' => t('The update number to run.'),
    ),
  );

  return $items;
}

/*
 * MODULE_NAME_drush_update_1_batch_process() actually processes the field collection
 * logic and migration. The process was timing out because it was running for too long, so it had to be
 * broken up by a batch process using this function.
 */
function MODULE_NAME_drush_update_1() {

  $operations = array();
  $old_field_collections = array();
  
  // Sorting by "entity_id ASC, delta ASC" will ensure that field collection items are weighted the same on the new
  // field collection field as the original.
  $results = db_query("SELECT * FROM {field_data_field_image_hero} ORDER BY entity_id ASC, delta ASC");
  foreach ($results as $key => $result) {
    $old_field_collections[] = $result;

    // Every 20 records, create a new batch process and reset the old_field_collections array.
    if (($key != 0) && ($key % 20 === 0)) {
      $operations[] = array('MODULE_NAME_drush_update_1_batch_process', array($old_field_collections));
      $old_field_collections = array();
    }

  }
  
  // process remaining results
  $operations[] = array('MODULE_NAME_drush_update_1_batch_process', array($old_field_collections));
  
  $batch = array(
    'operations' => $operations,
  );

  batch_set($batch);
  $batch = & batch_get();
  $batch['progressive'] = FALSE;
  drush_backend_batch_process();
  
}

/*
 * Migrate data from field collection field_image_hero to field_image_hero_landing on the same node,
 * and perform other minor updates to the nodes.
 */
function MODULE_NAME_drush_update_1_batch_process($old_field_collections = array()) {

  foreach ($old_field_collections as $result) {

    $node = node_load($result->entity_id);

    // Load the original field collection.
    $field_collection_item = field_collection_item_load($result->field_image_hero_value);
    
    // Make the Entity API think this is a new entity by unsetting its item_id and revision_id.
    unset($field_collection_item->item_id);
    unset($field_collection_item->revision_id);
    
    // Adjust the bundle (property field_name) to the new field collection field name.
    $field_collection_item->field_name = 'field_image_hero_landing';
    
    // Map node title and subtitle fields to the field collection item entity
    $field_collection_item->title_field['und'][0]['value'] = $node->title;
    $field_collection_item->field_subtitle['und'][0]['value'] = !empty($node->field_subtitle['und'][0]['value']) ? $node->field_subtitle['und'][0]['value'] : '';
    
    // Save new record. The object will be given a new item_id and revision_id that are used in the next step. These are the
    // properties of the new field_image_hero_landing field collection.
    $field_collection_item->save(TRUE);

    // The field collection has been stored properly using the new field collection field, but it still needs to be related
    // to the base node. Add the new field_image_hero_landing field collection to the end of any existing field collections. 
    $node->field_image_hero_landing['und'][] = array('value' => $field_collection_item->item_id, 'revision_id' => $field_collection_item->revision_id);
    node_save($node);
    
    drush_log(t('Successfully migrated field collection item !item_id on node !nid.', array('!item_id' => $field_collection_item->item_id, '!nid' => $node->nid)), 'success');
    
  }

}
          
          

Comments

Placeholder: I'll extend node comments with ajax and other functionality here.