Hi,
I read that the migrate module can map a unique key from the source to a unique key on the destination database, and will remember these mappings to correctly map foreign keys in the future.
However, I am still unclear how exactly this works.
Assuming you have a source table A with primary key A.id.
The destination table is B with primary key B.id (auto-increment).
At the time I start the migration, table B has only 4 rows with id = 3 .. 6, and the auto_increment counter remembers 7 as the last insert id. Yep, that's not identical with the count, because 1, 2 and 7 have been deleted already (just to make it more interesting).
Source table A contains 50 rows, with id = 1 .. 50. All of these are to be migrated to B.
The migrate logic looks at row A[1], and sees that the id is 1.
Now it could follow one of two strategies:
- Check if the slot at B[1] is free. Yeah, it is! So, save as B[1]. This time, we have A.id and B.id match up, at least for this one row. This continues to go well for A[2] -> B[2]. Then we get to A[3], which clashes with the existing row at B[3]. So for this row, we let auto_increment do the job: A[3] -> B[8]. Then A[4] -> B[9], etc. For those cases where we used auto_increment, we have to remember the mapping.
- Don't care about B[1], and always do auto-increment. So, we get A[1] -> B[8], A[2] -> B[9], etc. No match-up of A.id and B.id. Still, the associations are remembered in the mappings table.
Honestly, option (1) scares me like hell. This could result in 100 record ids being matched up 1:1, and then the 101st has a mismatch. In the meantime, one developer got so used to those two the ids matching up 1:1, that some code depends on it. Bad, bad, bad! I'd prefer if they all mismatch from the beginning.
Option (2) is much better for my taste.
-----------
I tried to find out, what is the default strategy that migrate module uses, or how I can influence that strategy.
At some point I will probably need to write sth like this:
$this->addFieldMapping('id', 'id');
But, some of the docs can be understood like this would activate option (1).
What happens if I don't write the above? Will this still fill the mapping tables?
Comments
Comment #0.0
donquixote commentedassociations still remembered in case (2).
Comment #1
mikeryanThe migrate module calls the normal Drupal APIs such as node_save() to store the destination objects - these produce the destination IDs in the normal way, and Migrate saves those in the map table. You do not need to (and indeed should not) make an explicit mapping of old ID to new ID, this just works.
To maintain the original key as the Drupal ID requires support on the destination side - in Drupal 7, nodes and users support this. Here's an example from migrate_example's beer.inc:
Comment #2
donquixote commentedAh, this is useful to know!
So, Drupal already knows that B.id is the primary key of B, and that it is auto-increment.
Then I tell MigrateSQLMap which is the primary key of A, via constructor argument.
Finally, no need to mention A.id nor B.id with addFieldMapping.
This makes me feel a lot better :)
Comment #3
donquixote commentedHm, one more question :)
What if two of my migrations write to the same entity?
Example: Migration A creates user entities.
Migration B updates those user entities with additional information, such as, group memberships.
Ok, in this special case I'd probably rather create a new destination type plugin for "group membership".
But in general, how do I build this second migration? How do I map the primary keys?
Comment #4
mikeryanMigration B, since it wants to update the users without affecting any data in the user record other than the specific fields being changes, needs to set $this->systemOfRecord = Migration::DESTINATION;. Assuming it's querying the same source keys as were used in Migration A, you would have an explicit mapping of the keys, with ->sourceMigration('MigrationA'). See WineUserUpdatesMigration in wine.inc for a concrete example.
Comment #5.0
(not verified) commentedclarify a bit, about why i dislike option (1).