29

I wanted to retrieve taxonomy terms from a certain vocabulary in Drupal 8.

Apparently I can still do it using taxonomy_get_tree but it's deprecated.

I now have to use TermStorageInterface::loadTree

I am trying to access this function from a Block but I don't understand how to instantiate the TermStorageInterface class.

I tried accessing the function directly but it's not a static function :

TermStorageInterface::loadTree('categories')

I tried instantiating the class but it told me Cannot instantiate interface Drupal\taxonomy\TermStorageInterface

$test = new TermStorageInterface();

I don't understand how this class works and how I can access taxonomy links. I think I'm missing a big part of understanding how Drupal works.

12 Answers 12

58

Replacing a deprecated function is in most cases trivial. Just look at it. There you can see this:

\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid, $parent, $max_depth, $load_entities);

If you are looking for a function that was already removed, search for it on Change records for Drupal core page. Pretty much every function that was removed should have more or less (usually more) detailed instructions on how to do it in Drupal 8 instead.

The storage class is an entity storage handler, that you get through the entity manager. In general, 99% of the classes in D8 are not meant to be created yourself, but as a service, or entity handler, plugin.

For example:

$vid = 'vocabulary_name';
$terms =\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid);
foreach ($terms as $term) {
 $term_data[] = array(
  'id' => $term->tid,
  'name' => $term->name
 );
}
7
  • Great, thank you. Although I don't understand where you found that line of code? Jan 13, 2015 at 23:13
  • As I said, that is copied straigt from taxonomy_get_tree(). A deprecated function means there is a new way to do something, but the old function still has to work.
    – Berdir
    Jan 13, 2015 at 23:44
  • Oh ok. Awesome. I see it now. Thank you very much for your help! Jan 13, 2015 at 23:46
  • 1
    Define not there? Method autocomplete will not work but it is there
    – Berdir
    Apr 28, 2016 at 17:08
  • 2
    @usethe23 Implement what? This is just about replacing a deprecated function call with the new approach. It doesn't do anything that it didn't already before. You probably need to create a new question and describe what you want to do.
    – Berdir
    Dec 19, 2016 at 18:37
15

This is what I use to create a list of tags:

  use Drupal\taxonomy\Entity\Term;      
  use Drupal\Core\Link;
  use Drupal\Core\Url;

  $vocabulary_name = 'YOUR_VOCABULARY_NAME'; //name of your vocabulary
  $query = \Drupal::entityQuery('taxonomy_term');
  $query->condition('vid', $vocabulary_name);
  $query->sort('weight');
  $tids = $query->execute();
  $terms = Term::loadMultiple($tids);
  $output = '<ul>';
  foreach($terms as $term) {
      $name = $term->getName();;
      $url = Url::fromRoute('entity.taxonomy_term.canonical', ['taxonomy_term' => $term->id()]);
      $link = Link::fromTextAndUrl($name, $url);
      $link = $link->toRenderable();
      $rendered = \Drupal::service('renderer')->render($link);
      $output .='<li>'.$rendered.'</li>';
  }
  $output .= '</ul>';
  print $output;

Edit: updated the deprecated method render();

2
  • I searched like half an hour for this... it's so complicated and doesnt make sense... and what if I want just the URL alias of the taxonomy term, how do I get that?
    – Raf A.
    Dec 6, 2017 at 14:41
  • 1
    Hi Raf. I decided to post this, because I also had a hard time to get to such a simple task as this. To just get the alias: $url = Url::fromRoute('entity.taxonomy_term.canonical', ['taxonomy_term' => $term->id()]); Dec 11, 2017 at 8:54
11

If you need term entity, you can use 'loadByProperties()'.

$vid = 'vocabulary_name';
/** @var \Drupal\taxonomy\Entity\Term[] $terms */
$terms =\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties(['vid' => $vid]);
1
5

The error you are getting is because you are trying to create an instance of an interface, which is not something PHP allows. PHP interfaces describe the methods classes should implement for specific cases, but they cannot be used to create an object, for example with new InterfaceName().

Now that taxonomy_get_tree() has been removed, and the entity manager service has been deprecated, you need to use the following code.

$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid, $parent, $max_depth, $load_entities);

There is not need to use \Drupal::getContainer() since \Drupal exposes a helper method to get the entity type manager service.

0
4

\Drupal::entityManager() is now deprecated so this worked for me

$vids = Vocabulary::loadMultiple();
foreach ($vids as $vid) {
  if ($vid->label() == 'YourVocab') {
    $container = \Drupal::getContainer();
    $terms = $container->get('entity.manager')->getStorage('taxonomy_term')->loadTree($vid->id());
    if (!empty($terms)) {
      foreach($terms as $term) {
        dsm($term->name);
      }
    }
    break;
  }
}
2
  • 1
    entityManager is deprecated because is got split into 11 classes - drupal.org/node/2337191 - so use \Drupal::entityTypeManager instead and you should be good. Apr 12, 2016 at 20:52
  • Anytime you have to loadMultiple and loop until you get the specific one you want, I call that shitty code fwiw
    – AlxVallejo
    Jan 23, 2018 at 19:43
4

Load using machine name of the vocabulary (vid):

  $vid = 'name_of_your_vocabulary';
  $terms =\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid);
  foreach ($terms as $term) {
   $term_data[] = array(
    "id" => $term->tid,
    "name" => $term->name
   );
   dpm($term_data);
  }
0
2

I've just wrote a function, feel free to edit and use :) I needed the id of the term, but you can return whatever you want.

function checkTaxonomyTerm($vocab_name, $term_name){
    $query = \Drupal::entityQuery('taxonomy_term');
    $query->condition('vid', $vocab_name);
    $tids = $query->execute();
    $terms = Term::loadMultiple($tids);
    foreach($terms as $term) {
        $name = $term->getName();
        if($name == $term_name) {
            print_r($term->id());
            if (is_null($term->id())) {
                return null;
            }
            else{
                return array(true, $term->id());
            }
        }
        else {return addTaxonomyTerm($term->getVocabularyId(), $name);}
    }
}
3
  • I might be wrong, but this appears not to be entirely right. $query->condition after $query->execute() has no effect. Also if the first term is not the right one, the foreach will return null rather than checking the next term. (For your use case, you might be able to use taxonomy_term_load_multiple_by_name instead?) Sep 5, 2017 at 22:09
  • Ops, yes sorry I copied old code sorry, I edit it Sep 6, 2017 at 7:15
  • Well and yes, other way is right, as you said, for that better to use that. Sep 6, 2017 at 7:20
2

This question have already been answered correctly, but if you need to get the translated name of your term, here's how I did it :

$values = [];
$lang_code = \Drupal::languageManager()->getCurrentLanguage()->getId();
$vocabulary_name = 'VOCABULARY_NAME_HERE';
$query = \Drupal::entityQuery('taxonomy_term');
$query->condition('vid', $vocabulary_name);
$query->sort('weight');
$tids = $query->execute();
$terms = Term::loadMultiple($tids);
foreach ($terms as $term) {
  $taxonomy_term_trans = \Drupal::service('entity.repository')->getTranslationFromContext($term, $lang_code);
  $values[$term->id()] = $taxonomy_term_trans->getName();
}
1

Here is the D8 example how to access taxonomy terms based on the vocabulary machine name:

$terms = \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree('categories');
foreach ($terms as $term) {
  //$value = $term->get('field_example')->getValue();
  var_dump($term);
}

To load the whole entities, use: loadTree('categories', 0, NULL, TRUE).

0
0
$vid = 'MACHINE_NAME_OF_VACABULARY';
$parent_tid = 0;//parent id
$depth = 2; //depth upto which level you want 
$load_entities = FALSE;
$tree = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid, $parent_tid, $depth, $load_entities);

foreach ($tree as $term) {
     $treeNames[] = array(
      'name' => $term->name
     );
}
dump($treeNames);
0

I have a custom function which works fine in Drupal 9 i.e

  /**
   * Get taxonomy term options for a form field.
   *
   * @param string $taxonomy_term_vid
   *
   * @return array
   */
  public function get_taxonomy_term_options_form($taxonomy_term_vid) {
    // Load taxonomy term data with vid.
    $term = \Drupal::service('entity_type.manager')->getStorage('taxonomy_term')->loadByProperties(['vid' => $taxonomy_term_vid]);

    // Create an array.
    $taxonomy_term_array = [];

    // Loop through field widget and sanitize.
    foreach($term as $key=>$value){
      $taxonomy_term_array[$term[$key]->tid->value] = $term[$key]->name->value;
    }

    return $taxonomy_term_array;
   }
0

if you want to load taxonomy terms with lower memory usage in Drupal 10, you can use the TermStorageInterface::getQuery() method to build a query that retrieves only the term IDs, and then use the TermStorageInterface::load() method to load each term one by one. Here's an example code snippet:

  $entity_type_manager = \Drupal::entityTypeManager();
  $vid = 'your_vocabulary_machine_name';
  $term_storage = $entity_type_manager->getStorage('taxonomy_term');
  $query = $term_storage->getQuery()
    ->condition('vid', $vid);
  $tids = $query->execute();

  foreach ($tids as $tid) {
    $term = $term_storage->load($tid);
    // Do something with the term.
  }

In this example, replace 'your_vocabulary_machine_name' with the machine name of the vocabulary containing the terms you want to load. The $query->execute() method returns an array of term IDs that match the query, which you can then use to load each term individually with the $term_storage->load($tid) method.

This approach is more memory-efficient than using TermStorageInterface::loadMultiple() for large vocabularies, as it loads each term one by one instead of loading all terms into memory at once. However, it may be slower than loading multiple terms at once if the vocabulary is small or if you need to load a large number of terms.

Not the answer you're looking for? Browse other questions tagged or ask your own question.