I'm using url aliases on my d6 site, which I've just migrated to d6. I couldn't find any documentation on url redirection in the Migrate issue queue, or in the Migrate handbook.

It looks like uri_map_redirect.php should be able to handle redirects from the old urls to the new ones, but in reading the code it looks that it only works with urls of the pattern "node/nid" handled per content type (ignoring user accounts and taxonomy terms for now).

This seems to be handling the case where the d6 site didn't use url aliases, but the d7 site does, which doesn't work in my case.

http://stackoverflow.com/questions/7481544/migrating-url-alias-from-drup... has a similar question, and the advice is to play with the url_alias table since they're pretty similar.

Also, it would be helpful to have an example of the Apache rewrite rule which maps legacy requests to uri_map_redirect.php.

I need to figure out how to place all d6 url_aliases of migrated nodes (and taxonomy terms, users, etc) into a d7 table, and then query that table. Which I'm going to do shortly...

Comments

burningdog’s picture

The apache rewrite rule for legacy requests makes sense if the legacy requests are ALL different from the new requests. But if there is some crossover with the d6 and d7 url aliases (like in my case) then there's no way to know which requests to send to uri_map_redirect.php and which to send to drupal.

In this case, it's better to processes these requests inside drupal. One option is to use the redirect module and migrate all d6 url aliases to the redirect table.

The only downside is that there's then no easy way to tell the difference between redirects that have been added on the new d7 site from legacy redirects.

mikeryan’s picture

Status: Active » Postponed (maintainer needs more info)

If you want to preserve your old aliases, just map them them directly:

$this->addFieldMapping('path', 'path');

If you'd like to change your paths in the course of your migration, and have 403 redirects from your legacy paths to the new ones, then use Pathauto to set up the desired patterns, and the Redirect module to manage the redirects. In your migration map the legacy paths to the redirect module:

$this->addFieldMapping('migrate_redirects', 'path');
burningdog’s picture

I've got a working prototype of this, where I load up all migrations, go through each individual mapping, grab the url alias as its used on the source, and redirect it to the destination drupal path. Like so:

Drupal 6 nid Drupal 6 alias Drupal 7 nid Drupal 7 alias
100 alias/to/my/awesome/node-title 100 content/node-title

 
For this node, we'd add an entry to the redirect table in the redirect module, like so:

Source path Redirect alias
alias/to/my/awesome/node-title node/100

 
The redirect to node/100 will then still work if the url alias for that node ever changes, and the globalredirect module can be used to automatically send users to the url alias for that node.

I'm not sure where this code would best fit. Perhaps in includes/url_alias_mapping.inc or maybe contributed to the redirect module. I'm sure there's a way to write this all as some kind of migrate destination handler, but that's too complicated for me. Anyway, here it is.

<?php
/**
 * Migrate all aliases from d6 to d7
 */
function migrate_all_url_aliases() {
  // Load up all migrations
  $migrations = migrate_migrations();

  foreach ($migrations as $migration) {
    // Find out what type of migration this is (node, taxonomy_term, user, file, etc)
    $entity_type = $migration->getDestination()->getEntityType();

    // Only migrate url aliases of the entity types which we want to
    if ($entity_type == 'node' || $entity_type == 'taxonomy_term') {
      $machine_name = $migration->getMachineName();

      //Ignore certain migrations
      if ($machine_name == 'diTermCities' || $machine_name == 'diTermCitiesOrphaned') {
        // Ignore
      }
      else {
        migrate_url_aliases($machine_name, $entity_type);
      }
    }
  }
}

/**
 * Migrate all url aliases for a specific machine name.
 *
 * @param string $machine_name
 *   Machine name of migration to process.
 * @param string $entity_type
 *   The entity type (node, user, etc.) of the migration.
 */
function migrate_url_aliases($machine_name, $entity_type) {
  $patterns = array(
    'node' => 'node/:source_id',
    'taxonomy_term' => 'taxonomy/term/:source_id',
    'user' => 'user/:source_id',
  );

  $pattern = $patterns[$entity_type];

  $map_table = 'migrate_map_' . drupal_strtolower($machine_name);

  $query = db_select($map_table, 'map')
      ->fields('map', array('sourceid1', 'destid1'));
  $result = $query->execute();
  $url_alias = array();

  foreach ($result as $row) {
    $source_uri = str_replace(':source_id', $row->sourceid1, $pattern);

    // Find the source url alias destination
    $query = Database::getConnection('default', 'd6')
      ->select('url_alias', 'a')
      ->fields('a', array('dst'));
    $query->condition('src', $source_uri);
    $result = $query->execute();
    foreach ($result as $row) {
      $source_alias = $row->dst;
    }

    // Find the destination url source and alias
    $query = db_select('url_alias', 'a')
      ->fields('a', array('alias', 'source'));
    $query->condition('a.source', $source_uri);
    $result = $query->execute();
    foreach ($result as $row) {
      $dest_alias = $row->alias;
      $dest_source = $row->source;
    }

    $url_alias[] = array(
     'source_source' => $source_uri,  // Column src in source url_alias
     'source_alias' => $source_alias, // Column dst in source url_alias
     'dest_source' => $dest_source,   // Column source in dest url_alias
     'dest_alias' => $dest_alias,     // Column alias in dest url_alias
    );
  }

  // Save the d6 url alias -> d7 internal drupal path redirect
  foreach ($url_alias as $alias) {
    // If it exists already, update it
    $redirect = redirect_load_by_source($alias['source_alias']);

    if (!empty($redirect)) {
      // Update the existing redirect if the destination has changed
      if ($redirect->redirect != $alias['dest_source']) {
        $redirect->redirect = $alias['dest_source'];
        redirect_save($redirect);
      }
    }
    else {
      $redirect = new stdClass();
      $redirect->source = $alias['source_alias'];
      $redirect->redirect = $alias['dest_source'];
      $redirect->type = 'redirect';
      $redirect->status_code = 302;
      $redirect->source_options = array();
      $redirect->redirect_options = array();
      redirect_save($redirect);
    }
  }
}
?>
burningdog’s picture

Status: Postponed (maintainer needs more info) » Active

Just saw #2 - which you wrote while I was writing #3! No, I don't want to keep my legacy paths. The pathauto aliases for the d7 site are set, but they might change sometime, so I want to map the source to a drupal path, rather than to an alias.

Oh wow - does the following single line of code really do everything I've done in #3?!

$this->addFieldMapping('migrate_redirects', 'path');

Is it the path field handler operating here - MigratePathEntityHandler? I can't find any integration with the Redirect module in the Migrate module, or in the Redirect module. Am I missing something obvious?

mikeryan’s picture

Yes, that does it - it's in the redirect module, in redirect.migrate.inc. Oh damn, looks like they still haven't committed it: #1116408: Support migrate module: Destination handler class. I have used the version of the patch at http://drupal.org/node/1116408#comment-6040494 in real projects and it works fine for me.

burningdog’s picture

Status: Active » Fixed

it's in the redirect module, in redirect.migrate.inc

*sigh.* I *did* download the latest Redirect release before doing any work but didn't think to use the -dev branch. That...summarizes this week for me so far! If I'd seen this earlier in my current migration project I would've used it, but I'm going to stick with my method in #3 for now, since I'm running around 25 individual migrations and I don't want to have to write an update for each of them.

Thanks for the input :)

mikeryan’s picture

It's not even in -dev, it has yet to be committed. I just submitted an updated patch and set it to needs review, trying to get some attention back on it...

burningdog’s picture

Good luck! How come the addition is in redirect.migrate.inc rather than in a separate module, like the way Date module does it with date_migrate? I ask because in the process of this migration I've ended up needed to write some field handlers for amazons3, media_vimeo, media_youtube, remote_file_source and am working on the video field...and want to contribute them back to those modules, and have made a module for each of them (which can be enabled for a migration, and then disabled afterwards).

mikeryan’s picture

I did originally do that in its own module, but in hindsight I think it's overkill. With both hook_migrate_api() and your migration code in .migrate.inc, the migration code incurs no overhead except when migration operations are being performed, so there's no reason not to just put it in the main module.

burningdog’s picture

Ok thanks - that's how I'll contribute them, then :)

burningdog’s picture

mikeryan, I'd appreciate your input over at #1835478: Migrating to the video field with the migrate module

The Video field has 3 distinct (and different) files which it uses (a source video file, a thumbnail file, and multiple playable video files) and I'm not sure how to get the VideoMigrateFieldHandler which I'm writing to reference all 3 file entity migrations.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.