Backend Drupal 8 101 (Part 1): Data Digging in Drupal 8
Backend Drupal 8 101 (Part 1): Data Digging in Drupal 8
Ryan Loos | Developer
July 12, 2018
One of the most fundamental tasks of back-end Drupal 8 development is learning how to capture and utilize data. Unfortunately, as a new developer, trying to do so feels like wandering into an endless labyrinth of arrays, methods, objects, and arcane wizardry.
Say you want to get the summary off of a body field so you try something like $node->values['field_article_body'][0]['summary']
, but that doesn’t work. So you remember that you probably need to use a get method and you remember seeing something like $node->getValue('field_article_body')
before, but that doesn’t work either.
Suddenly you find yourself lost in the labyrinth and desperately hoping for one of your guesses to be correct only to eventually get eaten by a minotaur (read: get frustrated and give up). Now, If you remember your Greek mythology, the way Theseus was able to triumph over the labyrinth where others had failed was by tying some twine to himself so that he could retrace his steps. The point of this blog post is to give you that twine so that next time you don’t have to guess your way to the data.
Remember your training
First, remember that D8 is based on object oriented programming (OOP) and think about what that really means. While it is indeed intricate, at its core it’s really just a series of classes and objects that extend off of each other. Plugins, entities, blocks, services, etc. might sound like complex concepts, but at the end of the day these are all just different kinds of classes with different uses and rulesets.
For a long time I was conscious of D8’s OOP nature, but I really only thought of it in terms of building new things from that foundation. I never thought about this crucial principle when I was trying to pull data out of the system, and pulling in my OOP knowledge was the first step in solving this problem.
Down into the labyrinth
Let’s take a simple example. Say you have the following node loaded and you want to use the title of the node.
(note: these screenshots are from xdebug, but you can get the same information by printing the variables to the page using var_dump() or kint())
After digging around in the node you find the title here:
But as we’ve already established, something like $node->values['title']
won’t work. This is because $node
is not simply an array, it’s a full object. Near the top you’ll notice that Xdebug is telling us exactly what class is creating the object below, Drupal\node\Entity\Node
. If you go to that file you will see the following method on that class that will get you the data that you need:
public function getTitle() { return $this->get('title')->value; }
Meaning, you can just run $node->getTitle()
to get that nodes’s title. Notice the host of other useful functions there as well, getCreatedTime()
, getOwner()
,postSave()
. All of these methods and more are available and documented for when you want to manipulate that node.
These aren’t the only methods you have available to you. In fact, if you look at the actual code in the getTitle()
function you’ll see that it’s using a get method that’s nowhere to be found in this class. The rules of OOP suggest that if the method is useable but not in the class itself it’s probably being extended from another class. In fact, the class declaration for node extends EditorialContentEntityBase
, which might not have anything useful on its own but it does extend ContentEntityBase
which holds a plethora of useful methods, including the aforementioned get function!
public function get($field_name) { if (!isset($this->fields[$field_name][$this->activeLangcode])) { return $this->getTranslatedField($field_name, $this->activeLangcode); } return $this->fields[$field_name][$this->activeLangcode]; }
Notice how this get method seems to be designed for getting field values, we could probably get closer to the summary value I mentioned earlier by going $node->get(‘field_article_body')
. If you run that method you get a different object entirely, a FieldItemList
.
Once again, we can dig through the provided classes and available methods. FieldItemList
extends ItemList
which has a getValue()
method which gets us even closer.
Now, instead of an object, we’re returning a simple array, which means we can use regular array notation to finally get that summary value: $node->get('field_article_body')->getvalue()[0]['summary']
.
So what did we actually do?
Pay special attention to the structure of this final call we’re using. Parsing it out like so demonstrates that it’s no mere guess-and-check, rather it’s a very logical sequence of events.
/** @var \Drupal\node\Entity\Node $field */ $field = $node->get('field_article_body'); // Object notation to get a property from an object /** @var \Drupal\Core\Field\FieldItemList $field_values*/ $field_values = $field->getvalue(); // Object notation to get a property from an object /** @var array $summary */ $summary = $field_values[0]['summary']; // Array notation to get the value from an array
This also makes it obvious why our previous attempt of $node->values['title'] can’t work. It’s trying to get a values
property off node object, when such a thing doesn’t exist in the node class declaration.
Rule-breaking magic!
That being said, another perfectly valid way to get the summary field is $node->field_article_body->summary
. Now, on first glance, this appears to contradict what I just said. The $node
object obviously doesn’t have a field_article_body
property in its class declaration. The reason this works is because it is successfully calling a magic method. PHP has a number of magic methods that can always be called on an object, these methods are easy to find because they start with a double underscore (__set()
, __get()
, __construct()
, etc.). In this case, since we’re attempting to call a property that does not exist on the class, Drupal knows to use the __get()
magic method to look at the properties on this instance of the object, and this instance is a node with a field named field_article_body
and by definition a property of the same name. If you look further down the ContentEntityBase
class you’ll see this very &__get()
method.
PHPstorm Shortcuts
It’s worth noting that if you’re writing your code in an IDE like PHPstorm, this whole process gets a lot easier because the autocomplete will show you all the methods that are available to you regardless of which class they come from. That being said, being able to manually drill down through the classes is still useful for when you need a clearer idea of what the methods are actually doing.
Another amazing PHPstorm tool is being able to jump directly to the declaration of a method to see where it’s coming from. For instance in the aforementioned getTitle()
method, you can right click the contained get()
method and select Go To > Declaration to jump directly to the section of ContentEntityBase
where that get()
method is first declared.
Whats Next?
Don’t worry if this doesn’t make perfect sense your first time through, this is a complex system. It might help to think of this as simply reverse Object Oriented Programming. Rather than using OOP to build something new, you’re reverse-engineering it to grab something that already exists. I recommend messing around with this technique in your own Drupal environment. Try it with different object types, try it in different contexts (for instance, in an alter hook versus in a plugin). The more practice you get, the more comfortable you’ll feel with this essential technique.
Thanks for reading Part 1 of my Backend Drupal 8 series. Check back soon for Part 2, where I’m going to try to convince you to start using Xdebug.