I can't find any specific reference to this problem elsewhere so here we go:

I have a source field of multiple items (comma-separated)

$row->content_authors = "55,56,57"

Which converts fine into an entityreference field using a sourceMigration from which to map values.

But if the field is empty ("") bad things happen, the resultant array is:

$node->field_author = array('und' => array(0 => array('target_id' => FALSE)));

Which generates an error when node_save() is called - because FALSE is not an integer.

I tracked the error down to the source mapping function, which returns a FALSE when there's nothing. This is treated as an actual value and transferred to the array.

I realise this is not necessarily a simple issue. But if the mapping function returned NULL if there's nothing to map, which is then used to determine that the field should be set to:

$node->field_author = array();

which will save correctly.

To get around it I use the prepare() method to check for the bad array and replace it. Obviously this is not ideal.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

greenjuls’s picture

Version: 7.x-2.4-rc1 » 7.x-2.4
FileSize
525 bytes

I have the same issue, with some node_reference fields.
When source fields are empty, it throws the same error as adaddinsane on node_save().

I got this error since I updated Migrate from 2.3 to 2.4

I suspect that #1569612: Field handlers with skip_empty should only skip NULL values broke this by only skipping NULL but not FALSE anymore. And FALSE is what sourceMigration() returns when destination is not found.

I found no other way than patching the MigrateNodeReferenceFieldHandler class.

greenjuls’s picture

Status: Active » Needs review
Dave Reid’s picture

@greenjuls: This may probably help you out for all your fields if you add this to your base abstract Migration class:

  public function prepare($object, $row) {
    // Apply entity-generic alterations before the entity object is saved.
    if ($this->destination instanceof MigrateDestinationEntity) {
      $entity_type = $this->destination->getEntityType();

      // Remove empty field values.
      $this->removeEmptyEntityFieldValues($entity_type, $object);
    }
  }

  /**
   * Remove the empty field values from an entity.
   *
   * We run this on migrations because empty field values are only removed when
   * an entity is submitted via the UI and forms, and not programmatically.
   *
   * @param string $entity_type
   *   An entity type.
   * @param object $entity
   *   An entity object.
   */
  public function removeEmptyEntityFieldValues($entity_type, $entity) {
    // Invoke field_default_submit() which will filter out empty field values.
    $form = $form_state = array();
    _field_invoke_default('submit', $entity_type, $entity, $form, $form_state);
  }

Just ensure that all your specific migration classes' prepare() functions call parent::prepare($entity, $row) as the first line. I would really love for this to make it make to Migrate as well so that we wouldn't have to do this for each time we use Migrate.

ayalon’s picture

Life saver!

mikeryan’s picture

Status: Needs review » Fixed

I've committed greenjuls fix for node reference fields, applying it to user reference fields as well, for those narrow cases.

I've opened a separate issue for calling into the field defaulting mechanism: #1792894: Set configured defaults for empty fields

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

joelstein’s picture

Here's a DRY approach to Dave Reid's code, without patching Migrate:

/**
 * Removes empty values prior to migrating.
 * @see http://drupal.org/node/1665332
 * @see http://drupal.org/node/1792894
 */
function mymodule_remove_empties($destination, $entity) {
  if ($destination instanceof MigrateDestinationEntity) {
    $entity_type = $destination->getEntityType();
    $form = $form_state = array();
    _field_invoke_default('submit', $entity_type, $entity, $form, $form_state);
  }
}

And in each of your Migrate classes:

public function prepare($entity, stdClass $source_row) {
  mymodule_remove_empties($this->destination, $entity);
}
webdrips’s picture

Issue summary: View changes

For those of you using migrate_d2d with the latest stable versions of all the migrate modules, if you're still having a problem with empty CCK textarea fields appearing in your migrated content (which may lead to theme problems), here's what I had to do as none of the above worked in my abstract class:

  public function prepare($entity, stdClass $row) {
    foreach ($row as $key => $value) {
      if (!isset($row->$key) || $row->$key === null) {
        $entity->$key = NULL;
      }
    }
  }

Note I have not fully tested the above to see if it's too liberally setting NULLs, so you may want to do this in your node class instead:

  public function prepare($entity, stdClass $row) {
    if (!isset($row->field_my_field) || $row->field_my_field === null) {
      $entity->field_my_field = NULL;
    }
  }

Don't forget to call parent::prepare($entity, $row) if your abstract class already has a prepare() method.