taxonomy_manager-2.0.3/src/TaxonomyManagerHelper.php
src/TaxonomyManagerHelper.php
<?php
namespace Drupal\taxonomy_manager;
use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\language\Entity\ContentLanguageSettings;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Class for taxonomy manager helper.
*/
class TaxonomyManagerHelper {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $taxonomyTypeManager;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* The manages modules.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The entity definition update manager.
*
* @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
*/
protected $entityLastInstalledSchemaRepository;
/**
* Create an TaxonomyManagerHelper object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The manages modules.
* @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entityLastInstalledSchemaRepository
* Entity Last Installed Schema Repository.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, LanguageManagerInterface $language_manager, AccountInterface $current_user, ModuleHandlerInterface $module_handler, EntityLastInstalledSchemaRepositoryInterface $entityLastInstalledSchemaRepository) {
$this->taxonomyTypeManager = $entity_type_manager->getStorage('taxonomy_term');
$this->languageManager = $language_manager;
$this->currentUser = $current_user;
$this->moduleHandler = $module_handler;
$this->entityLastInstalledSchemaRepository = $entityLastInstalledSchemaRepository;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('language_manager'),
$container->get('current_user'),
$container->get('module_handler'),
$container->get('entity.last_installed_schema.repository')
);
}
/**
* Checks if voc has terms.
*
* @param int $vid
* The term ID.
*
* @return bool
* True, if terms already exists, else false
*/
public function vocabularyIsEmpty($vid) {
/** @var \Drupal\taxonomy\TermStorageInterface $term_storage */
$term_storage = $this->taxonomyTypeManager;
$query = $term_storage
->getQuery()
// Skip access check for counting.
->accessCheck(FALSE)
->condition('vid', $vid)
->count();
$count = $query->execute();
return $count == 0;
}
/**
* Helper function for mass adding of terms.
*
* @param string $input
* The textual input with terms. Each line contains a single term. Child
* term can be prefixed with a dash '-' (one dash for each level). Term
* names starting with a dash and should not become a child term need
* to be wrapped in quotes.
* @param int $vid
* The vocabulary id.
* @param int $parents
* An array of parent term ids for the new inserted terms. Can be 0.
* @param array $term_names_too_long
* Return value that is used to indicate that some term names were too long
* and truncated to 255 characters.
* @param bool $keep_order
* Defines whether the term should have the weights corresponding to the
* provided order.
*
* @return array
* An array of the newly inserted term objects
*/
public function massAddTerms($input, $vid, $parents, array &$term_names_too_long = [], $keep_order = FALSE) {
$new_terms = [];
$terms = explode("\n", str_replace("\r", '', $input));
$parents = !empty($parents) ? $parents : 0;
$base_weight = 0;
if ($keep_order) {
$max_weight = self::getMaxWeight($vid, $parents);
$base_weight = $max_weight + 1;
}
// Stores the current lineage of newly inserted terms.
$last_parents = [];
foreach ($terms as $term_index => $name) {
if (empty($name)) {
continue;
}
$matches = [];
// Child term prefixed with one or more dashes.
if (preg_match('/^(-){1,}/', $name, $matches)) {
$depth = strlen($matches[0]);
$name = substr($name, $depth);
$current_parents = isset($last_parents[$depth - 1]) ? [$last_parents[$depth - 1]->id()] : 0;
}
// Parent term containing dashes at the beginning and is therefore wrapped
// in double quotes.
elseif (preg_match('/^\"(-){1,}.*\"/', $name, $matches)) {
$name = substr($name, 1, -1);
$depth = 0;
$current_parents = $parents;
}
else {
$depth = 0;
$current_parents = $parents;
}
// Truncate long string names that will cause database exceptions.
$fieldNameLength = $this->getFieldNameLength();
if (strlen($name) > $fieldNameLength) {
$term_names_too_long[] = $name;
$name = substr($name, 0, $fieldNameLength);
}
$filter_formats = filter_formats();
$format = array_pop($filter_formats);
// Set default language.
$langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
// Check if site is multilingual.
if ($this->moduleHandler->moduleExists('language')) {
$language_configuration = ContentLanguageSettings::loadByEntityTypeBundle('taxonomy_term', $vid);
$lang_setting = $language_configuration->getDefaultLangcode();
if ($lang_setting == 'site_default') {
$langcode = $this->languageManager->getDefaultLanguage()->getId();
}
elseif ($lang_setting == 'current_interface') {
$langcode = $this->languageManager->getCurrentLanguage()->getId();
}
elseif ($lang_setting == 'authors_default') {
$langcode = $this->currentUser->getPreferredLangcode();
}
elseif ($lang_setting == 'und' || $lang_setting == 'zxx') {
$langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
}
else {
// Fixed value set like 'en'.
$langcode = $lang_setting;
}
}
$values = [
'name' => $name,
// @todo do we need to set a format?
'format' => $format->id(),
'vid' => $vid,
'langcode' => $langcode,
];
if ($keep_order) {
$values['weight'] = $base_weight + $term_index;
}
if (!empty($current_parents)) {
foreach ($current_parents as $p) {
$values['parent'][] = ['target_id' => $p];
}
}
$term = $this->taxonomyTypeManager->create($values);
$term->save();
$new_terms[] = $term;
$last_parents[$depth] = $term;
}
return $new_terms;
}
/**
* Helper function that deletes terms.
*
* Optionally orphans (terms where parent get deleted) can be deleted as well.
*
* Difference to core: deletion of orphans optional.
*
* @param array $tids
* Array of term ids to delete.
* @param bool $delete_orphans
* If TRUE, orphans get deleted.
*/
public function deleteTerms(array $tids, $delete_orphans = FALSE) {
$deleted_terms = [];
$remaining_child_terms = [];
while (count($tids) > 0) {
$orphans = [];
foreach ($tids as $tid) {
$children = $this->taxonomyTypeManager->loadChildren($tid);
if (!empty($children)) {
foreach ($children as $child) {
$parents = $this->taxonomyTypeManager->loadParents($child->id());
if ($delete_orphans) {
if (count($parents) == 1) {
$orphans[$child->id()] = $child->id();
}
else {
$remaining_child_terms[$child->id()] = $child->getName();
}
}
else {
$remaining_child_terms[$child->id()] = $child->getName();
if ($parents) {
// Parents structure see TermStorage::updateTermHierarchy.
$parents_array = [];
foreach ($parents as $parent) {
if ($parent->id() != $tid) {
$parent->target_id = $parent->id();
$parents_array[$parent->id()] = $parent;
}
}
if (empty($parents_array)) {
$parents_array = [0];
}
$child->parent = $parents_array;
$this->taxonomyTypeManager->deleteTermHierarchy([$child->id()]);
$this->taxonomyTypeManager->updateTermHierarchy($child);
}
}
}
}
$term = $this->taxonomyTypeManager->load($tid);
if ($term) {
$deleted_terms[] = $term->getName();
$term->delete();
}
}
$tids = $orphans;
}
return ['deleted_terms' => $deleted_terms, 'remaining_child_terms' => $remaining_child_terms];
}
/**
* Get the maximum weight for this vocabulary and these parents.
*
* @param int $vid
* The vocabulary id.
* @param int[] $parents
* An array of parent term ids for the new inserted terms. Can be 0.
*
* @return int
* The max weight.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
protected static function getMaxWeight($vid, $parents) {
$taxonomy_entity_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$vocabulary = $taxonomy_entity_storage->loadTree($vid, 0, 1, FALSE);
if (empty($vocabulary)) {
return 0;
}
// The base weight is the starting weight of the new terms.
$max_weight = 0;
if (!$parents) {
// Sorted by weight, then name, we can pull the last's weight to get max.
$max_weight = (end($vocabulary)->weight);
}
else {
$parent_vocabularies = [];
$parent_max_weights = [];
foreach ($parents as $index => $parent) {
$parent_vocabularies[$index] = $taxonomy_entity_storage->loadTree($vid, $parent, 1, FALSE);
$parent_max_weights[$index] = $parent_vocabularies[$index] ? (end($parent_vocabularies[$index])->weight) : 0;
}
$max_weight = max($parent_max_weights) ?? 0;
}
return $max_weight;
}
/**
* Helper function to retrieve the field name length.
*
* @return int
* The field name length.
*/
public function getFieldNameLength(): int {
$fieldStorageDefinitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions('taxonomy_term');
if (!array_key_exists('name', $fieldStorageDefinitions)) {
return 255;
}
$fieldNameStorageDefinition = $fieldStorageDefinitions['name'];
return $fieldNameStorageDefinition->getSetting('max_length');
}
}
