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:
- For information on how to setup a Drupal Docker development environment, read my post on the subject at jimfrenette.com/2017/05/docker-drupal-dev-environment.
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.