You are here

protected function TermDevelGenerate::generateTerms in Devel 4.x

Same name and namespace in other branches
  1. 8.3 devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php \Drupal\devel_generate\Plugin\DevelGenerate\TermDevelGenerate::generateTerms()
  2. 8 devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php \Drupal\devel_generate\Plugin\DevelGenerate\TermDevelGenerate::generateTerms()
  3. 8.2 devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php \Drupal\devel_generate\Plugin\DevelGenerate\TermDevelGenerate::generateTerms()

Generates taxonomy terms for a list of given vocabularies.

Parameters

array $parameters: The input parameters from the settings form or drush command.

Return value

array Information about the created terms.

1 call to TermDevelGenerate::generateTerms()
TermDevelGenerate::generateElements in devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php
Business logic relating with each DevelGenerate plugin.

File

devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php, line 258

Class

TermDevelGenerate
Provides a TermDevelGenerate plugin.

Namespace

Drupal\devel_generate\Plugin\DevelGenerate

Code

protected function generateTerms(array $parameters) {
  $info = [
    'terms' => 0,
    'terms_translations' => 0,
  ];
  $min_depth = $parameters['minimum_depth'];
  $max_depth = $parameters['maximum_depth'];

  // $parameters['vids'] from the UI has keys of the vocab ids. From drush
  // the array is keyed 0,1,2. Therefore create $vocabs which has keys of the
  // vocab ids, so it can be used with array_rand().
  $vocabs = array_combine($parameters['vids'], $parameters['vids']);

  // Build an array of potential parents for the new terms. These will be
  // terms in the vocabularies we are creating in, which have a depth of one
  // less than the minimum for new terms up to one less than the maximum.
  $all_parents = [];
  foreach ($parameters['vids'] as $vid) {
    $info['vocabs'][$vid] = $this->vocabularyStorage
      ->load($vid)
      ->label();

    // Initialise the nested array for this vocabulary.
    $all_parents[$vid] = [
      'top_level' => [],
      'lower_levels' => [],
    ];
    for ($depth = 1; $depth < $max_depth; $depth++) {
      $query = \Drupal::entityQuery('taxonomy_term')
        ->condition('vid', $vid);
      if ($depth == 1) {

        // For the top level the parent id must be zero.
        $query
          ->condition('parent', 0);
      }
      else {

        // For lower levels use the $ids array obtained in the previous loop.
        // phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis.UndefinedVariable
        $query
          ->condition('parent', $ids, 'IN');
      }
      $ids = $query
        ->execute();
      if (empty($ids)) {

        // Reached the end, no more parents to be found.
        break;
      }

      // Store these terms as parents if they are within the depth range for
      // new terms.
      if ($depth == $min_depth - 1) {
        $all_parents[$vid]['top_level'] = array_fill_keys($ids, $depth);
      }
      elseif ($depth >= $min_depth) {
        $all_parents[$vid]['lower_levels'] += array_fill_keys($ids, $depth);
      }
    }

    // No top-level parents will have been found above when the minimum depth
    // is 1 so add a record for that data here.
    if ($min_depth == 1) {
      $all_parents[$vid]['top_level'] = [
        0 => 0,
      ];
    }
    elseif (empty($all_parents[$vid]['top_level'])) {

      // No parents for required minimum level so cannot use this vocabulary.
      unset($vocabs[$vid]);
    }
  }
  if (empty($vocabs)) {

    // There are no available parents at the required depth in any vocabulary
    // so we cannot create any new terms.
    throw new \Exception(sprintf('Invalid minimum depth %s because there are no terms in any vocabulary at depth %s', $min_depth, $min_depth - 1));
  }

  // Only delete terms from the vocabularies we can create new terms in.
  if ($parameters['kill']) {
    $deleted = $this
      ->deleteVocabularyTerms($vocabs);
    $this
      ->setMessage($this
      ->formatPlural($deleted, 'Deleted 1 existing term', 'Deleted @count existing terms'));
  }

  // Insert new data:
  for ($i = 1; $i <= $parameters['num']; $i++) {

    // Select a vocabulary at random.
    $vid = array_rand($vocabs);

    // Set the group to use to select a random parent from. Using < 50 means
    // on average half of the new terms will be top_level. Also if no terms
    // exist yet in 'lower_levels' then we have to use 'top_level'.
    $group = mt_rand(0, 100) < 50 || empty($all_parents[$vid]['lower_levels']) ? 'top_level' : 'lower_levels';
    $parent = array_rand($all_parents[$vid][$group]);
    $depth = $all_parents[$vid][$group][$parent] + 1;
    $name = $this
      ->getRandom()
      ->word(mt_rand(2, $parameters['title_length']));
    $values = [
      'name' => $name,
      'description' => 'Description of ' . $name . ' (depth ' . $depth . ')',
      'format' => filter_fallback_format(),
      'weight' => mt_rand(0, 10),
      'vid' => $vid,
      'parent' => [
        $parent,
      ],
    ];
    if (isset($parameters['add_language'])) {
      $values['langcode'] = $this
        ->getLangcode($parameters['add_language']);
    }
    $term = $this->termStorage
      ->create($values);

    // A flag to let hook implementations know that this is a generated term.
    $term->devel_generate = TRUE;

    // Populate all fields with sample values.
    $this
      ->populateFields($term);
    $term
      ->save();

    // Add translations.
    if (isset($parameters['translate_language']) && !empty($parameters['translate_language'])) {
      $info['terms_translations'] += $this
        ->generateTermTranslation($parameters['translate_language'], $term);
    }

    // If the depth of the new term is less than the maximum depth then it can
    // also be saved as a potential parent for the subsequent new terms.
    if ($depth < $max_depth) {
      $all_parents[$vid]['lower_levels'] += [
        $term
          ->id() => $depth,
      ];
    }

    // Store data about the newly generated term.
    $info['terms']++;
    @$info[$vid][$depth]['total']++;

    // List only the first 10 new terms at each vocab/level.
    if (!isset($info[$vid][$depth]['terms']) || count($info[$vid][$depth]['terms']) < 10) {
      $info[$vid][$depth]['terms'][] = $term
        ->label();
    }
    unset($term);
  }
  return $info;
}