Migrating with the migrate module I am using Drupal 7 and the latest Migrate module. I am trying to create a class to migrate products from an OpenCart DB into my new D7 site. I figured out all the SQL I need and got most of that programmed in. But I am having a little trouble understanding how I am going to do the categories.

My migration target is Ubercart.

What I am trying to understand sits in the migrate_example module which is a submodule of Migrate. Specifically I am looking at the wine.inc file at the WineWineMigration class. I am trying to understand the term migration.

I have two lists that will become taxonomy terms in the Ubercart products. First is the categories. I have setup the categories manually, so when I bring the list of those over (using the GROUP_CONCAT SQL func) I will have a bunch of IDs that I will map using an array that will hold all the ID conversions. That is fine, but when I look at their BestWith info that they are migrating it shows that it is using the WineBestWith class above to import the term.

I am confused by this because it looks like some kind of secondary migration of terms. Is that what is happening? Further, is this where I would put my mapping array, in this term migration class?

The next thing I need to do is deal with tags. In OpenCart we have a bunch of free form tags. And when I manually create a product in Ubercart I have an autocomplete field for tags. In that field I can just put in a comma separated list of terms and it creates multiple terms. Can I do the same thing in the Migrate module? Can I just map the field to the list of comma separated terms? Will that add the tags in there for each product?
I'm working on the same problem right now so I understand your confusion.  You have a lot of questions, but I think they can be summed up into a singular question: 


  How does the Migrate module handle migrating a normalized database
  into a Drupal installation?


It's definitely not straightforward, but here's my understanding of how it works.  We'll start from the top (WineWineMigration) going down and ask questions as we go.

We start off by looking at the code at the top of the WineWineMigration class.

...
$this-&gt;dependencies = array('WineVariety', 'WineRegion',
  'WineBestWith', 'WineUser', 'WineProducer');
...


This tells the Migrate module that in order to migrate your Wine content, the dependent migrations - WineVariety, WineRegion, WineBestWith, WineUser, WineProduce - must be completed first.

So what we've learned here is that migrations can be dependent on other migrations.

Next we have the mapping between the table that currently holds the base wine info and the Drupal node:

$this-&gt;map = new MigrateSQLMap($this-&gt;machineName,
  array(
    'wineid' =&gt; array(
      'type' =&gt; 'int',
      'unsigned' =&gt; TRUE,
      'not null' =&gt; TRUE,
      'description' =&gt; 'Wine ID',
      'alias' =&gt; 'w',
    )
  ),
  MigrateDestinationNode::getKeySchema()
);


This is pretty straightforward so if you need clarification I will provide it.

I'm going to skip over some interim stuff that isn't really pertinent to hooking up the various categories and wine objects.

Now we get to the field mappings.  Observe:

// Mapped fields
$this-&gt;addFieldMapping('title', 'name')
     -&gt;description(t('Mapping wine name in source to node title'));
$this-&gt;addFieldMapping('uid', 'accountid')
     -&gt;sourceMigration('WineUser')
     -&gt;defaultValue(1);
// TIP: By default, term relationship are assumed to be passed by name.
// In this case, the source values are IDs, so we specify the relevant
// migration (so the tid can be looked up in the map), and tell the term
// field handler that it is receiving tids instead of names
$this-&gt;addFieldMapping('migrate_example_wine_varieties', 'variety')
     -&gt;sourceMigration('WineVariety')
     -&gt;arguments(array('source_type' =&gt; 'tid'));
$this-&gt;addFieldMapping('migrate_example_wine_regions', 'region')
     -&gt;sourceMigration('WineRegion')
     -&gt;arguments(array('source_type' =&gt; 'tid'));
$this-&gt;addFieldMapping('migrate_example_wine_best_with', 'best_with')
     -&gt;separator(',')
     -&gt;sourceMigration('WineBestWith')
     -&gt;arguments(array('source_type' =&gt; 'tid'));
$this-&gt;addFieldMapping('field_migrate_example_wine_ratin', 'rating');
$this-&gt;addFieldMapping('field_migrate_example_top_vintag', 'best_vintages');


See where it says:

-&gt;sourceMigration(...)


This indicates to the migration that in order to map this field, another migration must be satisfied first.  I believe this is the "secondary migration" that you spoke of.  Let's use the region field mapping as the example here.  Breaking it down...

$this-&gt;addFieldMapping('migrate_example_wine_regions', 'region')
     -&gt;sourceMigration('WineRegion')
     -&gt;arguments(array('source_type' =&gt; 'tid'));


This says that the category region in the source database is mapped to a region vocabulary term.  As the TIP comment states in the field mappings code chunk, it assumes that mappings are made based on field_names, but since we are dependent on a secondary migration as you put it, we need to specify the migration it is dependent on and instruct it to use tids instead of field names.

So pretty much for every normalized table you have in your source database, you're going to be specifying a migration for each, and then in the relevant field mappings that involve those tables, you'll be specifying dependent migrations in your field mapping calls, as well as the dependent migrations declaration at the beginning of each migration.

I hope this helps.  I don't fully understand this myself so I used this question as an opportunity to enhance my understanding of how Migrate relates databases.  As I learn a bit more I'll update my answer accordingly.