diff --git a/core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php b/core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php index 0a53d9894a..8880652008 100644 --- a/core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php +++ b/core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php @@ -128,4 +128,21 @@ public function defaultValuesFormSubmit(array $element, array &$form, FormStateI return $default_value; } + /** + * Removes the items with a specific target id. + * + * @param int $target_id + * The target id of the item to be removed. + * + * @return $this + * + * @todo Add this to EntityReferenceFieldItemListInterface in Drupal 9.0.x. + * https://www.drupal.org/node/2785673 + */ + public function removeItemsByTargetId($target_id) { + return $this->filter(function ($item) use ($target_id) { + return $item->target_id != $target_id; + }); + } + } diff --git a/core/modules/forum/forum.views.inc b/core/modules/forum/forum.views.inc index 6f2425912b..9a37c25a9f 100644 --- a/core/modules/forum/forum.views.inc +++ b/core/modules/forum/forum.views.inc @@ -74,7 +74,7 @@ function forum_views_data() { 'filter' => [ 'title' => t('Has taxonomy term'), 'id' => 'taxonomy_index_tid', - 'hierarchy table' => 'taxonomy_term_hierarchy', + 'hierarchy table' => 'taxonomy_term__parent', 'numeric' => TRUE, 'skip base' => 'taxonomy_term_data', 'allow empty' => TRUE, diff --git a/core/modules/forum/tests/src/Functional/ForumTest.php b/core/modules/forum/tests/src/Functional/ForumTest.php index 9ba4be7b2a..2bc6a45d84 100644 --- a/core/modules/forum/tests/src/Functional/ForumTest.php +++ b/core/modules/forum/tests/src/Functional/ForumTest.php @@ -433,7 +433,7 @@ public function createForum($type, $parent = 0) { // Verify forum hierarchy. $tid = $term['tid']; - $parent_tid = db_query("SELECT t.parent FROM {taxonomy_term_hierarchy} t WHERE t.tid = :tid", [':tid' => $tid])->fetchField(); + $parent_tid = db_query("SELECT t.parent_target_id FROM {taxonomy_term__parent} t WHERE t.entity_id = :tid", [':tid' => $tid])->fetchField(); $this->assertTrue($parent == $parent_tid, 'The ' . $type . ' is linked to its container'); $forum = $this->container->get('entity.manager')->getStorage('taxonomy_term')->load($tid); diff --git a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php index fbcae8c758..7b81f6fe2d 100644 --- a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php +++ b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php @@ -3,6 +3,7 @@ namespace Drupal\hal\Normalizer; use Drupal\Core\Entity\FieldableEntityInterface; +use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; use Drupal\hal\LinkManager\LinkManagerInterface; use Drupal\serialization\EntityResolver\EntityResolverInterface; use Drupal\serialization\EntityResolver\UuidReferenceInterface; @@ -50,15 +51,15 @@ public function __construct(LinkManagerInterface $link_manager, EntityResolverIn * {@inheritdoc} */ public function normalize($field_item, $format = NULL, array $context = []) { - /** @var $field_item \Drupal\Core\Field\FieldItemInterface */ - $target_entity = $field_item->get('entity')->getValue(); - // If this is not a content entity, let the parent implementation handle it, // only content entities are supported as embedded resources. - if (!($target_entity instanceof FieldableEntityInterface)) { + if (!static::targetEntityIsContentEntity($field_item)) { return parent::normalize($field_item, $format, $context); } + /** @var $field_item \Drupal\Core\Field\FieldItemInterface */ + $target_entity = $field_item->get('entity')->getValue(); + // If the parent entity passed in a langcode, unset it before normalizing // the target entity. Otherwise, untranslatable fields of the target entity // will include the langcode. @@ -91,6 +92,31 @@ public function normalize($field_item, $format = NULL, array $context = []) { ]; } + /** + * Checks whether the referenced entity is of a content entity type. + * + * @param \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $ref + * The reference field item whose target entity needs to be checked. + * + * @return bool + * TRUE when the referenced entity is of a content entity type. + * + * @todo Add logic for the case where the target entity type is not the same + * as the referencing entity's entity type. This will require looking up + * that entity type's class via EntityTypeManager. Issue: + */ + protected static function targetEntityIsContentEntity(EntityReferenceItem $ref) { + $target_entity = $ref->get('entity')->getValue(); + if ($target_entity !== NULL) { + return $target_entity instanceof FieldableEntityInterface; + } + else { + $referencing_entity = $ref->getEntity(); + $target_entity_type_id = $ref->getFieldDefinition()->getSettings()['target_type']; + return $target_entity_type_id === $referencing_entity->getEntityTypeId() && $referencing_entity instanceof FieldableEntityInterface; + } + } + /** * {@inheritdoc} */ diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php index 73a1549e0a..58dbf3b6c1 100644 --- a/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php +++ b/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\hal\Functional\EntityResource\Term; +use Drupal\taxonomy\Entity\Term; use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait; use Drupal\Tests\rest\Functional\AnonResourceTestTrait; use Drupal\Tests\rest\Functional\EntityResource\Term\TermResourceTestBase; @@ -37,6 +38,12 @@ protected function getExpectedNormalizedEntity() { $normalization = $this->applyHalFieldNormalization($default_normalization); + // The parent term is either 0 () or 2. + // @see ::createEntity() + // @see ::testGet() + // @see ::testGetTermWithParent() + $parent_term_id = (int) $this->entity->get('parent')->target_id; + return $normalization + [ '_links' => [ 'self' => [ @@ -45,6 +52,32 @@ protected function getExpectedNormalizedEntity() { 'type' => [ 'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids', ], + $this->baseUrl . '/rest/relation/taxonomy_term/camelids/parent' => [ + $parent_term_id === 0 + ? NULL + : [ + 'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json', + ], + ], + ], + '_embedded' => [ + $this->baseUrl . '/rest/relation/taxonomy_term/camelids/parent' => [ + $parent_term_id === 0 + ? NULL + : [ + '_links' => [ + 'self' => [ + 'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json', + ], + 'type' => [ + 'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids', + ], + ], + 'uuid' => [ + ['value' => Term::load($parent_term_id)->uuid()], + ], + ], + ], ], ]; } diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php index e0a15813a8..08e5f82d28 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php @@ -90,6 +90,12 @@ protected function createEntity() { * {@inheritdoc} */ protected function getExpectedNormalizedEntity() { + // The parent term is either 0 () or 2. + // @see ::createEntity() + // @see ::testGet() + // @see ::testGetTermWithParent() + $parent_term_id = (int) $this->entity->get('parent')->target_id; + return [ 'tid' => [ ['value' => 1], @@ -113,7 +119,18 @@ protected function getExpectedNormalizedEntity() { 'format' => NULL, ], ], - 'parent' => [], + 'parent' => [ + $parent_term_id === 0 + ? [ + 'target_id' => 0, + ] + : [ + 'target_id' => $parent_term_id, + 'target_type' => 'taxonomy_term', + 'target_uuid' => Term::load($parent_term_id)->uuid(), + 'url' => base_path() . 'taxonomy/term/' . $parent_term_id, + ] + ], 'weight' => [ ['value' => 0], ], @@ -230,4 +247,30 @@ public function testPatchPath() { $this->assertSame($normalization['path'], $updated_normalization['path']); } + /** + * Tests GETting a term with a parent term other than the default (0). + * + * @see ::getExpectedNormalizedEntity() + */ + public function testGetTermWithParent() { + // Create parent term and update the entity under test to use it. + Term::create(['vid' => Vocabulary::load('camelids')->id()]) + ->setName('Lamoids') + ->save(); + $this->entity->set('parent', 2)->save(); + + $this->initAuthentication(); + $url = $this->getEntityResourceUrl(); + $url->setOption('query', ['_format' => static::$format]); + $request_options = $this->getAuthenticationRequestOptions('GET'); + $this->provisionEntityResource(); + $this->setUpAuthorization('GET'); + $response = $this->request('GET', $url, $request_options); + $expected = $this->getExpectedNormalizedEntity(); + static::recursiveKSort($expected); + $actual = $this->serializer->decode((string) $response->getBody(), static::$format); + static::recursiveKSort($actual); + $this->assertSame($expected, $actual); + } + } diff --git a/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php new file mode 100644 index 0000000000..893cfa3723 --- /dev/null +++ b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php @@ -0,0 +1,62 @@ +insert('config') + ->fields(['collection', 'name', 'data']) + ->values([ + 'collection' => '', + 'name' => "views.view.test_taxonomy_parent", + 'data' => serialize($view_config), + ]) + ->execute(); + +$uuid = \Drupal::service('uuid'); + +// The root tid. +$tids = [0]; + +for ($i = 0; $i < 4; $i++) { + $name = $this->randomString(); + + $tid = $connection->insert('taxonomy_term_data') + ->fields(['vid', 'uuid', 'langcode']) + ->values(['vid' => 'tags', 'uuid' => $uuid->generate(), 'langcode' => 'en']) + ->execute(); + + $connection->insert('taxonomy_term_field_data') + ->fields(['tid', 'vid', 'langcode', 'name', 'weight', 'changed', 'default_langcode']) + ->values(['tid' => $tid, 'vid' => 'tags', 'langcode' => 'en', 'name' => $name, 'weight' => 0, 'changed' => REQUEST_TIME, 'default_langcode' => 1]) + ->execute(); + + $tids[] = $tid; +} + +$hierarchy = [ + // This reads as: tid with index 1 has tids with index 2 and 3 as parents. + 1 => [2, 3], + 2 => [3, 0], + 3 => [0], +]; + +$query = $connection->insert('taxonomy_term_hierarchy')->fields(['tid', 'parent']); + +foreach ($hierarchy as $tid => $parents) { + foreach ($parents as $parent) { + $query->values(['tid' => $tids[$tid], 'parent' => $tids[$parent]]); + } +} + +$query->execute(); diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.yml similarity index 97% copy from core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml copy to core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.yml index 256f618991..65df6a80e8 100644 --- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml +++ b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.yml @@ -124,8 +124,8 @@ display: relationships: parent: id: parent - table: taxonomy_term_hierarchy - field: parent + table: taxonomy_term__parent + field: parent_target_id relationship: none group_type: group admin_label: Parent diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php index 2e70a1cf7d..c86466775c 100644 --- a/core/modules/taxonomy/src/Entity/Term.php +++ b/core/modules/taxonomy/src/Entity/Term.php @@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\taxonomy\TermHierarchyInterface; use Drupal\taxonomy\TermInterface; /** @@ -52,10 +53,17 @@ * permission_granularity = "bundle" * ) */ -class Term extends ContentEntityBase implements TermInterface { +class Term extends ContentEntityBase implements TermInterface, TermHierarchyInterface { use EntityChangedTrait; + /** + * Array of all loaded term ancestry keyed by ancestor term ID. + * + * @var \Drupal\taxonomy\TermInterface[] + */ + protected $ancestors; + /** * {@inheritdoc} */ @@ -64,22 +72,26 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti // See if any of the term's children are about to be become orphans. $orphans = []; - foreach (array_keys($entities) as $tid) { - if ($children = $storage->loadChildren($tid)) { + /** @var \Drupal\taxonomy\TermInterface $term */ + foreach ($entities as $tid => $term) { + if ($children = $term->getChildren()) { + /** @var \Drupal\taxonomy\TermInterface $child */ foreach ($children as $child) { + $parent = $child->get('parent'); + // Update child parents item list. + $parent->removeItemsByTargetId($tid); + // If the term has multiple parents, we don't delete it. - $parents = $storage->loadParents($child->id()); - if (empty($parents)) { + if ($parent->count()) { + $child->save(); + } + else { $orphans[] = $child; } } } } - // Delete term hierarchy information after looking up orphans but before - // deleting them so that their children/parent information is consistent. - $storage->deleteTermHierarchy(array_keys($entities)); - if (!empty($orphans)) { $storage->delete($orphans); } @@ -88,14 +100,11 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti /** * {@inheritdoc} */ - public function postSave(EntityStorageInterface $storage, $update = TRUE) { - parent::postSave($storage, $update); - - // Only change the parents if a value is set, keep the existing values if - // not. - if (isset($this->parent->target_id)) { - $storage->deleteTermHierarchy([$this->id()]); - $storage->updateTermHierarchy($this); + public function preSave(EntityStorageInterface $storage) { + parent::preSave($storage); + // Terms with no parents are mandatory children of . + if (!$this->get('parent')->count()) { + $this->parent->target_id = static::ROOT_ID; } } @@ -156,8 +165,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setLabel(t('Term Parents')) ->setDescription(t('The parents of this term.')) ->setSetting('target_type', 'taxonomy_term') - ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED) - ->setCustomStorage(TRUE); + ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED); $fields['changed'] = BaseFieldDefinition::create('changed') ->setLabel(t('Changed')) @@ -248,4 +256,64 @@ protected function getFieldsToSkipFromTranslationChangesCheck() { return $fields; } + /** + * {@inheritdoc} + */ + public function getParents() { + $parents = $ids = []; + // Cannot use $this->get('parent')->referencedEntities() here because that + // strips out the '0' reference. + foreach ($this->get('parent') as $item) { + if ($item->target_id == static::ROOT_ID) { + // The parent. + $parents[0] = NULL; + continue; + } + $ids[] = $item->target_id; + } + + // @todo Better way to do this? AND handle the NULL/0 parent? + // Querying the terms again so that the same access checks are run when + // getParents() is called as in Drupal version prior to 8.3. + $loaded_parents = []; + + if ($ids) { + $query = \Drupal::entityQuery('taxonomy_term') + ->condition('tid', $ids, 'IN'); + + $loaded_parents = static::loadMultiple($query->execute()); + } + + return $parents + $loaded_parents; + } + + /** + * {@inheritdoc} + */ + public function getAncestors() { + if (!isset($this->ancestors)) { + $this->ancestors = [$this->id() => $this]; + $search[] = $this->id(); + + while ($tid = array_shift($search)) { + foreach (static::load($tid)->getParents() as $id => $parent) { + if ($parent && !isset($this->ancestors[$id])) { + $this->ancestors[$id] = $parent; + $search[] = $id; + } + } + } + } + return $this->ancestors; + } + + /** + * {@inheritdoc} + */ + public function getChildren() { + $query = \Drupal::entityQuery('taxonomy_term') + ->condition('parent', $this->id()); + return static::loadMultiple($query->execute()); + } + } diff --git a/core/modules/taxonomy/src/Entity/Vocabulary.php b/core/modules/taxonomy/src/Entity/Vocabulary.php index d61294b4e3..e61fa731ab 100644 --- a/core/modules/taxonomy/src/Entity/Vocabulary.php +++ b/core/modules/taxonomy/src/Entity/Vocabulary.php @@ -4,6 +4,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBundleBase; use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\taxonomy\TermInterface; use Drupal\taxonomy\VocabularyInterface; /** diff --git a/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php index 37abdd565e..77da59b74e 100644 --- a/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php +++ b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php @@ -111,18 +111,19 @@ public function query($group_by = FALSE) { $last = "tn"; if ($this->options['depth'] > 0) { - $subquery->leftJoin('taxonomy_term_hierarchy', 'th', "th.tid = tn.tid"); + $subquery->leftJoin('taxonomy_term__parent', 'th', "th.entity_id = tn.tid"); $last = "th"; foreach (range(1, abs($this->options['depth'])) as $count) { - $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.parent = th$count.tid"); - $where->condition("th$count.tid", $tids, $operator); + $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.parent_target_id = th$count.entity_id"); + $where->condition("th$count.entity_id", $tids, $operator); $last = "th$count"; } } elseif ($this->options['depth'] < 0) { foreach (range(1, abs($this->options['depth'])) as $count) { - $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.tid = th$count.parent"); - $where->condition("th$count.tid", $tids, $operator); + $field = $count == 1 ? 'tid' : 'entity_id'; + $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.$field = th$count.parent_target_id"); + $where->condition("th$count.entity_id", $tids, $operator); $last = "th$count"; } } diff --git a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php index 802786e467..ebb5b51f22 100644 --- a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php +++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php @@ -77,18 +77,19 @@ public function query() { $last = "tn"; if ($this->options['depth'] > 0) { - $subquery->leftJoin('taxonomy_term_hierarchy', 'th', "th.tid = tn.tid"); + $subquery->leftJoin('taxonomy_term__parent', 'th', "th.entity_id = tn.tid"); $last = "th"; foreach (range(1, abs($this->options['depth'])) as $count) { - $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.parent = th$count.tid"); - $where->condition("th$count.tid", $this->value, $operator); + $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.parent_target_id = th$count.entity_id"); + $where->condition("th$count.entity_id", $this->value, $operator); $last = "th$count"; } } elseif ($this->options['depth'] < 0) { foreach (range(1, abs($this->options['depth'])) as $count) { - $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.tid = th$count.parent"); - $where->condition("th$count.tid", $this->value, $operator); + $field = $count == 1 ? 'tid' : 'entity_id'; + $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.$field = th$count.parent_target_id"); + $where->condition("th$count.entity_id", $this->value, $operator); $last = "th$count"; } } diff --git a/core/modules/taxonomy/src/TermHierarchyInterface.php b/core/modules/taxonomy/src/TermHierarchyInterface.php new file mode 100644 index 0000000000..9c465463a2 --- /dev/null +++ b/core/modules/taxonomy/src/TermHierarchyInterface.php @@ -0,0 +1,39 @@ + parent, that item is keyed with ROOT_ID and will have NULL as + * value. + */ + public function getParents(); + + /** + * Returns all ancestors of this term. + * + * @return \Drupal\taxonomy\TermInterface[] + * A list of ancestor taxonomy term entities keyed by term ID. + */ + public function getAncestors(); + + /** + * Returns all children terms of this term. + * + * @return \Drupal\taxonomy\TermInterface[] + * A list of children taxonomy term entities keyed by term ID. + */ + public function getChildren(); + +} diff --git a/core/modules/taxonomy/src/TermInterface.php b/core/modules/taxonomy/src/TermInterface.php index 3087a56ecb..9120a61906 100644 --- a/core/modules/taxonomy/src/TermInterface.php +++ b/core/modules/taxonomy/src/TermInterface.php @@ -7,9 +7,17 @@ /** * Provides an interface defining a taxonomy term entity. + * + * @todo Merge here \Drupal\taxonomy\TermHierarchyInterface in Drupal 9.0.x. + * https://www.drupal.org/node/2785685 */ interface TermInterface extends ContentEntityInterface, EntityChangedInterface { + /** + * Root ID for terms with no parent. + */ + const ROOT_ID = 0; + /** * Gets the term's description. * diff --git a/core/modules/taxonomy/src/TermStorage.php b/core/modules/taxonomy/src/TermStorage.php index 0a00c13de5..113286bc36 100644 --- a/core/modules/taxonomy/src/TermStorage.php +++ b/core/modules/taxonomy/src/TermStorage.php @@ -2,35 +2,14 @@ namespace Drupal\taxonomy; -use Drupal\Core\Entity\Sql\SqlContentEntityStorage; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\Sql\SqlContentEntityStorage; /** * Defines a Controller class for taxonomy terms. */ class TermStorage extends SqlContentEntityStorage implements TermStorageInterface { - /** - * Array of loaded parents keyed by child term ID. - * - * @var array - */ - protected $parents = []; - - /** - * Array of all loaded term ancestry keyed by ancestor term ID. - * - * @var array - */ - protected $parentsAll = []; - - /** - * Array of child terms keyed by parent term ID. - * - * @var array - */ - protected $children = []; - /** * Array of term parents keyed by vocabulary ID and child term ID. * @@ -80,9 +59,6 @@ public function create(array $values = []) { */ public function resetCache(array $ids = NULL) { drupal_static_reset('taxonomy_term_count_nodes'); - $this->parents = []; - $this->parentsAll = []; - $this->children = []; $this->treeChildren = []; $this->treeParents = []; $this->treeTerms = []; @@ -93,100 +69,46 @@ public function resetCache(array $ids = NULL) { /** * {@inheritdoc} */ - public function deleteTermHierarchy($tids) { - $this->database->delete('taxonomy_term_hierarchy') - ->condition('tid', $tids, 'IN') - ->execute(); - } + public function deleteTermHierarchy($tids) { } /** * {@inheritdoc} */ - public function updateTermHierarchy(EntityInterface $term) { - $query = $this->database->insert('taxonomy_term_hierarchy') - ->fields(['tid', 'parent']); - - foreach ($term->parent as $parent) { - $query->values([ - 'tid' => $term->id(), - 'parent' => (int) $parent->target_id, - ]); - } - $query->execute(); - } + public function updateTermHierarchy(EntityInterface $term) { } /** * {@inheritdoc} */ public function loadParents($tid) { - if (!isset($this->parents[$tid])) { - $parents = []; - $query = $this->database->select('taxonomy_term_field_data', 't'); - $query->join('taxonomy_term_hierarchy', 'h', 'h.parent = t.tid'); - $query->addField('t', 'tid'); - $query->condition('h.tid', $tid); - $query->condition('t.default_langcode', 1); - $query->addTag('taxonomy_term_access'); - $query->orderBy('t.weight'); - $query->orderBy('t.name'); - if ($ids = $query->execute()->fetchCol()) { - $parents = $this->loadMultiple($ids); + $terms = []; + /** @var \Drupal\taxonomy\TermInterface $term */ + if ($tid && $term = $this->load($tid)) { + foreach ($term->getParents() as $id => $parent) { + // This method currently doesn't return the parent. + // @see https://www.drupal.org/node/2019905 + if (!empty($id)) { + $terms[$id] = $parent; + } } - $this->parents[$tid] = $parents; } - return $this->parents[$tid]; + + return $terms; } /** * {@inheritdoc} */ public function loadAllParents($tid) { - if (!isset($this->parentsAll[$tid])) { - $parents = []; - if ($term = $this->load($tid)) { - $parents[$term->id()] = $term; - $terms_to_search[] = $term->id(); - - while ($tid = array_shift($terms_to_search)) { - if ($new_parents = $this->loadParents($tid)) { - foreach ($new_parents as $new_parent) { - if (!isset($parents[$new_parent->id()])) { - $parents[$new_parent->id()] = $new_parent; - $terms_to_search[] = $new_parent->id(); - } - } - } - } - } - - $this->parentsAll[$tid] = $parents; - } - return $this->parentsAll[$tid]; + /** @var \Drupal\taxonomy\TermInterface $term */ + return (!empty($tid) && $term = $this->load($tid)) ? $term->getAncestors() : []; } /** * {@inheritdoc} */ public function loadChildren($tid, $vid = NULL) { - if (!isset($this->children[$tid])) { - $children = []; - $query = $this->database->select('taxonomy_term_field_data', 't'); - $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid'); - $query->addField('t', 'tid'); - $query->condition('h.parent', $tid); - if ($vid) { - $query->condition('t.vid', $vid); - } - $query->condition('t.default_langcode', 1); - $query->addTag('taxonomy_term_access'); - $query->orderBy('t.weight'); - $query->orderBy('t.name'); - if ($ids = $query->execute()->fetchCol()) { - $children = $this->loadMultiple($ids); - } - $this->children[$tid] = $children; - } - return $this->children[$tid]; + /** @var \Drupal\taxonomy\TermInterface $term */ + return (!empty($tid) && $term = $this->load($tid)) ? $term->getChildren() : []; } /** @@ -202,11 +124,11 @@ public function loadTree($vid, $parent = 0, $max_depth = NULL, $load_entities = $this->treeParents[$vid] = []; $this->treeTerms[$vid] = []; $query = $this->database->select('taxonomy_term_field_data', 't'); - $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid'); + $query->join('taxonomy_term__parent', 'p', 't.tid = p.entity_id'); + $query->addExpression('parent_target_id', 'parent'); $result = $query ->addTag('taxonomy_term_access') ->fields('t') - ->fields('h', ['parent']) ->condition('t.vid', $vid) ->condition('t.default_langcode', 1) ->orderBy('t.weight') @@ -254,7 +176,9 @@ public function loadTree($vid, $parent = 0, $max_depth = NULL, $load_entities = $term = clone $term; } $term->depth = $depth; - unset($term->parent); + if (!$load_entities) { + unset($term->parent); + } $tid = $load_entities ? $term->id() : $term->tid; $term->parents = $this->treeParents[$vid][$tid]; $tree[] = $term; @@ -351,7 +275,7 @@ public function getNodeTerms(array $nids, array $vocabs = [], $langcode = NULL) public function __sleep() { $vars = parent::__sleep(); // Do not serialize static cache. - unset($vars['parents'], $vars['parentsAll'], $vars['children'], $vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees']); + unset($vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees']); return $vars; } @@ -361,9 +285,6 @@ public function __sleep() { public function __wakeup() { parent::__wakeup(); // Initialize static caches. - $this->parents = []; - $this->parentsAll = []; - $this->children = []; $this->treeChildren = []; $this->treeParents = []; $this->treeTerms = []; diff --git a/core/modules/taxonomy/src/TermStorageInterface.php b/core/modules/taxonomy/src/TermStorageInterface.php index 4ab2d2deaf..227919d85b 100644 --- a/core/modules/taxonomy/src/TermStorageInterface.php +++ b/core/modules/taxonomy/src/TermStorageInterface.php @@ -15,6 +15,10 @@ * * @param array $tids * Array of terms that need to be removed from hierarchy. + * + * @todo Remove this method in Drupal 9.0.x. Now the parent references are + * automatically cleared when deleting a taxonomy term. + * https://www.drupal.org/node/2785693 */ public function deleteTermHierarchy($tids); @@ -23,6 +27,10 @@ public function deleteTermHierarchy($tids); * * @param \Drupal\Core\Entity\EntityInterface $term * Term entity that needs to be added to term hierarchy information. + * + * @todo remove this method Drupal 9.0.x. Now the parent references are + * automatically updates when when a taxonomy term is added/updated. + * https://www.drupal.org/node/2785693 */ public function updateTermHierarchy(EntityInterface $term); @@ -34,6 +42,9 @@ public function updateTermHierarchy(EntityInterface $term); * * @return \Drupal\taxonomy\TermInterface[] * An array of term objects which are the parents of the term $tid. + * + * @deprecated in Drupal 8.3.x, will be removed in Drupal 9.0.0. Use + * \Drupal\taxonomy\Entity\Term::load($tid)->getParents() instead. */ public function loadParents($tid); @@ -45,6 +56,9 @@ public function loadParents($tid); * * @return \Drupal\taxonomy\TermInterface[] * An array of term objects which are the ancestors of the term $tid. + * + * @deprecated in Drupal 8.3.x, will be removed in Drupal 9.0.0. Use + * \Drupal\taxonomy\Entity\Term::load($tid)->getAncestors() instead. */ public function loadAllParents($tid); @@ -58,6 +72,9 @@ public function loadAllParents($tid); * * @return \Drupal\taxonomy\TermInterface[] * An array of term objects that are the children of the term $tid. + * + * @deprecated in Drupal 8.3.x, will be removed in Drupal 9.0.0. Use + * \Drupal\taxonomy\Entity\Term::load($tid)->getChildren($vid) instead. */ public function loadChildren($tid, $vid = NULL); diff --git a/core/modules/taxonomy/src/TermStorageSchema.php b/core/modules/taxonomy/src/TermStorageSchema.php index 5bcb088db4..2b49ee247b 100644 --- a/core/modules/taxonomy/src/TermStorageSchema.php +++ b/core/modules/taxonomy/src/TermStorageSchema.php @@ -22,36 +22,6 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res 'taxonomy_term__vid_name' => ['vid', 'name'], ]; - $schema['taxonomy_term_hierarchy'] = [ - 'description' => 'Stores the hierarchical relationship between terms.', - 'fields' => [ - 'tid' => [ - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Primary Key: The {taxonomy_term_data}.tid of the term.', - ], - 'parent' => [ - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => "Primary Key: The {taxonomy_term_data}.tid of the term's parent. 0 indicates no parent.", - ], - ], - 'indexes' => [ - 'parent' => ['parent'], - ], - 'foreign keys' => [ - 'taxonomy_term_data' => [ - 'table' => 'taxonomy_term_data', - 'columns' => ['tid' => 'tid'], - ], - ], - 'primary key' => ['tid', 'parent'], - ]; - $schema['taxonomy_index'] = [ 'description' => 'Maintains denormalized information about node/term relationships.', 'fields' => [ diff --git a/core/modules/taxonomy/src/TermViewsData.php b/core/modules/taxonomy/src/TermViewsData.php index 5e98bfe9b2..6d47f3ffb6 100644 --- a/core/modules/taxonomy/src/TermViewsData.php +++ b/core/modules/taxonomy/src/TermViewsData.php @@ -36,7 +36,7 @@ public function getViewsData() { $data['taxonomy_term_field_data']['tid']['filter']['id'] = 'taxonomy_index_tid'; $data['taxonomy_term_field_data']['tid']['filter']['title'] = $this->t('Term'); $data['taxonomy_term_field_data']['tid']['filter']['help'] = $this->t('Taxonomy term chosen from autocomplete or select widget.'); - $data['taxonomy_term_field_data']['tid']['filter']['hierarchy table'] = 'taxonomy_term_hierarchy'; + $data['taxonomy_term_field_data']['tid']['filter']['hierarchy table'] = 'taxonomy_term__parent'; $data['taxonomy_term_field_data']['tid']['filter']['numeric'] = TRUE; $data['taxonomy_term_field_data']['tid_raw'] = [ @@ -146,8 +146,8 @@ public function getViewsData() { 'left_field' => 'nid', 'field' => 'nid', ], - 'taxonomy_term_hierarchy' => [ - 'left_field' => 'tid', + 'taxonomy_term__parent' => [ + 'left_field' => 'entity_id', 'field' => 'tid', ], ]; @@ -181,7 +181,7 @@ public function getViewsData() { 'filter' => [ 'title' => $this->t('Has taxonomy term'), 'id' => 'taxonomy_index_tid', - 'hierarchy table' => 'taxonomy_term_hierarchy', + 'hierarchy table' => 'taxonomy_term__parent', 'numeric' => TRUE, 'skip base' => 'taxonomy_term_field_data', 'allow empty' => TRUE, @@ -223,40 +223,15 @@ public function getViewsData() { ], ]; - $data['taxonomy_term_hierarchy']['table']['group'] = $this->t('Taxonomy term'); - $data['taxonomy_term_hierarchy']['table']['provider'] = 'taxonomy'; - - $data['taxonomy_term_hierarchy']['table']['join'] = [ - 'taxonomy_term_hierarchy' => [ - // Link to self through left.parent = right.tid (going down in depth). - 'left_field' => 'tid', - 'field' => 'parent', - ], - 'taxonomy_term_field_data' => [ - // Link directly to taxonomy_term_field_data via tid. - 'left_field' => 'tid', - 'field' => 'tid', - ], + // Link to self through left.parent = right.tid (going down in depth). + $data['taxonomy_term__parent']['table']['join']['taxonomy_term__parent'] = [ + 'left_field' => 'entity_id', + 'field' => 'parent_target_id', ]; - $data['taxonomy_term_hierarchy']['parent'] = [ - 'title' => $this->t('Parent term'), - 'help' => $this->t('The parent term of the term. This can produce duplicate entries if you are using a vocabulary that allows multiple parents.'), - 'relationship' => [ - 'base' => 'taxonomy_term_field_data', - 'field' => 'parent', - 'label' => $this->t('Parent'), - 'id' => 'standard', - ], - 'filter' => [ - 'help' => $this->t('Filter the results of "Taxonomy: Term" by the parent pid.'), - 'id' => 'numeric', - ], - 'argument' => [ - 'help' => $this->t('The parent term of the term.'), - 'id' => 'taxonomy', - ], - ]; + $data['taxonomy_term__parent']['parent_target_id']['help'] = $this->t('The parent term of the term. This can produce duplicate entries if you are using a vocabulary that allows multiple parents.'); + $data['taxonomy_term__parent']['parent_target_id']['relationship']['label'] = $this->t('Parent'); + $data['taxonomy_term__parent']['parent_target_id']['argument']['id'] = 'taxonomy'; return $data; } diff --git a/core/modules/taxonomy/src/VocabularyStorage.php b/core/modules/taxonomy/src/VocabularyStorage.php index bdbf8938f7..0538e71b49 100644 --- a/core/modules/taxonomy/src/VocabularyStorage.php +++ b/core/modules/taxonomy/src/VocabularyStorage.php @@ -3,6 +3,7 @@ namespace Drupal\taxonomy; use Drupal\Core\Config\Entity\ConfigEntityStorage; +use Drupal\taxonomy\Entity\Vocabulary; /** * Defines a storage handler class for taxonomy vocabularies. @@ -21,7 +22,12 @@ public function resetCache(array $ids = NULL) { * {@inheritdoc} */ public function getToplevelTids($vids) { - return db_query('SELECT t.tid FROM {taxonomy_term_data} t INNER JOIN {taxonomy_term_hierarchy} th ON th.tid = t.tid WHERE t.vid IN ( :vids[] ) AND th.parent = 0', [':vids[]' => $vids])->fetchCol(); + $tids = \Drupal::entityQuery('taxonomy_term') + ->condition('vid', $vids, 'IN') + ->condition('parent.target_id', TermInterface::ROOT_ID) + ->execute(); + + return array_values($tids); } } diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install new file mode 100644 index 0000000000..e1a2c45dcf --- /dev/null +++ b/core/modules/taxonomy/taxonomy.install @@ -0,0 +1,134 @@ +getFieldStorageDefinition('parent', 'taxonomy_term'); + $definition->setCustomStorage(FALSE); + $manager->updateFieldStorageDefinition($definition); + // Update the entity type because a new interface was added to Term entity. + $manager->updateEntityType($manager->getEntityType('taxonomy_term')); +} + +/** + * Copy hierarchy from {taxonomy_term_hierarchy} to {taxonomy_term__parent}. + */ +function taxonomy_update_8502(&$sandbox) { + $database = \Drupal::database(); + + if (!isset($sandbox['current'])) { + // Set batch ops sandbox. + $sandbox['current'] = 0; + $sandbox['max'] = $database->select('taxonomy_term_hierarchy') + ->countQuery() + ->execute() + ->fetchField(); + } + + // Save the hierarchy. + $select = $database->select('taxonomy_term_hierarchy', 'h'); + $select->join('taxonomy_term_data', 'd', 'h.tid = d.tid'); + $hierarchy = $select + ->fields('h', ['tid', 'parent']) + ->fields('d', ['vid', 'langcode']) + ->range($sandbox['current'], $sandbox['current'] + 100) + ->execute() + ->fetchAll(); + + // Restore data. + $insert = $database->insert('taxonomy_term__parent') + ->fields(['bundle', 'entity_id', 'revision_id', 'langcode', 'delta', 'parent_target_id']); + $tid = -1; + + foreach ($hierarchy as $row) { + if ($row->tid !== $tid) { + $delta = 0; + $tid = $row->tid; + } + + $insert->values([ + 'bundle' => $row->vid, + 'entity_id' => $row->tid, + 'revision_id' => $row->tid, + 'langcode' => $row->langcode, + 'delta' => $delta, + 'parent_target_id' => $row->parent, + ]); + + $delta++; + $sandbox['current']++; + } + + $insert->execute(); + + $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['current'] / $sandbox['max']); + + if ($sandbox['#finished'] >= 1) { + return t('Taxonomy term hierarchy converted.'); + } + return t('Taxonomy term hierarchy NOT FULLY converted.'); +} + +/** + * Update views to use {taxonomy_term__parent} in relationships. + */ +function taxonomy_update_8503() { + $config_factory = \Drupal::configFactory(); + foreach ($config_factory->listAll('views.view.') as $id) { + $view = $config_factory->getEditable($id); + $changed = FALSE; + + foreach (array_keys($view->get('display')) as $display_id) { + $base_path = "display.$display_id.display_options.relationships.parent"; + $table_path = $base_path . '.table'; + $field_path = $base_path . '.field'; + + if (!empty($table = $view->get($table_path)) && $table == 'taxonomy_term_hierarchy') { + $view->set($table_path, 'taxonomy_term__parent'); + $view->set($field_path, 'parent_target_id'); + + $base_path = "display.{$display_id}.display_options.filters.parent"; + $table_path = $base_path . '.table'; + $field_path = $base_path . '.field'; + + if (!empty($table = $view->get($table_path)) && $table == 'taxonomy_term_hierarchy') { + $view->set($table_path, 'taxonomy_term__parent'); + $view->set($field_path, 'parent_target_id'); + } + + $base_path = "display.{$display_id}.display_options.arguments.parent.table"; + $table_path = $base_path . '.table'; + $field_path = $base_path . '.field'; + + if (!empty($table = $view->get($table_path)) && $table == 'taxonomy_term_hierarchy') { + $view->set($table_path, 'taxonomy_term__parent'); + $view->set($field_path, 'parent_target_id'); + } + + $changed = TRUE; + } + } + + if ($changed) { + $view->save(TRUE); + } + } +} + +/** + * Drop the legacy table {taxonomy_term_hierarchy}. + */ +function taxonomy_update_8504(&$sandbox) { + $database = \Drupal::database(); + // Drop the legacy table. + $database->schema()->dropTable('taxonomy_term_hierarchy'); + return t('Taxonomy term legacy tables dropped.'); +} diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml index 256f618991..65df6a80e8 100644 --- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml +++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml @@ -124,8 +124,8 @@ display: relationships: parent: id: parent - table: taxonomy_term_hierarchy - field: parent + table: taxonomy_term__parent + field: parent_target_id relationship: none group_type: group admin_label: Parent diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml index 80295b6ea2..664ae27cf0 100644 --- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml +++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml @@ -186,8 +186,8 @@ display: plugin_id: standard parent: id: parent - table: taxonomy_term_hierarchy - field: parent + table: taxonomy_term__parent + field: parent_target_id relationship: none group_type: group admin_label: Parent diff --git a/core/modules/taxonomy/tests/src/Functional/TaxonomyQueryAlterTest.php b/core/modules/taxonomy/tests/src/Functional/TaxonomyQueryAlterTest.php index 85310e9aa9..58ab9e825e 100644 --- a/core/modules/taxonomy/tests/src/Functional/TaxonomyQueryAlterTest.php +++ b/core/modules/taxonomy/tests/src/Functional/TaxonomyQueryAlterTest.php @@ -32,7 +32,7 @@ public function testTaxonomyQueryAlter() { } // Set up hierarchy. Term 2 is a child of 1. - $terms[2]->parent = $terms[1]->id(); + $terms[2]->parent->setValue($terms[1]->id()); $terms[2]->save(); $term_storage = \Drupal::entityManager()->getStorage('taxonomy_term'); @@ -50,12 +50,12 @@ public function testTaxonomyQueryAlter() { $this->setupQueryTagTestHooks(); $loaded_terms = $term_storage->loadParents($terms[2]->id()); $this->assertEqual(count($loaded_terms), 1, 'All parent terms were loaded'); - $this->assertQueryTagTestResult(2, 1, 'TermStorage::loadParents()'); + $this->assertQueryTagTestResult(3, 1, 'TermStorage::loadParents()'); $this->setupQueryTagTestHooks(); $loaded_terms = $term_storage->loadChildren($terms[1]->id()); $this->assertEqual(count($loaded_terms), 1, 'All child terms were loaded'); - $this->assertQueryTagTestResult(2, 1, 'TermStorage::loadChildren()'); + $this->assertQueryTagTestResult(3, 1, 'TermStorage::loadChildren()'); $this->setupQueryTagTestHooks(); $query = db_select('taxonomy_term_data', 't'); diff --git a/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyParentUpdateTest.php b/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyParentUpdateTest.php new file mode 100644 index 0000000000..dae3cc2925 --- /dev/null +++ b/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyParentUpdateTest.php @@ -0,0 +1,89 @@ +db = $this->container->get('database'); + } + + /** + * {@inheritdoc} + */ + public function setDatabaseDumpFiles() { + $this->databaseDumpFiles = [ + __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz', + __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php', + ]; + } + + /** + * Tests taxonomy term parents update. + * + * @see taxonomy_update_8501() + * @see taxonomy_update_8502() + * @see taxonomy_update_8503() + * @see taxonomy_update_8504() + */ + public function testTaxonomyUpdateParents() { + // Run updates. + $this->runUpdates(); + + $tids = [ + // This reads as: tid with index 1 has tids with index 2 and 3 as parents. + 1 => [2, 3], + 2 => [3, 0], + 3 => [0], + ]; + + /** @var \Drupal\taxonomy\TermInterface $term */ + $term = Term::load(1); + $parents = [2, 3]; + $this->assertSame(count($term->parent), 2); + $this->assertTrue(in_array($term->parent[0]->entity->id(), $parents)); + $this->assertTrue(in_array($term->parent[1]->entity->id(), $parents)); + + $term = Term::load(2); + $parents = [0, 3]; + $this->assertSame(count($term->parent), 2); + $this->assertTrue(in_array($term->parent[0]->target_id, $parents)); + $this->assertTrue(in_array($term->parent[1]->target_id, $parents)); + + $term = Term::load(3); + $this->assertSame(count($term->parent), 1); + // Target ID is returned as string. + $this->assertSame((int) $term->get('parent')[0]->target_id, 0); + + // Test if the view has been converted to use {taxonomy_term__parent} table + // instead of {taxonomy_term_hierarchy}. + $view = $this->config("views.view.test_taxonomy_parent"); + $path = 'display.default.display_options.relationships.parent.table'; + $this->assertSame($view->get($path), 'taxonomy_term__parent'); + + // The {taxonomy_term_hierarchy} table has been removed. + $this->assertFalse($this->db->schema()->tableExists('taxonomy_term_hierarchy')); + } + +} diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php index 03be3e15bb..3299964909 100644 --- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php +++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php @@ -61,24 +61,24 @@ public function testTaxonomyRelationships() { $this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['field'], 'tid'); $this->assertEqual($views_data['table']['join']['node_field_data']['left_field'], 'nid'); $this->assertEqual($views_data['table']['join']['node_field_data']['field'], 'nid'); - $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['left_field'], 'tid'); - $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['field'], 'tid'); + $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['left_field'], 'entity_id'); + $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['field'], 'tid'); - // Check the generated views data of taxonomy_term_hierarchy. - $views_data = Views::viewsData()->get('taxonomy_term_hierarchy'); + // Check the generated views data of taxonomy_term__parent. + $views_data = Views::viewsData()->get('taxonomy_term__parent'); // Check the table join data. - $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['left_field'], 'tid'); - $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['field'], 'parent'); + $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['left_field'], 'entity_id'); + $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['field'], 'parent_target_id'); $this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['left_field'], 'tid'); - $this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['field'], 'tid'); + $this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['field'], 'entity_id'); // Check the parent relationship data. - $this->assertEqual($views_data['parent']['relationship']['base'], 'taxonomy_term_field_data'); - $this->assertEqual($views_data['parent']['relationship']['field'], 'parent'); - $this->assertEqual($views_data['parent']['relationship']['label'], t('Parent')); - $this->assertEqual($views_data['parent']['relationship']['id'], 'standard'); + $this->assertEqual($views_data['parent_target_id']['relationship']['base'], 'taxonomy_term_field_data'); + $this->assertEqual($views_data['parent_target_id']['relationship']['base field'], 'tid'); + $this->assertEqual($views_data['parent_target_id']['relationship']['label'], t('Parent')); + $this->assertEqual($views_data['parent_target_id']['relationship']['id'], 'standard'); // Check the parent filter and argument data. - $this->assertEqual($views_data['parent']['filter']['id'], 'numeric'); - $this->assertEqual($views_data['parent']['argument']['id'], 'taxonomy'); + $this->assertEqual($views_data['parent_target_id']['filter']['id'], 'numeric'); + $this->assertEqual($views_data['parent_target_id']['argument']['id'], 'taxonomy'); // Check an actual test view. $view = Views::getView('test_taxonomy_term_relationship'); @@ -95,7 +95,7 @@ public function testTaxonomyRelationships() { if (!$index) { $this->assertTrue($row->_relationship_entities['parent'] instanceof TermInterface); $this->assertEqual($row->_relationship_entities['parent']->id(), $this->term2->id()); - $this->assertEqual($row->taxonomy_term_field_data_taxonomy_term_hierarchy_tid, $this->term2->id()); + $this->assertEqual($row->taxonomy_term_field_data_taxonomy_term__parent_tid, $this->term2->id()); } $this->assertTrue($row->_relationship_entities['nid'] instanceof NodeInterface); $this->assertEqual($row->_relationship_entities['nid']->id(), $this->nodes[$index]->id()); diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php index 6450e4d91e..66fa25f5d9 100644 --- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\taxonomy\Kernel\Migrate\d6; use Drupal\taxonomy\Entity\Term; +use Drupal\taxonomy\TermInterface; use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase; /** @@ -92,7 +93,7 @@ public function testTaxonomyTerms() { $this->assertSame($values['vid'], $term->vid->target_id); $this->assertSame((string) $values['weight'], $term->weight->value); if ($values['parent'] === [0]) { - $this->assertNull($term->parent->target_id); + $this->assertSame((int) $term->parent->target_id, TermInterface::ROOT_ID); } else { $parents = [];