Issue #3009014 by amateescu, mikelutz, quietone, heddn: Convert path alias migrations to use entity:path_alias destination

merge-requests/2419/head
Lee Rowlands 2019-10-11 17:50:02 +10:00
parent 7dc65c5c5d
commit 445132dfbd
No known key found for this signature in database
GPG Key ID: 2B829A3DF9204DC4
9 changed files with 576 additions and 78 deletions

View File

@ -0,0 +1,57 @@
<?php
namespace Drupal\migrate\Plugin\migrate\process;
use Drupal\migrate\MigrateException;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
/**
* Provides a Null Coalesce process plugin.
*
* Given a set of values provided to the plugin, the plugin will return the
* first non-null value.
*
* Available configuration keys:
* - source: The input array.
* - default_value: (optional) The value to return if all values are NULL.
* if not provided, NULL is returned if all values are NULL.
*
* Example:
* Given source keys of foo, bar, and baz:
*
* process_key:
* plugin: null_coalesce
* source:
* - foo
* - bar
* - baz
*
* This plugin will return the equivalent of `foo ?? bar ?? baz`
*
* @MigrateProcessPlugin(
* id = "null_coalesce"
* )
*/
class NullCoalesce extends ProcessPluginBase {
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
if (!is_array($value)) {
throw new MigrateException("The input value should be an array.");
}
foreach ($value as $val) {
if (NULL !== $val) {
return $val;
}
}
if (isset($this->configuration['default_value'])) {
return $this->configuration['default_value'];
}
return NULL;
}
}

View File

@ -0,0 +1,92 @@
<?php
namespace Drupal\Tests\migrate\Unit\process;
use Drupal\migrate\MigrateException;
use Drupal\migrate\Plugin\migrate\process\NullCoalesce;
/**
* Tests the null_coalesce process plugin.
*
* @group migrate
*
* @coversDefaultClass \Drupal\migrate\Plugin\migrate\process\NullCoalesce
*/
class NullCoalesceTest extends MigrateProcessTestCase {
/**
* Tests that an exception is thrown for a non-array value.
*
* @covers ::transform
*/
public function testExceptionOnInvalidValue() {
$this->expectException(MigrateException::class);
(new NullCoalesce([], 'null_coalesce', []))->transform('invalid', $this->migrateExecutable, $this->row, 'destinationproperty');
}
/**
* Tests null_coalesce.
*
* @param array $source
* The source value.
* @param mixed $expected_result
* The expected result.
*
* @covers ::transform
*
* @dataProvider transformDataProvider
*
* @throws \Drupal\migrate\MigrateException
*/
public function testTransform(array $source, $expected_result) {
$plugin = new NullCoalesce([], 'null_coalesce', []);
$result = $plugin->transform($source, $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertSame($expected_result, $result);
}
/**
* Provides Data for ::testTransform.
*/
public function transformDataProvider() {
return [
'all null' => [
'source' => [NULL, NULL, NULL],
'expected_result' => NULL,
],
'false first' => [
'source' => [FALSE, NULL, NULL],
'expected_result' => FALSE,
],
'no null' => [
'source' => ['test', 'test2'],
'expected_result' => 'test',
],
'string first' => [
'source' => ['test', NULL, 'test2'],
'expected_result' => 'test',
],
'empty string' => [
'source' => [NULL, '', NULL],
'expected_result' => '',
],
'array' => [
'source' => [NULL, NULL, [1, 2, 3]],
'expected_result' => [1, 2, 3],
],
];
}
/**
* Tests null_coalesce with default value.
*
* @covers ::transform
*/
public function testTransformWithDefault() {
$plugin = new NullCoalesce(['default_value' => 'default'], 'null_coalesce', []);
$result = $plugin->transform([NULL, NULL, 'Test', 'Test 2'], $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertSame('Test', $result);
$this->assertSame('default', $plugin->transform([NULL, NULL], $this->migrateExecutable, $this->row, 'destinationproperty'));
}
}

View File

@ -8,7 +8,10 @@ source:
constants:
slash: '/'
process:
source:
# If you are using this file to build a custom migration consider removing
# the id field to allow incremental migrations.
id: pid
_path:
plugin: concat
source:
- constants/slash
@ -18,9 +21,7 @@ process:
source:
- constants/slash
- dst
langcode:
plugin: d6_url_alias_language
source: language
node_translation:
-
plugin: explode
@ -35,5 +36,19 @@ process:
-
plugin: migration_lookup
migration: d6_node_translation
langcode:
-
plugin: null_coalesce
source:
- '@node_translation/1'
- language
-
plugin: default_value
default_value: 'und'
path:
plugin: path_set_translated
source:
- '@_path'
- '@node_translation'
destination:
plugin: url_alias
plugin: entity:path_alias

View File

@ -8,7 +8,10 @@ source:
constants:
slash: '/'
process:
source:
# If you are using this file to build a custom migration consider removing
# the id field to allow incremental migrations.
id: pid
_path:
plugin: concat
source:
- constants/slash
@ -18,7 +21,6 @@ process:
source:
- constants/slash
- alias
langcode: language
node_translation:
-
plugin: explode
@ -33,5 +35,15 @@ process:
-
plugin: migration_lookup
migration: d7_node_translation
langcode:
plugin: null_coalesce
source:
- '@node_translation/1'
- language
path:
plugin: path_set_translated
source:
- '@_path'
- '@node_translation'
destination:
plugin: url_alias
plugin: entity:path_alias

View File

@ -2,100 +2,51 @@
namespace Drupal\path\Plugin\migrate\destination;
use Drupal\Core\Path\AliasStorageInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
use Drupal\migrate\Row;
use Drupal\migrate\Plugin\migrate\destination\DestinationBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@trigger_error('UrlAlias is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the entity:path_alias destination instead. See https://www.drupal.org/node/3013865', E_USER_DEPRECATED);
/**
* Legacy destination class for non-entity path aliases.
*
* @MigrateDestination(
* id = "url_alias"
* )
*
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
* the entity:path_alias destination instead.
*
* @see https://www.drupal.org/node/3013865
*/
class UrlAlias extends DestinationBase implements ContainerFactoryPluginInterface {
/**
* The alias storage service.
*
* @var \Drupal\Core\Path\AliasStorageInterface
*/
protected $aliasStorage;
/**
* Constructs an entity destination plugin.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
* The migration.
* @param \Drupal\Core\Path\AliasStorageInterface $alias_storage
* The alias storage service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, AliasStorageInterface $alias_storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);
$this->aliasStorage = $alias_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$migration,
$container->get('path.alias_storage')
);
}
class UrlAlias extends EntityContentBase {
/**
* {@inheritdoc}
*/
public function import(Row $row, array $old_destination_id_values = []) {
$source = $row->getDestinationProperty('source');
$alias = $row->getDestinationProperty('alias');
$langcode = $row->getDestinationProperty('langcode');
$pid = $old_destination_id_values ? $old_destination_id_values[0] : NULL;
if ($row->getDestinationProperty('source')) {
$row->setDestinationProperty('path', $row->getDestinationProperty('source'));
}
$path = $row->getDestinationProperty('path');
// Check if this alias is for a node and if that node is a translation.
if (preg_match('/^\/node\/\d+$/', $source) && $row->hasDestinationProperty('node_translation')) {
if (preg_match('/^\/node\/\d+$/', $path) && $row->hasDestinationProperty('node_translation')) {
// Replace the alias source with the translation source path.
$node_translation = $row->getDestinationProperty('node_translation');
$source = '/node/' . $node_translation[0];
$langcode = $node_translation[1];
$row->setDestinationProperty('path', '/node/' . $node_translation[0]);
$row->setDestinationProperty('langcode', $node_translation[1]);
}
$path = $this->aliasStorage->save($source, $alias, $langcode, $pid);
return [$path['pid']];
return parent::import($row, $old_destination_id_values);
}
/**
* {@inheritdoc}
*/
public function getIds() {
$ids['pid']['type'] = 'integer';
return $ids;
}
/**
* {@inheritdoc}
*/
public function fields(MigrationInterface $migration = NULL) {
return [
'pid' => 'The path id',
'source' => 'The source path.',
'alias' => 'The URL alias.',
'langcode' => 'The language code for the URL.',
];
protected static function getEntityTypeId($plugin_id) {
return 'path_alias';
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace Drupal\path\Plugin\migrate\process;
use Drupal\migrate\MigrateException;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
/**
* A process plugin to update the path of a translated node.
*
* Available configuration keys:
* - source: An array of two values, the first being the original path, and the
* second being an array of the format [nid, langcode] if a translated node
* exists (likely from a migration lookup). Paths not of the format
* '/node/<nid>' will pass through unchanged, as will any inputs with invalid
* or missing translated nodes.
*
* This plugin will return the correct path for the translated node if the above
* conditions are met, and will return the original path otherwise.
*
* Example:
* node_translation:
* -
* plugin: explode
* source: source
* delimiter: /
* -
* # If the source path has no slashes return a dummy default value.
* plugin: extract
* default: 'INVALID_NID'
* index:
* - 1
* -
* plugin: migration_lookup
* migration: d7_node_translation
* _path:
* plugin: concat
* source:
* - constants/slash
* - source
* path:
* plugin: path_set_translated
* source:
* - '@_path'
* - '@node_translation'
*
* In the example above, if the node_translation lookup succeeds and the
* original path is of the format '/node/<original node nid>', then the new path
* will be set to '/node/<translated node nid>'
*
* @MigrateProcessPlugin(
* id = "path_set_translated"
* )
*/
class PathSetTranslated extends ProcessPluginBase {
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
if (!is_array($value)) {
throw new MigrateException("The input value should be an array.");
}
$path = isset($value[0]) ? $value[0] : '';
$nid = (is_array($value[1]) && isset($value[1][0])) ? $value[1][0] : FALSE;
if (preg_match('/^\/node\/\d+$/', $path) && $nid) {
return '/node/' . $nid;
}
return $path;
}
}

View File

@ -0,0 +1,115 @@
<?php
namespace Drupal\Tests\path\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
use Drupal\Tests\Traits\ExpectDeprecationTrait;
/**
* Tests legacy URL alias migration.
*
* @group path
* @group legacy
*/
class LegacyMigrateUrlAliasTest extends MigrateUrlAliasTest {
use ExpectDeprecationTrait;
/**
* The legacy stub migration to use.
*
* @var array
*/
protected $stubMigration = [
'id' => 'd6_url_alias',
'label' => 'URL aliases',
'migration_tags' =>
[
0 => 'Drupal 6',
1 => 'Content',
],
'source' =>
[
'plugin' => 'd6_url_alias',
'constants' =>
[
'slash' => '/',
],
],
'process' =>
[
'source' =>
[
'plugin' => 'concat',
'source' =>
[
0 => 'constants/slash',
1 => 'src',
],
],
'alias' =>
[
'plugin' => 'concat',
'source' =>
[
0 => 'constants/slash',
1 => 'dst',
],
],
'langcode' =>
[
'plugin' => 'd6_url_alias_language',
'source' => 'language',
],
'node_translation' =>
[
0 =>
[
'plugin' => 'explode',
'source' => 'src',
'delimiter' => '/',
],
1 =>
[
'plugin' => 'extract',
'default' => 'INVALID_NID',
'index' =>
[
0 => 1,
],
],
2 =>
[
'plugin' => 'migration_lookup',
'migration' => 'd6_node_translation',
],
],
],
'destination' =>
[
'plugin' => 'url_alias',
],
];
/**
* {@inheritdoc}
*/
protected function setUp() {
MigrateDrupal6TestBase::setUp();
$this->installEntitySchema('node');
$this->installEntitySchema('path_alias');
$this->installConfig(['node']);
$this->installSchema('node', ['node_access']);
$this->migrateUsers(FALSE);
$this->migrateFields();
$this->executeMigrations([
'language',
'd6_node_settings',
'd6_node',
'd6_node_translation',
]);
$this->executeMigration(\Drupal::service('plugin.manager.migration')->createStubMigration($this->stubMigration));
$this->expectDeprecation('UrlAlias is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the entity:path_alias destination instead. See https://www.drupal.org/node/3013865');
}
}

View File

@ -0,0 +1,110 @@
<?php
namespace Drupal\Tests\path\Kernel\Migrate\d7;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
use Drupal\Tests\Traits\ExpectDeprecationTrait;
/**
* Tests legacy URL alias migration.
*
* @group path
* @group legacy
*/
class LegacyMigrateUrlAliasTest extends MigrateUrlAliasTest {
use ExpectDeprecationTrait;
/**
* The legacy stub migration to use.
*
* @var array
*/
protected $stubMigration = [
'id' => 'd7_url_alias',
'label' => 'URL aliases',
'migration_tags' =>
[
0 => 'Drupal 7',
1 => 'Content',
],
'source' =>
[
'plugin' => 'd7_url_alias',
'constants' =>
[
'slash' => '/',
],
],
'process' =>
[
'source' =>
[
'plugin' => 'concat',
'source' =>
[
0 => 'constants/slash',
1 => 'source',
],
],
'alias' =>
[
'plugin' => 'concat',
'source' =>
[
0 => 'constants/slash',
1 => 'alias',
],
],
'langcode' => 'language',
'node_translation' =>
[
0 =>
[
'plugin' => 'explode',
'source' => 'source',
'delimiter' => '/',
],
1 =>
[
'plugin' => 'extract',
'default' => 'INVALID_NID',
'index' =>
[
0 => 1,
],
],
2 =>
[
'plugin' => 'migration_lookup',
'migration' => 'd7_node_translation',
],
],
],
'destination' =>
[
'plugin' => 'url_alias',
],
];
/**
* {@inheritdoc}
*/
protected function setUp() {
MigrateDrupal7TestBase::setUp();
$this->installEntitySchema('node');
$this->installEntitySchema('path_alias');
$this->installConfig('node');
$this->installSchema('node', ['node_access']);
$this->migrateUsers(FALSE);
$this->migrateContentTypes();
$this->executeMigrations([
'language',
'd7_node',
'd7_node_translation',
]);
$this->executeMigration(\Drupal::service('plugin.manager.migration')->createStubMigration($this->stubMigration));
$this->expectDeprecation('UrlAlias is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the entity:path_alias destination instead. See https://www.drupal.org/node/3013865');
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace Drupal\Tests\path\Unit\migrate\process;
use Drupal\path\Plugin\migrate\process\PathSetTranslated;
use Drupal\Tests\migrate\Unit\process\MigrateProcessTestCase;
/**
* Tests the path_set_translated process plugin.
*
* @group path
* @coversDefaultClass \Drupal\path\Plugin\migrate\process\PathSetTranslated
*/
class PathSetTranslatedTest extends MigrateProcessTestCase {
/**
* Tests the transform method.
*
* @param string $path
* The path to test.
* @param mixed $node_translation
* The translated node value to test.
* @param string $expected_result
* The expected result.
*
* @covers ::transform
*
* @dataProvider transformDataProvider
*/
public function testTransform($path, $node_translation, $expected_result) {
$plugin = new PathSetTranslated([], 'path_set_translated', []);
$this->assertSame($expected_result, $plugin->transform([$path, $node_translation], $this->migrateExecutable, $this->row, 'destination_property'));
}
/**
* Provides data for the testTransform method.
*
* @return array
* The data.
*/
public function transformDataProvider() {
return [
'non-node-path' => [
'path' => '/non-node-path',
'node_translation' => [1, 'en'],
'expected_result' => '/non-node-path',
],
'no_translated_node_1' => [
'path' => '/node/1',
'node_translation' => 'INVALID_NID',
'expected_result' => '/node/1',
],
'no_translated_node_2' => [
'path' => '/node/1',
'node_translation' => NULL,
'expected_result' => '/node/1',
],
'no_translated_node_3' => [
'path' => '/node/1',
'node_translation' => FALSE,
'expected_result' => '/node/1',
],
'valid_transform' => [
'path' => '/node/1',
'node_translation' => [3, 'en'],
'expected_result' => '/node/3',
],
];
}
}