Issue #2957381 by amateescu, GaëlG, alexpott, douggreen, jibran, catch: Data model problems with Vocabulary hierarchy

8.7.x
Nathaniel Catchpole 2018-11-15 14:42:08 +00:00
parent 9444c6a26a
commit 70e40101ec
23 changed files with 193 additions and 116 deletions

View File

@ -7,5 +7,4 @@ dependencies:
name: Forums name: Forums
vid: forums vid: forums
description: 'Forum navigation vocabulary' description: 'Forum navigation vocabulary'
hierarchy: 1
weight: -10 weight: -10

View File

@ -27,9 +27,6 @@ taxonomy.vocabulary.*:
description: description:
type: label type: label
label: 'Description' label: 'Description'
hierarchy:
type: integer
label: 'Hierarchy'
weight: weight:
type: integer type: integer
label: 'Weight' label: 'Weight'

View File

@ -25,7 +25,6 @@ process:
label: name label: name
name: name name: name
description: description description: description
hierarchy: hierarchy
weight: weight weight: weight
destination: destination:
plugin: entity:taxonomy_vocabulary plugin: entity:taxonomy_vocabulary

View File

@ -23,7 +23,6 @@ process:
label: name label: name
name: name name: name
description: description description: description
hierarchy: hierarchy
weight: weight weight: weight
destination: destination:
plugin: entity:taxonomy_vocabulary plugin: entity:taxonomy_vocabulary

View File

@ -53,7 +53,6 @@ use Drupal\taxonomy\VocabularyInterface;
* "name", * "name",
* "vid", * "vid",
* "description", * "description",
* "hierarchy",
* "weight", * "weight",
* } * }
* ) * )
@ -81,18 +80,6 @@ class Vocabulary extends ConfigEntityBundleBase implements VocabularyInterface {
*/ */
protected $description; protected $description;
/**
* The type of hierarchy allowed within the vocabulary.
*
* Possible values:
* - VocabularyInterface::HIERARCHY_DISABLED: No parents.
* - VocabularyInterface::HIERARCHY_SINGLE: Single parent.
* - VocabularyInterface::HIERARCHY_MULTIPLE: Multiple parents.
*
* @var int
*/
protected $hierarchy = VocabularyInterface::HIERARCHY_DISABLED;
/** /**
* The weight of this vocabulary in relation to other vocabularies. * The weight of this vocabulary in relation to other vocabularies.
* *
@ -104,14 +91,16 @@ class Vocabulary extends ConfigEntityBundleBase implements VocabularyInterface {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getHierarchy() { public function getHierarchy() {
return $this->hierarchy; @trigger_error('\Drupal\taxonomy\VocabularyInterface::getHierarchy() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.x. Use \Drupal\taxonomy\TermStorage::getVocabularyHierarchyType() instead.', E_USER_DEPRECATED);
return $this->entityTypeManager()->getStorage('taxonomy_term')->getVocabularyHierarchyType($this->id());
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setHierarchy($hierarchy) { public function setHierarchy($hierarchy) {
$this->hierarchy = $hierarchy; @trigger_error('\Drupal\taxonomy\VocabularyInterface::setHierarchy() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.x. Reset the cache of the taxonomy_term storage controller instead.', E_USER_DEPRECATED);
$this->entityTypeManager()->getStorage('taxonomy_term')->resetCache();
return $this; return $this;
} }

View File

@ -112,6 +112,7 @@ class OverviewTerms extends FormBase {
global $pager_page_array, $pager_total, $pager_total_items; global $pager_page_array, $pager_total, $pager_total_items;
$form_state->set(['taxonomy', 'vocabulary'], $taxonomy_vocabulary); $form_state->set(['taxonomy', 'vocabulary'], $taxonomy_vocabulary);
$vocabulary_hierarchy = $this->storageController->getVocabularyHierarchyType($taxonomy_vocabulary->id());
$parent_fields = FALSE; $parent_fields = FALSE;
$page = $this->getRequest()->query->get('page') ?: 0; $page = $this->getRequest()->query->get('page') ?: 0;
@ -277,7 +278,7 @@ class OverviewTerms extends FormBase {
'#title' => $term->getName(), '#title' => $term->getName(),
'#url' => $term->urlInfo(), '#url' => $term->urlInfo(),
]; ];
if ($taxonomy_vocabulary->getHierarchy() != VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) { if ($vocabulary_hierarchy != VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) {
$parent_fields = TRUE; $parent_fields = TRUE;
$form['terms'][$key]['term']['tid'] = [ $form['terms'][$key]['term']['tid'] = [
'#type' => 'hidden', '#type' => 'hidden',
@ -384,7 +385,7 @@ class OverviewTerms extends FormBase {
]; ];
} }
if (($taxonomy_vocabulary->getHierarchy() !== VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) && $change_weight_access->isAllowed()) { if (($vocabulary_hierarchy !== VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) && $change_weight_access->isAllowed()) {
$form['actions'] = ['#type' => 'actions', '#tree' => FALSE]; $form['actions'] = ['#type' => 'actions', '#tree' => FALSE];
$form['actions']['submit'] = [ $form['actions']['submit'] = [
'#type' => 'submit', '#type' => 'submit',
@ -425,9 +426,6 @@ class OverviewTerms extends FormBase {
uasort($form_state->getValue('terms'), ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']); uasort($form_state->getValue('terms'), ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
$vocabulary = $form_state->get(['taxonomy', 'vocabulary']); $vocabulary = $form_state->get(['taxonomy', 'vocabulary']);
// Update the current hierarchy type as we go.
$hierarchy = VocabularyInterface::HIERARCHY_DISABLED;
$changed_terms = []; $changed_terms = [];
$tree = $this->storageController->loadTree($vocabulary->id(), 0, NULL, TRUE); $tree = $this->storageController->loadTree($vocabulary->id(), 0, NULL, TRUE);
@ -444,7 +442,6 @@ class OverviewTerms extends FormBase {
$changed_terms[$term->id()] = $term; $changed_terms[$term->id()] = $term;
} }
$weight++; $weight++;
$hierarchy = $term->parents[0] != 0 ? VocabularyInterface::HIERARCHY_SINGLE : $hierarchy;
$term = $tree[$weight]; $term = $tree[$weight];
} }
@ -471,7 +468,6 @@ class OverviewTerms extends FormBase {
$term->parent->target_id = $values['term']['parent']; $term->parent->target_id = $values['term']['parent'];
$changed_terms[$term->id()] = $term; $changed_terms[$term->id()] = $term;
} }
$hierarchy = $term->parents[0] != 0 ? VocabularyInterface::HIERARCHY_SINGLE : $hierarchy;
$weight++; $weight++;
} }
} }
@ -484,7 +480,6 @@ class OverviewTerms extends FormBase {
$term->setWeight($weight); $term->setWeight($weight);
$changed_terms[$term->id()] = $term; $changed_terms[$term->id()] = $term;
} }
$hierarchy = $term->parents[0] != 0 ? VocabularyInterface::HIERARCHY_SINGLE : $hierarchy;
} }
// Save all updated terms. // Save all updated terms.
@ -492,11 +487,6 @@ class OverviewTerms extends FormBase {
$term->save(); $term->save();
} }
// Update the vocabulary hierarchy to flat or single hierarchy.
if ($vocabulary->getHierarchy() != $hierarchy) {
$vocabulary->setHierarchy($hierarchy);
$vocabulary->save();
}
$this->messenger()->addStatus($this->t('The configuration options have been saved.')); $this->messenger()->addStatus($this->t('The configuration options have been saved.'));
} }

View File

@ -2,7 +2,6 @@
namespace Drupal\taxonomy\Form; namespace Drupal\taxonomy\Form;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\ContentEntityDeleteForm; use Drupal\Core\Entity\ContentEntityDeleteForm;
use Drupal\Core\Url; use Drupal\Core\Url;
@ -43,21 +42,4 @@ class TermDeleteForm extends ContentEntityDeleteForm {
return $this->t('Deleted term %name.', ['%name' => $this->entity->label()]); return $this->t('Deleted term %name.', ['%name' => $this->entity->label()]);
} }
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
/** @var \Drupal\Core\Entity\ContentEntityInterface $term */
$term = $this->getEntity();
if ($term->isDefaultTranslation()) {
$storage = $this->entityManager->getStorage('taxonomy_vocabulary');
$vocabulary = $storage->load($this->entity->bundle());
// @todo Move to storage http://drupal.org/node/1988712
taxonomy_check_vocabulary_hierarchy($vocabulary, ['tid' => $term->id()]);
}
}
} }

View File

@ -18,6 +18,7 @@ class TermForm extends ContentEntityForm {
public function form(array $form, FormStateInterface $form_state) { public function form(array $form, FormStateInterface $form_state) {
$term = $this->entity; $term = $this->entity;
$vocab_storage = $this->entityManager->getStorage('taxonomy_vocabulary'); $vocab_storage = $this->entityManager->getStorage('taxonomy_vocabulary');
/** @var \Drupal\taxonomy\TermStorageInterface $taxonomy_storage */
$taxonomy_storage = $this->entityManager->getStorage('taxonomy_term'); $taxonomy_storage = $this->entityManager->getStorage('taxonomy_term');
$vocabulary = $vocab_storage->load($term->bundle()); $vocabulary = $vocab_storage->load($term->bundle());
@ -28,7 +29,7 @@ class TermForm extends ContentEntityForm {
$form['relations'] = [ $form['relations'] = [
'#type' => 'details', '#type' => 'details',
'#title' => $this->t('Relations'), '#title' => $this->t('Relations'),
'#open' => $vocabulary->getHierarchy() == VocabularyInterface::HIERARCHY_MULTIPLE, '#open' => $taxonomy_storage->getVocabularyHierarchyType($vocabulary->id()) == VocabularyInterface::HIERARCHY_MULTIPLE,
'#weight' => 10, '#weight' => 10,
]; ];
@ -142,26 +143,11 @@ class TermForm extends ContentEntityForm {
} }
$current_parent_count = count($form_state->getValue('parent')); $current_parent_count = count($form_state->getValue('parent'));
$previous_parent_count = count($form_state->get(['taxonomy', 'parent']));
// Root doesn't count if it's the only parent. // Root doesn't count if it's the only parent.
if ($current_parent_count == 1 && $form_state->hasValue(['parent', 0])) { if ($current_parent_count == 1 && $form_state->hasValue(['parent', 0])) {
$current_parent_count = 0;
$form_state->setValue('parent', []); $form_state->setValue('parent', []);
} }
// If the number of parents has been reduced to one or none, do a check on the
// parents of every term in the vocabulary value.
$vocabulary = $form_state->get(['taxonomy', 'vocabulary']);
if ($current_parent_count < $previous_parent_count && $current_parent_count < 2) {
taxonomy_check_vocabulary_hierarchy($vocabulary, $form_state->getValues());
}
// If we've increased the number of parents and this is a single or flat
// hierarchy, update the vocabulary immediately.
elseif ($current_parent_count > $previous_parent_count && $vocabulary->getHierarchy() != VocabularyInterface::HIERARCHY_MULTIPLE) {
$vocabulary->setHierarchy($current_parent_count == 1 ? VocabularyInterface::HIERARCHY_SINGLE : VocabularyInterface::HIERARCHY_MULTIPLE);
$vocabulary->save();
}
$form_state->setValue('tid', $term->id()); $form_state->setValue('tid', $term->id());
$form_state->set('tid', $term->id()); $form_state->set('tid', $term->id());
} }

View File

@ -4,6 +4,7 @@ namespace Drupal\taxonomy;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage; use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Entity\Sql\TableMappingInterface;
/** /**
* Defines a Controller class for taxonomy terms. * Defines a Controller class for taxonomy terms.
@ -46,6 +47,19 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
*/ */
protected $ancestors; protected $ancestors;
/**
* The type of hierarchy allowed within a vocabulary.
*
* Possible values:
* - VocabularyInterface::HIERARCHY_DISABLED: No parents.
* - VocabularyInterface::HIERARCHY_SINGLE: Single parent.
* - VocabularyInterface::HIERARCHY_MULTIPLE: Multiple parents.
*
* @var int[]
* An array of one the possible values above, keyed by vocabulary ID.
*/
protected $vocabularyHierarchyType;
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
@ -72,6 +86,7 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
$this->treeParents = []; $this->treeParents = [];
$this->treeTerms = []; $this->treeTerms = [];
$this->trees = []; $this->trees = [];
$this->vocabularyHierarchyType = [];
parent::resetCache($ids); parent::resetCache($ids);
} }
@ -357,13 +372,50 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
return $terms; return $terms;
} }
/**
* {@inheritdoc}
*/
public function getVocabularyHierarchyType($vid) {
// Return early if we already computed this value.
if (isset($this->vocabularyHierarchyType[$vid])) {
return $this->vocabularyHierarchyType[$vid];
}
$parent_field_storage = $this->entityManager->getFieldStorageDefinitions($this->entityTypeId)['parent'];
$table_mapping = $this->getTableMapping();
$target_id_column = $table_mapping->getFieldColumnName($parent_field_storage, 'target_id');
$delta_column = $table_mapping->getFieldColumnName($parent_field_storage, TableMappingInterface::DELTA);
$query = $this->database->select($table_mapping->getFieldTableName('parent'), 'p');
$query->addExpression("MAX($target_id_column)", 'max_parent_id');
$query->addExpression("MAX($delta_column)", 'max_delta');
$query->condition('bundle', $vid);
$result = $query->execute()->fetchAll();
// If all the terms have the same parent, the parent can only be root (0).
if ((int) $result[0]->max_parent_id === 0) {
$this->vocabularyHierarchyType[$vid] = VocabularyInterface::HIERARCHY_DISABLED;
}
// If no term has a delta higher than 0, no term has multiple parents.
elseif ((int) $result[0]->max_delta === 0) {
$this->vocabularyHierarchyType[$vid] = VocabularyInterface::HIERARCHY_SINGLE;
}
else {
$this->vocabularyHierarchyType[$vid] = VocabularyInterface::HIERARCHY_MULTIPLE;
}
return $this->vocabularyHierarchyType[$vid];
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function __sleep() { public function __sleep() {
$vars = parent::__sleep(); $vars = parent::__sleep();
// Do not serialize static cache. // Do not serialize static cache.
unset($vars['ancestors'], $vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees']); unset($vars['ancestors'], $vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees'], $vars['vocabularyHierarchyType']);
return $vars; return $vars;
} }
@ -378,6 +430,7 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
$this->treeParents = []; $this->treeParents = [];
$this->treeTerms = []; $this->treeTerms = [];
$this->trees = []; $this->trees = [];
$this->vocabularyHierarchyType = [];
} }
} }

View File

@ -126,4 +126,19 @@ interface TermStorageInterface extends ContentEntityStorageInterface {
*/ */
public function getNodeTerms(array $nids, array $vocabs = [], $langcode = NULL); public function getNodeTerms(array $nids, array $vocabs = [], $langcode = NULL);
/**
* Returns the hierarchy type for a specific vocabulary ID.
*
* @param string $vid
* Vocabulary ID to retrieve the hierarchy type for.
*
* @return int
* The vocabulary hierarchy.
* Possible values:
* - VocabularyInterface::HIERARCHY_DISABLED: No parents.
* - VocabularyInterface::HIERARCHY_SINGLE: Single parent.
* - VocabularyInterface::HIERARCHY_MULTIPLE: Multiple parents.
*/
public function getVocabularyHierarchyType($vid);
} }

View File

@ -108,4 +108,29 @@ class TermStorageSchema extends SqlContentEntityStorageSchema {
return $schema; return $schema;
} }
/**
* {@inheritdoc}
*/
protected function getDedicatedTableSchema(FieldStorageDefinitionInterface $storage_definition, ContentEntityTypeInterface $entity_type = NULL) {
$dedicated_table_schema = parent::getDedicatedTableSchema($storage_definition, $entity_type);
// Add an index on 'bundle', 'delta' and 'parent_target_id' columns to
// increase the performance of the query from
// \Drupal\taxonomy\TermStorage::getVocabularyHierarchyType().
if ($storage_definition->getName() === 'parent') {
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = $this->storage->getTableMapping();
$dedicated_table_name = $table_mapping->getDedicatedDataTableName($storage_definition);
unset($dedicated_table_schema[$dedicated_table_name]['indexes']['bundle']);
$dedicated_table_schema[$dedicated_table_name]['indexes']['bundle_delta_target_id'] = [
'bundle',
'delta',
$table_mapping->getFieldColumnName($storage_definition, 'target_id'),
];
}
return $dedicated_table_schema;
}
} }

View File

@ -29,6 +29,9 @@ interface VocabularyInterface extends ConfigEntityInterface {
* *
* @return int * @return int
* The vocabulary hierarchy. * The vocabulary hierarchy.
*
* @deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.x. Use
* \Drupal\taxonomy\TermStorage::getVocabularyHierarchyType() instead.
*/ */
public function getHierarchy(); public function getHierarchy();
@ -43,6 +46,9 @@ interface VocabularyInterface extends ConfigEntityInterface {
* - VocabularyInterface::HIERARCHY_MULTIPLE: Multiple parents. * - VocabularyInterface::HIERARCHY_MULTIPLE: Multiple parents.
* *
* @return $this * @return $this
*
* @deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.x. Reset
* the cache of the taxonomy_term storage handler instead.
*/ */
public function setHierarchy($hierarchy); public function setHierarchy($hierarchy);

View File

@ -186,3 +186,12 @@ function taxonomy_update_8601() {
return t('The publishing status field has been added to taxonomy terms.'); return t('The publishing status field has been added to taxonomy terms.');
} }
/**
* Add an index on the 'taxonomy_term__parent' field table.
*/
function taxonomy_update_8701() {
$entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
$storage_definition = $entity_definition_update_manager->getFieldStorageDefinition('parent', 'taxonomy_term');
$entity_definition_update_manager->updateFieldStorageDefinition($storage_definition);
}

View File

@ -81,8 +81,9 @@ function taxonomy_help($route_name, RouteMatchInterface $route_match) {
case 'entity.taxonomy_vocabulary.overview_form': case 'entity.taxonomy_vocabulary.overview_form':
$vocabulary = $route_match->getParameter('taxonomy_vocabulary'); $vocabulary = $route_match->getParameter('taxonomy_vocabulary');
$vocabulary_hierarchy = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->getVocabularyHierarchyType($vocabulary->id());
if (\Drupal::currentUser()->hasPermission('administer taxonomy') || \Drupal::currentUser()->hasPermission('edit terms in ' . $vocabulary->id())) { if (\Drupal::currentUser()->hasPermission('administer taxonomy') || \Drupal::currentUser()->hasPermission('edit terms in ' . $vocabulary->id())) {
switch ($vocabulary->getHierarchy()) { switch ($vocabulary_hierarchy) {
case VocabularyInterface::HIERARCHY_DISABLED: case VocabularyInterface::HIERARCHY_DISABLED:
return '<p>' . t('You can reorganize the terms in %capital_name using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.', ['%capital_name' => Unicode::ucfirst($vocabulary->label()), '%name' => $vocabulary->label()]) . '</p>'; return '<p>' . t('You can reorganize the terms in %capital_name using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.', ['%capital_name' => Unicode::ucfirst($vocabulary->label()), '%name' => $vocabulary->label()]) . '</p>';
case VocabularyInterface::HIERARCHY_SINGLE: case VocabularyInterface::HIERARCHY_SINGLE:
@ -92,7 +93,7 @@ function taxonomy_help($route_name, RouteMatchInterface $route_match) {
} }
} }
else { else {
switch ($vocabulary->getHierarchy()) { switch ($vocabulary_hierarchy) {
case VocabularyInterface::HIERARCHY_DISABLED: case VocabularyInterface::HIERARCHY_DISABLED:
return '<p>' . t('%capital_name contains the following terms.', ['%capital_name' => Unicode::ucfirst($vocabulary->label())]) . '</p>'; return '<p>' . t('%capital_name contains the following terms.', ['%capital_name' => Unicode::ucfirst($vocabulary->label())]) . '</p>';
case VocabularyInterface::HIERARCHY_SINGLE: case VocabularyInterface::HIERARCHY_SINGLE:
@ -156,11 +157,10 @@ function taxonomy_theme() {
} }
/** /**
* Checks and updates the hierarchy flag of a vocabulary. * Checks the hierarchy flag of a vocabulary.
* *
* Checks the current parents of all terms in a vocabulary and updates the * Checks the current parents of all terms in a vocabulary. If no term has
* vocabulary's hierarchy setting to the lowest possible level. If no term * parent terms then the vocabulary will be given a hierarchy of
* has parent terms then the vocabulary will be given a hierarchy of
* VocabularyInterface::HIERARCHY_DISABLED. If any term has a single parent then * VocabularyInterface::HIERARCHY_DISABLED. If any term has a single parent then
* the vocabulary will be given a hierarchy of * the vocabulary will be given a hierarchy of
* VocabularyInterface::HIERARCHY_SINGLE. If any term has multiple parents then * VocabularyInterface::HIERARCHY_SINGLE. If any term has multiple parents then
@ -172,33 +172,15 @@ function taxonomy_theme() {
* @param $changed_term * @param $changed_term
* An array of the term structure that was updated. * An array of the term structure that was updated.
* *
* @return * @return int
* An integer that represents the level of the vocabulary's hierarchy. * An integer that represents the level of the vocabulary's hierarchy.
*
* @deprecated in Drupal 8.7.x. Will be removed before Drupal 9.0.0. Use
* \Drupal\taxonomy\TermStorage::getVocabularyHierarchyType() instead.
*/ */
function taxonomy_check_vocabulary_hierarchy(VocabularyInterface $vocabulary, $changed_term) { function taxonomy_check_vocabulary_hierarchy(VocabularyInterface $vocabulary, $changed_term) {
$tree = \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree($vocabulary->id()); @trigger_error('taxonomy_check_vocabulary_hierarchy() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.x. Use \Drupal\taxonomy\TermStorage::getVocabularyHierarchyType() instead.', E_USER_DEPRECATED);
$hierarchy = VocabularyInterface::HIERARCHY_DISABLED; return \Drupal::entityTypeManager()->getStorage('taxonomy_term')->getVocabularyHierarchyType($vocabulary->id());
foreach ($tree as $term) {
// Update the changed term with the new parent value before comparison.
if ($term->tid == $changed_term['tid']) {
$term = (object) $changed_term;
$term->parents = $term->parent;
}
// Check this term's parent count.
if (count($term->parents) > 1) {
$hierarchy = VocabularyInterface::HIERARCHY_MULTIPLE;
break;
}
elseif (count($term->parents) == 1 && !isset($term->parents[0])) {
$hierarchy = VocabularyInterface::HIERARCHY_SINGLE;
}
}
if ($hierarchy != $vocabulary->getHierarchy()) {
$vocabulary->setHierarchy($hierarchy);
$vocabulary->save();
}
return $hierarchy;
} }
/** /**

View File

@ -125,3 +125,12 @@ function taxonomy_post_update_handle_publishing_status_addition_in_views(&$sandb
return TRUE; return TRUE;
}); });
} }
/**
* Remove the 'hierarchy' property from vocabularies.
*/
function taxonomy_post_update_remove_hierarchy_from_vocabularies(&$sandbox = NULL) {
\Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'taxonomy_vocabulary', function () {
return TRUE;
});
}

View File

@ -54,7 +54,6 @@ abstract class VocabularyResourceTestBase extends EntityResourceTestBase {
'dependencies' => [], 'dependencies' => [],
'name' => 'Llama', 'name' => 'Llama',
'description' => NULL, 'description' => NULL,
'hierarchy' => 0,
'weight' => 0, 'weight' => 0,
]; ];
} }

View File

@ -90,11 +90,11 @@ class TermTest extends TaxonomyTestBase {
$term2 = $this->createTerm($this->vocabulary); $term2 = $this->createTerm($this->vocabulary);
// Get the taxonomy storage. // Get the taxonomy storage.
$taxonomy_storage = $this->container->get('entity.manager')->getStorage('taxonomy_term'); /** @var \Drupal\taxonomy\TermStorageInterface $taxonomy_storage */
$taxonomy_storage = $this->container->get('entity_type.manager')->getStorage('taxonomy_term');
// Check that hierarchy is flat. // Check that hierarchy is flat.
$vocabulary = Vocabulary::load($this->vocabulary->id()); $this->assertEquals(0, $taxonomy_storage->getVocabularyHierarchyType($this->vocabulary->id()), 'Vocabulary is flat.');
$this->assertEqual(0, $vocabulary->getHierarchy(), 'Vocabulary is flat.');
// Edit $term2, setting $term1 as parent. // Edit $term2, setting $term1 as parent.
$edit = []; $edit = [];

View File

@ -0,0 +1,46 @@
<?php
namespace Drupal\Tests\taxonomy\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests that the 'hierarchy' property is removed from vocabularies.
*
* @group taxonomy
* @group Update
* @group legacy
*/
class TaxonomyVocabularyHierarchyUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
public function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8-rc1.filled.standard.php.gz',
];
}
/**
* Tests that the 'hierarchy' property is removed from vocabularies.
*
* @see taxonomy_post_update_remove_hierarchy_from_vocabularies()
* @see taxonomy_update_8701()
*/
public function testTaxonomyUpdateParents() {
$hierarchy = \Drupal::config('taxonomy.vocabulary.test_vocabulary')->get('hierarchy');
$this->assertSame(1, $hierarchy);
// Run updates.
$this->runUpdates();
$hierarchy = \Drupal::config('taxonomy.vocabulary.test_vocabulary')->get('hierarchy');
$this->assertNull($hierarchy);
$database = \Drupal::database();
$this->assertFalse($database->schema()->indexExists('taxonomy_term__parent', 'bundle'));
$this->assertTrue($database->schema()->indexExists('taxonomy_term__parent', 'bundle_delta_target_id'));
}
}

View File

@ -35,13 +35,11 @@ class MigrateTaxonomyVocabularyTest extends MigrateDrupal6TestBase {
$this->assertSame($this->getMigration('d6_taxonomy_vocabulary')->getIdMap()->lookupDestinationId([$j]), [$vocabulary->id()]); $this->assertSame($this->getMigration('d6_taxonomy_vocabulary')->getIdMap()->lookupDestinationId([$j]), [$vocabulary->id()]);
$this->assertSame("vocabulary $j (i=$i)", $vocabulary->label()); $this->assertSame("vocabulary $j (i=$i)", $vocabulary->label());
$this->assertSame("description of vocabulary $j (i=$i)", $vocabulary->getDescription()); $this->assertSame("description of vocabulary $j (i=$i)", $vocabulary->getDescription());
$this->assertSame($i, $vocabulary->getHierarchy());
$this->assertSame(4 + $i, $vocabulary->get('weight')); $this->assertSame(4 + $i, $vocabulary->get('weight'));
} }
$vocabulary = Vocabulary::load('vocabulary_name_much_longer_than'); $vocabulary = Vocabulary::load('vocabulary_name_much_longer_than');
$this->assertSame('vocabulary name much longer than thirty two characters', $vocabulary->label()); $this->assertSame('vocabulary name much longer than thirty two characters', $vocabulary->label());
$this->assertSame('description of vocabulary name much longer than thirty two characters', $vocabulary->getDescription()); $this->assertSame('description of vocabulary name much longer than thirty two characters', $vocabulary->getDescription());
$this->assertSame(3, $vocabulary->getHierarchy());
$this->assertSame(7, $vocabulary->get('weight')); $this->assertSame(7, $vocabulary->get('weight'));
} }

View File

@ -35,18 +35,15 @@ class MigrateTaxonomyVocabularyTest extends MigrateDrupal7TestBase {
* The label the migrated entity should have. * The label the migrated entity should have.
* @param $expected_description * @param $expected_description
* The description the migrated entity should have. * The description the migrated entity should have.
* @param $expected_hierarchy
* The hierarchy setting the migrated entity should have.
* @param $expected_weight * @param $expected_weight
* The weight the migrated entity should have. * The weight the migrated entity should have.
*/ */
protected function assertEntity($id, $expected_label, $expected_description, $expected_hierarchy, $expected_weight) { protected function assertEntity($id, $expected_label, $expected_description, $expected_weight) {
/** @var \Drupal\taxonomy\VocabularyInterface $entity */ /** @var \Drupal\taxonomy\VocabularyInterface $entity */
$entity = Vocabulary::load($id); $entity = Vocabulary::load($id);
$this->assertTrue($entity instanceof VocabularyInterface); $this->assertTrue($entity instanceof VocabularyInterface);
$this->assertIdentical($expected_label, $entity->label()); $this->assertIdentical($expected_label, $entity->label());
$this->assertIdentical($expected_description, $entity->getDescription()); $this->assertIdentical($expected_description, $entity->getDescription());
$this->assertIdentical($expected_hierarchy, $entity->getHierarchy());
$this->assertIdentical($expected_weight, $entity->get('weight')); $this->assertIdentical($expected_weight, $entity->get('weight'));
} }
@ -54,10 +51,10 @@ class MigrateTaxonomyVocabularyTest extends MigrateDrupal7TestBase {
* Tests the Drupal 7 taxonomy vocabularies to Drupal 8 migration. * Tests the Drupal 7 taxonomy vocabularies to Drupal 8 migration.
*/ */
public function testTaxonomyVocabulary() { public function testTaxonomyVocabulary() {
$this->assertEntity('tags', 'Tags', 'Use tags to group articles on similar topics into categories.', VocabularyInterface::HIERARCHY_DISABLED, 0); $this->assertEntity('tags', 'Tags', 'Use tags to group articles on similar topics into categories.', 0);
$this->assertEntity('forums', 'Sujet de discussion', 'Forum navigation vocabulary', VocabularyInterface::HIERARCHY_SINGLE, -10); $this->assertEntity('forums', 'Sujet de discussion', 'Forum navigation vocabulary', -10);
$this->assertEntity('test_vocabulary', 'Test Vocabulary', 'This is the vocabulary description', VocabularyInterface::HIERARCHY_SINGLE, 0); $this->assertEntity('test_vocabulary', 'Test Vocabulary', 'This is the vocabulary description', 0);
$this->assertEntity('vocabulary_name_much_longer_than', 'vocabulary name clearly different than machine name and much longer than thirty two characters', 'description of vocabulary name much longer than thirty two characters', VocabularyInterface::HIERARCHY_SINGLE, 0); $this->assertEntity('vocabulary_name_much_longer_than', 'vocabulary name clearly different than machine name and much longer than thirty two characters', 'description of vocabulary name much longer than thirty two characters', 0);
} }
} }

View File

@ -4,5 +4,4 @@ dependencies: { }
name: 'Recipe category' name: 'Recipe category'
vid: recipe_category vid: recipe_category
description: 'Use this taxonomy to group recipes of the same type together.' description: 'Use this taxonomy to group recipes of the same type together.'
hierarchy: 0
weight: 0 weight: 0

View File

@ -4,5 +4,4 @@ dependencies: { }
name: Tags name: Tags
vid: tags vid: tags
description: 'Use tags to group articles on similar topics into categories.' description: 'Use tags to group articles on similar topics into categories.'
hierarchy: 0
weight: 0 weight: 0

View File

@ -4,5 +4,4 @@ dependencies: { }
name: Tags name: Tags
vid: tags vid: tags
description: 'Use tags to group articles on similar topics into categories.' description: 'Use tags to group articles on similar topics into categories.'
hierarchy: 0
weight: 0 weight: 0