Upgrade Your Drupal Skills
We trained 1,000+ Drupal Developers over the last decade.
See Advanced Courses NAH, I know EnoughWriting custom fields in Drupal 8 - Part 2
Continuing from Evan's blog post on building pages with Paragraphs and writing custom blocks of content as fields, I will walk you through how to create a custom field-formatter in Drupal 8 by example.
A field-formatter is the last piece of code to go with the field-type and the field-widget that Evan wrote about in the previous blog post. While the field-type tells Drupal about what data comprises a field, the field-formatter is responsible for telling Drupal how to display the data stored in the field.
To recap, we defined a hashtag_search
field type in the previous blog post whose instances will be composed of two items: the hashtag to search for, and the number of items to display. We want to convert this
data into a list of the most recent n tweets with the specified hashtag.
A field-formatter is a Drupal plugin, just like its respective field-type and field-widget. They live in
and are namespaced appropriately: Drupal\
.
namespace Drupal\my_module\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;
class HashtagFormatter extends FormatterBase
{
public function viewElements(FieldItemListInterface $items, $langcode)
{
return array();
}
}
We tell Drupal important details about our new field-formatter using a @FieldFormatter
class annotation. We declare its unique id
; a human-readable, translatable label
; and a list of field_types
that it supports.
The most important method in a field-formatter is the viewElements
method. It's responsibility is returning a render array based on field data being passed as
.
Let's look at the code:
use Drupal\my_module\Twitter\TwitterClient;
use Drupal\my_module\Twitter\TweetFormatter;
...
protected $twitter;
protected $formatter;
...
public function viewElements(FieldItemListInterface $items, $langcode)
{
$element = [];
foreach ($items as $delta => $item) {
try {
$results = $this->twitter->search($item->hashtag_search, $item->count);
$statuses = array_map(function ($s) {
$s['formatted_text'] = $this->formatter->toHtml($s['text'], $s['entities']);
return $s;
}, $results['statuses']);
if (!empty($statuses)) {
$element[$delta]['header'] = [
'#markup' => '#'</span> . $item->hashtag_search . '
'
];
}
foreach ($statuses as $status) {
$element[$delta]['status'][] = [
'#theme' => 'my_module_status',
'#status' => $status
];
}
} catch (\Exception $e) {
$this->logger->error('[:exception]: %message', [
':exception' => get_class($e),
'%message' => $e->getMessage(),
]);
continue;
}
}
$element['#attached']['library'][] = 'my_module/twitter_intents';
return $element;
}
...
See https://github.com/bezhermoso/tweet-to-html-php for how
TweetFormatter
works. Also, you can find the source-code for the basic Twitter HTTP client here: https://gist.github.com/bezhermoso/5a04e03cedbc77f6662c03d774f784c5
Custom theme renderer
As shown above, each individual tweets are using the my_module_status
render theme. We'll define it in the my_module.module
file:
function my_module_theme($existing, $type, $theme, $path) {
$theme = [];
$theme['my_module_status'] = array(
'variables' => array(
'status' => NULL
),
'template' => 'twitter-status',
'render element' => 'element',
'path' => $path . '/templates'
);
return $theme;
}
With this, we are telling Drupal to use the template file modules/my_module/templates/twitter-status.twig.html
for any render array using my_module_status
as its theme.
Render caching
Drupal 8 does a good job caching content: typically any field formatter is only called once and the resulting collective render arrays are cached for subsequent page loads until the Drupal cache is cleared. We don't really want our Twitter block to be cached for that long. Since it is always great practice to keep caching enabled, we can define how caching is to be applied to our Twitter blocks. This is done by adding cache definitions in the render array before we return it:
public function viewElements(...)
{
...
$element['#attached']['library'][] = 'my_module/twitter_intents';
$element['#cache']['max-age'] = 60 * 5;
return $element;
}
Here we are telling Drupal to keep the render array in cache for 5 minutes. Drupal will still cache the rest of the page's elements how they want to be cached, but will call our field formatter again -- which pulls fresh data from Twitter -- if 5 minutes has passed since the last time it was called.
About Drupal Sun
Drupal Sun is an Evolving Web project. It allows you to:
- Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
- Facet based on tags, author, or feed
- Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
- View the entire article text inline, or in the context of the site where it was created
See the blog post at Evolving Web