diff -u b/core/modules/menu_link_content/migration_templates/d6_menu_links_translation.yml b/core/modules/menu_link_content/migration_templates/d6_menu_links_translation.yml --- b/core/modules/menu_link_content/migration_templates/d6_menu_links_translation.yml +++ b/core/modules/menu_link_content/migration_templates/d6_menu_links_translation.yml @@ -7,8 +7,16 @@ process: id: mlid langcode: language - title: title - description: description + title: + plugin: menu_link_translation + source: + - title_untranslated + - title_translated + description: + plugin: menu_link_translation + source: + - description_untranslated + - description_translated menu_name: - plugin: migration @@ -20,11 +28,6 @@ map: management: admin bypass: true - 'link/uri': - plugin: link_uri - source: - - link_path - 'link/options': options destination: plugin: entity:menu_link_content default_bundle: menu_link_content diff -u b/core/modules/menu_link_content/src/Plugin/migrate/source/d6/MenuLinkTranslation.php b/core/modules/menu_link_content/src/Plugin/migrate/source/d6/MenuLinkTranslation.php --- b/core/modules/menu_link_content/src/Plugin/migrate/source/d6/MenuLinkTranslation.php +++ b/core/modules/menu_link_content/src/Plugin/migrate/source/d6/MenuLinkTranslation.php @@ -3,6 +3,7 @@ namespace Drupal\menu_link_content\Plugin\migrate\source\d6; use Drupal\Component\Utility\Unicode; +use Drupal\migrate\Row; use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; /** @@ -15,80 +16,76 @@ */ class MenuLinkTranslation extends DrupalSqlBase { - const TITLE = "CASE WHEN property = 'title' THEN translation ELSE link_title END"; - const DESCRIPTION = "CASE WHEN property = 'description' THEN translation END"; - /** * {@inheritdoc} */ - protected function initializeIterator() { - return new \ArrayIterator($this->values()); - } - - /** - * {@inheritdoc} - */ - public function count() { - return $this->initializeIterator()->count(); - } - - /** - * Returns the menu link translation values. - * - * @return array - * Each result is an associative array where the values are the menu IDs - * and the translations for the title and description, if available. If not - * available then the default language values are used. - */ - protected function values() { - $values = []; - $query = $this->prepareQuery(); - $result = $query->execute()->fetchAll(); - $cnt = count($result); - for ($i = 0; $i < $cnt; $i++) { - if (isset($result[$i])) { - if ($result[$i]['description'] == NULL) { - // Check next result for the description value. - $j = $i + 1; - if (isset($result[$j])) { - // If the same menu_link, then this is a description translation. - if ($result[$i]['mlid'] == $result[$j]['mlid'] && - $result[$i]['language'] == $result[$j]['language']) { - if ($result[$j]['description'] != NULL) { - $result[$i]['description'] = $result[$j]['description']; - unset($result[$j]); - } - } - else { - // Get the description fom the options array. - $tmp = unserialize($result[$i]['options']); - $result[$i]['description'] = Unicode::truncate($tmp['attributes']['title'], 255); - } - } - } - $values[] = $result[$i]; - } - } - return $values; + public function query() { + // Ideally, the query would return rows for each language for each menu link + // with the translations for with both the title and description or just the + // title translation or just the description translation. That query quickly + // became complex and would be difficult to maintain. + // Therefore, build a query based on 18n_strings table where each row has + // the translation for only one property, either title or description. The + // method prepareRow() is then used to obtain the translation for the other + // property. + // The query starts with the same query as menu_link. + $query = $this->select('menu_links', 'ml') + ->fields('ml'); + $and = $query->andConditionGroup() + ->condition('ml.module', 'menu') + ->condition('ml.router_path', [ + 'admin/build/menu-customize/%', + 'admin/structure/menu/manage/%', + ], 'NOT IN'); + $condition = $query->orConditionGroup() + ->condition('ml.customized', 1) + ->condition($and); + $query->condition($condition); + $query->leftJoin('menu_links', 'pl', 'ml.plid = pl.mlid'); + $query->addField('pl', 'link_path', 'parent_link_path'); + $query->orderBy('ml.depth'); + $query->orderby('ml.mlid'); + + // Add in the property, which is either title or description. + $query->leftJoin('i18n_strings', 'i18n', 'ml.mlid = i18n.objectid'); + $query->isNotNull('i18n.lid'); + $query->addField('i18n', 'lid'); + $query->addField('i18n', 'property'); + + // Add in the translation for the property. + $query->leftJoin('locales_target', 'lt', 'i18n.lid = lt.lid'); + $query->addField('lt', 'language'); + $query->addField('lt', 'translation'); + return $query; } /** * {@inheritdoc} */ - public function query() { - $query = $this->select('menu_links', 'ml') - ->fields('ml', ['mlid', 'link_path', 'options']) - ->fields('i18n', ['objectid']) - ->fields('lt', ['language']) - ->condition('i18n.type', 'item') - ->condition('ml.module', 'menu') - ->orderBy('mlid', 'language', 'title'); - $query->addExpression(self::TITLE, 'title'); - $query->addExpression(self::DESCRIPTION, 'description'); - $query->addField('lt', 'lid', 'lt.lid'); - $query->Join('i18n_strings', 'i18n', 'i18n.objectid = ml.mlid'); - $query->leftJoin('locales_target', 'lt', 'lt.lid = i18n.lid'); - return $query; + public function prepareRow(Row $row) { + // Set untranslated values as defaults. + $row->setSourceProperty('title_untranslated', $row->getSourceProperty('link_title')); + $row->setSourceProperty('options', unserialize($row->getSourceProperty('options'))); + $row->setSourceProperty('description_untranslated', Unicode::truncate($row->getSourceProperty('options/attributes/title'), 255)); + + // Save the translation for this property. + $property = $row->getSourceProperty('property'); + $row->setSourceProperty($property . '_translated', $row->getSourceProperty('translation')); + + // Get the translation for the property not already in the row. + $language = $row->getSourceProperty('language'); + $mlid = $row->getSourceProperty('mlid'); + $property2 = ($property == 'title') ? 'description' : 'title'; + $query = $this->select('i18n_strings', 'i18n') + ->fields('i18n', ['lid']); + $query + ->condition('i18n.property', $property2) + ->condition('i18n.objectid', $mlid); + $query->leftJoin('locales_target', 'lt', 'i18n.lid = lt.lid'); + $query->condition('lt.language', $language); + $query->addField('lt', 'translation'); + $results = $query->execute()->fetchAssoc(); + $row->setSourceProperty($property2 . '_translated', $results['translation']); } /** @@ -98,10 +95,8 @@ return [ 'mlid' => t('The menu link ID.'), 'language' => $this->t('Language for this menu.'), - 'title' => $this->t('Menu link title.'), - 'description' => $this->t('Menu link description.'), - 'link_path' => t('The Drupal path or external path this link points to.'), - 'options' => t('A serialized array of options to set on the URL, such as a query string or HTML attributes.'), + 'title' => $this->t('Menu link title translation.'), + 'description' => $this->t('Menu link description translation.'), ]; } @@ -110,6 +105,7 @@ */ public function getIds() { $ids['mlid']['type'] = 'integer'; + $ids['mlid']['alias'] = 'ml'; $ids['language']['type'] = 'string'; return $ids; } diff -u b/core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/d6/MenuLinkSourceTranslationTest.php b/core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/d6/MenuLinkSourceTranslationTest.php --- b/core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/d6/MenuLinkSourceTranslationTest.php +++ b/core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/d6/MenuLinkSourceTranslationTest.php @@ -8,7 +8,7 @@ * Tests menu link translation source plugin. * * @covers \Drupal\menu_link_content\Plugin\migrate\source\d6\MenuLinkTranslation - * @group config_translation + * @group menu_link_content */ class MenuLinkSourceTranslationTest extends MigrateSqlSourceTestBase { @@ -79,6 +79,62 @@ 'updated' => '0', 'description' => 'Test menu link 2', ], + [ + 'menu_name' => 'menu-test-menu', + 'mlid' => 140, + 'plid' => 0, + 'link_path' => 'https://www.drupal.org', + 'router_path' => 'admin/modules', + 'link_title' => 'Test 2', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:16:"Test menu link 2";}}', + 'module' => 'menu', + 'hidden' => 0, + 'external' => 0, + 'has_children' => 0, + 'expanded' => 0, + 'weight' => 12, + 'depth' => 2, + 'customized' => 1, + 'p1' => '0', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', + 'description' => 'Test menu link 2', + ], + [ + 'menu_name' => 'menu-test-menu', + 'mlid' => 141, + 'plid' => 0, + 'link_path' => 'https://api.drupal.org/api/drupal/8.3.x', + 'router_path' => 'admin/modules', + 'link_title' => 'Test 3', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:16:"Test menu link 3";}}', + 'module' => 'menu', + 'hidden' => 0, + 'external' => 0, + 'has_children' => 0, + 'expanded' => 0, + 'weight' => 12, + 'depth' => 2, + 'customized' => 1, + 'p1' => '0', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', + 'description' => 'Test menu link 3', + ], ]; $test[0]['source_data']['i18n_strings'] = [ [ @@ -99,12 +155,20 @@ ], [ 'lid' => 3, - 'objectid' => 138, + 'objectid' => 140, 'type' => 'item', 'property' => 'description', 'objectindex' => 0, 'format' => 0, ], + [ + 'lid' => 4, + 'objectid' => 141, + 'type' => 'item', + 'property' => 'title', + 'objectindex' => 0, + 'format' => 0, + ], ]; $test[0]['source_data']['locales_target'] = [ [ @@ -131,28 +195,58 @@ 'plural' => 0, 'u18n_status' => 0, ], + [ + 'lid' => 4, + 'language' => 'zu', + 'translation' => 'zu - title translation', + 'plid' => 0, + 'plural' => 0, + 'u18n_status' => 0, + ], ]; $test[0]['expected_results'] = [ [ - 'mlid' => 138, - 'link_path' => 'admin', - 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:16:"Test menu link 1";}}', - 'objectid' => 138, - 'language' => 'zu', - 'ltlid' => 3, - 'title' => 'Test 1', - 'description' => 'zu - description translation', + 'menu_name' => 'menu-test-menu', + 'mlid' => 139, + 'property' => 'title', + 'language' => 'fr', + 'title_untranslated' => 'Test 2', + 'description_untranslated' => 'Test menu link 2', + 'title_translated' => 'fr - title translation', + 'description_translated' => 'fr - description translation', + ], [ + 'menu_name' => 'menu-test-menu', 'mlid' => 139, - 'link_path' => 'admin/modules', - 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:16:"Test menu link 2";}}', - 'objectid' => 139, + 'property' => 'description', 'language' => 'fr', - 'ltlid' => 1, - 'title' => 'fr - title translation', - 'description' => 'fr - description translation', + 'title_untranslated' => 'Test 2', + 'description_untranslated' => 'Test menu link 2', + 'title_translated' => 'fr - title translation', + 'description_translated' => 'fr - description translation', + ], + [ + 'menu_name' => 'menu-test-menu', + 'mlid' => 140, + 'description' => 'Test menu link 2', + 'property' => 'description', + 'language' => 'zu', + 'title_untranslated' => 'Test 2', + 'description_untranslated' => 'Test menu link 2', + 'title_translated' => NULL, + 'description_translated' => 'zu - description translation', + ], + [ + 'menu_name' => 'menu-test-menu', + 'mlid' => 141, + 'property' => 'title', + 'language' => 'zu', + 'title_untranslated' => 'Test 3', + 'description_untranslated' => 'Test menu link 3', + 'title_translated' => 'zu - title translation', + 'description_translated' => NULL, ], ]; only in patch2: unchanged: --- /dev/null +++ b/core/modules/menu_link_content/src/Plugin/migrate/process/d6/MenuLinkTranslation.php @@ -0,0 +1,31 @@ +