Blob of contradictions

Saving and retrieving taxonomy terms programmatically for Drupal 8


Over the past month, I've been attempting to learn more about Drupal 8 by attempting to port the @font-your-face module which has a lot of different pieces; it touches on Content Entities, Config Entities, regular entities, views, classes, hooks, and more! I'll try and blog on my experiences with that in the near future but an interesting problem that I ran into is that I am using taxonomy terms to categorize Font Classifications, supported languages/subsets, and generic tags. However, because fonts are imported from various sources (and thus are almost never created by a user), the various entities need to be saved programmatically. And since terms cannot be passed directory for Taxonomy to try and associate/create, we need a way to do so ourselves.

But the first step is figuring out how to save entities. The way this is done in drupal 8 is the following (substitute ENTITY with the name of your entity):

  1. $content = ENTITY::create([
  2. 'property' => 'value',
  3. 'property2' => 'value2',
  4. 'etc' => '...'
  5. ]);
  6. $content->save();

Assuming you have named the fields/properties correctly, its a fairly elegant way to save your data. Taxonomies do follow this pattern, so a term can be saved as:

  1. $term = Term::create([
  2. 'name' => $term_value,
  3. 'vid' => 'vocabulary',
  4. ]);
  5. $term->save();

And this will save a term to the right vocabulary. However, since we also want unique terms (or we could have a tag saved over and over), we need to first do a check and make sure the term does not already exist:

  1. if ($terms = taxonomy_term_load_multiple_by_name($term_value, 'vocabulary')) {
  2. // Only use the first term returned; there should only be one anyways if we do this right.
  3. $term = reset($terms);
  4. }
  5. else {
  6. $term = Term::create([
  7. 'name' => $term_value,
  8. 'vid' => 'vocabulary',
  9. ]);
  10. $term->save();
  11. }

Now going back to our first problem, I have the necessary pieces to be able to retrieve a term for a given vocabulary (or create and retrieve if it does not previously exist). But repeating this piece of code over and over again would be fairly pointless so we can wrap this into a nice function:

  1. /**
  2.  * Retrieves a tid for use from taxonomy.
  3.  */
  4. function _fontyourface_get_vocabulary_term($term_value, $vocabulary) {
  5. if ($terms = taxonomy_term_load_multiple_by_name($term_value, $vocabulary)) {
  6. $term = reset($terms);
  7. }
  8. else {
  9. $term = Term::create([
  10. 'name' => $term_value,
  11. 'vid' => $vocabulary,
  12. ]);
  13. $term->save();
  14. }
  15. return $term->id();
  16. }

Barring the docblock that needs fixing, we can now have something like this which can be reused over and over for various terms:

  1. if (!empty($font_data->classification)) {
  2. $font->field_classification = [];
  3. foreach ($font_data->classification as $classification) {
  4. $font->field_classification[] = [
  5. 'target_id' => _fontyourface_get_vocabulary_term($classification, 'font_classification'),
  6. ];
  7. }
  8. }

And voila, what could have been a giant pain is surprisingly simple.