6

I am attempting to use the patch in comment #222 of this issue (https://drupal.org/node/1175082#comment-7625869) to migrate data into a multi-value field collection.

I create an array of the objects in prepareRow() but when the migration completes and I review the nodes, only the first item in the array has populated the field collection and no subsequent items have been added. Am I missing something important?

<?php
class IngredientsMigration extends BasicMigration {
  public function __construct($arguments) {
    parent::__construct($arguments);
    $this->description = t('Migrate recipe ingredients from the source database into field collections.');
    $this->dependencies = array('Recipe');

    $query = Database::getConnection('default', 'legacy')
      ->select('recipes', 'r')
      ->fields('r', array('id', 'ingredients'))
        ->condition('r.id', array(2), 'IN');

    $this->source = new MigrateSourceSQL($query);
    $this->destination = new MigrateDestinationFieldCollection(
      'field_collection_ingredients',
      array('host_entity_type' => 'node')
    );

    $this->map = new MigrateSQLMap($this->machineName,
      array(
        'id' => array('type' => 'int',
          'length' => 11,
          'not null' => TRUE,
          'description' => 'Recipe ID',
          'alias' => 'r',
          ),
      ),
      MigrateDestinationFieldCollection::getKeySchema()
    );

    $this->addFieldMapping('host_entity_id', 'id')
      ->sourceMigration('Recipe')
      ->issueNumber(16);
    $this->addFieldMapping('field_ingredient_text', 'ingredients')
      ->description(t('See prepareRow method'))
      ->issueNumber(16);

    $this->addUnmigratedDestinations(array(
      'path',
      )
    );
  }

  public function prepareRow($row) {
    // Always include this fragment at the beginning of every prepareRow()
    // implementation, so parent classes can ignore rows.
    if (parent::prepareRow($row) === FALSE) {
      return FALSE;
    }

    $ingredients_array = preg_split('/$\R?^/m', $row->ingredients);
    $row->ingredients = array();
    foreach ($ingredients_array as $ingredient) {
      $row->ingredients[] = $ingredient;
    }
    return TRUE;
  }
}
  • Did you ever get this sorted? I am trying to migrate to a multi value field collection. – 2pha Mar 20 '14 at 10:55
  • @2pha Posted my solution below. – markdorison Mar 20 '14 at 14:59
1

I ended up writing a drush command to run before the migration that would take the 'ingredients' field from the source db and create a new table with the ingredients each parsed into their own row and a delta column to keep them in order. I then configured the ingredients migration to point to this table instead of the recipe table.

My DB connection for this migration ended up looking like the following:

$query = Database::getConnection('default', 'legacy')
  ->select('_recipe_ingredients', 'i')
    ->fields('i', array('id', 'recipe_id', 'ingredient'))
    ->orderBy('i.recipe_id')
    ->orderBy('i.delta');
1

You can also try this (also answered on this question: Migrating to a Field Collection):

Another option is the implement the complete method of the Migration class and populate the field collection programmatically there.

The following code will save a single field collection item to a node.

public function complete($entity, $row) {

  // load the newly created node (just in case entity isn't fully populated).
  $node = node_load($entity->nid);

  // set the values of all field collection fields
  $values = array(
    'field_name' => 'FIELD_COLLECTION_ITEM_FIELD_NAME',
    'field1'     => array(LANGUAGE_NONE => array(array('value' => 'VALUE'))),
    'field3'     => array(LANGUAGE_NONE => array(array('value' => 'VALUE'))),
  );

  // create the field collection item entity
  $item = entity_create('field_collection_item', $values);

  // attach it to the node loaded above
  $item->setHostEntity('node', $node);

  // save the entity
  $item->save();
}

You can modify it to save multiple field collection items fairly easily.

public function complete($entity, $row) {

  // load the newly created node (just in case entity isn't fully populated).
  $node = node_load($entity->nid);

  // loop values to add as field collection items (assumes you have them in a $values array).
  foreach ($values as $key => $value) {

    // set the values of all field collection fields
    $values = array(
      'field_name' => 'FIELD_COLLECTION_ITEM_FIELD_NAME',
      'field1'     => array(LANGUAGE_NONE => array(array('value' => 'VALUE'))),
      'field3'     => array(LANGUAGE_NONE => array(array('value' => 'VALUE'))),
    );

    // create the field collection item entity
    $item = entity_create('field_collection_item', $values);

    // attach it to the node loaded above
    $item->setHostEntity('node', $node);

    // save the entity
    $item->save();
  }
}

NOTE: If you are adding files to the field collection item, they will not be removed on a rollback unless you explicitly do that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.