---
title: "Let’s talk about Migrate, Drupal’s module"
date: 2023-09-11T13:27:03.000Z
canonical: https://www.ekino.fr/publications/lets-talk-about-migrate-drupals-module/
---

> Let me start by saying that this is the first in a series of articles about my experience with Migrate.
> If you want, you can go further by looking at [this video](https://www.youtube.com/watch?v=BPpYq9Nuljc&list=PL9_3w34sKcM16zdp5viU4NEV8Q26LF6o9&index=17) from the [Drupal Dev Days 2023](https://ddd23.drupalcamp.at/).

![](https://www.ekino.fr/media/original/0000/00/01da8483-f2cb-f6a5-16ed-40297cba21af.jpg)

Have you already used Migrate for Drupal? No? 
Then first, let’s talk about some basics to start understanding this module!

### What’s the purpose?

> The migrate module provides a flexible framework for migrating content into Drupal from other sources.[
> drupal.org](https://www.drupal.org/node/415260)

This means that it will help us transform some kind of data source into Drupal content.

![](https://www.ekino.fr/media/original/0000/00/4fc0997a-c2a4-a073-9e4f-eccc1df41923.png)

### But how does our source get transformed into Drupal contents?

Let’s look at an example. A simple one, to understand how the process works. Let’s say I want to migrate my Drupal 7 into a new Drupal 10.

![](https://www.ekino.fr/media/original/0000/00/6216deac-0cc7-6bc6-6407-3bfca486a55e.png)
*Basic page with a description field in Drupal 7*

I need to transform my node *basic page *that contains one custom field for the node’s description on Drupal 7 into a *basic page *containing a custom field for the description too.

![](https://www.ekino.fr/media/original/0000/00/35e58d86-25be-937c-fd9f-6392644da924.png)
*Basic page with a description field in Drupal 10*

First, we’ll have our *plugin sources *communicating with the *data source. *The plugin source is going to query (in a way or another) the data source.

![](https://www.ekino.fr/media/original/0000/00/2a8908a4-10fe-f9dc-4cd5-2bc249da9c45.png)

This means that we need to find a way to query our Drupal 7 to obtain all the nodes.
The *Node* module already proposes a [*plugin source*](https://api.drupal.org/api/drupal/core%21modules%21node%21src%21Plugin%21migrate%21source%21d7%21Node.php/class/Node/10) for this:

```
/** 
 * @MigrateSource( 
 *   id = "d7_node", 
 *   source_module = "node" 
 * ) 
 */ 

class Node extends FieldableEntity {
  public function query() {
    $query = $this->select('node_revision', 'nr')
       ->fields('n', ['nid', 'type', ...])
       ->fields('nr', ['vid', 'title', …]);
 
    ...

    $query->innerJoin('node', 'n', static::JOIN);

    ... 

    return $query;
  }
```

So here, it’s going to select anything needed from *node\_revision* and from *node*.

Now that we know which source plugin to use, let’s use it:

```
id: node_page_to_node_page 
label: 'My simple from D7 node page to D10 node page' 
status: true 
migration_tags:  
  - all  
  - basic_page  
  - node 
source:  
  plugin: d7_node
```

Here I started describing my migration file. 
First, I write down everything that helps me to identify it like *id, label *and* migration\_tags*.
Then, I’ll go ahead and specify that we’re going to be using Drupal’s *Node plugin source.*

Now that the module knows how we’re going to retrieve our base nodes, let’s prepare each line necessary.

![](https://www.ekino.fr/media/original/0000/00/876bf974-fa9b-5e7d-4da3-328274f3cf79.png)

> But what do you mean by preparing? Aren’t the rows already prepared when executing the query?

Well, sort of.
Each property on your row is going to be named the way you named it in the query (or the alias if you used one).
But you can rename those properties, work with them, or create new ones if you want to.

```
/** 
 * @MigrateSource( 
 *   id = "d7_node", 
 *   source_module = "node" 
 * ) 
 */
class Node extends FieldableEntity {

  public function prepareRow(Row $row): bool {  
    $nid = $row->getSourceProperty('nid'); 

    ... 

    if ($this->moduleExists('title')) { 
      $title_field = $row->getSourceProperty('title_field'); 
      if (isset($title_field[0]['value'])) { 
        $row->setSourceProperty('title', $title_field[0]['value']); 
      } 
    } 
    return parent::prepareRow($row);
 }
```

For example, *node* is going to create a new property *title* where the title of the node is going to be put in.

Thanks to this possibility, we can rename and process any data before to put it inside the Drupal contents.

We have fetched every data and prepared everything we needed. But we haven’t yet mapped the fields from D7 to the new fields on D10!

![](https://www.ekino.fr/media/original/0000/00/a66d1013-7f91-4c4b-d471-04fceb0d7921.png)

Here is where the *process* part comes in. Now we can map all the D7 fields into our new D10 fields.

```
process:   
  title: title
  field_description/0/value: description  
  field_description/0/format: 
    - plugin: default_value 
      default_value: 'basic_html'
```

We can specify how our fields will be mapped from D7 into our new D10.

And now we can use the *title* property prepared by *node *and our own property *description *into our new fields.

Something is hidden here, we are using the *get* *process* plugin for *title* and *description*:

```
process:   
  title:
    - plugin: get
      source: title
  field_description/0/value: 
    - plugin: get
      source: description
```

*You may find more information about it in the *[*documentation*](https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21process%21Get.php/class/Get)*.*

We’re almost finished! 
We have fetched all the necessary rows, prepared specific properties where necessary and mapped the fields.

> But, how do we specify the kind of Drupal content we want to create?

![](https://www.ekino.fr/media/original/0000/00/2c3b4622-41d1-15d5-41b0-a16bbbc81da9.png)

We just need to use another type of plugin, a *destination* plugin.

```
destination: 
  plugin: 'entity:node' 
  default_bundle: 'page’
```

We are going to use [one](https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21destination%21EntityContentBase.php/class/EntityContentBase/10) from *core* this time, and specify which *bundle* we are going to create.

In the end, all together, our migration file is going to look something like this:

```
id: node_page_to_node_page 
label: 'My simple from D7 node page to D10 node page' 
status: true 
migration_tags:  
  - all  
  - basic_page  
  - node 
source:  
  plugin: d7_node
process:   
  title: title
  field_description/0/value: description  
  field_description/0/format: 
    - plugin: default_value 
      default_value: 'basic_html'
destination: 
  plugin: 'entity:node' 
  default_bundle: 'page’
```

And *voilà*! You now have a big picture view of how it all works! 
As you can see, the different parts allow us to deliver some pretty powerful features. For simple cases, almost everything already exists, but for more advanced cases, we have everything at our disposal to deal with them !

---

[Let’s talk about Migrate, Drupal’s module](https://medium.com/ekino-france/lets-talk-about-migrate-drupal-s-module-4ad20ecf8b5d) was originally published in [ekino-france](https://medium.com/ekino-france) on Medium, where people are continuing the conversation by highlighting and responding to this story.
