You are here

class MenuLinksController in Structure Sync 8

Same name and namespace in other branches
  1. 2.x src/Controller/MenuLinksController.php \Drupal\structure_sync\Controller\MenuLinksController

Controller for syncing menu links.

Hierarchy

Expanded class hierarchy of MenuLinksController

1 file declares its use of MenuLinksController
StructureSyncHelper.php in src/StructureSyncHelper.php

File

src/Controller/MenuLinksController.php, line 13

Namespace

Drupal\structure_sync\Controller
View source
class MenuLinksController extends ControllerBase {
  private $config;

  /**
   * Constructor for menu links controller.
   */
  public function __construct() {
    $this->config = $this
      ->getEditableConfig();
    $this
      ->entityTypeManager();
  }

  /**
   * Gets the editable version of the config.
   */
  private function getEditableConfig() {
    $this
      ->config('structure_sync.data');
    return $this->configFactory
      ->getEditable('structure_sync.data');
  }

  /**
   * Function to export menu links.
   */
  public function exportMenuLinks(array $form = NULL, FormStateInterface $form_state = NULL) {
    StructureSyncHelper::logMessage('Menu links export started');
    if (is_object($form_state) && $form_state
      ->hasValue('export_menu_list')) {
      $menu_list = $form_state
        ->getValue('export_menu_list');
      $menu_list = array_filter($menu_list, 'is_string');
    }
    $this->config
      ->clear('menus')
      ->save();
    if (isset($menu_list)) {
      $menuLinks = [];
      foreach ($menu_list as $menu_name) {
        $menuLinks = array_merge($this->entityTypeManager
          ->getStorage('menu_link_content')
          ->loadByProperties([
          'menu_name' => $menu_name,
        ]), $menuLinks);
      }
    }
    else {
      $menuLinks = $this
        ->entityTypeManager()
        ->getStorage('menu_link_content')
        ->loadMultiple();
    }
    $customMenuLinks = [];
    foreach ($menuLinks as $menuLink) {
      $customMenuLinks[] = [
        'menu_name' => $menuLink->menu_name->value,
        'title' => $menuLink->title->value,
        'parent' => $menuLink->parent->value,
        'uri' => $menuLink->link->uri,
        'link_title' => $menuLink->link->title,
        'description' => $menuLink->description->value,
        'enabled' => $menuLink->enabled->value,
        'expanded' => $menuLink->expanded->value,
        'weight' => $menuLink->weight->value,
        'langcode' => $menuLink->langcode->value,
        'uuid' => $menuLink
          ->uuid(),
      ];
      if (array_key_exists('drush', $form) && $form['drush'] === TRUE) {
        drush_log('Exported "' . $menuLink->title
          ->getValue()[0]['value'] . '" of menu "' . $menuLink->menu_name
          ->getValue()[0]['value'] . '"', 'ok');
      }
      StructureSyncHelper::logMessage('Exported "' . $menuLink->title->value . '" of menu "' . $menuLink->menu_name->value . '"');
    }
    $this->config
      ->set('menus', $customMenuLinks)
      ->save();
    drupal_set_message($this
      ->t('The menu links have been successfully exported.'));
    StructureSyncHelper::logMessage('Menu links exported');
  }

  /**
   * Function to import menu links.
   *
   * When this function is used without the designated form, you should assign
   * an array with a key value pair for form with key 'style' and value 'full',
   * 'safe' or 'force' to apply that import style.
   */
  public function importMenuLinks(array $form, FormStateInterface $form_state = NULL) {
    StructureSyncHelper::logMessage('Menu links import started');

    // Check if the there is a selection made in a form for what menus need to
    // be imported.
    if (is_object($form_state) && $form_state
      ->hasValue('import_menu_list')) {
      $menusSelected = $form_state
        ->getValue('import_menu_list');
      $menusSelected = array_filter($menusSelected, 'is_string');
    }
    if (array_key_exists('style', $form)) {
      $style = $form['style'];
    }
    else {
      StructureSyncHelper::logMessage('No style defined on menu links import', 'error');
      return;
    }
    StructureSyncHelper::logMessage('Using "' . $style . '" style for menu links import');

    // Get menu links from config.
    $menusConfig = $this->config
      ->get('menus');
    $menus = [];
    if (isset($menusSelected)) {
      foreach ($menusConfig as $menu) {
        if (in_array($menu['menu_name'], array_keys($menusSelected))) {
          $menus[] = $menu;
        }
      }
    }
    else {
      $menus = $menusConfig;
    }
    if (array_key_exists('drush', $form) && $form['drush'] === TRUE) {
      $context = [];
      $context['drush'] = TRUE;
      switch ($style) {
        case 'full':
          self::deleteDeletedMenuLinks($menus, $context);
          self::importMenuLinksFull($menus, $context);
          self::menuLinksImportFinishedCallback(NULL, NULL, NULL);
          break;
        case 'safe':
          self::importMenuLinksSafe($menus, $context);
          self::menuLinksImportFinishedCallback(NULL, NULL, NULL);
          break;
        case 'force':
          self::deleteMenuLinks($context);
          self::importMenuLinksForce($menus, $context);
          self::menuLinksImportFinishedCallback(NULL, NULL, NULL);
          break;
      }
      return;
    }

    // Import the menu links with the chosen style of importing.
    switch ($style) {
      case 'full':
        $batch = [
          'title' => $this
            ->t('Importing menu links...'),
          'operations' => [
            [
              '\\Drupal\\structure_sync\\Controller\\MenuLinksController::deleteDeletedMenuLinks',
              [
                $menus,
              ],
            ],
            [
              '\\Drupal\\structure_sync\\Controller\\MenuLinksController::importMenuLinksFull',
              [
                $menus,
              ],
            ],
          ],
          'finished' => '\\Drupal\\structure_sync\\Controller\\MenuLinksController::menuLinksImportFinishedCallback',
        ];
        batch_set($batch);
        break;
      case 'safe':
        $batch = [
          'title' => $this
            ->t('Importing menu links...'),
          'operations' => [
            [
              '\\Drupal\\structure_sync\\Controller\\MenuLinksController::importMenuLinksSafe',
              [
                $menus,
              ],
            ],
          ],
          'finished' => '\\Drupal\\structure_sync\\Controller\\MenuLinksController::menuLinksImportFinishedCallback',
        ];
        batch_set($batch);
        break;
      case 'force':
        $batch = [
          'title' => $this
            ->t('Importing menu links...'),
          'operations' => [
            [
              '\\Drupal\\structure_sync\\Controller\\MenuLinksController::deleteMenuLinks',
              [],
            ],
            [
              '\\Drupal\\structure_sync\\Controller\\MenuLinksController::importMenuLinksForce',
              [
                $menus,
              ],
            ],
          ],
          'finished' => '\\Drupal\\structure_sync\\Controller\\MenuLinksController::menuLinksImportFinishedCallback',
        ];
        batch_set($batch);
        break;
      default:
        StructureSyncHelper::logMessage('Style not recognized', 'error');
        break;
    }
  }

  /**
   * Function to delete the menu links that should be removed in this import.
   */
  public static function deleteDeletedMenuLinks($menus, &$context) {
    $uuidsInConfig = [];
    foreach ($menus as $menuLink) {
      $uuidsInConfig[] = $menuLink['uuid'];
    }
    if (!empty($uuidsInConfig)) {
      $query = StructureSyncHelper::getEntityQuery('menu_link_content');
      $query
        ->condition('uuid', $uuidsInConfig, 'NOT IN');
      $ids = $query
        ->execute();
      $controller = StructureSyncHelper::getEntityManager()
        ->getStorage('menu_link_content');
      $entities = $controller
        ->loadMultiple($ids);
      $controller
        ->delete($entities);
    }
    if (array_key_exists('drush', $context) && $context['drush'] === TRUE) {
      drush_log('Deleted menu links that were not in config', 'ok');
    }
    StructureSyncHelper::logMessage('Deleted menu links that were not in config');
  }

  /**
   * Function to fully import the menu links.
   *
   * Basically a safe import with update actions for already existing menu
   * links.
   */
  public static function importMenuLinksFull($menus, &$context) {
    $uuidsInConfig = [];
    foreach ($menus as $menuLink) {
      $uuidsInConfig[] = $menuLink['uuid'];
    }
    $entities = [];
    if (!empty($uuidsInConfig)) {
      $query = StructureSyncHelper::getEntityQuery('menu_link_content');
      $query
        ->condition('uuid', $uuidsInConfig, 'IN');
      $ids = $query
        ->execute();
      $controller = StructureSyncHelper::getEntityManager()
        ->getStorage('menu_link_content');
      $entities = $controller
        ->loadMultiple($ids);
    }
    $parents = array_column($menus, 'parent');
    foreach ($parents as &$parent) {
      if (!is_null($parent)) {
        if (($pos = strpos($parent, ":")) !== FALSE) {
          $parent = substr($parent, $pos + 1);
        }
        else {
          $parent = NULL;
        }
      }
    }
    $idsDone = [];
    $idsLeft = [];
    $firstRun = TRUE;
    $context['sandbox']['max'] = count($menus);
    $context['sandbox']['progress'] = 0;
    while ($firstRun || count($idsLeft) > 0) {
      foreach ($menus as $menuLink) {
        $query = StructureSyncHelper::getEntityQuery('menu_link_content');
        $query
          ->condition('uuid', $menuLink['uuid']);
        $ids = $query
          ->execute();
        $currentParent = $menuLink['parent'];
        if (!is_null($currentParent)) {
          if (($pos = strpos($currentParent, ":")) !== FALSE) {
            $currentParent = substr($currentParent, $pos + 1);
          }
        }
        if (!in_array($menuLink['uuid'], $idsDone) && ($menuLink['parent'] === NULL || !in_array($menuLink['parent'], $parents) || in_array($currentParent, $idsDone))) {
          if (count($ids) <= 0) {
            MenuLinkContent::create([
              'title' => $menuLink['title'],
              'link' => [
                'uri' => $menuLink['uri'],
                'title' => $menuLink['link_title'],
              ],
              'menu_name' => $menuLink['menu_name'],
              'expanded' => $menuLink['expanded'] === '1' ? TRUE : FALSE,
              'enabled' => $menuLink['enabled'] === '1' ? TRUE : FALSE,
              'parent' => $menuLink['parent'],
              'description' => $menuLink['description'],
              'weight' => $menuLink['weight'],
              'langcode' => $menuLink['langcode'],
              'uuid' => $menuLink['uuid'],
            ])
              ->save();
          }
          else {
            foreach ($entities as $entity) {
              if ($menuLink['uuid'] === $entity
                ->uuid()) {
                $customMenuLink = MenuLinkContent::load($entity
                  ->id());
                if (!empty($customMenuLink)) {
                  $customMenuLink
                    ->set('title', $menuLink['title'])
                    ->set('link', [
                    'uri' => $menuLink['uri'],
                    'title' => $menuLink['link_title'],
                  ])
                    ->set('expanded', $menuLink['expanded'] === '1' ? TRUE : FALSE)
                    ->set('enabled', $menuLink['enabled'] === '1' ? TRUE : FALSE)
                    ->set('parent', $menuLink['parent'])
                    ->set('description', $menuLink['description'])
                    ->set('weight', $menuLink['weight'])
                    ->save();
                }
                break;
              }
            }
          }
          $idsDone[] = $menuLink['uuid'];
          if (in_array($menuLink['uuid'], $idsLeft)) {
            unset($idsLeft[$menuLink['uuid']]);
          }
          if (array_key_exists('drush', $context) && $context['drush'] === TRUE) {
            drush_log('Imported "' . $menuLink['title'] . '" into ' . $menuLink['menu_name'], 'ok');
          }
          StructureSyncHelper::logMessage('Imported "' . $menuLink['title'] . '" into ' . $menuLink['menu_name']);
          $context['sandbox']['progress']++;
          if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
            $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
          }
        }
        else {
          $idsLeft[$menuLink['uuid']] = $menuLink['uuid'];
        }
      }
      $firstRun = FALSE;
    }
    $context['finished'] = 1;
  }

  /**
   * Function to import menu links safe (only adding what isn't already there).
   */
  public static function importMenuLinksSafe($menus, &$context) {
    $menusFiltered = $menus;
    $entities = StructureSyncHelper::getEntityManager()
      ->getStorage('menu_link_content')
      ->loadMultiple();
    foreach ($entities as $entity) {
      for ($i = 0; $i < count($menus); $i++) {
        if ($entity
          ->uuid() === $menus[$i]['uuid']) {
          unset($menusFiltered[$i]);
        }
      }
    }
    foreach ($menusFiltered as $menuLink) {
      MenuLinkContent::create([
        'title' => $menuLink['title'],
        'link' => [
          'uri' => $menuLink['uri'],
          'title' => $menuLink['link_title'],
        ],
        'menu_name' => $menuLink['menu_name'],
        'expanded' => $menuLink['expanded'] === '1' ? TRUE : FALSE,
        'enabled' => $menuLink['enabled'] === '1' ? TRUE : FALSE,
        'parent' => $menuLink['parent'],
        'description' => $menuLink['description'],
        'weight' => $menuLink['weight'],
        'langcode' => $menuLink['langcode'],
        'uuid' => $menuLink['uuid'],
      ])
        ->save();
      if (array_key_exists('drush', $context) && $context['drush'] === TRUE) {
        drush_log('Imported "' . $menuLink['title'] . '" into "' . $menuLink['menu_name'] . '" menu', 'ok');
      }
      StructureSyncHelper::logMessage('Imported "' . $menuLink['title'] . '" into "' . $menuLink['menu_name'] . '" menu');
    }
  }

  /**
   * Function to delete all menu links.
   */
  public static function deleteMenuLinks(&$context) {
    $entities = StructureSyncHelper::getEntityManager()
      ->getStorage('menu_link_content')
      ->loadMultiple();
    StructureSyncHelper::getEntityManager()
      ->getStorage('menu_link_content')
      ->delete($entities);
    if (array_key_exists('drush', $context) && $context['drush'] === TRUE) {
      drush_log('Deleted all (content) menu links', 'ok');
    }
    StructureSyncHelper::logMessage('Deleted all (content) menu links');
  }

  /**
   * Function to import (create) all menu links that need to be imported.
   */
  public static function importMenuLinksForce($menus, &$context) {
    foreach ($menus as $menuLink) {
      MenuLinkContent::create([
        'title' => $menuLink['title'],
        'link' => [
          'uri' => $menuLink['uri'],
          'title' => $menuLink['link_title'],
        ],
        'menu_name' => $menuLink['menu_name'],
        'expanded' => $menuLink['expanded'] === '1' ? TRUE : FALSE,
        'enabled' => $menuLink['enabled'] === '1' ? TRUE : FALSE,
        'parent' => $menuLink['parent'],
        'description' => $menuLink['description'],
        'weight' => $menuLink['weight'],
        'langcode' => $menuLink['langcode'],
        'uuid' => $menuLink['uuid'],
      ])
        ->save();
      if (array_key_exists('drush', $context) && $context['drush'] === TRUE) {
        drush_log('Imported "' . $menuLink['title'] . '" into "' . $menuLink['menu_name'] . '" menu', 'ok');
      }
      StructureSyncHelper::logMessage('Imported "' . $menuLink['title'] . '" into "' . $menuLink['menu_name'] . '" menu');
    }
  }

  /**
   * Function that signals that the import of menu links has finished.
   */
  public static function menuLinksImportFinishedCallback($success, $results, $operations) {
    StructureSyncHelper::logMessage('Flushing all caches');
    drupal_flush_all_caches();
    StructureSyncHelper::logMessage('Successfully flushed caches');
    StructureSyncHelper::logMessage('Successfully imported menu links');
    drupal_set_message(t('Successfully imported menu links'));
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ControllerBase::$configFactory protected property The configuration factory.
ControllerBase::$currentUser protected property The current user service. 1
ControllerBase::$entityFormBuilder protected property The entity form builder.
ControllerBase::$entityManager protected property The entity manager.
ControllerBase::$entityTypeManager protected property The entity type manager.
ControllerBase::$formBuilder protected property The form builder. 2
ControllerBase::$keyValue protected property The key-value storage. 1
ControllerBase::$languageManager protected property The language manager. 1
ControllerBase::$moduleHandler protected property The module handler. 2
ControllerBase::$stateService protected property The state service.
ControllerBase::cache protected function Returns the requested cache bin.
ControllerBase::config protected function Retrieves a configuration object.
ControllerBase::container private function Returns the service container.
ControllerBase::create public static function Instantiates a new instance of this class. Overrides ContainerInjectionInterface::create 40
ControllerBase::currentUser protected function Returns the current user. 1
ControllerBase::entityFormBuilder protected function Retrieves the entity form builder.
ControllerBase::entityManager Deprecated protected function Retrieves the entity manager service.
ControllerBase::entityTypeManager protected function Retrieves the entity type manager.
ControllerBase::formBuilder protected function Returns the form builder service. 2
ControllerBase::keyValue protected function Returns a key/value storage collection. 1
ControllerBase::languageManager protected function Returns the language manager service. 1
ControllerBase::moduleHandler protected function Returns the module handler. 2
ControllerBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
ControllerBase::state protected function Returns the state storage service.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MenuLinksController::$config private property
MenuLinksController::deleteDeletedMenuLinks public static function Function to delete the menu links that should be removed in this import.
MenuLinksController::deleteMenuLinks public static function Function to delete all menu links.
MenuLinksController::exportMenuLinks public function Function to export menu links.
MenuLinksController::getEditableConfig private function Gets the editable version of the config.
MenuLinksController::importMenuLinks public function Function to import menu links.
MenuLinksController::importMenuLinksForce public static function Function to import (create) all menu links that need to be imported.
MenuLinksController::importMenuLinksFull public static function Function to fully import the menu links.
MenuLinksController::importMenuLinksSafe public static function Function to import menu links safe (only adding what isn't already there).
MenuLinksController::menuLinksImportFinishedCallback public static function Function that signals that the import of menu links has finished.
MenuLinksController::__construct public function Constructor for menu links controller.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.