On a recent project we had to create a section that is basically a Twitter search for a hashtag. It needed to be usuable in different sections of the layout and work the same. Also, we were using the Paragraphs module and came up with a pretty nifty (we think) solution of creating a custom field that solved this particular problem for us. I will walk you through how to create a custom field/widget/formatter for Drupal 8. There are Drupal console commands for generating boilerplate code for this... which I will list before going through each of the methods for the components.
Field Type creation
The first thing to do is create a custom field. In a custom module (here as "my_module") either run drupal:generate:fieldtype
or create a file called HashTagSearchItem.php
in src/Plugin/Field/FieldType
. The basic structure for the class will be:
<?php
namespace Drupal\my_module\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Plugin implementation of the 'hashtag_search' field type.
*
* @FieldType(
* id = "hashtag_search",
* label = @Translation("Hashtag Search"),
* description = @Translation("An field for a hashtag search"),
* default_widget = "hashtag_search_widget",
* default_formatter = "hashtag_formatter"
* )
*/
class HashtagSearchItem extends FieldItemBase {
/// methods here.
}
Next, implement a few methods that will tell Drupal how our field will be structured. Provide a default field settings for the field that will be the count for the amount of tweets to pull. This will return of default settings keyed by the setting's name.
<?php
/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
return [
'count' => 6
] + parent::defaultFieldSettings();
}
Then provide the field item's properties. In this case there will be an input for hashtag and a count. Each property will be keyed by the property name and be a DataDefinition
defining what the properties will hold.
<?php
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties = [];
$properties['hashtag_search'] = DataDefinition::create('string')
->setLabel(t('The hashtag to search for.'));
$properties['count'] = DataDefinition::create('integer')
->setLabel(t('The count of twitter items to pull.'));
return $properties;
}
Then provide a schema for the field. This will be the properties that we have created above.
<?php
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return [
'columns' => [
'hashtag_search' => [
'type' => 'varchar',
'length' => 32,
],
'count' => [
'type' => 'int',
'default' => 6
]
]
];
}
Field widget creation
Next create the widget for the field, which is the actual form element and it's settings. Either drupal:generate:fieldwidget
or create a file in src/Plugin/Field/FieldWidget/
called HashtagSearchWidget.php
. This is the class' skeleton:
<?php
namespace Drupal\my_module\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
/**
* Plugin implementation of the 'hashtag_search_widget' widget.
*
* @FieldWidget(
* id = "hashtag_search_widget",
* label = @Translation("Hastag Search"),
* field_types = {
* "hashtag_search"
* },
* )
*/
class HashtagSearchWidget extends WidgetBase {
/// methods here
}
Then implement several methods. Provide a default count of tweets to pull for new fields and the settings form for the field item:
<?php
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return [
'default_count' => 6,
] + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements = [];
$elements['default_count'] = [
'#type' => 'number',
'#title' => $this->t('Default count'),
'#default_value' => $this->getSetting('default_count'),
'#empty_value' => '',
'#min' => 1
];
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = [];
$summary[] = t('Default count: !count', array('!count' => $this->getSetting('default_count')));
return $summary;
}
Then create the actual form element. Add the hashtag textfield and count number field and wrap it in a fieldset for a better experience:
<?php
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$item = $items[$delta];
$element['hashtag_search'] = [
'#type' => 'textfield',
'#title' => $this->t('Hashtag'),
'#required' => FALSE,
'#size' => 60,
'#default_value' => (!$item->isEmpty()) ? $item->hashtag_search : NULL,
];
$element['count'] = [
'#type' => 'number',
'#title' => $this->t('Pull count'),
'#default_value' => $this->getSetting('default_count'),
'#size' => 2
];
$element += [
'#type' => 'fieldset',
];
return $element;
}
In part 2, Bez will show you how to pull the tweets and create a field formatter for the display of the tweets. You can read that post here!