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.
*
*/
protected $trees = [];
+ /**
+ * Array of all loaded term ancestry keyed by ancestor term ID, keyed by term
+ * ID.
+ *
+ * @var \Drupal\taxonomy\TermInterface[][]
+ */
+ protected $ancestors;
+
/**
* {@inheritdoc}
*
*/
public function resetCache(array $ids = NULL) {
drupal_static_reset('taxonomy_term_count_nodes');
- $this->parents = [];
- $this->parentsAll = [];
- $this->children = [];
+ $this->ancestors = [];
$this->treeChildren = [];
$this->treeParents = [];
$this->treeTerms = [];
/**
* {@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 ($this->getParents($term) as $id => $parent) {
+ // This method currently doesn't return the <root> parent.
+ // @see https://www.drupal.org/node/2019905
+ if (!empty($id)) {
+ $terms[$id] = $parent;
+ }
+ }
+ }
+
+ return $terms;
+ }
+
+ /**
+ * Returns a list of parents of this term.
+ *
+ * @return \Drupal\taxonomy\TermInterface[]
+ * The parent taxonomy term entities keyed by term ID. If this term has a
+ * <root> parent, that item is keyed with 0 and will have NULL as value.
+ *
+ * @internal
+ * @todo Refactor away when TreeInterface is introduced.
+ */
+ protected function getParents(TermInterface $term) {
+ $parents = $ids = [];
+ // Cannot use $this->get('parent')->referencedEntities() here because that
+ // strips out the '0' reference.
+ foreach ($term->get('parent') as $item) {
+ if ($item->target_id == 0) {
+ // The <root> parent.
+ $parents[0] = NULL;
+ continue;
}
- $this->parents[$tid] = $parents;
+ $ids[] = $item->target_id;
}
- return $this->parents[$tid];
+
+ // @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 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();
- }
- }
+ /** @var \Drupal\taxonomy\TermInterface $term */
+ return (!empty($tid) && $term = $this->load($tid)) ? $this->getAncestors($term) : [];
+ }
+
+ /**
+ * Returns all ancestors of this term.
+ *
+ * @return \Drupal\taxonomy\TermInterface[]
+ * A list of ancestor taxonomy term entities keyed by term ID.
+ *
+ * @internal
+ * @todo Refactor away when TreeInterface is introduced.
+ */
+ protected function getAncestors(TermInterface $term) {
+ if (!isset($this->ancestors[$term->id()])) {
+ $this->ancestors[$term->id()] = [$term->id() => $term];
+ $search[] = $term->id();
+
+ while ($tid = array_shift($search)) {
+ foreach ($this->getParents(static::load($tid)) as $id => $parent) {
+ if ($parent && !isset($this->ancestors[$term->id()][$id])) {
+ $this->ancestors[$term->id()][$id] = $parent;
+ $search[] = $id;
}
}
}
-
- $this->parentsAll[$tid] = $parents;
}
- return $this->parentsAll[$tid];
+ return $this->ancestors[$term->id()];
}
/**
* {@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)) ? $this->getChildren($term) : [];
+ }
+
+ /**
+ * Returns all children terms of this term.
+ *
+ * @return \Drupal\taxonomy\TermInterface[]
+ * A list of children taxonomy term entities keyed by term ID.
+ *
+ * @internal
+ * @todo Refactor away when TreeInterface is introduced.
+ */
+ public function getChildren(TermInterface $term) {
+ $query = \Drupal::entityQuery('taxonomy_term')
+ ->condition('parent', $term->id());
+ return static::loadMultiple($query->execute());
}
/**
$this->treeChildren[$vid] = [];
$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 = $this->database->select($this->getDataTable(), 't');
+ $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')
$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;
public function nodeCount($vid) {
$query = $this->database->select('taxonomy_index', 'ti');
$query->addExpression('COUNT(DISTINCT ti.nid)');
- $query->leftJoin('taxonomy_term_data', 'td', 'ti.tid = td.tid');
+ $query->leftJoin($this->getBaseTable(), 'td', 'ti.tid = td.tid');
$query->condition('td.vid', $vid);
$query->addTag('vocabulary_node_count');
return $query->execute()->fetchField();
* {@inheritdoc}
*/
public function resetWeights($vid) {
- $this->database->update('taxonomy_term_field_data')
+ $this->database->update($this->getDataTable())
->fields(['weight' => 0])
->condition('vid', $vid)
->execute();
* {@inheritdoc}
*/
public function getNodeTerms(array $nids, array $vocabs = [], $langcode = NULL) {
- $query = db_select('taxonomy_term_field_data', 'td');
+ $query = db_select($this->getDataTable(), 'td');
$query->innerJoin('taxonomy_index', 'tn', 'td.tid = tn.tid');
$query->fields('td', ['tid']);
$query->addField('tn', 'nid', 'node_nid');
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['ancestors'], $vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees']);
return $vars;
}
public function __wakeup() {
parent::__wakeup();
// Initialize static caches.
- $this->parents = [];
- $this->parentsAll = [];
- $this->children = [];
+ $this->ancestors = [];
$this->treeChildren = [];
$this->treeParents = [];
$this->treeTerms = [];