Drupal
Drupal8
DisplaySuite
DrupalDay 1

Drupal 8 の Display Suite にカスタムのフィールドを追加する

Drupal でノードなどの表示形式を変更する場合、Display Suite を使用してカスタマイズすることがあります。この際、ノードなどで定義されているフィールドは表示・非表示を切り替えることはできます。しかし、固定の文言を表示するためのフィールドや複数フィールドの値を組み合わせといったフィールドの表示を行うことは、Display Suite が標準で提供する機能だけでは難しいです。そこで、Display Suite にカスタムフィールドを追加するための方法を記載します。

Display Suite のカスタムフィールド追加の基本形

Display Suite にカスタムフィールドを実装する際の基本形は下記になります。

<?php

namespace Drupal\some_module\Plugin\DsField;

use Drupal\ds\Plugin\DsField\DsFieldBase;

/**
 * Generated field.
 *
 * @DsField(
 *   id = "some_module_DSFieldTest",
 *   title = @Translation("DSFieldTest"),
 *   entity_type = "user"
 * )
 */
class DSFieldTest extends DsFieldBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    return [
      '#markup' => 'Something field value.',
    ];
  }

}

コメント文のようになっている場所はアノテーションで、Display Suite フィールドの定義を行う場所です。
build() は、Display Suite で追加されたフィールドの出力を行う場所です。メソッドの返り値に Render arrays を与えることで、表示内容を決定することができます。
なお、Display Suite の drush コマンドでフィールドを作成した場合、テンプレートは下記のようになります。build() 以外に関しては後述する Display Suite 作者のスライドがあるので、そちらで確認してください。

<?php

namespace Drupal\some_module\Plugin\DsField;

use Drupal\Core\Form\FormStateInterface;
use Drupal\ds\Plugin\DsField\DsFieldBase;

/**
 * Generated field.
 *
 * @DsField(
 *   id = "some_module_DSFieldTest",
 *   title = @Translation("DSFieldTest"),
 *   entity_type = "user"
 * )
 */
class DSFieldTest extends DsFieldBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm($form, FormStateInterface $form_state) {
    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function formatters() {
    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function isAllowed() {
    return TRUE;
  }

}

フィールドを呼び出す Entity の制約をかける 

アノテーションに ui_limit を追加し、{}内に"エンティティID|ディスプレイモード" という値を設定することで、特定のエンティティや特定のディスプレイモードにのみフィールドを追加するように制約をかけることができます。例えば、下記のように "article|*" という値を設定すれば、article コンテンツタイプのみにフィールドを作成することができ、その他のコンテンツタイプでは出てこなくなります。逆に特定のディスプレイモードにのみ表示するように制約をかけるには、"*|some_display_mode" という値を設定します。

<?php

namespace Drupal\some_module\Plugin\DsField;

use Drupal\ds\Plugin\DsField\DsFieldBase;

/**
 * Generated limited field.
 *
 * @DsField(
 *   id = "some_module_DSFieldLimitTest",
 *   title = @Translation("DSFieldLimitTest"),
 *   entity_type = "node",
 *   ui_limit = {"article|*"}
 * )
 */
class DSFieldLimitTest extends DsFieldBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    return [];
  }

}

複数の制約条件を書く際は、下記のようにカンマ区切りで値を設定していきます。

/**
 * @DsField(
 *   id = "some_module_DSFieldMultiLimitTest",
 *   title = @Translation("DSFieldMultiLimitTest"),
 *   entity_type = "node",
 *   ui_limit = {
 *     "page|hogehoge",
 *     "article|foobar"
 *   }
 * )
 */

フィールドを使用している Entity を呼び出す

Display Suite で作成したフィールドは、呼び出し元の Entity を取得可能です。
Display Suite にある DsFieldBase 抽象クラスのソースコードを見ると、下記のように $this->counfiguration['entity'] に定義されていることが分かります。

/ds/src/Plugin/DsField/DsFieldBase.php
  public function entity() {
    return $this->configuration['entity'];
  }

作成したフィールドでは、$this->entity() を呼び出せるのでそれを使って呼び出し元の Entity の他のフィールドの値を使って処理を書くことができます。

DI の設定

Display Suite では、ブロックなどとは違い DsFieldBase に DI するためのサービスコンテナを呼び出す処理が書かれているため、DsFieldBase を呼び出すだけで DI を設定することができます。例えば、ログイン中のユーザを取得して何かしらの処理をする場合は下記のようになります。

<?php

namespace Drupal\some_module\Plugin\DsField;

use Drupal\ds\Plugin\DsField\DsFieldBase;

/**
 * Generated field.
 *
 * @DsField(
 *   id = "some_module_DSFieldTest",
 *   title = @Translation("DSFieldTest"),
 *   entity_type = "user"
 * )
 */
class DSFieldTest extends DsFieldBase {

  /**
   * Current User AccountProxy definition.
   *
   * @var \Drupal\Core\Session\AccountProxy
   */
  protected $currentUser;


  /**
   * DSFieldTest constructor.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Session\AccountProxy $current_user
   *   The current user account proxy.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, AccountProxy $current_user) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->currentUser = $current_user;
  }


  /**
   * {@inheritdoc}
   */
  public function create() {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('current_user')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    $uid = $this->currentUser->id();
    return [
      '#markup' => $uid . ':',
    ];
  }

}

まとめ

この記事では、Display Suite で追加可能になるフィールドを作成する方法を記載しました。
Display Suite のフィールドの設定フォームやフォーマッタの作成などといった、さらに詳しいことは、Display Suite の作者のスライドに書いてあるのでそちらを参照してください。