In this blog,I am explaining the successful method to
migrate an xml node into a drupal 7 multi valued field collection.I believe this can be useful to all facing issues with field
collection migration in drupal 7.
What is a field collection in drupal?
A field collection is internally represented as an entity, which is embedded in the host entity. It allows a set of fields to be combined together and treated as a single field. For example, a field collection named Playlist can be created consisting of the text fields Song and Artist. When a Playlist entry is created the Song and Artist fields are presented together as elements of the Playlist field.
Issues in field collection migration
In drupal 7,it is very easy to migrate the entity reference fields from source xml files as it will search the source fields, creates an array and maps to the corresponding field.If the field can have multiple values,array will populate the values in the corresponding fields.But in case of field collection,it is an entity embedded in the host entity.So we should deal with field collection migration in a different way.
Solution which worked for me
I am using migrate contrib module for the migration task.I am going to elaborate with an example code.
Any migration should have a
source,destination and field mapping.
We will begin with an example.
I have a content type “studio” and it has a field collection
with field machine name “field_photos” in drupal.
field_photos has three fields
field_name (type :text )
field_credit (type :text )
field_attachment(type :entity reference field which has an
“image” content type as a target type)
The following code is a sample node taken from an xml file for the studio content type. We are going to
migrate this xml node values to mutli valued field collection “field_photos” in a content type "studio".
<myphotos_grid grid="true"><![CDATA[<rows>
<row
rowID="1">
<rowAttribute>
<param>photo-name</param>
<value>photname1</value>
</rowAttribute>
<rowAttribute>
<param>photo-credit</param>
<value>my credit</value>
</rowAttribute>
<rowAttribute>
<param>attachment</param>
<value>1009343343</value>
</rowAttribute>
</row>
<row
rowID="2">
<rowAttribute>
<param>photo-name</param>
<value/>
</rowAttribute>
<rowAttribute>
<param>photo-credit</param>
<value/>
</rowAttribute>
<rowAttribute>
<param>attachment</param>
<value>4432434344</value>
</rowAttribute>
</row>
</rows>]]></myphotos_grid>
I want to map the photo-name to the field_name,photo-credit to
field_credit and attachment to the field_attachment
Generally for the migration of content type studio ,we will
write one class extending the xml migration class and will specify the field
mappings. After the field mappings are done, override the prepare function.
$node - Entity object to build. Prefilled with any fields mapped in the Migration.
$row - Raw source data object - passed through to prepare handlers.
Below is the code used to create field collection for each content after field mappings are done.
fieldmappings -> prepare -> save
//$node will be the stdClass object for the current studio(contains the fields done in migration mapping) and $row will be source row object
public function prepare($node, stdClass $row) {
//fetching the field value, assume path to node is //fields/my_photos in the xml file
$rows_grid = $row->xml->fields->my_photos;
//CDATA function will contain the data not in xml format, load it as xml
$grid_rows_items = simplexml_load_string($rows_grid);
$collection_value = array('field_name' => 'field_photos’);
$counter = -1;
//loop through the grid rows
foreach($grid_rows_items as $grid_rows_item) {
$counter++;
$photo_id_destination = NULL;
//fetching the photo name.
$photo_name= $grid_rows_item>xpath('rowAttribute[param="photoname"]/value');
//fetching the photo credit
$photo_credit = $grid_rows_item->xpath('rowAttribute[param="photo-credit"]/value');
//fetching the photo id
$photo_id = $grid_rows_item->xpath('rowAttribute[param="attachment"]/value');
if ($slide_item != ''){
$photo_id = ‘image/’. $photo_id;
//looking up the migration map ,$this means current pointer (pointer used for field mapping), image xml files will be present inside the image folder
$photo_id_destination = $this->handleSourceMigration(array(
'image',
), array($photo_id));
}
//Use function entity_create to create a field collection entity
//arg1: The type of the entity.
//arg2: An array of values to set, keyed by property name. If the entity type has bundles the bundle key has to be specified.
$collection_entity = entity_create('field_collection', $collection_value);
//set the host as the studio content type
$collection_entity->setHostEntity('node', $node);
//setting the values
$collection_entity->field_name[LANGUAGE_NONE][0]['value'] = $photo_name;
$collection_entity->field_credit[LANGUAGE_NONE][0]['value'] = $photo_credit;
if (!is_null($photo_id_destination )) {
$collection_entity->field_attachment[LANGUAGE_NONE][0]['target_id'] = $photo_id_destination ;
}
$collection_entity->save(TRUE);
$node->field_gallery_slides_grid[LANGUAGE_NONE][$counter]['value'] =
$collection_entity->item_id;
$node->field_gallery_slides_grid[LANGUAGE_NONE][$counter]['revision_id'] =
$collection_entity->revision_id
}
When in doubt, "whatever works" is the correct standard for migration code. Hope that helps get you started!