Programmatically render node field respecting the view mode settings in Drupal 10

In Drupal, sometimes you may want to render a node's field inside a block while still respecting the view mode settings. In this blog post, we will demonstrate how to achieve this using Drupal 10.

Using the view() Method

To render a single field with the display setting of a view mode, you can use the view() method of the field. Here's an example of rendering an image as it's configured in the node's teaser view-mode:


$build['image'] = $node->field_image->view('teaser');

Or the body as it's configured in the node's full view-mode:


$build['body'] = $node->body->view('full');

If you want to have more fine-grained control, you can programmatically recreate how Drupal is doing view-modes:


$display_options = [
  'label' => 'hidden',
  'type' => 'entity_reference_entity_view',
  'settings' => [
    'view_mode' => 'some_media_image_view_mode',
  ],
];
$build['image'] = $node->field_image->view($display_options);

You can figure out the type/settings by inspecting the HTML on the view-modes form (e.g. /admin/structure/types/manage//display/full) or export the configuration and inspect the YAML files.

Using the ViewBuilder

Another approach is using the ViewBuilder to render fields separately. Here's an example:


public function build() {
  $node = \Drupal::routeMatch()->getParameter('node');
  $build = array();
  $markup = array();

  $fieldsToRender = array(
    'field_node_ref', 'field_foo', 'field_bar',
  );

  $viewmode = 'default';
  $entityType = 'node';
  $display = entity_get_display($entityType, $node->getType(), $viewmode);
  $viewBuilder = \Drupal::entityTypeManager()->getViewBuilder($entityType);

  foreach ($fieldsToRender as $field_name) {
    if (isset($node->{$field_name}) && $field = $node->{$field_name}) {
      $fieldRenderable = $viewBuilder->viewField($field, $display->getComponent($field_name));
      if (count($fieldRenderable) &&! empty($fieldRenderable)) {
        $markup[] = \Drupal::service('renderer')->renderRoot($fieldRenderable);
      }
    }  
  }

  if (count($markup)) {
    $build = array(
      '#type' => 'markup',
      '#markup' => implode("", $markup),
    );
  }

  return $build;
}

This code uses the $viewBuilder->viewField method to render any fields separately as needed. Note that you might need to add caching depending on the view mode settings, but that would be a separate topic.

 

Saved you some valuable time?

Buy me a drink 🍺 to keep me motivated to create free content like this!