From a3ca88ebbfd559ce2ac4676e617f60d08153a69b Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Thu, 6 May 2021 01:16:13 +0100 Subject: [PATCH] Issue #2882276 by benjifisher, estoyausente, nuez, kristiaanvandeneynde, alexpott, ravi.shankar, osopolar, marvil07, danflanagan8, heddn, quietone, Matroskeen, hudri, joshi.rohit100: Extended callback process plugin to call functions with multiple parameters --- .../src/Plugin/migrate/process/Callback.php | 32 ++++++++++- .../tests/src/Unit/process/CallbackTest.php | 56 ++++++++++++++++++- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/core/modules/migrate/src/Plugin/migrate/process/Callback.php b/core/modules/migrate/src/Plugin/migrate/process/Callback.php index a438189d2da..f1b2f98be53 100644 --- a/core/modules/migrate/src/Plugin/migrate/process/Callback.php +++ b/core/modules/migrate/src/Plugin/migrate/process/Callback.php @@ -2,6 +2,7 @@ namespace Drupal\migrate\Plugin\migrate\process; +use Drupal\migrate\MigrateException; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\ProcessPluginBase; use Drupal\migrate\Row; @@ -10,11 +11,13 @@ use Drupal\migrate\Row; * Passes the source value to a callback. * * The callback process plugin allows simple processing of the value, such as - * strtolower(). The callable takes the source value as the single mandatory - * argument. No additional arguments can be passed to the callback. + * strtolower(). To pass more than one argument, pass an array as the source + * and set the unpack_source option. * * Available configuration keys: * - callable: The name of the callable method. + * - unpack_source: (optional) Whether to interpret the source as an array of + * arguments. * * Examples: * @@ -38,6 +41,25 @@ use Drupal\migrate\Row; * source: source_field * @endcode * + * An example where the callback accepts more than one argument: + * + * @code + * source: + * plugin: source_plugin_goes_here + * constants: + * slash: / + * process: + * field_link_url: + * plugin: callback + * callable: rtrim + * unpack_source: true + * source: + * - url + * - constants/slash + * @endcode + * + * This will remove the trailing '/', if any, from a URL. + * * @see \Drupal\migrate\Plugin\MigrateProcessInterface * * @MigrateProcessPlugin( @@ -63,6 +85,12 @@ class Callback extends ProcessPluginBase { * {@inheritdoc} */ public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) { + if (!empty($this->configuration['unpack_source'])) { + if (!is_array($value)) { + throw new MigrateException(sprintf("When 'unpack_source' is set, the source must be an array. Instead it was of type '%s'", gettype($value))); + } + return call_user_func($this->configuration['callable'], ...$value); + } return call_user_func($this->configuration['callable'], $value); } diff --git a/core/modules/migrate/tests/src/Unit/process/CallbackTest.php b/core/modules/migrate/tests/src/Unit/process/CallbackTest.php index b34ce15b4b2..0cc93ae934d 100644 --- a/core/modules/migrate/tests/src/Unit/process/CallbackTest.php +++ b/core/modules/migrate/tests/src/Unit/process/CallbackTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\migrate\Unit\process; +use Drupal\migrate\MigrateException; use Drupal\migrate\Plugin\migrate\process\Callback; /** @@ -33,15 +34,60 @@ class CallbackTest extends MigrateProcessTestCase { ]; } + /** + * Test callback with valid "callable" and multiple arguments. + * + * @dataProvider providerCallbackArray + */ + public function testCallbackArray($callable, $args, $result) { + $configuration = ['callable' => $callable, 'unpack_source' => TRUE]; + $this->plugin = new Callback($configuration, 'map', []); + $value = $this->plugin->transform($args, $this->migrateExecutable, $this->row, 'destination_property'); + $this->assertSame($result, $value); + } + + /** + * Data provider for ::testCallbackArray(). + */ + public function providerCallbackArray() { + return [ + 'date format' => [ + 'date', + ['Y-m-d', 995328000], + '2001-07-17', + ], + 'rtrim' => [ + 'rtrim', + ['https://www.example.com/', '/'], + 'https://www.example.com', + ], + 'str_replace' => [ + 'str_replace', + [['One', 'two'], ['1', '2'], 'One, two, three!'], + '1, 2, three!', + ], + ]; + } + /** * Test callback exceptions. * + * @param string $message + * The expected exception message. + * @param array $configuration + * The plugin configuration being tested. + * @param string $class + * (optional) The expected exception class. + * @param mixed $args + * (optional) Arguments to pass to the transform() method. + * * @dataProvider providerCallbackExceptions */ - public function testCallbackExceptions($message, $configuration) { - $this->expectException(\InvalidArgumentException::class); + public function testCallbackExceptions($message, array $configuration, $class = 'InvalidArgumentException', $args = NULL) { + $this->expectException($class); $this->expectExceptionMessage($message); $this->plugin = new Callback($configuration, 'map', []); + $this->plugin->transform($args, $this->migrateExecutable, $this->row, 'destination_property'); } /** @@ -57,6 +103,12 @@ class CallbackTest extends MigrateProcessTestCase { 'message' => 'The "callable" must be a valid function or method.', 'configuration' => ['callable' => 'nonexistent_callable'], ], + 'array required' => [ + 'message' => "When 'unpack_source' is set, the source must be an array. Instead it was of type 'string'", + 'configuration' => ['callable' => 'count', 'unpack_source' => TRUE], + 'class' => MigrateException::class, + 'args' => 'This string is not an array.', + ], ]; }