migmag-1.0.x-dev/migmag_menu_link_migrate/src/MigMagMenuLinkMigrate.php

migmag_menu_link_migrate/src/MigMagMenuLinkMigrate.php
<?php

declare(strict_types=1);

namespace Drupal\migmag_menu_link_migrate;

use Drupal\Component\Utility\Unicode;
use Drupal\migmag\Utility\MigMagArrayUtility;
use Drupal\migmag\Utility\MigMagMigrationUtility;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;

// cspell:ignore plid

/**
 * Alters and prepares menu link migrations to migrate as much data as possible.
 */
class MigMagMenuLinkMigrate {

  /**
   * The plugin ID of the menu link trap migration.
   *
   * @const string
   */
  const TRAP_MIGRATION_ID = 'migmag_unmigratable_menu_link_trap';

  /**
   * The path set for unmigratable (or stub) menu links.
   *
   * @const string
   */
  const NON_MIGRATABLE_LINKS_PATH = 'route:<current>';

  /**
   * Adds fixes to menu link migrations.
   *
   * @param array $migrations
   *   An array of migration configurations keyed by migration ID. See
   *   hook_migration_plugins_alter().
   * @param bool $migrate_as_much_as_possible
   *   Whether link/uri validation exceptions also should be suppressed.
   */
  public static function applyMenuLinkMigrationConfigurationFixes(array &$migrations, bool $migrate_as_much_as_possible = FALSE): void {
    $menu_link_migrations = array_filter(
      $migrations,
      function (array $definition) {
        return in_array(
          $definition['id'],
          [
            'd7_menu_links',
            'node_translation_menu_links',
          ]
        );
      }
    );

    foreach ($menu_link_migrations as $plugin_id => $plugin_definition) {
      if ($migrate_as_much_as_possible) {
        // If we want to migrate as much data as possible, we will wrap the
        // whole 'link/uri' process pipeline into migmag_try.
        $plugin_definition['process']['link/uri'] = [
          'plugin' => 'migmag_try',
          'process' => $plugin_definition['process']['link/uri'],
          'catch' => ['Drupal\migrate\MigrateException' => self::NON_MIGRATABLE_LINKS_PATH],
        ];

        // Ensure that 'enabled' and 'title' follow 'link/uri'.
        MigMagArrayUtility::moveAfterKey(
          $plugin_definition['process'],
          'link/uri',
          'enabled'
        );
        MigMagArrayUtility::moveAfterKey(
          $plugin_definition['process'],
          'link/uri',
          'title'
        );

        // ...so we can use the processed value of 'path_is_available' in the
        // process pipeline of 'title' and 'enabled'! 'path_is_available'
        // determines whether the migrated link path exists on the destination.
        MigMagArrayUtility::insertAfterKey(
          $plugin_definition['process'],
          'link/uri',
          'path_is_available',
          [
            'plugin' => 'static_map',
            'source' => '@link/uri',
            'map' => [self::NON_MIGRATABLE_LINKS_PATH => 0],
            'default_value' => 1,
          ]
        );

        // We add the original 'enabled' process as 'original_enabled' in front
        // of 'enabled', and add call 'intval' on its result: we want to use
        // this later in a static map.
        MigMagArrayUtility::insertInFrontOfKey(
          $plugin_definition['process'],
          'enabled',
          'original_enabled',
          array_merge(
            MigMagMigrationUtility::getAssociativeMigrationProcess($plugin_definition['process']['enabled']),
           [['plugin' => 'callback', 'callable' => 'intval']]
          )
        );

        // ...then we change the process pipeline of 'enabled': If we are about
        // to migrate a menu link whose destination path is missing then the
        // menu link should be disabled.
        $plugin_definition['process']['enabled'] = [
          'plugin' => 'static_map',
          'source' => [
            '@path_is_available',
            '@original_enabled',
          ],
          'map' => [
            1 => [
              0 => 0,
              1 => 1,
            ],
          ],
          'default_value' => 0,
        ];

        // To help our users, we also add a suffix to the menu link's title
        // which will contain the original menu link path.
        // First add the constants we will use.
        $plugin_definition['source']['constants']['missing_path_prefix'] = " (unavailable: '";
        $plugin_definition['source']['constants']['missing_path_suffix'] = "')";
        // Add the pipeline of 'title_suffix' which will contain the path in the
        // source if the link does not exist on the destination. It will be NULL
        // otherwise.
        MigMagArrayUtility::insertInFrontOfKey(
          $plugin_definition['process'],
          'title',
          'title_suffix',
          [
            [
              'plugin' => 'static_map',
              'source' => ['@path_is_available', '@stub'],
              'map' => [
                0 => ['do not skip'],
              ],
              'default_value' => NULL,
            ],
            [
              'plugin' => 'skip_on_empty',
              'method' => 'process',
            ],
            [
              'plugin' => 'concat',
              'source' => [
                'constants/missing_path_prefix',
                'link_path',
                'constants/missing_path_suffix',
              ],
            ],
          ]
        );
        MigMagArrayUtility::insertInFrontOfKey(
          $plugin_definition['process'],
          'title_suffix',
          'stub',
          [
            'plugin' => 'default_value',
            'source' => 'stub',
            'default_value' => 0,
          ]
        );
        // Re-add the original process pipeline of 'title' as 'original_title',
        // in front of 'title'.
        MigMagArrayUtility::insertInFrontOfKey(
          $plugin_definition['process'],
          'title',
          'original_title',
          $plugin_definition['process']['title']
        );
        // Finally, concatenate the processed value of 'original_title' and
        // 'title_suffix'!
        $plugin_definition['process']['title'] = [
          'plugin' => 'concat',
          'source' => [
            '@original_title',
            '@title_suffix',
          ],
        ];
      }

      // Add the required constant for 'lookup_stub_parent'.
      $plugin_definition['source']['constants']['plugin_prefix'] = 'menu_link_content';
      MigMagArrayUtility::insertInFrontOfKey(
        $plugin_definition['process'],
        'parent',
        'lookup_stub_parent',
        [
          [
            'plugin' => 'skip_on_empty',
            'source' => '@parent_uuid',
            'method' => 'process',
          ],
          [
            'plugin' => 'concat',
            'source' => [
              'constants/plugin_prefix',
              '@parent_uuid',
            ],
            'delimiter' => ':',
          ],
        ]
      );

      // Add the 'parent_uuid' process right before the new 'parent' process
      // pipeline.
      MigMagArrayUtility::insertInFrontOfKey(
        $plugin_definition['process'],
        'lookup_stub_parent',
        'parent_uuid',
        [
          [
            'plugin' => 'callback',
            'callable' => 'is_string',
            'source' => '@original_parent',
          ],
          [
            'plugin' => 'callback',
            'callable' => 'intval',
          ],
          [
            'plugin' => 'static_map',
            'map' => [1, 0],
          ],
          [
            'plugin' => 'skip_on_empty',
            'method' => 'process',
          ],
          [
            'plugin' => 'skip_on_empty',
            'source' => 'plid',
            'method' => 'process',
          ],
          [
            'plugin' => 'migmag_lookup',
            'stub_default_values' => [
              'menu_name' => '@menu_name',
            ],
            'migration' => [
              'd7_menu_links',
              'd7_menu_links_localized',
              'd7_menu_links_translation',
              'node_translation_menu_links',
            ],
            'fallback_stub_id' => static::TRAP_MIGRATION_ID,
          ],
          [
            'plugin' => 'skip_on_empty',
            'method' => 'process',
          ],
          [
            'plugin' => 'migmag_get_entity_property',
            'entity_type_id' => 'menu_link_content',
            'property' => 'uuid',
          ],
        ]
      );

      // We need the original parent process pipeline because it also searches
      // for parents based on the parent menu link's path. But we will use
      // migmag_try to catch its exceptions.
      MigMagArrayUtility::insertInFrontOfKey(
        $plugin_definition['process'],
        'parent_uuid',
        'original_parent',
        [
          [
            'plugin' => 'migmag_try',
            'process' => $plugin_definition['process']['parent'],
            'catch' => ['Drupal\migrate\MigrateSkipRowException' => NULL],
            'saveMessage' => FALSE,
          ],
          [
            'plugin' => 'default_value',
            'default_value' => NULL,
          ],
        ]
      );

      // Change the parent process to use null coalesce.
      $plugin_definition['process']['parent'] = [
        'plugin' => 'null_coalesce',
        'source' => [
          '@original_parent',
          '@lookup_stub_parent',
        ],
      ];

      $migrations[$plugin_id] = $plugin_definition;
    }
  }

  /**
   * Prepares the migration of menu link stubs.
   *
   * @param \Drupal\migrate\Row $row
   *   The row being imported.
   * @param \Drupal\migrate\Plugin\MigrationInterface $migration
   *   The current migration.
   */
  public static function prepareMenuLinkStubMigration(Row $row, MigrationInterface $migration): void {
    if (
      !$migration->getDestinationPlugin()->getPluginId() === 'entity:menu_link_content' ||
      !$row->isStub()
    ) {
      return;
    }

    // Do not process plid, link_path for menu link content stubs.
    $source_path = $row->getSourceProperty('link_path');
    $title_suffix = $source_path
      ? " (stub: '{$source_path}')"
      : '';
    $row->setSourceProperty(
      'link_title',
      $row->getSourceProperty('link_title') . $title_suffix
    );
    $row->setSourceProperty('stub', 1);
    $row->setSourceProperty('plid', 0);
    $row->setSourceProperty('link_path', 'route:<current>');
    // Stubs must be disabled.
    $row->setSourceProperty('enabled', FALSE);
  }

  /**
   * Returns the link path of the specified menu link.
   *
   * @param string|int $mlid
   *   The menu link identifier in the source Drupal 7 instance.
   *
   * @return array|null
   *   The crucial data of the specified menu link, or NULL if the menu link
   *   cannot be found in the source.
   */
  public static function getSourceMenuLinkData($mlid): ?array {
    // We need a Drupal Sql based source plugin.
    // @see MigrationDeriverTrait::getSourcePlugin()
    $manager = \Drupal::service('plugin.manager.migration');
    assert($manager instanceof MigrationPluginManagerInterface);
    $variable = $manager->createStubMigration([
      'source' => ['ignore_map' => TRUE, 'plugin' => 'variable'],
      'destination' => ['plugin' => 'null'],
      'idMap' => ['plugin' => 'null'],
    ])->getSourcePlugin();
    assert($variable instanceof DrupalSqlBase);

    $menu_link_path = $variable->getDatabase()
      ->select('menu_links', 'ml')
      ->fields('ml', [
        'link_title',
        'link_path',
        'options',
        'weight',
        'hidden',
        'expanded',
      ])
      ->condition('ml.mlid', $mlid)
      ->execute()
      ->fetch(\PDO::FETCH_ASSOC);

    if (!$menu_link_path) {
      return NULL;
    }

    // See \Drupal\menu_link_content\Plugin\migrate\source::prepareRow()
    $menu_link_path['enabled'] = (int) !$menu_link_path['hidden'];
    $menu_link_path['options'] = unserialize($menu_link_path['options']);
    $menu_link_path['description'] = Unicode::truncate($menu_link_path['options']['attributes']['title'] ?? '', 255);

    return $menu_link_path;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc