Drupal 8 OOP Part 1: Building a Block

Posted By Hillary Lewandowski on Thursday, May 28, 2015 - 16:56

Object Oriented Programming (or OOP for short) organizes code into objects with properties and behaviors. Drupal developers are comfortable with the paradigm of procedural programming, which uses functions to pass in data, manipulate it, and return something. In the following post, I will help explain to tech-savvy developers how OOP will look and feel in Drupal 8.

As a Drupal developer, objects seem foreign because we work in a procedural mindset every day. We rely on steps that must be executed in a certain order for things to work. Drupal 8 will still let us work in the way we’re used to, with hooks and custom functions, so don’t fret. But, we have the opportunity to step outside of our comfort zone and use objects where it makes the most sense.

Block Plugin API

Here at Commercial Progression, we absolutely love blocks. Blocks are a great introduction to learning what’s different about Drupal 7 and 8. In Drupal 7, to create a new block in code, you did something like this:

  1. /**
  2.  * @file
  3.  * Provides a block crediting CP for site design.
  4.  */
  5.  
  6. /**
  7.  * Implements hook_block_info().
  8.  */
  9. function compro_credit_block_info() {
  10.   $blocks['compro_credit'] = array(
  11.     'info' => t('Compro credit'),
  12.   );
  13.   return $blocks;
  14. }
  15.  
  16. /**
  17.  * Implements hook_block_view().
  18.  */
  19. function compro_credit_block_view($delta = '') {
  20.   $block = array();
  21.  
  22.   switch ($delta) {
  23.     case 'compro_credit':
  24.       $block['content'] = compro_credit_block_content();
  25.       break;
  26.   }
  27.   return $block;
  28. }
  29.  
  30. /**
  31.  * Content for compro credit block.
  32.  */
  33. function compro_credit_block_content() {
  34.   // Render array that returns link and text for block content.
  35.   $output = array(
  36.     'compro_text' => array(
  37.       '#markup' => l(t('Drupal website design'), 'http://www.commercialprogression.com') .
  38.                   ' ' . t('by Commercial Progression'),
  39.     ),
  40.   );
  41.   return $output;
  42. }

Here’s an example of the same block in Drupal 8:

  1. namespace Drupal\compro_credit\Plugin\Block;
  2. use Drupal\Core\Block\BlockBase;
  3. use Drupal\Core\Url;
  4. /**
  5.  * Provides a block crediting CP for site design.
  6.  *
  7.  * @Block(
  8.  *   id = "compro_credit",
  9.  *   admin_label = @Translation("Compro credit"),
  10.  * )
  11.  */
  12. class ComproCreditBlock extends BlockBase {
  13.   /**
  14.    * {@inheritdoc}
  15.    */
  16.   public function build() {
  17.     // External Uri.
  18.     $url = Url::fromUri('http://www.commercialprogression.com');
  19.     $external_link = \Drupal::l(t('Drupal website design'), $url);
  20.     // Render array that returns link and text for block content.
  21.     return array(
  22.       'compro_text' => array(
  23.         '#markup' => $external_link . ' ' . t('by Commercial Progression'),
  24.       ),
  25.     );
  26.   }
  27. }

These two pieces of code create a block called “compro_credit” which outputs the linked text “Drupal website design by Commercial Progression.” In Drupal 7, we’re writing 3 separate functions, but in Drupal 8 we can do this with one method in one child class.

Inheritance

In an extremely simplified definition of inheritance, a parent class defines default behaviors for its children. Therefore, child classes can reuse the code they are inheriting from their parent class. Here we have a file called ComproCreditBlock.php, with a ComproCreditBlock class. Instead of implementing hook_block_view() to define the content of our block, all we have to do is extend the BlockBase class to define our parent class.

  1. class ComproCreditBlock extends BlockBase

This allows us to inherit all of the properties and behaviors of a basic block, such as a configuration screen with cache, visibility, and region settings. We can change or build upon these behaviors by overriding or implementing methods, which are functions specific to an object class. In this example, we are implementing the build() method to define the content of our block. This very basic level of inheritance is what will be most common in custom Drupal 8 code.

Interfaces and the Plugin API

In Drupal 8, a block is a type of Plugin. In our particular piece of code here, we’re not worrying about the BlockPluginInterface because BlockBase is doing that for us.

  1. abstract class BlockBase implements BlockPluginInterface

The interface helps define how all blocks should act in the system by giving them required methods to implement. BlockBase, our parent class, knows to give us a configuration screen, so we don’t have to worry about setting them up with every new block we create. And we don’t have to worry about the code that does this for us, until we specifically want to change something, or add to it. Our BlockBase class does not implement the required build() method, so any class that extends BlockBase needs to implement build() to define it’s custom content.

Annotations

In our code here, we’ve replaced hook_block_info() with annotations right above our class.

  1. /**
  2.  * @Block(
  3.  *   id = "compro_credit_block",
  4.  *   admin_label = @Translation("Compro credit"),
  5.  * )
  6.  */

We’ve defined our block ID and administrative title within the @Block annotation. Annotations allow us to have configuration adjacent to our functions instead of in a separate file. This helps keep all necessary data for a plugin in one file, is useful for dependency injection, and even helps with performance. Because annotations look like comments, they can be cause for some confusion. Keep documentation above annotations to avoid confusion - your comments should be just as clean as your code.

Namespaces

  1. namespace Drupal\compro_credit\Plugin\Block;
  2. use Drupal\Core\Block\BlockBase;
  3. use Drupal\Core\Url;

The bigger Drupal has become, the more common it is to run into name collision, where two methods have the same name. Namespaces help to organize our code into packages, and avoid name collision without making our functions names unnecessarily long. We could create multiple classes named “Block” within our project, but PHP would not throw any errors as long as they exist within their own namespace. We should still name our classes in terms of what they will do, but now we can worry less about name collision with third party modules.

In our example, we declare the namespace of our custom class at the very top to distinguish it from classes in the global namespace. Then, we use other classes by referencing their namespaces. In our example, we extended BlockBase, so we needed to tell our file where that class lives, and we also needed to construct an external link with the Url class. We could omit these lines at the top and reference the namespace everywhere we needed to like so:

  1. class ComproCreditBlock extends Drupal\Core\Block\BlockBase

  1. $url = Drupal\Core\Url::fromUri('http://www.commercialprogression.com');

but this results in messier, harder to read code.

I hope you’re a little less intimidated by OOP now. I skipped over some concepts in the code here - like abstract classes, instantiation, references, dependency injection, and design patterns - but I’m going to save those more advanced lessons for later blog posts.

Drupal 8 website design and development by Commercial Progression