Migrating data in Drupal 5 to 6 using Table Wizard and Migrate (Part 3 - Company Nodes)
After importing taxonomies and users, now I need to bring in the actual nodes. First thing to do is remove the primary key option from the node table's vid field. Remember, Table Wizard only works when the table has a single primary key.
Alright, so the things that need to be updated during the migrate are the taxonomy terms for each node, any node-reference fields (user-reference fields are fine as-is because we simply copied the existing user base to the new site), and our meta tags from the nodewords module.
One of the many cool features of the Migrate module is that it keeps track of stuff that has been migrated already and does things like automatically create a mapping table of old node id and the new node id. Without this feature, I wouldn't want to even attempt migrations, as you would have to manually update node ids for things like comments, and noderef fields.
Another cool feature are the prepare (and complete) hooks. What these allow is for you to come in before the data (node, user, comment) is saved to the new database and change stuff. In this case, change taxonomy term and noderef node ids from the old ones to the new ones. All you do is write a custom module with the hook in it e.g.
<?php
mymodue_migrate_prepare_node(&$node, $tblinfo, $row) {
....code for changing stuff goes here....
}
?>And since the node object is being passed by reference (the & symbol in front of $node tells us that), you don't have to "return" anything, simply set the value and its done.
Taxonomy terms
Depending on how you have assigned taxonomy terms to your nodes, your code in the prepare hook will be a little different. In my case, I have used regular Taxonomy, custom text fields, as well as CCK Taxonomy module to add terms to my nodes. So this requires a bit of extra work, as I have to account for all three situations. Nice for you folks reading this, though, as you get to see all options right here.
I'll start with the company nodes as they need to be in place first, before I bring in the product nodes. This is because the product nodes have a noderef to the company nodes.
Company nodes
My company nodes have a CCK Taxonomy field that links to a parent company (I know, it could have been a noderef field, but this was done before my time). I also have a regular taxonomy assigned to company nodes to add Company Features. In the Drupal 6 site, I have made this a Content Taxonomy field. The CCK Taxonomy field has also been changed to a Content Taxonomy field as the module has been more or less deprecated in D6.
Now that we know what we have and what it needs to become, let's take a look at the prepare hook's code that will handle the changes for us.
<?php
/**
* Implementation of hook_migrate_prepare_node().
*/
function mymodule_migrate_prepare_node(&$node, $tblinfo, $row) {
$errors = array(); // always here in this hook
switch ($node->type) { // we will have multiple node types
case 'company':
$co_tids = array();
// find the tid from the old term_node table for the node
// we're about to import
$result = db_query('SELECT tid FROM b_term_node WHERE nid = %d', $row->vid); // need to use vid instead of nid here to match primary key in migrate settings
while ($co_terms = db_fetch_object($result)) {
// run a custom function to get the new tid from the map
// table that migrate made when importing "company other
// features vocabulary. Add the new tid to an array.
$co_tids[] = _get_new_tids($co_terms->tid, 'migrate_map_b_company_other');
}
// go through the new tid array and get the name of the
// term using another custom function.
foreach ($co_tids as $co_tid) {
$co_term_name = _get_term_name($co_tid);
// create the array that is used by the Content Taxonomy
// module (I used a print_r on the new nodes to see their
// structure and figure this out)
$co_term = array('value' => $co_tid, 'view' => $co_term_name);
// assign the term array to the other features field
$node->field_company_other_features[] = $co_term;
}
break;
}
return $errors;
}
// Custom functions that look up the term name and
// get the new term id:
/*
* Convert old category term ids to new tids.
*
* @param $oldid
* Old category term.
* @param $table
* Text string for the migrate map table to look in.
* @return
* New tid.
*/
function _get_new_tids($oldid, $table) {
$result = db_result(db_query('SELECT destid FROM %s WHERE sourceid = %d', $table, $oldid));
return $result;
}
/**
* Lookup a term's name.
*
* @param $tid
* The term id number.
* @return
* The name of the term.
*/
function _get_term_name($tid) {
$name = db_result(db_query('SELECT name FROM {term_data} WHERE tid = %d', $tid));
return $name;
}
?>The above code doesn't show you the parent company modifications, because there weren't any parent companies selected for this dataset. However, the code will be pretty much the same, except that it would look in the migrate_map_b_company_vocab table for the new term values. I would also modify the first SELECT query to search for terms from a specific vocabulary to avoid any problems.
On the D5 site, I used the CCK_Address module to handle company addresses, but in D6 I am using the Addresses module. Only one minor change is needed to handle the switch over, and that is to convert the country field to lowercase. Simply add the following line of code before the break statement.
<?php
// make country id code lowercase for the Address module
$node->field_address[0]['country'] = strtolower($node->field_address[0]['country']);
?>Company nodes also had customizable meta tags using the nodewords module. I had previously customized the module to handle the META Title tag. In D6, I am still using nodewords, but I have included the Page_Title module to handle the title meta tag. Nodewords has changed its format for storing the information, so some modifications are needed there too.
I had all nodewords stuff stored in one table (including the page title meta information), so its a relatively simple matter of getting the info out of that table and sticking into the correct node variables. Here's the code I used for that. Again, just insert it before the break statement.
<?php
// get the info from the old table
$result_nw = db_query('SELECT * FROM b_nodewords WHERE id = %d', $row->vid);
while ($nodewords = db_fetch_object($result_nw)) {
// check each field to get the right information
if ($nodewords->name == 'description') {
$description = $nodewords->content;
}
if ($nodewords->name == 'title') {
$title = $nodewords->content;
}
if ($nodewords->name == 'keywords') {
$keywords = $nodewords->content;
}
}
// assign the information to correct fields in the node
$node->page_title = $title;
$node->nodewords['description']['value'] = $description;
$node->nodewords['keywords']['value'] = $keywords;
?>Pretty simple, really - the nodewords part, that is :)
OK, so now we have our company nodes in place, with their correct taxonomy terms, nodewords, page titles, etc. The one thing I couldn't figure out was bringing across the logos that were uploaded on the D5 site. However, there were only a handful, so I will do them manually for now. If I figure out how to do it "properly", I'll update this post, or write one just for that.
The next step will be bringing in the product nodes. They're a bit more complex, and require a bit more work. They also use nodewords, but its exactly the same code as for company nodes, so I won't talk about it again.
