Drupal - Why do so few code examples use the ::load($id) method for loading entities?

There is actually an issue about adding this info to the docs.

In short, the long winded version is unit test friendly.

A comment by one of the Core's contributors fully detailed explains why. Just to highlight the main point:

If we call Node::load() in OO code we are breaking this principle (dependency inversion), because we are hardcoding the specific implementation (\Drupal\node\Entity\Node::load() to be exact). This code path is hard coupled to \Drupal::entityManager() which is hard coupled to \Drupal::$container. So now our code becomes impossible to run outside of Drupal.

Furthermore, since code that uses the dependency inversion principle is fully standalone it is very easy to unit test it by mocking the dependencies according to the requested interfaces. If we introduce Node::load() then it becomes really hard to "properly" unit test the class, since it will start polluting the test environment with external code, and the dependency injection container is not available in unit tests.

Now if you wonder then why does Entity::load exists

There were many loud voices at the time that D8 was going to be a DX disaster, that the OO approach was too difficult to understand and write, that it would never get adopted by the wider community. There was a huge push going on to make D8 more DX friendly, mainly driven by the Backdrop fork that was announced a few months earlier.

So we created these static methods that wrap the loading of the entity manager from the container, and can be called directly from the procedural side. It was great, since it offers a drop-in replacement that works and looks very much like the original code. This made porting of procedural D7 code vastly easier:

// Original D7 procedural code. 
$node = node_load($nid);

// New D8 procedural code. 
$node = Node::load($nid);

I think this is about the target audience of the code example. If it is for a hook, then I agree, use the first more easy to read approach.

If it is for a service, then the correct approach would be to inject the entity type manager and use it instead of the static \Drupal wrapper.

$term = $this->entityTypeManager->getStorage('taxonomy_term')->load($tid);

Now the question is what if you want to provide a more universal code example, then

$term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($tid);

is probably the best approach, because it works in procedural code if you copy and paste it and can be easily adapted for a service where you want to inject the dependencies.

Tags:

Entities

8