Migrate in core is among my favorite parts of Drupal 8 and 9. The framework is super flexible, and it makes migrating content from any source you can dream up pretty straight forward. Today I want to show a trick that I use when I receive a csv (or Excel file) from clients, where they want all of the contents in it migrated to Drupal. One very simple example would be a list of categories.

Typically the file will come with one term on each line. However, migrate would want us to set an ID for all of the terms, which currently none of the rows have. One solution to this is to place an ID on all of the rows manually with some sort of spreadsheet software, and then point our migration to the new column for its IDs. But since that both involves the words "manual" and "spreadsheet software" it immediately makes me want to find another solution. Is there a way we can set the row id programmatically based on the row number instead? Why, yes, it is!

So, here is a trick I use to set the ID from the line number:

The migration configuration looks something like this:

id: my_module_categories_csv
label: My module categories
migration_group: my_module
source:
  # We will use a custom source plugin, so we can set the 
  # ID from there.
  plugin: my_module_categories_csv
  track_changes: TRUE
  header_row_count: 1
  keys:
    - id
  delimiter: ';'
  # ... And the rest of the file 

As stated in the yaml file, we will use a custom source plugin for this. Let's say we have a custom module called "my_module". Inside that module folder, we create a file called Categories Csv.php inside the folder src/Plugin/migrate/source/CategoriesCsv.php. And in that file we put something like this:

<?php

namespace Drupal\my_module\Plugin\Migrate\source;

use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
use Drupal\migrate_source_csv\Plugin\migrate\source\CSV;

/**
 * Source plugin for Categories in csv.
 *
 * @MigrateSource(
 *   id = "my_module_categories_csv"
 * )
 */
class CategoriesCsv extends CSV {

  /**
   * {@inheritdoc}
   */
  public function prepareRow(Row $row) {
    // Delta is here the row number.
    $delta = $this->file->key();
    $row->setSourceProperty('id', $delta);
    return parent::prepareRow($row);
  }

}
   

In the code above we set the source property of id to the delta (the row number) of the row. Which means you can have a source like this:

Name
Category1
Category2
Category3

Instead of this

id;Name
1;Category1
2;Category2
3;Category3

The best part of this is that when your client changes their mind, you can just update the file instead of editing it before updating it. And with editing, I mean "manually" and with "spreadsheet software". Yuck.

To finish this post, here is an animated gif called "spreadsheet software yuck"