While simple migrations may get by with just a constructor, in many real-world cases you'll find a need to implement one or more of these Migration class methods:

Note: if you have generic classes used for common configuration and mapping that implement one of the common migration methods listed below, any class that extends the generic class that uses the same methods in the generic class should call the parent method. For example, if your generic class has a prepare() method, and you need one in your extended class, add the following line to your extended class: parent::prepare($entity,$row);. prepareRow() should always have the code added as per the example below.

function prepareRow($row)

The prepareRow() method is called by the source class next() method, after loading the data row. The argument $row is a stdClass object containing the raw data as provided by the source. There are two primary reasons to implement prepareRow():

  1. To modify the data row before it passes through any further methods and handlers: for example, fetching related data, splitting out source fields, combining or creating new source fields based on some logic.
  2. To conditionally skip a row (by returning FALSE).

Consider this example, where there is additional data not directly accessible to the source class (e.g., the source is an XML feed and there's also related data from a database, or vice versa):

public function prepareRow($row) {
  // Always include this fragment at the beginning of every prepareRow()
  // implementation, so parent classes can ignore rows.
  if (parent::prepareRow($row) === FALSE) {
    return FALSE;
  }

  $related_data = $this->getRelatedData($row->id);
  // If marked as spam in the related data, skip this row
  if ($related_data->spam) {
    return FALSE;
  }

  // Add the related data of interest
  $row->foo = $related_data->foo;
  $row->bar = $related_data->bar;
  return TRUE;
}

It is important to note that rejected rows in prepareRow() are not removed from the total rows reported by drush migrate-status or the migrate dashboard - the counts reported there are the total rows available to the source, and rows which will be rejected by prepareRow() on import cannot be predicted.

To get the source key for the current row, use the following:

$row_source_key = $this->source->getCurrentKey();
// $row_source_key is an array whose keys are those passed for the Migrate map creation.

To record an error message for the current row

If you wish to record an error, use $this->queueMessage('your message text') and it will appear among the messages for this migration, automatically including the source ID.
Note: don't use $this->setMessage('message text') in prepareRow().

function prepare(stdClass $entity, stdClass $row)

The Migration class prepare() method is called by the destination class prepare() method, after it has called all field-level prepare() methods and immediately before the destination object is saved. The $entity argument is the destination object as populated by the initial field mappings and manipulated by the field-level methods; the $row argument is an object containing the data after prepareRow() and any callbacks have been applied. The prepare() method is the last chance to manipulate the destination object before it is saved to the Drupal database. It is important to remember that, since it is called after the field handlers, fields will be in their fully-expanded forms (i.e., in Drupal 7 a text field value will be at $entity->field_textual_data['und'][0]['value'] rather than simply $entity->field_textual_data).

This is an example for a node migration:

public function prepare($entity, stdClass $row) {
  // Let's pretend we couldn't have done this as easily in prepareRow...
  $entity->title = 'My site: ' . $row->source_title;
}

To record an error message in prepare()

If you wish to record an error, use $this->saveMessage('your message text'). Do not use $this->queueMessage('your message text') in prepare(), because message will be assigned to the next row.

function complete($entity, stdClass $row)

The Migration class complete() method is analogous to prepare(), but is called immediately after the complete destination object is saved. This may be used to do application-specific logging, or to update additional tables related to the object - it's mainly used when you need the Drupal ID of the newly-created object.

public function complete($entity, stdClass $row) {
  // Load term.
  $term = taxonomy_term_load($entity->tid);
  
  // Localized taxonomy term.
  $context = array(
    'term',
    $term->tid,
    'description'
  );
  i18n_string_textgroup('taxonomy')->update_translation($context, 'fr', $row->description_fr);
}

function createStub(Migration $migration, array $source_id)

If a migration's destination objects could potentially be referenced during migration before they run through migration themselves, consider defining a createStub() method for the migration. This method should create a simple object of the desired type, and return its ID (or FALSE if it could not be created). See Chickens and eggs: using stubs for more information.

Comments

jeffwpetersen’s picture

There is no method getRelatedData.

chellman’s picture

prepareRow() is where you do your own custom work. That method name is an example of something you might use there, not something that actually exists.

ClassicCut’s picture

The complete($entity, stdClass $row) method is not actually part of the Migration class, but rather the MigrateDestinationXX class (where XX is the destination type). You'll have to extend one of those destination classes and override the complete method in there rather than in your migrate class for it to work.

2pha’s picture

I don't think this is correct. I have a class that extends the Migration class and my complete function get called without a problem

ebeyrent’s picture

You're both correct, in that it depends on the destination class. For example, MigrateDestinationTable has this:


public function complete($entity, stdClass $source_row) {
    $migration = Migration::currentMigration();

    // Call any complete handler for this specific Migration.
    if (method_exists($migration, 'complete')) {
      $migration->complete($entity, $source_row);
    }
  }

However, MigrateDestinationUser does not. You'll need to check the destination class you're using and extend it if the complete() method doesn't exist. It's too bad this function wasn't added to the abstract MigrationDestination class.

tusharbodke’s picture

My migration using 'sourceMigration' for fieldmapping to set value for preiously migrated entity ref, Want to create new node if its reference entity migrated else please skip current node import.

We can check parameters set in $row at 'prepare_row' which allow to return 'FALSE' to skip record.
Can we do same at 'prepare($entity, stdClass $row)'?


// Something like..??
 public function prepare($node, stdClass $row) {
    if (empty($node->field_request_reference)) {
	  return FALSE;
    }

   // Let's pretend we couldn't have done this as easily in prepareRow...
   //$node->title = 'My site: ' . $row->source_title;
 }

Tushar Shantaram Bodake

aangel’s picture

This doesn't appear possible. The function is called with no regard to its return value because it's part of the built in entity system.

botris’s picture

So here's my big lifesaver.
When doing big migrations you will usually test a migration with a specific (node) id in drush like so:
drush mi Pages --idlist=1234
Then using a prepareRow($row) or prepare($entity, stdClass $row) function (or a callback on a fieldMapping) can leave you guessing what the object parameters are and there is no place to "dpm" stuff.
But I discovered this gem: https://www.drupal.org/project/object_log where you can write a 'dpm' to a log file. For instance:

public function prepare($node, stdClass $row) {
  object_log('node', $node);
  object_log('row', $row);
}
ashhishhh’s picture

Thanks for sharing this Boris

webdrips’s picture

public function prepare($node, stdClass $row) {
  drush_print_r($node);
  drush_print_r($row);
}

Looking to Migrate to Drupal 9/10? Have a look at our Drupal 9 demo site and request access.

botris’s picture

Sure, but it probably depends on the size of the object and number of attributes.
I prefer the way Krumo allows me to inspect a deep nested attribute as apposed to printing everything to screen.

tmansveld’s picture

Since you're using drush, why not use an echo in your prepare/prepareRow function?

For example:
echo 'Nid: '. $row->nid . ' | title: '. $row->title;

For each row processed, drush will output the echo.

Very simple solution, no additional modules needed.

ovidenov’s picture

watchdog('test', '<pre>'. print_r($row, 1) .'</pre>');

would work also to display the row values in watchdog.

BenStallings’s picture

The complete() method seems to not be implemented in Drupal 8, but I can't find any information about what to use instead. Can anyone point me in the right direction? Thank you in advance.

2pha’s picture

Maybe this will help, though a little old and I have not tested or used it
https://www.drupal.org/node/2544874

BenStallings’s picture

Thank you, 2pha, I would never have found that without your help!

jdgva’s picture

I can't seem to find full context for how to create a custom Migration class to extend the default Drupal 8 Migration class, where to put it and how to include the standard class in the file, and how to register it. All I can find is example functions out of context.

ebeyrent’s picture

You need a custom module, I put mine in web/custom. So for example, let's call our module "custom_migration". It's path would be:
web/modules/custom/custom_migration

Within this module, you'll need to implement your class as a migration plugin, I'm assume as a source plugin. Your plugin path would be:
web/modules/custom/custom_module/src/Plugin/migrate/source/MySource.php

Your class would look something like this:

namespace Drupal\custom_module\Plugin\migrate\source;

use Drupal\migrate\Plugin\migrate\source\SqlBase;
use Drupal\migrate\Row;

/**
 * Source plugin for MySource content.
 *
 * @MigrateSource(
 *   id = "my_source"
 * )
 */
class MySource extends SqlBase {
  
  public function fields() {
    // .....
  }

  public function query() {
    // ...
  }

  public function getIds() {
    // ...
  }

  public function prepareRow(Row $row) {
    // ...
  }

}

I use migration_plus, which may be different from how you're doing things. My config file references the source plugin, the relevant bit would look like:

source:
  plugin: my_source
  high_water_property:
    name: changed
    alias: n