概要: カスタムフィールドの作成
このチュートリアルは元々 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 を使用して schema を定義します。Drupal 8 では、SnippetsItem クラスに schema() メソッドを追加して schema を定義します。
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つの値のみがあります。したがって、これらの値を文字列としてメソッドに追加しただけです。
詳細については、drupal.org のEntity API が Typed Data API のドキュメントを実装する方法のセクションを参照してください。
ここをクリックすると、ファイル全体が表示されます。注: これは 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 のフックに対応しています。
コードスニペットをテストしたい場合は、snippets モジュールの 8.x-dev リリースをダウンロードして試してみてください。