Drupal 8 Custom Block Module Dev

For those ready to move beyond a simple “Hello World” module, this post documents building a Drupal 8 module with a Giphy search form in a custom block. The form uses jQuery to request data from the Giphy API and display the results.

Drupal 8 module to demonstrate custom block creation with the following features:

  • configuration data that is passed into the modules js library
  • public API search using core jQuery functions
  • twig template for a front end Giphy search form
Source Code

Module Folder

To get started, create a folder for the module in either /modules/custom/ or /sites/all/modules/. The name of the module folder is typically the same as the name given to the module. In the case of this particular module, giphys is an appropriate name. For example:

# context is drupal project root

cd modules
mkdir custom
mkdir custom/giphys

Module Info File

An info.yml file is needed to store module metadata. Since the module machine name is giphys, the file will be named giphys.info.yml

# create the giphys.info.yml file

cd custom/giphys
touch giphys.info.yml

Here are the metadata contents of this file. Included is data to inform Drupal of the core compatibility, module dependencies and a description for the administration portal interface.

giphys.info.yml
name: Giphys
type: module
description: 'Giphys is a Giphy search block'
core: 8.x
package: Other
dependencies:
  - block

After saving giphys.info.yml, in Drupal, select the Extend menu, e.g., http://drupal.docker.localhost:8000/admin/modules. Giphys should be listed under Other:

Extend | Giphys

Module File

The giphys.module file is the entrypoint used to define help and theme hook functions that return respective data and paths. .module files should only contain functions that implement hooks.

# create the giphys.module file

touch giphys.module
giphys.module
<?php

/**
 * @file
 * Module file for giphys_module.
 */

use Drupal\Core\Routing\RouteMatchInterface;

/**
 * Implements hook_help().
 *
 * @see https://www.drupal.org/documentation/help-text-standards
 *
 * @see hook_help()
 */
function giphys_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.giphys':
      // Help text for the admin section, using the module name in the path.
      return t("This is help text created in giphys implementation of hook_help().");
  }
}

function giphys_theme($existing, $type, $theme, $path) {
  return [
    'giphys' => [
      'variables' => [
        'url' => 'http://example.com',
        'secret' => NULL
      ],
    ],
  ];
}

Twig Template

Note that Drupal 8 currently uses Twig version 1.x, and Drupal 8.4 is slated to use Twig version 2.x. Twig 2 also requires PHP 7.

# create the templates/giphys.html.twig file

mkdir templates

touch templates/giphys.html.twig
giphys.html.twig
<div>
<form id="giphys-search" accept-charset="UTF-8">
<label for="giphys-search-text" class="visually-hidden">Search</label>
<input id="giphys-search-text" class="required" title="Enter the terms you wish to search for." type="search" value="" size="15" maxlength="128" required="required" aria-required="true" />
<input class="search-form__submit button js-form-submit form-submit" type="submit" value="Search" />
</form>
<ul class="giphys-list"></ul>
</div>

Module Libraries

CSS and JavaScript for the module is defined in a libraries YAML data file. The CSS and JavaScript assets are loaded in the order they are listed. Additionally, core jQuery is required by the module and is listed as a module dependency to ensure that it gets loaded.

Create the giphys.libraries.yml in the root of the giphys folder together with the other data files.

# create the giphys.libraries.yml file

touch giphys.libraries.yml
giphys.libraries.yml
giphys:
  version: 1.x
  css:
    theme:
      css/giphys.css: {}
  js:
    js/giphys.js: {}
  dependencies:
    - core/jquery

CSS

The CSS for our module is very simple with only a few tweaks to display the giphy API search results list as tiles. Create a new css folder and the giphys.css file as shown.

# create the css/giphys.css file

mkdir css

touch css/giphys.css
giphys.css
ul.giphys-list {
    list-style: none;
    padding: 0;
    margin: 0;
}
ul.giphys-list li {
    margin-right: 5px;
    display: inline-block;
}

JavaScript

This file uses the drupalSettings object to get the Giphys module block configuration data. This data is used to make the Giphy API request when a user enters a search term. The results are parsed into html elements and injected into the unordered list element output by the twig template.

# create the js/giphys.js file

mkdir js

touch js/giphys.js
giphys.js
console.log(drupalSettings.giphys);

(function ($) {
  var $giphysList,
      giphysEndpoint,
      giphysSearchTerm;

  giphysEndpoint = drupalSettings.giphys.url + '?api_key=' + drupalSettings.giphys.secret;

  $giphysList = $('ul.giphys-list');

  $('#giphys-search').submit( function(e) {
    e.preventDefault();

    $giphysList.empty();

    giphysSearchTerm = $('#giphys-search-text').val();

    $.getJSON(giphysEndpoint + '&q=' + giphysSearchTerm).done(function(data) {
      if (data) {

        var $giphysListItem,
            giphysData = data.data,
            len = giphysData.length;

        for(var i = 0; i < len; i++) {
          $giphysListItem = '<li><img src="'+ giphysData[i].images.fixed_height_small.url +'" /></li>';
          $giphysList.append($giphysListItem);
        }
      }
    });

  });

})(jQuery);

Module PHP

The GiphysBlock.php uses an instance of the block plugin to define a custom block. Also contained in the GiphysBlock class are functions that add a form to the block configuration for setting the API secret, URL and default search term.

The build function attaches the block configuration values to the drupalSettings object for the front end code to use.

# create the src/Plugin/Block/GiphysBlock.php file

mkdir src
mkdir src/Plugin
mkdir src/Plugin/Block

touch src/Plugin/Block/GiphysBlock.php
GiphysBlock.php
<?php

namespace Drupal\giphys\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Defines a Giphys block block type.
 *
 * @Block(
 *   id = "giphys_block",
 *   admin_label = @Translation("Giphys block"),
 *   category = @Translation("Giphys"),
 * )
 */
class GiphysBlock extends BlockBase {

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

    $config = $this->getConfiguration();

    $form['url'] = [
      '#type' => 'textfield',
      '#title' => $this->t('url'),
      '#default_value' => 'http://api.giphy.com/v1/gifs/search',
      '#required' => TRUE,
      '#description' => $this->t('api url'),
    ];

    $form['secret'] = [
      '#type' => 'textfield',
      '#title' => $this->t('secret'),
      '#default_value' => 'dc6zaTOxFJmzC',
      '#required' => TRUE,
      '#description' => $this->t('api key'),
    ];

    $form['term'] = [
      '#type' => 'textfield',
      '#title' => $this->t('term'),
      '#default_value' => 'trump frog',
      '#required' => FALSE,
      '#description' => $this->t('default search query term or phrase'),
    ];

    return $form;

  }

  /**
   * {@inheritdoc}
   */
  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->setConfigurationValue('url', $form_state->getValue('url'));
    $this->setConfigurationValue('secret', $form_state->getValue('secret'));
    $this->setConfigurationValue('term', $form_state->getValue('term'));
  }

  /**
   * {@inheritdoc}
   */
  public function build() {

    $config = $this->getConfiguration();

    return array(
      '#theme' => 'giphys',
      '#attached' => array(
        'drupalSettings' => array(
            'giphys' => array(
                'url' => $config['url'],
                'secret' => $config['secret'],
                'term' => $config['term']
            )
        ),
        'library' => array(
          'giphys/giphys',
        ),
      ),
    );

  }
}

Here is what the Giphys block looks like placed in the content area with search results shown.

Giphys block placed
Source Code

Resources


Loading Disqus Comments ...
Loading Facebook Comments ...