How to Create a Custom Block that Embeds Nodes in Drupal 8

How to Create a Custom Block that Embeds Nodes in Drupal 8

This is a journey of epic proportions, not suited for the faint of heart. The other day we were looking for a way to embed a certain entity type and set the view mode, well a node, but it could've been any other entity type.

Now this may seem like a fairly easy task at first, but you will need to reach into your pocket of Drupal 8 skills to solve this one. You will need to know about Plugins, Dependency Injection, Interfaces, you name it.

So the journey begins with creating a php file for the block plugin. Easy, right? Open up your IDE, find your custom module (let's call this one project Foo module Bar) and create the file for the block (let's name it Node Embed Block) so /foo/modules/custom/bar/src/Plugin/Block/NodeEmbedBlock.php and then we start the fun.

As you may already know, the Block Plugin extends the BlockBase class.  You'll need your basic methods: build(), blockForm(), blockValidate() [optional] and your blockSubmit().

Assuming you already know how to create a Block plugin and how to put the respective pieces into each function (won't bore you to death with the details) let's skip to the good parts.

Entity Autocomplete: Sure having a text field that takes the NID of the node you want to render sounds effective enough. Much simpler than creating an AJAX callback to do some sort of fancy autocomplete, right? Wrong! All that cool work has already been done for you and all you have to do to leverage it is call this new field type: entity_autocomplete check it out on the blockForm() function in the code below.

Dependency Injection: For the view modes, there's a class that has all that I want (\Drupal\Core\Entity\EntityDisplayRepositoryInterface), but it's not part of my block class. Solution? Dependency injection.

For this we will have to tweak the module a little bit. First we need to declare which classes we're using:

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

Now we're ready to extend the block plugin a little bit more by telling PHP that our class implements the ContainerFactoryPluginInterface. And that means that we need to add two more methods to our class: public function __construct() to assign the class that we want to use  $this->entityDisplayRepository = $entity_display_repository; and public static function create() to create the container and inject our dependency: $container->get('entity_display.repository')

Again, see the code below for the implementation details, for instance in the __construct() function you will need to specify the type of the class you're expecting.

But now that we have all the pieces fitting together, to get the view modes we can now call something like this:

$view_modes = $this->entityDisplayRepository->getAllViewModes();

 

And now the code:

 

<?php

namespace Drupal\bar\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\node\Entity\Node;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * @file
 * Render a node inside a block.
 */

/**
 * Provides a 'Node Embed' block.
 *
 * @Block(
 *   id = "node_embed_block",
 *   admin_label = @Translation("Node Embed"),
 *   category = @Translation("Content")
 * )
 */
class NodeEmbedBlock extends BlockBase implements ContainerFactoryPluginInterface {

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
   */
  protected $entityDisplayRepository;

  /**
   * Constructs a NodeEmbedBlock instance.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the formatter.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
   *   The entity display repository.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityDisplayRepositoryInterface $entity_display_repository) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->entityDisplayRepository = $entity_display_repository;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('entity_display.repository')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    $block['content'] = t('Content not found');

    $config = $this->getConfiguration();

    if (isset($config['nid'])) {
      /* @var $node \Drupal\node\NodeInterface */
      $node = Node::load($config['nid']);
      $view_mode = (isset($config['view_mode']) && !empty($config['view_mode']) ? $config['view_mode'] : 'full');
      $block['content'] = node_view($node, $view_mode);
    }

    return $block;
  }

  /**
   * {@inheritdoc}
   */
  public function blockForm($form, FormStateInterface $form_state) {

    $form = parent::blockForm($form, $form_state);

    // Retrieve existing configuration for this block.
    $config = $this->getConfiguration();

    // Add a form field to the existing block configuration form.
    $form['nid'] = array(
      '#title' => t('Node'),
      '#description' => t('The node you want to display'),
      '#type' => 'entity_autocomplete',
      '#target_type' => 'node',
      '#selection_handler' => 'default',
      '#default_value' => ((isset($config['nid']) && !empty($config['nid'])) ? Node::load($config['nid']) : NULL),
      '#required' => TRUE,
    );

    // View modes.
    $options = [];
    $view_modes = $this->entityDisplayRepository->getAllViewModes();
    if (isset($view_modes['node'])) {
      foreach ($view_modes['node'] as $view_mode => $view_mode_info) {
        $options[$view_mode] = $view_mode_info['label'];
      }
    }

    $form['view_mode'] = array(
      '#title' => t('View mode'),
      '#description' => t('Select the view mode you want your node to render in.'),
      '#type' => 'select',
      '#options' => $options,
      '#default_value' => (isset($config['view_mode']) && !empty($config['view_mode']) ? $config['view_mode'] : 'full'),
      '#required' => TRUE,
    );

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function blockSubmit($form, FormStateInterface $form_state) {
    // Save our custom settings when the form is submitted.
    $this->setConfigurationValue('nid', $form_state->getValue('nid'));
    $this->setConfigurationValue('view_mode', $form_state->getValue('view_mode'));
  }

}

Rob Montero

Rob Montero, Drupal Association Individual Member

Roberto Montero Ortiz is an individual member of the Drupal Association, which fosters and supports the Drupal software project, the community and its growth. He also has an active role in the Drupal Community in Costa Rica and regularly contributes to Drupal Camps held in Latin America.

Rob Montero, Acquia Certified Developer 2014   Rob Montero, Certified Developer Back End Specialist 2015Rob Montero, Acquia Certified Developer Front End Specialist

Rob is also an Acquia Certified Developer, awarded with the Grand Masters title after completing his third certification within a year. 

Zend also certifies Rob's knowledge of PHP OOP skills, but at this point this block is getting a bit presumptious. Suffice it to say, Rob is competitive and competent. He's also a nice guy all around.

Drupal Latin America

Rob Montero is a proud sponsor and mentor of young Drupal talent of the Costa Rican Drupal Users Group and entrepreneur at Drupal Latin America

CHECK THEM OUT

Get in touch

  • Rob Montero
    Ave Popocatepetl 435 B 504
    Sta Cruz Atoyac, Benito Juarez
    Ciudad de México, 03310 
  • (858) 952 0762
  • roberto@montero-ortiz.com
Signup to our newsletter:
Error | Roberto Montero

Error

The website encountered an unexpected error. Please try again later.