概要:カスタムフィールドタイプの作成
このチュートリアルは元々Web Washで公開されていました。しかし、Berdirがチュートリアルをここに掲載できるか尋ねてきたので、ここにあります。
Drupal 7のモジュールでは、コードの例/スニペットをフィールドに保存できます。「スニペットフィールド」というカスタムフィールドが付属しており、3つのフォーム要素(説明、ソースコード、構文ハイライトモード(プログラミング言語))が表示されます。
しかし、今こそモジュールをDrupal 8に更新する時です。
このチュートリアルでは、Drupal 8で「基本的な」カスタムフィールドをどのように作成したかを示します。PSR-4、アノテーション、プラグインの詳細には触れません。そうするとこのチュートリアルが巨大になってしまうからです。
代わりに、この概念をさらに説明している他のサイトへのリンクを追加します。
ただし、Drupal 8のField APIの詳細なドキュメントを探している場合は、次のシリーズを確認してください:
Drupal 8では、フィールドはDrupal 7のようにフックを使用して実装されません。代わりに、新しいDrupal 8プラグインAPIを使用して作成されます。つまり、フックを実装する代わりに、ウィジェット、フォーマッター、フィールドアイテムのクラスを定義します。hook_field_schema、hook_field_is_emptyなどのDrupal 7のフックのほとんどは、現在ではクラスのメソッドになっています。
ステップ1:フィールドアイテムの実装
最初に必要な作業は、FieldItemBaseクラスを拡張するSnippetsItemという名前のフィールドアイテムクラスを定義することです。
1. Drupal 8では、クラスはPSR-4を使用して読み込まれます。
したがって、SnippetsItemクラスを定義するには、SnippetsItem.phpファイルを作成し、それをモジュールの/src/Plugin/Field/FieldType/SnippetsItem.phpに配置する必要があります
/**
* @file
* Contains \Drupal\snippets\Plugin\Field\FieldType\SnippetsItem.
*/
namespace Drupal\snippets\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
次に、ファイルにDrupal\snippets\Plugin\Field\FieldType名前空間と3つのuseステートメントを追加します:
- Drupal\Core\Field\FieldItemBase、
- Drupal\Core\Field\FieldStorageDefinitionInterface
- Drupal\Core\TypedData\DataDefinition。
2. 次に、フィールドID、ラベル、デフォルトのウィジェット、フォーマッターなどの実際のフィールドの詳細を定義する必要があります。これは、Drupal 7のhook_field_infoの実装に相当します。
Drupal 8では、すべてではないにしても多くの情報フックがアノテーションに置き換えられました。
/**
* Plugin implementation of the 'snippets' field type.
*
* @FieldType(
* id = "snippets_code",
* label = @Translation("Snippets field"),
* description = @Translation("This field stores code snippets in the database."),
* default_widget = "snippets_default",
* default_formatter = "snippets_default"
* )
*/
class SnippetsItem extends FieldItemBase { }
したがって、hook_field_infoを実装する代わりに、クラス上のコメント内でフィールドをアノテーションとして定義します。
アノテーションの属性は一目瞭然です。default_widgetとdefault_formatterがクラスではなくウィジェットとフォーマッターのアノテーションIDを参照していることを確認するだけです。
アノテーションについて詳しく知りたい場合は、drupal.orgのアノテーションベースのプラグインドキュメントのページをご覧ください。
3. フィールドアイテムクラスができたので、いくつかのメソッドを定義する必要があります。最初に見るのはschema()です。
Drupal 7では、カスタムフィールドを作成するときに、hook_field_schemaを使用してスキーマを定義します。Drupal 8では、SnippetsItemクラスにschema()メソッドを追加してスキーマを定義します。
スキーマAPIドキュメントには、スキーマ配列の構造と可能な値の説明が含まれています。
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field) {
return array(
'columns' => array(
'source_description' => array(
'type' => 'varchar',
'length' => 256,
'not null' => FALSE,
),
'source_code' => array(
'type' => 'text',
'size' => 'big',
'not null' => FALSE,
),
'source_lang' => array(
'type' => 'varchar',
'length' => 256,
'not null' => FALSE,
),
),
);
}
4. 次に、isEmpty()メソッドを追加して、空のフィールドアイテムを構成するものを定義する必要があります。このメソッドは、Drupal 7のhook_field_is_emptyの実装に似ています。
/**
* {@inheritdoc}
*/
public function isEmpty() {
$value = $this->get('source_code')->getValue();
return $value === NULL || $value === '';
}
5. クラスに追加する最後のメソッドは、propertyDefinitions()メソッドです。
/**
* {@inheritdoc}
*/
static $propertyDefinitions;
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['source_description'] = DataDefinition::create('string')
->setLabel(t('Snippet description'));
$properties['source_code'] = DataDefinition::create('string')
->setLabel(t('Snippet code'));
$properties['source_lang'] = DataDefinition::create('string')
->setLabel(t('Programming Language'))
->setDescription(t('Snippet code language'));
return $properties;
}
このメソッドは、フィールドの値に存在するデータのタイプを定義するために使用されます。「スニペットフィールド」には、説明、コード、言語の3つの値しかありません。そのため、これらの値を文字列としてメソッドに追加しました。
詳細については、Entity APIがTyped Data APIを実装する方法のdrupal.orgのドキュメントを参照してください。
ここをクリックしてファイル全体を表示します。注:PSR-4仕様に更新する必要があります。詳細については、https://www.drupal.org/node/2128865を参照してください。
ステップ2:フィールドウィジェットの実装
フィールドアイテムを定義したので、フィールドウィジェットを作成しましょう。WidgetBaseクラスを拡張するSnippetsDefaultWidgetという名前のクラスを作成する必要があります。
1. それでは、SnippetsDefaultWidget.phpファイルを作成し、モジュールの/src/Plugin/Field/FieldWidget/SnippetsDefaultWidget.phpに追加します。
/**
* @file
* Contains \Drupal\snippets\Plugin\Field\FieldWidget\SnippetsDefaultWidget.
*/
namespace Drupal\snippets\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
ファイルの名前空間がDrupal\snippets\Plugin\Field\FieldWidgetであることを確認し、次の3つのuseステートメントを追加します:
- Drupal\Core\Field\FieldItemListInterface
- Drupal\Core\Field\WidgetBase
- Drupal\Core\Form\FormStateInterface。
2. 次に、アノテーションを使用してウィジェットを定義する必要があります。これは、Drupal 7のhook_field_widget_infoの使用に相当します。
/**
* Plugin implementation of the 'snippets_default' widget.
*
* @FieldWidget(
* id = "snippets_default",
* label = @Translation("Snippets default"),
* field_types = {
* "snippets_code"
* }
* )
*/
class SnippetsDefaultWidget extends WidgetBase { }
先に進む前に、アノテーションのfield_types属性がフィールドタイプをIDで参照していることを確認してください。このモジュールの場合、@FieldTypeアノテーションにid = "snippets_code"を追加したため、これはsnippets_codeです。
3. そして最後に、ウィジェットの実際のフォームを定義する必要があります。これは、SnippetsDefaultWidgetクラスにformElement()メソッドを追加して行います。このメソッドは、Drupal 7のhook_field_widget_formの使用に似ています。
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['source_description'] = array(
'#title' => $this->t('Description'),
'#type' => 'textfield',
'#default_value' => isset($items[$delta]->source_description) ? $items[$delta]->source_description : NULL,
);
$element['source_code'] = array(
'#title' => $this->t('Code'),
'#type' => 'textarea',
'#default_value' => isset($items[$delta]->source_code) ? $items[$delta]->source_code : NULL,
);
$element['source_lang'] = array(
'#title' => $this->t('Source language'),
'#type' => 'textfield',
'#default_value' => isset($items[$delta]->source_lang) ? $items[$delta]->source_lang : NULL,
);
return $element;
}
ここをクリックしてファイル全体を表示します。注:PSR-4仕様に更新する必要があります。詳細については、https://www.drupal.org/node/2128865を参照してください。
ステップ3:フィールドフォーマッターの実装
パズルの最後のピースはフィールドフォーマッターです。これは、FormatterBaseクラスを拡張するSnippetsDefaultFormatterという名前のクラスを定義して作成します。
1. SnippetsDefaultFormatter.phpファイルを作成し、モジュールの/src/Plugin/Field/FieldFormatter/SnippetsDefaultFormatter.phpに追加します。
/**
* @file
* Contains \Drupal\snippets\Plugin\field\formatter\SnippetsDefaultFormatter.
*/
namespace Drupal\snippets\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
ファイルの名前空間がDrupal\snippets\Plugin\Field\FieldFormatterであることを確認し、次のuseステートメントを追加します:Drupal\Core\Field\FieldItemListInterfaceと Drupal\Core\Field\FormatterBase。
2. 次に、フォーマッターをアノテーションとして定義する必要があります。ウィジェットとフィールドタイプに対して行ったことと同じで、これはhook_field_formatter_infoの使用に相当します。
/**
* Plugin implementation of the 'snippets_default' formatter.
*
* @FieldFormatter(
* id = "snippets_default",
* label = @Translation("Snippets default"),
* field_types = {
* "snippets_code"
* }
* )
*/
class SnippetsDefaultFormatter extends FormatterBase { }
3. 残っているのは、viewElements()メソッドを追加して、実際のフィールドフォーマッターモジュールを定義することだけです。繰り返しますが、このメソッドはDrupal 7のhook_field_formatter_viewの使用に似ています。
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
foreach ($items as $delta => $item) {
// Render output using snippets_default theme.
$source = array(
'#theme' => 'snippets_default',
'#source_description' => $item->source_description,
'#source_code' => $item->source_code,
);
$elements[$delta] = array('#markup' => drupal_render($source));
}
return $elements;
}
フォーマッターで表示される前に、カスタムのsnippets_defaultテーマを使用してスニペットをレンダリングしていることに注意してください。
その理由は、viewElements()メソッドに多くのロジックやHTMLコードを置きたくなかったからです。
ここをクリックしてファイル全体を表示します。注:PSR-4仕様に更新する必要があります。詳細については、https://www.drupal.org/node/2128865を参照してください。
結論
前述のように、Drupal 8での最大の変更点は、フィールドがフックではなくプラグインAPIを使用して作成されることです。これを理解すれば、フィールドの作成の概念はDrupal 7と非常によく似ています。Drupal 8の多くのメソッドは、Drupal 7のフックに対応しています。
スニペットコードをテストしたい場合は、8.x-devリリースをダウンロードして試してみてください。