Loading core/modules/taxonomy/src/TermForm.php +6 −2 Original line number Diff line number Diff line Loading @@ -23,7 +23,12 @@ public function form(array $form, FormStateInterface $form_state) { $taxonomy_storage = $this->entityTypeManager->getStorage('taxonomy_term'); $vocabulary = $vocab_storage->load($term->bundle()); $parent = array_keys($taxonomy_storage->loadParents($term->id())); $parent = []; // Get the parent directly from the term as // \Drupal\taxonomy\TermStorageInterface::loadParents() excludes the root. foreach ($term->get('parent') as $item) { $parent[] = (int) $item->target_id; } $form_state->set(['taxonomy', 'parent'], $parent); $form_state->set(['taxonomy', 'vocabulary'], $vocabulary); Loading @@ -42,7 +47,6 @@ public function form(array $form, FormStateInterface $form_state) { if (!$this->config('taxonomy.settings')->get('override_selector')) { $exclude = []; if (!$term->isNew()) { $parent = array_keys($taxonomy_storage->loadParents($term->id())); $children = $taxonomy_storage->loadTree($vocabulary->id(), $term->id()); // A term can't be the child of itself, nor of its children. Loading core/modules/taxonomy/tests/src/Functional/TermTest.php +71 −12 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\field\Entity\FieldConfig; use Drupal\taxonomy\Entity\Term; use Drupal\taxonomy\TermInterface; use Drupal\Tests\system\Functional\Menu\AssertBreadcrumbTrait; /** Loading Loading @@ -535,32 +536,90 @@ public function testTermReorder() { * Tests saving a term with multiple parents through the UI. */ public function testTermMultipleParentsInterface() { // Add a new term to the vocabulary so that we can have multiple parents. $parent = $this->createTerm($this->vocabulary); // Add two new terms to the vocabulary so that we can have multiple parents. // These will be terms with tids of 1 and 2 respectively. $this->createTerm($this->vocabulary); $this->createTerm($this->vocabulary); // Add a new term with multiple parents. $edit = [ 'name[0][value]' => $this->randomMachineName(12), 'description[0][value]' => $this->randomMachineName(100), 'parent[]' => [0, $parent->id()], 'parent[]' => [0, 1], ]; // Save the new term. $this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add'); $this->submitForm($edit, 'Save'); // Check that the term was successfully created. $terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([ 'name' => $edit['name[0][value]'], ]); $term = reset($terms); $term = $this->reloadTermByName($edit['name[0][value]']); $this->assertNotNull($term, 'Term found in database.'); $this->assertEquals($edit['name[0][value]'], $term->getName(), 'Term name was successfully saved.'); $this->assertEquals($edit['description[0][value]'], $term->getDescription(), 'Term description was successfully saved.'); // Check that the parent tid is still there. The other parent (<root>) is // not added by \Drupal\taxonomy\TermStorageInterface::loadParents(). $parents = $this->container->get('entity_type.manager')->getStorage('taxonomy_term')->loadParents($term->id()); $parent = reset($parents); $this->assertEquals($edit['parent[]'][1], $parent->id(), 'Term parents were successfully saved.'); // Check that we have the expected parents. $this->assertEquals([0, 1], $this->getParentTids($term), 'Term parents (root plus one) were successfully saved.'); // Load the edit form and save again to ensure parent are preserved. // Generate a new name, so we know that the term really is saved. $this->drupalGet('taxonomy/term/' . $term->id() . '/edit'); $edit = [ 'name[0][value]' => $this->randomMachineName(12), ]; $this->submitForm($edit, 'Save'); $this->submitForm([], 'Save'); // Check that we still have the expected parents. $term = $this->reloadTermByName($edit['name[0][value]']); $this->assertEquals([0, 1], $this->getParentTids($term), 'Term parents (root plus one) were successfully saved again.'); // Save with two real parents. i.e., not including <root>. $this->drupalGet('taxonomy/term/' . $term->id() . '/edit'); $edit = [ 'name[0][value]' => $this->randomMachineName(12), 'parent[]' => [1, 2], ]; $this->submitForm($edit, 'Save'); $this->submitForm([], 'Save'); // Check that we have the expected parents. $term = $this->reloadTermByName($edit['name[0][value]']); $this->assertEquals([1, 2], $this->getParentTids($term), 'Term parents (two real) were successfully saved.'); } /** * Reloads a term by name. * * @param string $name * The name of the term. * * @return \Drupal\taxonomy\TermInterface * The reloaded term. */ private function reloadTermByName(string $name): TermInterface { \Drupal::entityTypeManager()->getStorage('taxonomy_term')->resetCache(); /** @var \Drupal\taxonomy\TermInterface[] $terms */ $terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties(['name' => $name]); return reset($terms); } /** * Get the parent tids for a term including root. * * @param \Drupal\taxonomy\TermInterface $term * The term. * * @return array * A sorted array of tids and 0 if the root is a parent. */ private function getParentTids($term) { $parent_tids = []; foreach ($term->get('parent') as $item) { $parent_tids[] = (int) $item->target_id; } sort($parent_tids); return $parent_tids; } /** Loading Loading
core/modules/taxonomy/src/TermForm.php +6 −2 Original line number Diff line number Diff line Loading @@ -23,7 +23,12 @@ public function form(array $form, FormStateInterface $form_state) { $taxonomy_storage = $this->entityTypeManager->getStorage('taxonomy_term'); $vocabulary = $vocab_storage->load($term->bundle()); $parent = array_keys($taxonomy_storage->loadParents($term->id())); $parent = []; // Get the parent directly from the term as // \Drupal\taxonomy\TermStorageInterface::loadParents() excludes the root. foreach ($term->get('parent') as $item) { $parent[] = (int) $item->target_id; } $form_state->set(['taxonomy', 'parent'], $parent); $form_state->set(['taxonomy', 'vocabulary'], $vocabulary); Loading @@ -42,7 +47,6 @@ public function form(array $form, FormStateInterface $form_state) { if (!$this->config('taxonomy.settings')->get('override_selector')) { $exclude = []; if (!$term->isNew()) { $parent = array_keys($taxonomy_storage->loadParents($term->id())); $children = $taxonomy_storage->loadTree($vocabulary->id(), $term->id()); // A term can't be the child of itself, nor of its children. Loading
core/modules/taxonomy/tests/src/Functional/TermTest.php +71 −12 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\field\Entity\FieldConfig; use Drupal\taxonomy\Entity\Term; use Drupal\taxonomy\TermInterface; use Drupal\Tests\system\Functional\Menu\AssertBreadcrumbTrait; /** Loading Loading @@ -535,32 +536,90 @@ public function testTermReorder() { * Tests saving a term with multiple parents through the UI. */ public function testTermMultipleParentsInterface() { // Add a new term to the vocabulary so that we can have multiple parents. $parent = $this->createTerm($this->vocabulary); // Add two new terms to the vocabulary so that we can have multiple parents. // These will be terms with tids of 1 and 2 respectively. $this->createTerm($this->vocabulary); $this->createTerm($this->vocabulary); // Add a new term with multiple parents. $edit = [ 'name[0][value]' => $this->randomMachineName(12), 'description[0][value]' => $this->randomMachineName(100), 'parent[]' => [0, $parent->id()], 'parent[]' => [0, 1], ]; // Save the new term. $this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary->id() . '/add'); $this->submitForm($edit, 'Save'); // Check that the term was successfully created. $terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([ 'name' => $edit['name[0][value]'], ]); $term = reset($terms); $term = $this->reloadTermByName($edit['name[0][value]']); $this->assertNotNull($term, 'Term found in database.'); $this->assertEquals($edit['name[0][value]'], $term->getName(), 'Term name was successfully saved.'); $this->assertEquals($edit['description[0][value]'], $term->getDescription(), 'Term description was successfully saved.'); // Check that the parent tid is still there. The other parent (<root>) is // not added by \Drupal\taxonomy\TermStorageInterface::loadParents(). $parents = $this->container->get('entity_type.manager')->getStorage('taxonomy_term')->loadParents($term->id()); $parent = reset($parents); $this->assertEquals($edit['parent[]'][1], $parent->id(), 'Term parents were successfully saved.'); // Check that we have the expected parents. $this->assertEquals([0, 1], $this->getParentTids($term), 'Term parents (root plus one) were successfully saved.'); // Load the edit form and save again to ensure parent are preserved. // Generate a new name, so we know that the term really is saved. $this->drupalGet('taxonomy/term/' . $term->id() . '/edit'); $edit = [ 'name[0][value]' => $this->randomMachineName(12), ]; $this->submitForm($edit, 'Save'); $this->submitForm([], 'Save'); // Check that we still have the expected parents. $term = $this->reloadTermByName($edit['name[0][value]']); $this->assertEquals([0, 1], $this->getParentTids($term), 'Term parents (root plus one) were successfully saved again.'); // Save with two real parents. i.e., not including <root>. $this->drupalGet('taxonomy/term/' . $term->id() . '/edit'); $edit = [ 'name[0][value]' => $this->randomMachineName(12), 'parent[]' => [1, 2], ]; $this->submitForm($edit, 'Save'); $this->submitForm([], 'Save'); // Check that we have the expected parents. $term = $this->reloadTermByName($edit['name[0][value]']); $this->assertEquals([1, 2], $this->getParentTids($term), 'Term parents (two real) were successfully saved.'); } /** * Reloads a term by name. * * @param string $name * The name of the term. * * @return \Drupal\taxonomy\TermInterface * The reloaded term. */ private function reloadTermByName(string $name): TermInterface { \Drupal::entityTypeManager()->getStorage('taxonomy_term')->resetCache(); /** @var \Drupal\taxonomy\TermInterface[] $terms */ $terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties(['name' => $name]); return reset($terms); } /** * Get the parent tids for a term including root. * * @param \Drupal\taxonomy\TermInterface $term * The term. * * @return array * A sorted array of tids and 0 if the root is a parent. */ private function getParentTids($term) { $parent_tids = []; foreach ($term->get('parent') as $item) { $parent_tids[] = (int) $item->target_id; } sort($parent_tids); return $parent_tids; } /** Loading