How to create nodes with hierarchical terms from excel

Last updated on
30 April 2025

The purpose of this cookbook is to show how to create node from a excel documents via csv files.
The example we use is a list of stores with name, adress and three column to convert to terms a state,capital ubication and a kind of store column.

Create migrate module

First thing we are going to do is make a tipically module named migratestores.

migratestores.info

 name = "Migration of Stores"
description = "Migration module"
package = "Development"
core = 7.x
dependencies[] = migrate
version = "1.0"

files[] = stores.inc

As we can see, we need to having installed Migrate and Migrate UI modules. Download and install it.
https://www.drupal.org/project/migrate

Check the following structure:






Number State Capital Name Kind of store Telephone
1 Distrito federal Distrito federal La tienda grande drugstores  6546546546
2 Puebla Puebla Las semitas restaurant 7897987988
3 Estado de Mexico Toluca Libros en frio bookstores  6546546546
4 Jalisco Guadalajara La principal bookstores 7897987988
5 Nuevo leon Monterrey El cabrito restaurant 6546546546
6 Veracruz Xalapa La cruz drugstores 7897987988
7 Michoacan Morelia La cura drugstores 6546546546
8 Chiapas Tuxtla Gutierrez La farmacia chiquita drugstores 7897987988
9 Tabasco Villahermosa libros del horno bookstores 7897987988
10 Oaxaca Oaxaca La mescaleria bookstores 7897987988

As you can see, we have repeated data. If we see this as a relational database, we can easily assume that we need a table for Cities, another for States, other for Kind of Store and finally a table of content which would have names and phones.

Lets do some work at Drupal Content Type section.

First we need to define two Taxonomies. (Location and Kind).
We’ll add vocabularies, and change their machine names for further purposes.

Once we’ve created taxonomies, we are gonna create the Content Type.

Add Location field

Choose the vocabulary we created previously

Add Kind field

With the terms

Add phone field

phone field

Now lets do some code.

Create an empty file and name it migratestores.module

Now we are going to write in migratestores.migrate.inc This file we’ll be recognized by Migrate UI module, and will show our migrations on corresponding section.

<?php

function migratestores_migrate_api() {
   $api = array(
       'api' => 2,

       'groups' => array(
           'stores' => array(
               'title' => t('Stores imports'),
           )
       ),

       'migrations' => array(
       ),
   );
   return $api;
}

?>

We need to write a function with module’s prefix followed by migrate_api() and this need to return an api array. The array have an api version, the group for our migrations. Lately we’ll fill the migrations array.

stores.inc

<?php

abstract class BasicStoresMigration extends Migration {
   // A Migration constructor takes an array of arguments as its first parameter.
   // The arguments must be passed through to the parent constructor.
   public function __construct($arguments) {
       parent::__construct($arguments);

       // With migrate_ui enabled, migration pages will indicate people involved in
       // the particular migration, with their role and contact info. We default the
       // list in the shared class; it can be overridden for specific migrations.
       $this->team = array(
           new MigrateTeamMember('Daniel García', 'daniel@dragonflylabs.com.mx',
               t('Developer')),
       );

       // Individual mappings in a migration can be linked to a ticket or issue
       // in an external tracking system. Define the URL pattern here in the shared
       // class with ':id:' representing the position of the issue number, then add
       // ->issueNumber(1234) to a mapping.
       $this->issuePattern = 'http://drupal.org/node/:id:';
   }
}

?>

Explanation is not necesary.

In the same file:

 <?php class KindMigration extends BasicStoresMigration{
   public function __construct($arguments){
       parent::__construct($arguments);

       $module_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'migratestores');

       //File with our data
       $csv_path = $module_path . '/kinds.csv';
       // Specify CSV columns
       $columns = array(
           0 => array('id_csv_kind', 'Id'),
           1 => array('kind_name','Kind')
       );

       $this->description = t('Migrate Kinds');

       // Migration configuration
       $this->source = new MigrateSourceCSV($csv_path, $columns, array('delimiter' => ',', 'header_rows' => 1));
       //Taxonomy destiny
       $this->destination = new MigrateDestinationTerm('taxonomy_kind');
       $this->map = new MigrateSQLMap($this->machineName,
           array(
               'id_csv_kind' => array(
                   'type' => 'int',
                   'unsigned' => TRUE,
                   'not null' => TRUE,
                   'alias' => 'import'
               )
           ),
           MigrateDestinationNode::getKeySchema()
       );

       //Here we put the machine name that we put later in Drupal and match with column name of CSV
       $this->addFieldMapping('name', 'kind_name');
   }
}

class LocationMigration extends BasicStoresMigration{
   public function __construct($arguments){
       parent::__construct($arguments);

       $module_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'migratestores');
       $csv_path = $module_path . '/locations.csv';
       $columns = array(
           0 => array('id_csv_location', 'Id'),
           1 => array('location_name','Name'),
           2 => array('parent', 'Parent')
       );

       $this->description = t('Migrate locations');

       $this->source = new MigrateSourceCSV($csv_path, $columns, array('delimiter' => ',', 'header_rows' => 1));
       $this->destination = new MigrateDestinationTerm('taxonomy_location');
       $this->map = new MigrateSQLMap($this->machineName,
           array(
               'id_csv_location' => array(
                   'type' => 'varchar',
                   'unsigned' => TRUE,
                   'not null' => TRUE,
                   'alias' => 'import'
               )
           ),
           MigrateDestinationNode::getKeySchema(),
           'loslos'
       );

       $this->addFieldMapping('name', 'location_name');
       $this->addFieldMapping('parent_name', 'parent');
   }
}

class StoresMigration extends BasicStoresMigration{
   public function __construct($arguments){
       parent::__construct($arguments);

       $module_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'migratestores');
       $csv_path = $module_path . '/stores.csv';
       // We can safely delete the state column, just the City columns, in this case renamed as Location
       $columns = array(
           0 => array('id_csv_stores', 'Id'),
           1 => array('location_store','Location'),
           2 => array('name_store', 'Name'),
           3 => array('kind_store', 'Kind'),
           4 => array('phone_store','Telephone')
       );

       $this->description = t('Stores Migration');

       $this->source = new MigrateSourceCSV($csv_path, $columns, array('delimiter' => ',', 'header_rows' => 1));
       // Our destination Content Type
       $this->destination = new MigrateDestinationNode('content_store');
       $this->map = new MigrateSQLMap($this->machineName,
           array(
               'id_csv_stores' => array(
                   'type' => 'int',
                   'unsigned' => TRUE,
                   'not null' => TRUE,
                   'alias' => 'import'
               )
           ),
           MigrateDestinationNode::getKeySchema()
       );

       // Now, we reference the list of terms
       $this->addFieldMapping('field_location', 'location_store')->arguments(array('source_type' => 'term'));
       $this->addFieldMapping('title', 'name_store')->arguments(array('source_type' => 'term'));
       $this->addFieldMapping('field_kind', 'kind_store')->arguments(array('source_type' => 'term'));
       $this->addFieldMapping('field_phone', 'phone_store');
   }
}

?>

We’ve created our three migrations necessary.

Now, we are going to organize our CSV’s content like follow:

cities.txt

kinds.txt

stores.txt

The last step is to registering our Migrates into migratestores.migrate.inc

<?php
 'migrations' => array(
   'StoresKind' => array(
       'class_name' => 'StoresKindMigration',
       'group_name' => 'stores'
   ),
   'StoresLocation' => array(
       'class_name' => 'StoresLocationMigration',
       'group_name' => 'stores'
   ),
   'Stores' => array(
       'class_name' => 'StoresMigration',
       'group_name' => 'stores'
   )
),
?>

Now we’ll open the Module section and enable our module created in Development section.

Once enabled, go to Migrate tab located at Content superior menu.
NOTE: Maybe you’ll see a Field mapping error, just ignore it.

We can see our group created

image migrate stores

After that, just check the migration and apply Migrate.

Help improve this page

Page status: Not set

You can: