Commit 32f4fde4 authored by catch's avatar catch
Browse files

fix: #3458923 StaticMap documents that NULL is a supported map value but causes an error

By: @acbramley
By: @alexpott
By: @longwave
(cherry picked from commit ecec7783)
parent 05460f77
Loading
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -610,7 +610,6 @@ default:

'⚡️ PHPUnit Unit tests on PHP 8.5':
  <<: [ *default-job-settings-lint, *default-phpunit-job-settings ]
  allow_failure: true
  variables:
    TESTSUITE: PHPUnit-Unit
    KUBERNETES_CPU_REQUEST: "4"
@@ -630,7 +629,6 @@ default:

'⚡️ Component unit tests on PHP 8.5':
  <<: [ *default-job-settings-lint, *default-component-unit-test-job-settings ]
  allow_failure: true
  variables:
    KUBERNETES_CPU_REQUEST: "2"
    _TARGET_PHP: "8.5-ubuntu"
+23 −12
Original line number Diff line number Diff line
@@ -113,18 +113,6 @@
 *       'TRUE': to
 * @endcode
 *
 * A NULL can be mapped. If the value of the source property 'foo' is NULL then
 * the value of the destination property bar will be 'to'.
 *
 * @code
 * process:
 *   bar:
 *     plugin: static_map
 *     source: foo
 *     map:
 *       NULL: to
 * @endcode
 *
 * If your source data contains booleans, the boolean is treated as a numeric 0
 * or 1. If the value of the source property 'foo' is TRUE then the value of the
 * destination property bar will be 'bar'. And if the value of the source
@@ -141,6 +129,10 @@
 *       1: bar
 * @endcode
 *
 * Note you cannot map NULL to a value. Use the default_value or bypass setting
 * instead. If you need to differentiate between NULL and empty strings,
 * then use the bypass setting and a custom process plugin.
 *
 * @see https://www.drupal.org/project/drupal/issues/2827897
 * @see \Drupal\migrate\Plugin\MigrateProcessInterface
 */
@@ -151,6 +143,25 @@ class StaticMap extends ProcessPluginBase {
   * {@inheritdoc}
   */
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
    if ($value === NULL) {
      if (array_key_exists('', $this->configuration['map'])) {
        if (array_key_exists('default_value', $this->configuration) && $this->configuration['default_value'] === $this->configuration['map']['']) {
          return $this->configuration['default_value'];
        }
        @trigger_error('Relying on mapping NULL values via an empty string map key in ' . __CLASS__ . '::transform() is deprecated in drupal:11.3.0 and will trigger a Drupal\migrate\MigrateSkipRowException from drupal:12.0.0. Set the empty string map value as the "default_value" in the plugin configuration. See https://www.drupal.org/node/3557003', E_USER_DEPRECATED);
        // Preserve the current behavior of returning the value mapped to an
        // empty string for NULL.
        return $this->configuration['map'][''];
      }
      if (array_key_exists('default_value', $this->configuration)) {
        return $this->configuration['default_value'];
      }
      if (!empty($this->configuration['bypass'])) {
        return NULL;
      }
      throw new MigrateSkipRowException(sprintf("No static mapping possible for NULL and no default value provided for destination '%s'.", $destination_property));
    }

    $new_value = $value;
    if (is_array($value)) {
      if (!$value) {
+40 −4
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
use Drupal\migrate\MigrateSkipRowException;
use Drupal\migrate\Plugin\migrate\process\StaticMap;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\IgnoreDeprecations;

/**
 * Tests the static map process plugin.
@@ -98,21 +99,56 @@ public function testMapWithInvalidSourceAndBypass(): void {
   */
  public function testWithNullSourceNotInMap(): void {
    $this->expectException(MigrateSkipRowException::class);
    $this->expectExceptionMessage("No static mapping found for 'NULL' and no default value provided for destination 'destination_property'");
    $this->expectExceptionMessage("No static mapping possible for NULL and no default value provided for destination 'destination_property'");
    $this->plugin->transform(NULL, $this->migrateExecutable, $this->row, 'destination_property');
  }

  /**
   * Tests when the source is invalid but there's a default.
   * Tests when the source is invalid but there's a mapping via an empty string.
   */
  public function testWithNullSource(): void {
  #[IgnoreDeprecations]
  public function testWithNullSourceWithEmptyStringMapping(): void {
    $configuration['map']['foo']['bar'] = 'baz';
    $configuration['map'][NULL] = 'mapped NULL';
    $configuration['map'][''] = 'mapped NULL';
    $this->plugin = new StaticMap($configuration, 'map', []);
    $this->expectDeprecation('Relying on mapping NULL values via an empty string map key in Drupal\migrate\Plugin\migrate\process\StaticMap::transform() is deprecated in drupal:11.3.0 and will trigger a Drupal\migrate\MigrateSkipRowException from drupal:12.0.0. Set the empty string map value as the "default_value" in the plugin configuration. See https://www.drupal.org/node/3557003');
    $value = $this->plugin->transform(NULL, $this->migrateExecutable, $this->row, 'destination_property');
    $this->assertSame('mapped NULL', $value);
  }

  /**
   * Tests when the source is invalid but bypass is set to TRUE.
   */
  public function testWithNullSourceBypass(): void {
    $configuration['map']['foo']['bar'] = 'baz';
    $configuration['bypass'] = TRUE;
    $this->plugin = new StaticMap($configuration, 'map', []);
    $value = $this->plugin->transform(NULL, $this->migrateExecutable, $this->row, 'destination_property');
    $this->assertNull($value);
  }

  /**
   * Tests when the source is invalid but default_value is set to TRUE.
   */
  public function testWithNullSourceDefaultValue(): void {
    $configuration['map']['foo']['bar'] = 'baz';
    $configuration['default_value'] = FALSE;
    $this->plugin = new StaticMap($configuration, 'map', []);
    $value = $this->plugin->transform(NULL, $this->migrateExecutable, $this->row, 'destination_property');
    $this->assertFalse($value);
  }

  /**
   * Tests when the source returns an empty string and it is mapped to a value.
   */
  public function testWithEmptyStringMap(): void {
    $configuration['map']['foo']['bar'] = 'baz';
    $configuration['map'][''] = 'mapped empty string';
    $this->plugin = new StaticMap($configuration, 'map', []);
    $value = $this->plugin->transform('', $this->migrateExecutable, $this->row, 'destination_property');
    $this->assertSame('mapped empty string', $value);
  }

  /**
   * Tests when there is a dot in a map key.
   */