8 – How can I reference previously imported media entities?

I have been trying to attach media entities to node entities in Drupal 8 with a custom migration but despite numerous attempts and countless hours reviewing forums i’m stumped – I’m desperately hoping someone can help explain where I might be going wrong.

Steps I have taken:

  1. Created a custom migration to import files (Using Migrate Plus & Migrate Source CSV) – successful

  2. Custom migration to import files into Drupal media entities (image bundle) – successful

Now that I have the media imported, I am trying to reference it so I can look to ditch the normal default field_image.

CSV format:

fid, uid, filename, filepath, uri, filemime, filesize, status, timestamp, uuid, alt 

As the files are now media elements I know I can’t reference them by file ID (FID), presumably it would be the media ID (MID) but I have no idea how to get this.

I tried to use the migrate_lookup to reference the existing migrations but during import i’m not getting any error messages which makes it hard to tell whether i’m targeting the right thing or not.

I have also tried to add a default value to test a single value but this also didn’t work.

id: news_nodes
label: News Articles
migration_group: custom

dependencies:
  enforced:
    module:
      - migrate_custom

source:
  plugin: news_nodes
  node_type: article
  target: db_migration

destination:
  plugin: entity:node
  default_bundle: article

process:
  nid: nid
  vid: vid
  uid:
    plugin: default_value
    default_value: 1
  langcode:
    plugin: default_value
    source: language
    default_value: en
  title: title
  uid: node_uid
  status: status
  created: created
  changed: changed
  promote: promote
  sticky: sticky
  revision_uid: revision_uid
  revision_log: log
  revision_timestamp: timestamp
  body/format:
    plugin: static_map
    bypass: true
    source: body_format
    map:
      1: plain_text
      2: restricted_html
      3: full_html
      4: full_html
  body/value: body_value
  body/summary: body_summary
  field_tags: tags

  field_image: image
  field_image: 
    -
      plugin: migration_lookup
      migration: import_files
      source: filename
    -
      plugin: skip_on_empty
      method: process

  field_media_image:
      plugin: sub_process
      source: field_media_image
      process: 
        target_id:
          - 
            plugin: migration_lookup
            migration: import_media_files
            source: fid
            no_stub: true
          -  
            plugin: skip_on_empty
            method: process

  # field_media_image/target_id:
  #   -
  #     plugin: migration_lookup
  #     migration: import_files
  #     source: image_fid
  #   -
  #     plugin: media_generate
  #     destinationField: field_media_image
  #     imageAltSource: 
  #       plugin: default_value
  #       default_value: 'Featured image'
  #     imageTitleSource:
  #       plugin: default_value
  #       default_value: 'Featured image'
  #   -  
  #     plugin: skip_on_empty
  #     method: process


  field_news_companies: company_tags
  field_news_companies:
    plugin: sub_process
    source: field_company_companies
    process:
      target_id: tid
migration_dependencies:
  required:
    - custom_user
    - import_files
    - import_media_files
    - upgrade_d7_taxonomy_term_company_companies
    - upgrade_d7_taxonomy_term_tags

The commented out section was trying this: https://drupal.stackexchange.com/a/249401/94547 however I get ‘Cannot load a NULL ID. in assert()’ error.

The node entity i’m trying to import to is the default article content type.

  • Default image field – field_image
  • Name of media field – field_media_image

I really would like to understand what i’m missing. I’m new to migrations so any advice on how to help would be appreciated!

Having looked at a number of replies, I think we have narrowed down the issue to my source plugin plugin: news_nodes. which I am listing below.

The files/media have been imported using the CSV source but the nodes use custom PHP to query other custom fields which are required (taxonomy).

If I use the same source and plugin details as field_image in my field_media_image a media entity is referenced but not one that matches the file used in field_image. I’m guessing it’s passing the file id and the media entity is handing back a media item with the same numeric id.

Below is the custom plugin. For all I know the way I have set this up entirely wrong and needs to be re-made – any advice would be great.

Full code available on Github: https://github.com/lmonk72/migrate_custom

<?php

namespace Drupalmigrate_customPluginmigratesource;

use DrupalmigrateRow;
use DrupalmigratePluginmigratesourceSqlBase;

/**
 * @file
 * Contains Drupalmigrate_customPluginmigratesourceNode.
 */

/**
 * Extract nodes from Drupal 7 database.
 *
 * @MigrateSource(
 *   id = "news_nodes"
 * )
 */
class NewsNodes extends SqlBase {

  /**
   * {@inheritdoc}
   */
  public function query() {
    // this queries the built-in metadata, but not the body, tags, or images.
    $query = $this->select('node', 'n')
      ->condition('n.type', 'article')
      ->fields('n', array(
        'nid',
        'vid',
        'type',
        'language',
        'title',
        'uid',
        'status',
        'created',
        'changed',
        'promote',
        'sticky',
      ));
    $query->orderBy('nid');
    return $query;
  }

  /**
   * {@inheritdoc}
   */
  public function fields() {
    $fields = $this->baseFields();
    $fields('body/format') = $this->t('Format of body');
    $fields('body/value') = $this->t('Full text of body');
    $fields('body/summary') = $this->t('Summary of body');
    $fields('field_image/target_id') = $this->t('Image');
    return $fields;
  }

  // /**
  //  * {@inheritdoc}
  //  */
  public function prepareRow(Row $row) {
    $nid = $row->getSourceProperty('nid');

    // body (compound field with value, summary, and format)
    $result = $this->getDatabase()->query('
      SELECT
        fld.body_value,
        fld.body_summary,
        fld.body_format
      FROM
        {field_data_body} fld
      WHERE
        fld.entity_id = :nid
    ', array(':nid' => $nid));
    foreach ($result as $record) {
      $row->setSourceProperty('body_value', $record->body_value );
      $row->setSourceProperty('body_summary', $record->body_summary );
      $row->setSourceProperty('body_format', $record->body_format );
    }

    // taxonomy term IDs
    // (here we use MySQL's GROUP_CONCAT() function to merge all values into one row.)
    $result = $this->getDatabase()->query('
      SELECT
        GROUP_CONCAT(fld.field_tags_tid) as tids
      FROM
        {field_data_field_tags} fld
      WHERE
        fld.entity_id = :nid
    ', array(':nid' => $nid));
    foreach ($result as $record) {
      if (!is_null($record->tids)) {
        $row->setSourceProperty('tags', explode(',', $record->tids) );
      }
    }

    // Company taxonomy term IDs
    // (here we use MySQL's GROUP_CONCAT() function to merge all values into one row.)
    $result = $this->getDatabase()->query('
      SELECT
        GROUP_CONCAT(fld.field_company_companies_tid) as tids
      FROM
        {field_data_field_company_companies} fld
      WHERE
        fld.entity_id = :nid
    ', array(':nid' => $nid));
    foreach ($result as $record) {
      if (!is_null($record->tids)) {
        $row->setSourceProperty('company_tags', explode(',', $record->tids) );
      }
    }

    $result = $this->getDatabase()->query('
      SELECT
        fld.field_image_fid,
        fld.field_image_alt,
        fld.field_image_title,
        fld.field_image_width,
        fld.field_image_height
      FROM
        {field_data_field_image} fld
      WHERE
        fld.entity_id = :nid
    ', array(':nid' => $nid));
    // Create an associative array for each row in the result. The keys
    // here match the last part of the column name in the field table. 
     $image = ();

    foreach ($result as $record) {
      $image() = (
        'target_id' => $record->field_image_fid,
        'alt' => $record->field_image_alt,
        'title' => $record->field_image_title,
        'width' => $record->field_image_width,
        'height' => $record->field_image_height,
      );
    }
    $row->setSourceProperty('image', $image);


    return parent::prepareRow($row);
  }

  /**
   * {@inheritdoc}
   */
  public function getIds() {
    $ids('nid')('type') = 'integer';
    $ids('nid')('alias') = 'n';
    return $ids;
  }

  /**
   * {@inheritdoc}
   */
  public function bundleMigrationRequired() {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function entityTypeId() {
    return 'node';
  }

  /**
   * Returns the user base fields to be migrated.
   *
   * @return array
   *   Associative array having field name as key and description as value.
   */
  protected function baseFields() {
    $fields = array(
      'nid' => $this->t('Node ID'),
      'vid' => $this->t('Version ID'),
      'type' => $this->t('Type'),
      'title' => $this->t('Title'),
      'format' => $this->t('Format'),
      'teaser' => $this->t('Teaser'),
      'uid' => $this->t('Authored by (uid)'),
      'created' => $this->t('Created timestamp'),
      'changed' => $this->t('Modified timestamp'),
      'status' => $this->t('Published'),
      'promote' => $this->t('Promoted to front page'),
      'sticky' => $this->t('Sticky at top of lists'),
      'language' => $this->t('Language (fr, en, ...)'),
    );
    return $fields;
  }
}
?>