comparison core/modules/taxonomy/src/TermStorage.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents 4c8ae668cc8c
children af1871eacc83
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
1 <?php 1 <?php
2 2
3 namespace Drupal\taxonomy; 3 namespace Drupal\taxonomy;
4 4
5 use Drupal\Core\Entity\EntityInterface;
5 use Drupal\Core\Entity\Sql\SqlContentEntityStorage; 6 use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
6 use Drupal\Core\Entity\EntityInterface;
7 7
8 /** 8 /**
9 * Defines a Controller class for taxonomy terms. 9 * Defines a Controller class for taxonomy terms.
10 */ 10 */
11 class TermStorage extends SqlContentEntityStorage implements TermStorageInterface { 11 class TermStorage extends SqlContentEntityStorage implements TermStorageInterface {
12 12
13 /** 13 /**
14 * Array of loaded parents keyed by child term ID. 14 * Array of term parents keyed by vocabulary ID and child term ID.
15 * 15 *
16 * @var array 16 * @var array
17 */ 17 */
18 protected $parents = []; 18 protected $treeParents = [];
19 19
20 /** 20 /**
21 * Array of all loaded term ancestry keyed by ancestor term ID. 21 * Array of term ancestors keyed by vocabulary ID and parent term ID.
22 * 22 *
23 * @var array 23 * @var array
24 */ 24 */
25 protected $parentsAll = []; 25 protected $treeChildren = [];
26 26
27 /** 27 /**
28 * Array of child terms keyed by parent term ID. 28 * Array of terms in a tree keyed by vocabulary ID and term ID.
29 * 29 *
30 * @var array 30 * @var array
31 */ 31 */
32 protected $children = []; 32 protected $treeTerms = [];
33 33
34 /** 34 /**
35 * Array of term parents keyed by vocabulary ID and child term ID. 35 * Array of loaded trees keyed by a cache id matching tree arguments.
36 * 36 *
37 * @var array 37 * @var array
38 */ 38 */
39 protected $treeParents = [];
40
41 /**
42 * Array of term ancestors keyed by vocabulary ID and parent term ID.
43 *
44 * @var array
45 */
46 protected $treeChildren = [];
47
48 /**
49 * Array of terms in a tree keyed by vocabulary ID and term ID.
50 *
51 * @var array
52 */
53 protected $treeTerms = [];
54
55 /**
56 * Array of loaded trees keyed by a cache id matching tree arguments.
57 *
58 * @var array
59 */
60 protected $trees = []; 39 protected $trees = [];
40
41 /**
42 * Array of all loaded term ancestry keyed by ancestor term ID, keyed by term
43 * ID.
44 *
45 * @var \Drupal\taxonomy\TermInterface[][]
46 */
47 protected $ancestors;
61 48
62 /** 49 /**
63 * {@inheritdoc} 50 * {@inheritdoc}
64 * 51 *
65 * @param array $values 52 * @param array $values
78 /** 65 /**
79 * {@inheritdoc} 66 * {@inheritdoc}
80 */ 67 */
81 public function resetCache(array $ids = NULL) { 68 public function resetCache(array $ids = NULL) {
82 drupal_static_reset('taxonomy_term_count_nodes'); 69 drupal_static_reset('taxonomy_term_count_nodes');
83 $this->parents = []; 70 $this->ancestors = [];
84 $this->parentsAll = [];
85 $this->children = [];
86 $this->treeChildren = []; 71 $this->treeChildren = [];
87 $this->treeParents = []; 72 $this->treeParents = [];
88 $this->treeTerms = []; 73 $this->treeTerms = [];
89 $this->trees = []; 74 $this->trees = [];
90 parent::resetCache($ids); 75 parent::resetCache($ids);
91 } 76 }
92 77
93 /** 78 /**
94 * {@inheritdoc} 79 * {@inheritdoc}
95 */ 80 */
96 public function deleteTermHierarchy($tids) { 81 public function deleteTermHierarchy($tids) {}
97 $this->database->delete('taxonomy_term_hierarchy') 82
98 ->condition('tid', $tids, 'IN') 83 /**
99 ->execute(); 84 * {@inheritdoc}
100 } 85 */
101 86 public function updateTermHierarchy(EntityInterface $term) {}
102 /**
103 * {@inheritdoc}
104 */
105 public function updateTermHierarchy(EntityInterface $term) {
106 $query = $this->database->insert('taxonomy_term_hierarchy')
107 ->fields(['tid', 'parent']);
108
109 foreach ($term->parent as $parent) {
110 $query->values([
111 'tid' => $term->id(),
112 'parent' => (int) $parent->target_id,
113 ]);
114 }
115 $query->execute();
116 }
117 87
118 /** 88 /**
119 * {@inheritdoc} 89 * {@inheritdoc}
120 */ 90 */
121 public function loadParents($tid) { 91 public function loadParents($tid) {
122 if (!isset($this->parents[$tid])) { 92 $terms = [];
123 $parents = []; 93 /** @var \Drupal\taxonomy\TermInterface $term */
124 $query = $this->database->select('taxonomy_term_field_data', 't'); 94 if ($tid && $term = $this->load($tid)) {
125 $query->join('taxonomy_term_hierarchy', 'h', 'h.parent = t.tid'); 95 foreach ($this->getParents($term) as $id => $parent) {
126 $query->addField('t', 'tid'); 96 // This method currently doesn't return the <root> parent.
127 $query->condition('h.tid', $tid); 97 // @see https://www.drupal.org/node/2019905
128 $query->condition('t.default_langcode', 1); 98 if (!empty($id)) {
129 $query->addTag('taxonomy_term_access'); 99 $terms[$id] = $parent;
130 $query->orderBy('t.weight'); 100 }
131 $query->orderBy('t.name'); 101 }
132 if ($ids = $query->execute()->fetchCol()) { 102 }
133 $parents = $this->loadMultiple($ids); 103
134 } 104 return $terms;
135 $this->parents[$tid] = $parents; 105 }
136 } 106
137 return $this->parents[$tid]; 107 /**
108 * Returns a list of parents of this term.
109 *
110 * @return \Drupal\taxonomy\TermInterface[]
111 * The parent taxonomy term entities keyed by term ID. If this term has a
112 * <root> parent, that item is keyed with 0 and will have NULL as value.
113 *
114 * @internal
115 * @todo Refactor away when TreeInterface is introduced.
116 */
117 protected function getParents(TermInterface $term) {
118 $parents = $ids = [];
119 // Cannot use $this->get('parent')->referencedEntities() here because that
120 // strips out the '0' reference.
121 foreach ($term->get('parent') as $item) {
122 if ($item->target_id == 0) {
123 // The <root> parent.
124 $parents[0] = NULL;
125 continue;
126 }
127 $ids[] = $item->target_id;
128 }
129
130 // @todo Better way to do this? AND handle the NULL/0 parent?
131 // Querying the terms again so that the same access checks are run when
132 // getParents() is called as in Drupal version prior to 8.3.
133 $loaded_parents = [];
134
135 if ($ids) {
136 $query = \Drupal::entityQuery('taxonomy_term')
137 ->condition('tid', $ids, 'IN');
138
139 $loaded_parents = static::loadMultiple($query->execute());
140 }
141
142 return $parents + $loaded_parents;
138 } 143 }
139 144
140 /** 145 /**
141 * {@inheritdoc} 146 * {@inheritdoc}
142 */ 147 */
143 public function loadAllParents($tid) { 148 public function loadAllParents($tid) {
144 if (!isset($this->parentsAll[$tid])) { 149 /** @var \Drupal\taxonomy\TermInterface $term */
145 $parents = []; 150 return (!empty($tid) && $term = $this->load($tid)) ? $this->getAncestors($term) : [];
146 if ($term = $this->load($tid)) { 151 }
147 $parents[$term->id()] = $term; 152
148 $terms_to_search[] = $term->id(); 153 /**
149 154 * Returns all ancestors of this term.
150 while ($tid = array_shift($terms_to_search)) { 155 *
151 if ($new_parents = $this->loadParents($tid)) { 156 * @return \Drupal\taxonomy\TermInterface[]
152 foreach ($new_parents as $new_parent) { 157 * A list of ancestor taxonomy term entities keyed by term ID.
153 if (!isset($parents[$new_parent->id()])) { 158 *
154 $parents[$new_parent->id()] = $new_parent; 159 * @internal
155 $terms_to_search[] = $new_parent->id(); 160 * @todo Refactor away when TreeInterface is introduced.
156 } 161 */
157 } 162 protected function getAncestors(TermInterface $term) {
163 if (!isset($this->ancestors[$term->id()])) {
164 $this->ancestors[$term->id()] = [$term->id() => $term];
165 $search[] = $term->id();
166
167 while ($tid = array_shift($search)) {
168 foreach ($this->getParents(static::load($tid)) as $id => $parent) {
169 if ($parent && !isset($this->ancestors[$term->id()][$id])) {
170 $this->ancestors[$term->id()][$id] = $parent;
171 $search[] = $id;
158 } 172 }
159 } 173 }
160 } 174 }
161 175 }
162 $this->parentsAll[$tid] = $parents; 176 return $this->ancestors[$term->id()];
163 }
164 return $this->parentsAll[$tid];
165 } 177 }
166 178
167 /** 179 /**
168 * {@inheritdoc} 180 * {@inheritdoc}
169 */ 181 */
170 public function loadChildren($tid, $vid = NULL) { 182 public function loadChildren($tid, $vid = NULL) {
171 if (!isset($this->children[$tid])) { 183 /** @var \Drupal\taxonomy\TermInterface $term */
172 $children = []; 184 return (!empty($tid) && $term = $this->load($tid)) ? $this->getChildren($term) : [];
173 $query = $this->database->select('taxonomy_term_field_data', 't'); 185 }
174 $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid'); 186
175 $query->addField('t', 'tid'); 187 /**
176 $query->condition('h.parent', $tid); 188 * Returns all children terms of this term.
177 if ($vid) { 189 *
178 $query->condition('t.vid', $vid); 190 * @return \Drupal\taxonomy\TermInterface[]
179 } 191 * A list of children taxonomy term entities keyed by term ID.
180 $query->condition('t.default_langcode', 1); 192 *
181 $query->addTag('taxonomy_term_access'); 193 * @internal
182 $query->orderBy('t.weight'); 194 * @todo Refactor away when TreeInterface is introduced.
183 $query->orderBy('t.name'); 195 */
184 if ($ids = $query->execute()->fetchCol()) { 196 public function getChildren(TermInterface $term) {
185 $children = $this->loadMultiple($ids); 197 $query = \Drupal::entityQuery('taxonomy_term')
186 } 198 ->condition('parent', $term->id());
187 $this->children[$tid] = $children; 199 return static::loadMultiple($query->execute());
188 }
189 return $this->children[$tid];
190 } 200 }
191 201
192 /** 202 /**
193 * {@inheritdoc} 203 * {@inheritdoc}
194 */ 204 */
199 // children, too. 209 // children, too.
200 if (!isset($this->treeChildren[$vid])) { 210 if (!isset($this->treeChildren[$vid])) {
201 $this->treeChildren[$vid] = []; 211 $this->treeChildren[$vid] = [];
202 $this->treeParents[$vid] = []; 212 $this->treeParents[$vid] = [];
203 $this->treeTerms[$vid] = []; 213 $this->treeTerms[$vid] = [];
204 $query = $this->database->select('taxonomy_term_field_data', 't'); 214 $query = $this->database->select($this->getDataTable(), 't');
205 $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid'); 215 $query->join('taxonomy_term__parent', 'p', 't.tid = p.entity_id');
216 $query->addExpression('parent_target_id', 'parent');
206 $result = $query 217 $result = $query
207 ->addTag('taxonomy_term_access') 218 ->addTag('taxonomy_term_access')
208 ->fields('t') 219 ->fields('t')
209 ->fields('h', ['parent'])
210 ->condition('t.vid', $vid) 220 ->condition('t.vid', $vid)
211 ->condition('t.default_langcode', 1) 221 ->condition('t.default_langcode', 1)
212 ->orderBy('t.weight') 222 ->orderBy('t.weight')
213 ->orderBy('t.name') 223 ->orderBy('t.name')
214 ->execute(); 224 ->execute();
252 // Clone the term so that the depth attribute remains correct 262 // Clone the term so that the depth attribute remains correct
253 // in the event of multiple parents. 263 // in the event of multiple parents.
254 $term = clone $term; 264 $term = clone $term;
255 } 265 }
256 $term->depth = $depth; 266 $term->depth = $depth;
257 unset($term->parent); 267 if (!$load_entities) {
268 unset($term->parent);
269 }
258 $tid = $load_entities ? $term->id() : $term->tid; 270 $tid = $load_entities ? $term->id() : $term->tid;
259 $term->parents = $this->treeParents[$vid][$tid]; 271 $term->parents = $this->treeParents[$vid][$tid];
260 $tree[] = $term; 272 $tree[] = $term;
261 if (!empty($this->treeChildren[$vid][$tid])) { 273 if (!empty($this->treeChildren[$vid][$tid])) {
262 $has_children = TRUE; 274 $has_children = TRUE;
291 * {@inheritdoc} 303 * {@inheritdoc}
292 */ 304 */
293 public function nodeCount($vid) { 305 public function nodeCount($vid) {
294 $query = $this->database->select('taxonomy_index', 'ti'); 306 $query = $this->database->select('taxonomy_index', 'ti');
295 $query->addExpression('COUNT(DISTINCT ti.nid)'); 307 $query->addExpression('COUNT(DISTINCT ti.nid)');
296 $query->leftJoin('taxonomy_term_data', 'td', 'ti.tid = td.tid'); 308 $query->leftJoin($this->getBaseTable(), 'td', 'ti.tid = td.tid');
297 $query->condition('td.vid', $vid); 309 $query->condition('td.vid', $vid);
298 $query->addTag('vocabulary_node_count'); 310 $query->addTag('vocabulary_node_count');
299 return $query->execute()->fetchField(); 311 return $query->execute()->fetchField();
300 } 312 }
301 313
302 /** 314 /**
303 * {@inheritdoc} 315 * {@inheritdoc}
304 */ 316 */
305 public function resetWeights($vid) { 317 public function resetWeights($vid) {
306 $this->database->update('taxonomy_term_field_data') 318 $this->database->update($this->getDataTable())
307 ->fields(['weight' => 0]) 319 ->fields(['weight' => 0])
308 ->condition('vid', $vid) 320 ->condition('vid', $vid)
309 ->execute(); 321 ->execute();
310 } 322 }
311 323
312 /** 324 /**
313 * {@inheritdoc} 325 * {@inheritdoc}
314 */ 326 */
315 public function getNodeTerms(array $nids, array $vocabs = [], $langcode = NULL) { 327 public function getNodeTerms(array $nids, array $vocabs = [], $langcode = NULL) {
316 $query = db_select('taxonomy_term_field_data', 'td'); 328 $query = db_select($this->getDataTable(), 'td');
317 $query->innerJoin('taxonomy_index', 'tn', 'td.tid = tn.tid'); 329 $query->innerJoin('taxonomy_index', 'tn', 'td.tid = tn.tid');
318 $query->fields('td', ['tid']); 330 $query->fields('td', ['tid']);
319 $query->addField('tn', 'nid', 'node_nid'); 331 $query->addField('tn', 'nid', 'node_nid');
320 $query->orderby('td.weight'); 332 $query->orderby('td.weight');
321 $query->orderby('td.name'); 333 $query->orderby('td.name');
349 * {@inheritdoc} 361 * {@inheritdoc}
350 */ 362 */
351 public function __sleep() { 363 public function __sleep() {
352 $vars = parent::__sleep(); 364 $vars = parent::__sleep();
353 // Do not serialize static cache. 365 // Do not serialize static cache.
354 unset($vars['parents'], $vars['parentsAll'], $vars['children'], $vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees']); 366 unset($vars['ancestors'], $vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees']);
355 return $vars; 367 return $vars;
356 } 368 }
357 369
358 /** 370 /**
359 * {@inheritdoc} 371 * {@inheritdoc}
360 */ 372 */
361 public function __wakeup() { 373 public function __wakeup() {
362 parent::__wakeup(); 374 parent::__wakeup();
363 // Initialize static caches. 375 // Initialize static caches.
364 $this->parents = []; 376 $this->ancestors = [];
365 $this->parentsAll = [];
366 $this->children = [];
367 $this->treeChildren = []; 377 $this->treeChildren = [];
368 $this->treeParents = []; 378 $this->treeParents = [];
369 $this->treeTerms = []; 379 $this->treeTerms = [];
370 $this->trees = []; 380 $this->trees = [];
371 } 381 }