On this page
Multiple source data rows
The Migration class is built on the fundamental assumption that there is a one-to-one correspondence between source rows coming in and destination objects going out. What if on the source side you have multiple values spread among multiple database rows for one or more of your fields? For example, if your source were a Drupal site, your first stab at a source query to migrate nodes and related terms might look like
$query = db_select('node', 'n')
->fields('n', array('nid', 'title')
->fields('tn', array('tid'));
$query->leftJoin('term_node', 'tn', 'n.vid=tn.vid');
The problem is, if you have multiple terms for a node, you'll end up with multiple rows for the same node feeding into the migration:
1 First node 23
1 First node 89
2 Second node 23
...
That won't work - you'll create 'First node' with term 23, then the second row will be skipped because nid 1 has already been migrated. What you want is a way to deal with one node at a time, and pull in all related terms with that node. Here are a couple of options:
GROUP_CONCAT
If your source data is in a MySQL database, you can use GROUP_CONCAT to pull the multiple values into a single row:
$query = db_select('node', 'n')
->fields('n', array('nid', 'vid', 'title'));
$query->leftJoin('term_node', 'tn', 'n.vid=tn.vid');
$query->addExpression('GROUP_CONCAT(DISTINCT tn.tid)', 'tag_list');
$query->groupBy('n.nid');
The results of your query then look like:
1 First node 23,89
2 Second node 23
Make sure you tell the field mapping to expect multiple comma-separated values (and, since we're migrating tids rather than term names, to expect tids):
$this->addFieldMapping('field_tags', 'tag_list')
->separator(',');
$this->addFieldMapping('field_tags:source_type')
->defaultValue('tid');
Query in prepareRow
An alternative (and the only option if your source data isn't in MySQL) is to pull the data in after the initial query.
$this->addFieldMapping('field_tags', 'tag_list')
->description('Populated in prepare()');
$this->addFieldMapping('field_tags:source_type')
->defaultValue('tid');
...
public function prepareRow($row) {
$row->tag_list = db_select('term_node', 'tn')
->fields('tn', array('tid'))
->condition('vid', $row->vid)
->execute()
->fetchCol();
}
$row->tag_list will be array(23, 89) for node 1, array(23) for node 2, etc.
Help improve this page
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion