diff --git a/src/Drush/Commands/MenuImportCommands.php b/src/Drush/Commands/MenuImportCommands.php new file mode 100644 index 0000000000000000000000000000000000000000..a8f2d015cc018d5967c13efdb1a3f10f27cc345f --- /dev/null +++ b/src/Drush/Commands/MenuImportCommands.php @@ -0,0 +1,200 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\menu_export\Drush\Commands; + +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\menu_link_content\Entity\MenuLinkContent; +use Drupal\system\Entity\Menu; +use Drush\Attributes as CLI; +use Drush\Commands\AutowireTrait; +use Drush\Commands\DrushCommands; + +/** + * A Drush commandfile for importing menus from Menu Export module. + */ +final class MenuImportCommands extends DrushCommands { + + use AutowireTrait; + + /** + * Constructs a MenuImportCommands object. + * + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager + * The entity type manager service. + * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory + * The configuration factory service. + */ + public function __construct( + private readonly EntityTypeManagerInterface $entityTypeManager, + private readonly ConfigFactoryInterface $configFactory, + ) { + parent::__construct(); + } + + /** + * Import menus from Menu Export configuration. + */ + #[CLI\Command(name: 'menu_export:menu-import', aliases: ['me-menu-import'])] + #[CLI\Usage(name: 'menu_export:menu-import', description: 'Import menus from Menu Export configuration.')] + public function importMenus(): void { + if (!$this->validateMenuExportConfig()) { + return; + } + + $this->displayMenusTable(); + + $menu_links = $this->configFactory->get('menu_export.export_data')->get(); + if (empty($menu_links)) { + $this->logger()->warning(dt('No menu links found to import.')); + return; + } + + if (!$this->confirmImport(count($menu_links))) { + return; + } + + $this->processMenuImport($menu_links); + } + + /** + * Validates that Menu Export module is properly configured. + * + * @return bool + * TRUE if configuration is valid, FALSE otherwise. + */ + private function validateMenuExportConfig(): bool { + $menus = $this->configFactory->get('menu_export.settings')->get('menus'); + + if (empty($menus)) { + $this->logger()->warning(dt('No menus configured for export/import in Menu Export module.')); + return FALSE; + } + + return TRUE; + } + + /** + * Displays a table of menus configured for import. + */ + private function displayMenusTable(): void { + $menus = $this->configFactory->get('menu_export.settings')->get('menus'); + $menu_entities = Menu::loadMultiple($menus); + + $rows = []; + foreach ($menu_entities as $menu_name => $menu_entity) { + $rows[] = [ + 'name' => $menu_name, + 'label' => $menu_entity->label(), + 'description' => $menu_entity->getDescription(), + ]; + } + + $this->io()->title(dt('Menus configured for import:')); + $this->io()->table( + ['Machine Name', 'Label', 'Description'], + $rows + ); + } + + /** + * Asks for confirmation before proceeding with import. + * + * @param int $count + * The number of menu links to import. + * + * @return bool + * TRUE if confirmed, FALSE otherwise. + */ + private function confirmImport(int $count): bool { + return $this->io()->confirm(dt('Do you want to proceed with the import of @count menu links?', [ + '@count' => $count, + ])); + } + + /** + * Process the menu links import. + * + * @param array $menu_links + * The menu links to import. + */ + private function processMenuImport(array $menu_links): void { + $import_count = 0; + $invalid_menus = []; + $total_links = count($menu_links); + + // Set up progress bar. + $this->io()->title(dt('Importing menu links:')); + $progress_bar = $this->io()->createProgressBar($total_links); + $progress_bar->setFormat( + ' %current%/%max% [%bar%] %percent:3s%% %message%' + ); + $progress_bar->setMessage('Starting import...'); + $progress_bar->start(); + + foreach ($menu_links as $menu) { + $menu_name = $menu['menu_name']['value']; + + $progress_bar->setMessage("Processing link from menu '$menu_name'"); + + if (!$this->entityTypeManager->getStorage('menu')->load($menu_name)) { + $invalid_menus[] = $menu_name; + $progress_bar->advance(); + continue; + } + + unset($menu['id']); + unset($menu['revision_id']); + + $menu_link_entity = \Drupal::entityQuery('menu_link_content') + ->accessCheck(FALSE) + ->condition('uuid', (!empty($menu['uuid']['value']) ? $menu['uuid']['value'] : $menu['uuid'])) + ->execute(); + + if (!$menu_link_entity) { + $menu_link_entity = MenuLinkContent::create(); + } + else { + $menu_link_entity = MenuLinkContent::load(reset($menu_link_entity)); + } + + foreach ($menu as $field_name => $items) { + $menu_link_entity->set($field_name, $items); + } + + $menu_link_entity->save(); + $import_count++; + + $progress_bar->advance(); + } + + $progress_bar->setMessage('Import completed'); + $progress_bar->finish(); + $this->io()->newLine(2); + + $this->displayImportResults($import_count, $invalid_menus); + } + + /** + * Displays import results. + * + * @param int $import_count + * The number of successfully imported menu links. + * @param array $invalid_menus + * The list of invalid menu names. + */ + private function displayImportResults(int $import_count, array $invalid_menus): void { + if (count($invalid_menus)) { + $this->logger()->warning(dt('Menu(s) not found: @menus', [ + '@menus' => implode(', ', $invalid_menus), + ])); + } + + $this->logger()->success(dt('Successfully imported @count menu links.', [ + '@count' => $import_count, + ])); + } + +}