taxonomy_overview-1.0.1/src/Form/TagsOverviewTermMergeForm.php

src/Form/TagsOverviewTermMergeForm.php
<?php

namespace Drupal\taxonomy_overview\Form;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * Form to merge multiple taxonomy terms into a single one.
 */
class TagsOverviewTermMergeForm extends FormBase {

  /**
   * The term IDs to merge.
   *
   * @var int[]
   */
  protected array $termIds = [];

  /**
   * The entity type manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected EntityTypeManagerInterface $entityTypeManager;

  /**
   * The taxonomy term storage.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $termStorage;

  /**
   * Constructs a new TagsOverviewTermMergeForm object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
    $this->entityTypeManager = $entity_type_manager;
    $this->termStorage = $entity_type_manager->getStorage('taxonomy_term');
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId(): string {
    return 'term_variation_merge_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $termIdsQuery = $this->getRequest()->query->get('term_ids', '');
    $this->termIds = array_filter(explode(',', $termIdsQuery));

    $terms = $this->entityTypeManager->getStorage('taxonomy_term')->loadMultiple($this->termIds);

    if (count($terms) < 1) {
      return ['#markup' => $this->t('At least 2 terms are required to merge.')];
    }

    $form['#attached']['library'][] = 'taxonomy_overview/taxonomy_overview.form';

    $form['fieldset0'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('You are about to merge the following terms'),
    ];

    $form['fieldset0']['description2'] = [
      '#markup' => $this->t('The unselected terms will be merged into the chosen one.'),
    ];

    $form['fieldset0']['fieldset1'] = [
      '#type' => 'fieldset',
    ];

    $form['fieldset0']['fieldset1']['targetTid'] = [
      '#type' => 'radios',
      '#title' => $this->t('Select the term to keep'),
      '#options' => array_map(fn($t) => $t->label(), $terms),
      '#required' => TRUE,
    ];

    $form['fieldset0']['fieldset1']['removeAfter'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Remove terms after merge?'),
      '#required' => FALSE,
    ];

    $form['fieldset0']['undo'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('I agree that this operation cannot be undone!'),
      '#required' => TRUE,
    ];

    $form['fieldset0']['actions'] = [
      '#type' => 'actions',
    ];

    $form['fieldset0']['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Merge Terms'),
      '#button_type' => 'primary',
      '#states' => [
        'enabled' => [
          ':input[name="undo"]' => ['checked' => TRUE],
        ],
      ],
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $vocabulary = $this->getRouteMatch()->getParameter('taxonomy_vocabulary');
    $form_values = $form_state->getValues();

    $removeTerms = $form_values['removeAfter'];
    $targetTid = $form_values['targetTid'];

    $tidsToReplace = $this->termIds;
    unset($tidsToReplace[array_search($targetTid, $tidsToReplace)]);
    $tidsNames = [];

    if ($tidsToReplace) {
      foreach ($tidsToReplace as $t) {
        $term = $this->termStorage->load($t);
        if ($term) {
          $tidsNames[] = $term->label() . ' (tid: ' . $t . ')';
        }
      }
    }
    $entitiesFields = $this->getFieldsEntityReference($vocabulary);
    $operations = [];

    foreach ($entitiesFields as $entityType => $bundles) {
      if ($entityType === 'node') {
        foreach ($bundles as $fields) {
          foreach ($fields as $field) {
            $query = $this->entityTypeManager->getStorage('node')->getQuery()
              ->accessCheck(FALSE)
              ->condition($field, $tidsToReplace, 'IN');
            $nids = $query->execute();
            foreach ($nids as $nid) {
              $operations[] = [
                [
                  self::class,
                  'processNodeMerge',
                ], [
                  $nid,
                  $targetTid,
                  $tidsToReplace,
                  $field,
                  $vocabulary,
                ],
              ];
            }
          }
        }
      }
      elseif ($entityType === 'paragraph') {
        foreach ($bundles as $fields) {
          foreach ($fields as $field) {
            $paragraphIds = $this->entityTypeManager->getStorage('paragraph')->getQuery()
              ->accessCheck(FALSE)
              ->condition('status', 1)
              ->condition($field . '.target_id', $tidsToReplace, 'IN')
              ->latestRevision()
              ->execute();
            foreach ($paragraphIds as $revisionId => $pid) {
              $operations[] = [
                [self::class, 'processParagraphMerge'], [
                  $pid, $revisionId, $targetTid, $tidsToReplace, $field, $vocabulary,
                ],
              ];
            }
          }
        }
      }
    }

    if ($removeTerms) {
      foreach ($tidsToReplace as $tid) {
        $operations[] = [[self::class, 'processCleanTerms'], [$tid, $vocabulary]];
      }
    }

    if (empty($operations)) {

      // No operations were created, redirect back with a message.
      if (empty($operations)) {
        $msg = $this->t('No fields use these terms: @terms, so there is nothing to merge. If you want to proceed and remove the similar terms, check the `Remove terms after merge` option.', [
          '@terms' => implode(', ', $tidsNames),
        ]);
        $this->messenger()->addMessage($msg, 'warning');
        return;
      }

      // Merge the two values into the term_ids parameter.
      $termIdsParam = implode(',', [$removeTerms, $targetTid]);

      // Generate the URL for redirection.
      $url = $this->urlGenerator->generateFromRoute('taxonomy_overview.group_similar.merge_form', [
        'taxonomy_vocabulary' => $vocabulary,
        'term_ids' => $termIdsParam,
      ]);

      // Redirect user.
      $response = new RedirectResponse($url);
      $response->send();
      return;
    }

    $batch = [
      'title' => $this->t('Merging taxonomy terms'),
      'operations' => $operations,
      'finished' => [self::class, 'batchFinished'],
    ];

    batch_set($batch);
  }

  /**
   * Process the merging of terms in a node's field.
   */
  public static function processNodeMerge($nid, $target_tid, $tids_to_replace, $field_name, $vocabulary, &$context) {
    $node = Node::load($nid);
    if (!$node) {
      $message = t('Node with nid %nid not found.', ['%nid' => $nid]);
      $context['sandbox'][] = (string) $message;
      return;
    }

    $translations = $node->getTranslationLanguages();
    foreach ($translations as $language) {
      $langcode = $language->getId();
      if ($node->hasTranslation($langcode)) {
        $node = $node->getTranslation($langcode);

        $tags = $node->get($field_name)->getValue();
        $new_tags = [];

        foreach ($tags as $key => $tag) {
          $tid = $tag['target_id'];
          if (in_array($tid, $tids_to_replace)) {
            $tid = $target_tid;
          }
          $new_tags[$key] = ['target_id' => $tid];
        }

        $node->set($field_name, array_values($new_tags));
        $node->save();
      }
    }

    $message = t('Processed node @nid: merged terms into @target_tid in field @field_name.', [
      '@nid' => $nid,
      '@target_tid' => $target_tid,
      '@field_name' => $field_name,
    ]);
    $context['message'] = $message;
    $context['sandbox'][] = (string) $message;
    $context['results'][] = $message;
    $context['results']['vocabulary'] = $vocabulary;
  }

  /**
   * Process the deletion of a term.
   */
  public static function processCleanTerms($tid, $vocabulary, &$context) {
    $term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($tid);
    if ($term) {
      $label = $term->label();
      $term->delete();
      $message = t('Deleted term "@label" (tid: @tid)', ['@label' => $label, '@tid' => $tid]);
      $context['message'] = $message;
      $context['sandbox'][] = (string) $message;
      $context['results'][] = $message;
      $context['results']['vocabulary'] = $vocabulary;
    }
  }

  /**
   * Process the merging of terms in a paragraph's field.
   */
  public static function processParagraphMerge($paragraph_id, $revision_id, $target_tid, $tids_to_replace, $field_name, $vocabulary, &$context) {

    $entity_type_manager = \Drupal::entityTypeManager();
    $paragraph_revision = $entity_type_manager->getStorage('paragraph')->loadRevision($revision_id);

    $translations = $paragraph_revision->getTranslationLanguages();
    foreach ($translations as $language) {
      $langcode = $language->getId();
      if ($paragraph_revision->hasTranslation($langcode)) {
        $paragraph_revision = $paragraph_revision->getTranslation($langcode);
        $values = $paragraph_revision->get($field_name)->getValue();

        $new_values = [];

        foreach ($values as $key => $value) {
          $tid = $value['target_id'];
          if (in_array($tid, $tids_to_replace)) {
            $tid = $target_tid;
          }
          $new_values[$key] = ['target_id' => $tid];
        }

        $paragraph_revision->set($field_name, $new_values);
        $paragraph_revision->save();
      }
    }

    $message = t(
      'Paragraph @pid on revision @rid field @field updated with merged terms.',
      [
        '@pid' => $paragraph_id,
        '@rid' => $revision_id,
        '@field' => $field_name,
      ]
    );
    $context['message'] = $message;
    $context['sandbox'][] = (string) $message;
    $context['results'][] = $message;
    $context['results']['vocabulary'] = $vocabulary;
  }

  /**
   * Batch finished callback.
   */
  public static function batchFinished($success, $results, $operations, $context) {
    $messenger = \Drupal::messenger();
    // Get the vocabulary from sandbox.
    $vid = $results['vocabulary'] ?? NULL;
    if ($success) {
      unset($results['vocabulary']);
      foreach ($results as $message) {
        $messenger->addStatus($message);
      }
    }
    else {
      $messenger->addError(t('An error occurred during the term merge process.'));
    }

    // Redirect to your custom route.
    $url = Url::fromRoute('taxonomy_overview.group_similar', [
      'taxonomy_vocabulary' => $vid,
    ]);
    return new RedirectResponse($url->toString());
  }

  /**
   * Return all fields that reference the given vocabulary.
   *
   * @param string $vocabulary
   *   The vocabulary machine name.
   *
   * @return array
   *   Array of entity_type => bundle => fields.
   */
  protected function getFieldsEntityReference(string $vocabulary): array {
    $arrData = [];
    $fieldStorageConfigs = $this->entityTypeManager->getStorage('field_storage_config')->loadMultiple();

    foreach ($fieldStorageConfigs as $fieldStorageConfig) {
      if (in_array($fieldStorageConfig->getType(), ['entity_reference', 'entity_reference_revisions'])) {
        $settings = $fieldStorageConfig->getSettings();
        if (($settings['target_type'] ?? '') === 'taxonomy_term') {
          $fieldConfigs = $this->entityTypeManager->getStorage('field_config')
            ->loadByProperties(['field_name' => $fieldStorageConfig->getName()]);
          foreach ($fieldConfigs as $fieldConfig) {
            $handlerSettings = $fieldConfig->getSetting('handler_settings') ?? [];
            if (isset($handlerSettings['target_bundles'][$vocabulary])) {
              $entityType = $fieldConfig->getTargetEntityTypeId();
              $bundle = $fieldConfig->getTargetBundle();
              $arrData[$entityType][$bundle][] = $fieldConfig->getName();
            }
          }
        }
      }
    }

    return $arrData;
  }

}

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

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