taxonomy_menu_sync-1.0.4/src/TaxonomyMenuSyncHelper.php

src/TaxonomyMenuSyncHelper.php
<?php

namespace Drupal\taxonomy_menu_sync;

use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Menu\MenuParentFormSelectorInterface;
use Drupal\menu_link_content\MenuLinkContentInterface;
use Drupal\node\NodeInterface;
use Drupal\taxonomy\TermInterface;
use Drupal\taxonomy_menu_sync\Entity\TaxonomyMenuSync;

/**
 * Autogenerated menu utility service.
 */
class TaxonomyMenuSyncHelper {

  /**
   * Logger name.
   */
  const string LOGGER_NAME = 'taxonomy_menu_sync';

  /**
   * Menu configuration id.
   *
   * @var string
   */
  protected string $menuConfigId;

  /**
   * Menu name to be used for sync-in.
   *
   * @var string
   */
  protected string $menuName;

  /**
   * Parent menu content item used as parent when sync-in.
   *
   * @var string
   */
  protected string $parentMenuItem;

  /**
   * Content type use for pull-in data(i.e. label, URL)
   *
   * @var string
   */
  protected string $bundleName;

  /**
   * Checkbox to use node.
   *
   * @var bool
   */
  protected bool $useNode = FALSE;

  /**
   * Use term weight to set order of menu.
   *
   * @var bool
   */
  protected bool $useTermWeight = FALSE;

  /**
   * Disable/enable menu item.
   *
   * @var bool
   */
  protected bool $hideEmptyTerms = FALSE;

  /**
   * DB stored mapping data.
   *
   * @var array
   */
  protected array $existingMapping = [];

  /**
   * Enabled languages.
   *
   * @var array
   */
  protected array $enabledLanguages = [];

  /**
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected EntityTypeManagerInterface $entityTypeManager;

  /**
   * @var \Drupal\Core\Menu\MenuParentFormSelectorInterface
   */
  protected MenuParentFormSelectorInterface $menuParentFormSelector;

  /**
   * @var \Drupal\Core\Database\Connection
   */
  protected Connection $connection;

  /**
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected LoggerChannelFactoryInterface $loggerFactory;

  /**
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected ModuleHandlerInterface $moduleHandler;

  /**
   * Autogenerated menu constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   Entity type manager service.
   * @param \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_form_selector
   *   Menu parent form selector service.
   * @param \Drupal\Core\Database\Connection $connection
   *   Database connection service.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   Logging service.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   Module handle service.
   */
  public function __construct(
    EntityTypeManagerInterface $entity_type_manager,
    MenuParentFormSelectorInterface $menu_parent_form_selector,
    Connection $connection,
    LoggerChannelFactoryInterface $logger_factory,
    ModuleHandlerInterface $module_handler,
  ) {
    $this->entityTypeManager = $entity_type_manager;
    $this->menuParentFormSelector = $menu_parent_form_selector;
    $this->connection = $connection;
    $this->loggerFactory = $logger_factory;
    $this->moduleHandler = $module_handler;
  }

  /**
   * Entity load.
   *
   * @return \Drupal\Core\Entity\EntityInterface|null
   *   An object of entity or null.
   */
  public function entityLoad(string $entity_type, string|int $entity_id):EntityInterface|null {
    try {
      $entity_storage = $this->entityTypeManager->getStorage($entity_type);
    }
    catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
      return NULL;
    }
    return $entity_storage->load($entity_id);
  }

  /**
   * Menu(s) list.
   *
   * @return array
   *   An array id, label pair.
   */
  public function getMenuList():array {
    $menus = [];

    try {
      $menu_storage = $this->entityTypeManager->getStorage('menu');
    }
    catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
      return [];
    }

    foreach ($menu_storage->loadMultiple() as $menu) {
      $menus[$menu->id()] = $menu->label();
    }

    return $menus;
  }

  /**
   * Menu(s) link content list.
   *
   * @param array|null $menus
   *   An array(id, label pair) of menu lists.
   * @param string $menu_id
   *   Menu link content id.
   *
   * @return array
   *   An array id, label pair.
   */
  public function getMenuLinkContentList(?array $menus = NULL, string $menu_id = '',):array {
    return $this->menuParentFormSelector->getParentSelectOptions($menu_id, $menus);
  }

  /**
   * Find taxonomy term id.
   *
   * @param string $vocab
   *   A string of taxonomy vocabulary machine name.
   * @param string|int $term_id
   *   Taxonomy term id.
   *
   * @return bool
   */
  public function verifyTermId(string $vocab, string|int $term_id): bool {
    try {
      return (bool) $this->entityTypeManager->getStorage('taxonomy_term')->getQuery()
        ->accessCheck(FALSE)
        ->condition('vid', $vocab)
        ->condition('tid', $term_id)
        ->execute();
    }
    catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
      return FALSE;
    }
  }

  /**
   * Lists of enabled languages.
   *
   * @return array|int[]|string[]
   *   An array of langcodes.
   */
  public function getLanguages(): array {
    try {
      $language_storage = $this->entityTypeManager->getStorage('configurable_language');
      return $this->enabledLanguages = array_keys($language_storage->loadByProperties(['locked' => FALSE]));
    }
    catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
      return [];
    }
  }

  /**
   * Set the initial requirement to autogenerate menu(s).
   *
   * @param string $id
   *   Menu configuration id.
   *
   * @return void
   */
  public function sync(string $id): void {
    try {
      /** @var \Drupal\taxonomy_menu_sync\Entity\TaxonomyMenuSync $entity */
      if (!empty($entity = $this->entityLoad('taxonomy_menu_sync', $id))) {
        $this->loggerFactory->get(self::LOGGER_NAME)->notice('Taxonomy terms -> Menu sync initiated for taxonomy menu sync, id: %id.', ['%id' => $id]);
        $this->getLanguages();
        $this->menuConfigId = $entity->id();
        $this->menuName = $entity->get('target_menu')->target_id ?? '';
        $this->parentMenuItem = $entity->get('target_menu_item')->value ?? '';
        $this->bundleName = $entity->get('source_bundle')->target_id ?? '';
        $this->useNode = (bool) $entity->get('use_entity')->value;
        $this->useTermWeight = (bool) $entity->get('use_term_weight')->value;
        $this->hideEmptyTerms = (bool) $entity->get('hide_empty_term')->value;
        $this->existingMapping = $entity->getMapping();
        $vocabulary = $entity->get('source_vocabulary')->target_id ?? '';
        $parent_tid = $entity->get('source_term')->target_id ?? 0;
        $depth = $entity->get('depth')->value ?? NULL;
        $include_source_term = (bool) $entity->get('include_source_term')->value;

        if ($this->menuName && $this->bundleName && $vocabulary) {
          $terms = [];
          $this->loggerFactory->get(self::LOGGER_NAME)->notice('Taxonomy terms -> Menu sync requirements found for taxonomy menu sync, id: %id.', ['%id' => $id]);
          /** @var \Drupal\taxonomy\TermStorageInterface $term_storage */
          $term_storage = $this->entityTypeManager->getStorage('taxonomy_term');
          // Include source taxonomy term.
          if ($include_source_term && !empty($parent_tid)) {
            $current_term = $term_storage->load($parent_tid);
            if ($current_term instanceof TermInterface) {
              $terms[] = $current_term;
            }
          }

          $childrens = $term_storage->loadTree($vocabulary, $parent_tid, $depth, TRUE);
          $data = array_merge($terms, $childrens);
          $this->generateMenuLinks($data, $entity);
          $this->loggerFactory->get(self::LOGGER_NAME)->notice('Taxonomy terms -> Menu sync completed for taxonomy menu sync, id: %id.', ['%id' => $id]);
        }
      }
      else {
        $this->loggerFactory->get(self::LOGGER_NAME)->notice('Taxonomy terms -> Menu sync found no records for taxonomy menu sync, id: %id.', ['%id' => $id]);
      }
    }
    catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
      $this->loggerFactory->get(self::LOGGER_NAME)->notice('Taxonomy terms -> Menu sync ran into issues for taxonomy menu sync, id: %id. Message: @message', [
        '%id' => $id,
        '@message' => $e->getMessage(),
      ]);
    }
  }

  /**
   * Generate menu(s) using the configurations provided.
   *
   * @param array $terms
   *   An array of term object.
   * @param \Drupal\taxonomy_menu_sync\Entity\TaxonomyMenuSync $storeEntity
   *   Taxonomy menu sync object.
   *
   * @return void
   */
  public function generateMenuLinks(array $terms, TaxonomyMenuSync $storeEntity): void {
    $mapping = $db_mapping = [];
    $disable_title_sync = (bool) $storeEntity->get('disable_title_sync')->value;
    $disable_parent_sync = (bool) $storeEntity->get('disable_parent_sync')->value;
    $parent_menu = preg_replace('/^' . preg_quote("$this->menuName:") . '/', '', $this->parentMenuItem);

    try {
      $this->loggerFactory->get(self::LOGGER_NAME)->notice('Generate menu(s) initiated for taxonomy menu sync, id: %id.', ['%id' => $this->menuConfigId]);

      /** @var \Drupal\menu_link_content\MenuLinkContentStorageInterface $menu_link_storage */
      $menu_link_storage = $this->entityTypeManager->getStorage('menu_link_content');

      /** @var \Drupal\taxonomy\TermInterface $term */
      foreach ($terms as $term) {
        if ($term instanceof TermInterface) {
          $entity = $term;
          $tid = $term->id();
          $parent_id = !$term->get('parent')->isEmpty() ? $term->get('parent')->getString() : NULL;
          $menu_link = 'entity:taxonomy_term/' . $tid;

          if ($this->useNode) {
            $select = $this->connection->select('taxonomy_index', 'tn');
            $select->fields('tn', ['nid']);
            $select->join('node', 'n', 'n.nid = tn.nid');
            $select->condition('tn.tid', $tid);
            $select->condition('n.type', $this->bundleName);
            $results = $select->execute()->fetchCol();

            if (!empty($results)) {
              $menu_link = 'entity:node/' . $results[0];
              $node = $this->entityTypeManager->getStorage('node')->load($results[0]);
              if ($node instanceof NodeInterface) {
                $entity = $node;
              }
            }
          }

          $menu_link_content = !empty($this->existingMapping[$tid]['mlid']) ? $menu_link_storage->load($this->existingMapping[$tid]['mlid']) : NULL;
          $prefill_data = [
            'link' => ['uri' => $menu_link],
            'title' => $entity->label(),
            'parent' => $mapping[$parent_id] ?? $parent_menu,
            'enabled' => 1,
            'expanded' => TRUE,
          ];

          if ($this->hideEmptyTerms) {
            if (!$this->termUseCount($tid)) {
              $prefill_data['enabled'] = 0;
            }
          }

          if ($this->useTermWeight) {
            $prefill_data['weight'] = $term->getWeight();
          }

          if ($menu_link_content instanceof MenuLinkContentInterface) {
            foreach ($prefill_data as $key => $value) {
              if (($disable_title_sync && $key === 'title') || ($disable_parent_sync && $key === 'parent')) {
                continue;
              }
              $menu_link_content->set($key, $value);
            }
          }
          else {
            $prefill_data['menu_name'] = $this->menuName;
            $menu_link_content = $menu_link_storage->create($prefill_data);
          }

          foreach ($this->enabledLanguages as $langcode) {
            if ($langcode !== 'en' && $entity->hasTranslation($langcode)) {
              $entity = $entity->getTranslation($langcode);
              if ($menu_link_content->hasTranslation($langcode)) {
                if (!$disable_title_sync) {
                  $menu_link_content->getTranslation($langcode)->set('title', $entity->label());
                }
              }
              else {
                $menu_link_content->addTranslation($langcode, ['title' => $entity->label()]);
              }
            }
          }

          // Alter `$menu_link_content` using `taxonomy_menu_sync` hooks.
          $this->moduleHandler->alter('taxonomy_menu_sync', $menu_link_content, $entity);

          if ($menu_link_content->save()) {
            $mapping[$tid] = $menu_link_content->getPluginId();
            $db_mapping[$tid] = ['mlid' => $menu_link_content->id(), 'uuid' => $menu_link_content->uuid()];
          }
        }
      }

      if (count($terms) === count($db_mapping)) {
        $json_mapping = json_encode($db_mapping);
        $storeEntity->set('is_synced', TRUE);
        $storeEntity->set('mapping', $json_mapping);
        if ($storeEntity->save()) {
          // Delete unmapped menu items.
          if (!empty($unused_menus = array_diff_key($this->existingMapping, $db_mapping))) {
            $mlid_ids = array_column($unused_menus, 'mlid');
            if (!empty($loaded_entities = $menu_link_storage->loadMultiple($mlid_ids))) {
              $menu_link_storage->delete($loaded_entities);
              $this->loggerFactory->get(self::LOGGER_NAME)->notice('Unused menu(s) deleted for taxonomy menu sync, id: %id. Mapping(tid:menu_id): @message', [
                '%id' => $this->menuConfigId,
                '@message' => json_encode($unused_menus),
              ]);
            }
          }

          $this->loggerFactory->get(self::LOGGER_NAME)->notice('Generate menu(s) completed for taxonomy menu sync, id: %id. Mapping(tid:menu_id): @message', [
            '%id' => $this->menuConfigId,
            '@message' => $json_mapping,
          ]);
        }
      }
    }
    catch (InvalidPluginDefinitionException | PluginNotFoundException | EntityStorageException | \Exception $e) {
      $this->loggerFactory->get(self::LOGGER_NAME)->notice('Generate menu(s) ran into issues for taxonomy menu sync, id: %id. Message: @message', [
        '%id' => $this->menuConfigId,
        '@message' => $e->getMessage(),
      ]);
    }
  }

  /**
   * Get the number of nodes for given term.
   *
   * @param int $tid
   *   A taxonomy term id.
   *
   * @return bool
   *   The number of nodes the given term is on.
   */
  public function termUseCount(int $tid): bool {
    try {
      $status = FALSE;
      $result = $this->connection->select('taxonomy_index', 'tn');
      $result->condition('tid', $tid);
      $result->condition('n.type', $this->bundleName);
      $result->join('node', 'n', 'n.nid = tn.nid');
      $result->addExpression('COUNT(n.nid)', 'term_count');
      $temp = $result->execute();
      $temp = $temp->fetchObject();
      if ($temp->term_count > 0) {
        $status = TRUE;
      }
      return $status;
    }
    catch (\Exception $e) {
      return FALSE;
    }
  }

}

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

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