Skip to main content
Home Home

Join

  • Get in touch

Main navigation

  • Blog
    • Tracking changes on field level on entities
  • Team
  • Home
  • Blog
  • How to skip fields for migration update if they're empty

How to skip fields for migration update if they're empty

by Dalibor
Co-owner, Chief Technical Officer
05 Feb 2019
Drupal

If you ever did migration of data with Drupal 8 migrate you know how the system works. So basically migrate is moving data from source to destination and you can write all sorts of plugins and some of them are in the core that allow you to process data before you send them to destination.

Since writing migration is pretty much standard now since Drupal 8 it is good to add possibility to your clients to modify their data via excel and then simply import this via migrate because clients are much faster at writing data in excel then on admin interface for example.

As you all know entities can contain a lot of fields and one of the requests you'll probably face if you go down this road is that someone will request that they only want to change some fields and leave the rest blank in their import document and they expect that to remain as it is in the database right now. If you don't already know migrate is taking fields as is and processes them and sends them to destination but if field is empty you can skip for example processing or entire row but what you really want in this case is to skip field for being set altogether.

I'll show you on example of user entity how you can achieve this.

So since we want to skip field completely in case of empty we cannot create process plugin for that as this is too late because process will always pass some value and we don't want for any value to be set in that case. What we actually need here is to create destination plugin. For this we can extend EntityUser destination that is in the core and modify updateEntity slightly to our purposes.

We start this by creating our class (we'll call it UsersUpdate for example) in folder some_module/src/Plugin/migrate/destination/ and name it UsersUpdate.php.

<?php
namespace Drupal\some_module\Plugin\migrate\destination;

use Drupal\Core\Entity\EntityInterface;
use Drupal\user\Plugin\migrate\destination\EntityUser;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\Core\TypedData\TypedDataInterface;

/**
 * Provides user destination, updating only the non-empty fields.
 *
 * @MigrateDestination(
 *   id = "user_update",
 * )
 */
class UserUpdate extends EntityUser {

  /**
   * Entity type.
   *
   * @var string $entityType
   */public static $entityType = 'user';

  /**
   * {@inheritdoc}
   */public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
    return parent::create($container, $configuration, 'entity:' . static::$entityType, $plugin_definition, $migration);
  }

  /**
   * {@inheritdoc}
   * @throws \Drupal\Core\TypedData\Exception\ReadOnlyException
   */protected function updateEntity(EntityInterface $entity, Row $row) {

    /** @var \Drupal\user\Entity\User $entity */// By default, an update will be preserved.
    $rollback_action = MigrateIdMapInterface::ROLLBACK_PRESERVE;

    // Make sure we have the right translation.if ($this->isTranslationDestination()) {
      $property = $this->storage->getEntityType()->getKey('langcode');
      if ($row->hasDestinationProperty($property)) {
        $language = $row->getDestinationProperty($property);
        if (!$entity->hasTranslation($language)) {
          $entity->addTranslation($language);

          // We're adding a translation, so delete it on rollback.
          $rollback_action = MigrateIdMapInterface::ROLLBACK_DELETE;
        }
        $entity = $entity->getTranslation($language);
      }
    }

    // If the migration has specified a list of properties to be overwritten,// clone the row with an empty set of destination values, and re-add only// the specified properties.if (isset($this->configuration['overwrite_properties'])) {
      $clone = $row->cloneWithoutDestination();
      foreach ($this->configuration['overwrite_properties'] as $property) {
        $clone->setDestinationProperty($property, $row->getDestinationProperty($property));
      }
      $row = $clone;
    }

    $emptyDestinationProperties = $row->getEmptyDestinationProperties();

    foreach ($row->getDestination() as $field_name => $values) {
      $field = $entity->$field_name;
      if ($field instanceof TypedDataInterface) {
        // Update field ONLY if the destination entity has no value for it.if (!in_array($field_name,$emptyDestinationProperties)) {
          $field->setValue($values);
        }
      }
    }

    $this->setRollbackAction($row->getIdMap(), $rollback_action);

    // We might have a different (translated) entity, so return it.return $entity;
  }

}

Main thing here to take a look at is code below:

$emptyDestinationProperties = $row->getEmptyDestinationProperties();

    foreach ($row->getDestination() as $field_name => $values) {
      $field = $entity->$field_name;
      if ($field instanceof TypedDataInterface) {
        // Update field ONLY if the destination entity has no value for it.if (!in_array($field_name,$emptyDestinationProperties)) {
          $field->setValue($values);
        }
      }
    }

Where we can check with helper function if our targeted field name is under empty destination properties and simply not set it if it is. After we have created our migrate destination class we need to set it in migration config yml file under destination. 

So instead of:

destination:
  plugin: 'entity:user'

you would set code below:

destination:
  plugin: user_update

Don't forget to do configuration import after modification of migrate config yml file.

Share
Tags
Drupal

LOOKING FOR A DIGITAL EXPERIENCE?

Looking for a digital experience built on Drupal?
Get in touch with Drupal experts!

Get in touch

Croatia

Svetice 21
10000 Zagreb
hello@ws.agency
+385 1 3445 884

USA

Atlanta
+1 404 549 5896
Home

Footer menu

  • Privacy
  • Sitemap
  • Blog
  • Careers
  • Get in touch

© 2021 Websolutions Agency. All Rights Reserved.