Search code examples
drupaldrupal-7migratedrupal-taxonomy

Drupal - Importing a taxonomy with migrate module from a table and creating/updating existing terms


I need to import a list of terms into my taxonomy from a source I loaded in the database. The problem is I allready have this taxonomy on my site (loaded wihtout migrate) with terms that are used by reference in other content, so I got to keep existing term and update them or create the new ones.

To link my taxonomy source and the existing taxonomy I have an unique code for each term, so I added a code field to my vocabulary and filled it for each existing term.

I am currently able to create and update terms with my current Migration class, but if the name of my term on the site and the name of the term in my source is different, the import will create a new term instead of updating its name even if the code is the same.

Here my Migration Class :

class TotoMigration extends Migration  {

      private $list_term = array();

      public function __construct($arguments) {
        parent::__construct();

        $this->softDependencies = array('TotoParent');

        // get data from the custom table containing the new terms to create or update
        $query = db_select('toto', 'f')
          ->fields('f', array(
              'CODE', // code
              'LIBLONG', // name
              'PARENT', // parent
          )
        );
        $this->source = new MigrateSourceSQL($query);

        $this->destination = new MigrateDestinationTerm('toto_tax');

        $this->map = new MigrateSQLMap($this->machineName,
            array(
                'CODE' => array('type' => 'varchar',
                    'length' => 5,
                    'not null' => TRUE,
                    'description' => 'Code',
                )
            ),
            MigrateDestinationTerm::getKeySchema()
        );


        $this->addFieldMapping('name', 'LIBLONG');
        $this->addFieldMapping('field_code', 'CODE');
        $this->addFieldMapping('parent', 'PARENT')
        ->arguments(array('source_type' => 'tid'))
        ->sourceMigration('TotoParent');

        // create a list of existing toto terms with code => tid
        $list_term = db_query("select fc.field_code_value, ttd.tid
                                                                from taxonomy_term_data ttd
                                                                left join taxonomy_term_hierarchy tth on tth.tid=ttd.tid
                                                                left join field_data_field_code fc on fc.entity_id = ttd.tid
                                                                where ttd.vid=10
                                                                and tth.parent!=0;")->fetchAllKeyed();
      }

      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;
        }
        // if the destination is not mapped in migrate we tell him where to go
        if (!isset($row->migrate_map_destid1) && isset($list_term[$row->CODE])) {
          $row->migrate_map_destid1 = $list_term[$row->CODE];
        }
      }
    }

I then load the import with drush (and --update option). I must be missing something, if anyone got a clue it will be welcome.


Solution

  • After many tries, the problem reside in the fact the module Migrate does not support Creating content and Updating content in the same migration class (I even read it will sometime claim to update content and just do nothing).

    So the solution is pretty simple, create 2 classes :

    • One for Creating content
    • One for Updating content

    Your Creating class will be the same.

    Your Updating class will need to have a systemeOfRecord set to DESTINATION :

    $this->systemOfRecord = Migration::DESTINATION;
    

    So it knows to only update and not recreate the content, it will keep current fields not mapped and update fields mapped that are not part of the MigrateSQLMap :

    $this->map = new MigrateSQLMap($this->machineName,array(...));
    

    The tricky part will be to find corresponding nid/tid of your content so you can map it to your imported data and then to separate data used to update or create content.