(追記20151014: 本記事は執筆時点の Drupal 8 のバージョン(開発版)にもとづく古い解説となっています)

本日は Drupal 8 入門第 5 弾ということで「 Drupal 8 でカスタムブロックを作成する方法」をご紹介したいと思います。

Drupal 8 についてあまり知らないぞという方にはこれまでの Drupal 8 入門記事が参考になるかもしれません。 そちらもよろしければご覧になってみてください。

今回ご紹介するのは PHP コードを使ってブロックを定義する方法です。 管理画面からコーディングレスにブロックを管理する方法ではなくファイルを触って書いていく方法になりますので、どちらかというと開発者の方向けのお話になります。

また、今回の記事執筆時点で Drupal 8 の正式安定版はリリースされていません。 今回ご紹介する仕様は安定版リリースの際には多少変わっている可能性があります。 これらの点をあらかじめご認識いただければと思います。

想定読者

想定読者は Drupal 7 での開発経験がある方です。 実際にコードを使ってブロックを定義したことのある方をイメージしています。 また、 Drupal 8 のカスタムモジュール開発の基礎についてはひととおり押さえられているものと想定しています。 ですので、モジュールの開発方法についてはサッと解説するだけにとどめます。

Drupal 8 におけるカスタムモジュール作成の方法については上掲の記事でも解説していますのでよろしければご参考にしてみてください。

「ブロックって何だっけ?」という方には用語集の記事などがご参考になるかと思います。

Drupal 8 のブロックの概要

実際のブロックの定義に入る前に、 Drupal 8 におけるブロックのポイントをいくつかご紹介できればと思います。

ポイント 1: ブロックは「ブロッククラス」で作成する

Drupal 7 ではブロックは hook_block_info() hook_block_view() などブロック関連の各種フック関数を使って定義しましたが、 Drupal 8 の場合ブロック関連の情報はすべてブロッククラスで定義します。

<?php

use Drupal\block\BlockBase;

class HelloBlock extends BlockBase {

ポイント 2: ブロックの特徴や機能はメソッドで定義する

ブロックの具体的な挙動や設定については、ブロック内のメソッドを使って定義します。

<?php

use Drupal\block\BlockBase;

class HelloBlock extends BlockBase {
  public function build() {
    return array(
      '#markup' => $this->t('Hello World!'),
    );
  }
}

実際にどのようなメソッドが利用できるかは Drupal.org API の BlockPluginInterface のページを見るとよいでしょう。

ポイント 3: ブロックは「インスタンス化」して複数生成できる

Drupal 8 では、ひとつのブロック定義をもとに、複数のブロックを生成することができます。 共通のブロックをページ内に複数個設置したり、挙動の少しだけ異なるふたつのブロックを設置したりということができます。

Drupal 8 のブロックの定義方法

では実際にブロックの定義方法を見ていきましょう。 今回は「 myblock 」というモジュールを作成し、その中で「 helloblock 」というブロックを定義します。

まずは容れ物をとなるモジュールの作成から。

1. モジュールを作成

myblock というモジュールを作成します。 モジュール作成方法の詳細はカスタムモジュール作成編の方に譲ることにして、ここでは .info.yml と .module の中身についてのみ書いておきます。

myblock.info.yml:

# modules/myblock/myblock.info.yml
name: My first block
core: 8.x
description: 'Provides a block with hello.'
type: module
dependencies:
  - block

myblock.module:

<?php

/**
 * @file
 * Hello ブロックを提供します。
 */

モジュールの容れ物となるディレクトリとこれら 2 つのファイルを作成したら、管理画面の「 Extend 」のページから「 My first block 」を探してモジュールを有効化しましょう。 Extend のページのパスは /admin/modules です。

Drupal 8 カスタムブロック作成 モジュールの有効化

2. ブロッククラスを定義

モジュールが無事に有効化できたら、ブロッククラス HelloBlock を定義しましょう。 まずはクラス名と同名のファイル helloblock.php を作成し、 myblock モジュールディレクトリ以下の src/Plugin/Block というところに配置します。 この配置場所は PSR-4 の規約に従う形になります(少し前までは PSR-0 準拠だったのが PSR-4 に変更となりました)。

つづいて helloblock.php を開いて、次の内容を記入して保存します。

<?php

// 名前空間を定義
namespace Drupal\myblock\Plugin\Block;

// 使用するクラスを宣言
use Drupal\block\BlockBase;

/**
 * Hello ブロックを提供する
 *
 * @Block(
 *   id = "hello_block",
 *   admin_label = @Translation("My hello block")
 * )
 */
class HelloBlock extends BlockBase {

  /**
   * 関数のコンテンツ部分をレンダーアレイを返す
   */
  public function build() {
    return array(
      '#markup' => $this->t('Hello World!'),
    );
  }

}

ここで、 BlockBase はブロッククラスの基底クラスです。 BlockBase を継承することにより、定義する必要のあるメソッドが最小限で済みます。

build() は関数のコンテンツ部分を記述するためのメソッドです。 Drupal 7 でいうところの hook_block_view() と捉えてよいかと思います。

ドキュメントブロック内の @Block はプラグインアノテーションです。 Drupal 8 で導入される新たな「プラグイン」システムにおいては、ドキュメントブロック内に正しい書式で「アノテーション」を記述することにより、 Drupal にそれをプラグイン(今回の場合はブロック)と認識させられるようになっています。 必要な引数は idadmin_label です。

ここまで来たらブロックそのものの作成は完了、です。 つづいて実際に管理画面からページにブロックを追加してみます。

3. ブロックをインスタンス化して追加

管理画面のブロックレイアウト管理ページ /admin/structure/block を開きましょう。 すると、画面右側に「 MY FIRST BLOCK 」というモジュール名と「 My hello block 」というリンクが表示されているかと思います。

Drupal 8 カスタムブロック作成 ブロックのインスタンス化

「 My hello block 」のリンクをクリックするとダイアログが出てくるので、設定を適当に行って「ブロックの保存」のボタンをクリックしましょう。

Drupal 8 カスタムブロック作成 ブロックインスタンスの設定

するとダイアログが消えて、元の画面の下の方に「 My hello block 」という項目が追加されているのが確認できるかと思います。 あとはこれを適当なリージョンに配置します。 このあたりは Drupal 7 と同じなので、詳細の手続きは割愛しますね。

Drupal 8 カスタムブロック作成 生成されたブロックインスタンスの確認

この後サイトのフロントページに戻れば、作成したブロックが無事表示されることが確認できるのではないかと思います。

Drupal 8 カスタムブロック作成 生成されたブロックのページ上での表示

ここでおもしろいのは、モジュールで定義したブロックが直接配置されたのではなく「インスタンス化」されて配置されたという点です。 試しにブロックレイアウト管理ページを再度開くと「 My hello block 」という項目が先ほどと変わらず表示されています。 こちらを再度クリックして同じ手順を踏むと、さきほど生成したブロックと同等のブロックをもうひとつ生成して配置することができます。 詳しいところはまた別の機会に見ていければと思いますが、 Drupal 8 のブロックを PHP で定義する場合は「クラス」と「インスタンス」でいうところの「クラス」を作成する、という作りになっているようです。

4. おまけ: ブロック設定を追加

ブロックの作成の手順については以上でしたが、少しだけおまけのお話を。 Drupal 8 のブロックインスタンス(もっと正式な呼び方があるかもしれません)では、インスタンスごとに個別の設定値を持つことができます。

たとえば、上記のブロックの定義では固定の文字列「 Hello World! 」を表示するだけでしたが、以下のようなコードに変えることによって、「 World 」の部分を任意の文字列に変更することができます。

追加したコードは、ふたつのメソッド blockForm()blockSubmit() 、そしてそれらに関係する use 文です。 さらに World の部分を設定された値に変えるために build() の中身を 1 行だけ変更しています。

<?php

// 名前空間を定義
namespace Drupal\myblock\Plugin\Block;

// 使用するクラスを宣言
use Drupal\block\BlockBase;
use Drupal\Core\Form\FormStateInterface;


/**
 * Hello ブロックを提供する
 *
 * @Block(
 *   id = "hello_block",
 *   admin_label = @Translation("My hello block")
 * )
 */
class HelloBlock extends BlockBase {
// class HelloBlock extends BlockBase implements BlockPluginInterface {

  /**
   * 関数のコンテンツ部分をレンダーアレイを返す
   */
  public function build() {
    // ブロックの設定から name の値を取得して表示する
    $config = $this->getConfiguration();
    $name = isset($config['name']) ? $config['name'] : 'world';
    return array(
      // '#markup' => $this->t('Hello World!'),
      '#markup' => $this->t('Hello') . ' ' . $name,
    );
  }

  /**
   * ブロックの設定フォームにカスタムフィールドを追加する
   */
  public function blockForm($form, FormStateInterface $form_state) {
    $form = parent::blockForm($form, $form_state);

    // name というテキストフィールドを追加
    // すでに値が設定されていればそれを入れた状態で表示する
    $config = $this->getConfiguration();
    $form['name'] = array(
      '#type' => 'textfield',
      '#title' => t('Name'),
      '#default_value' => isset($config['name']) ? $config['name'] : '',
    );

    return $form;
  }

  /**
   * ブロックの設定フォームが submit されたときの処理を行う
   */
  public function blockSubmit($form, FormStateInterface $form_state) {
    // name フィールドの値を設定に保存する
    $this->setConfigurationValue('name', $form_state['values']['name']);
  }

}

このあたりの仕様はまだ少しずつ変更されているようですので、ひとまずは「こういうことができるようです」というところの確認だけにとどめておいて、詳細の解説は正式版のリリースを待ってからにしたいと思います。

終わりに

以上です。 いかがだったでしょうか?

今回は Drupal 8 でコードを書いてカスタムブロックを作成する方法を見てみました。 Drupal 7 のときも非常に強力だった Drupal のブロック API ですが、 Drupal 8 ではオブジェクト指向スタイルとプラグインシステムを導入し、よりわかりやすく柔軟に使える形に進化しているようです。 バージョンアップに伴う開発者の学習コストが高そうな Drupal 8 ですが、「ブロック」というひとつの面を取ってみても CMS として大きく一歩進化しているのがわかります。

世界中のコントリビュータの力により、ベータブロッカも着実に少なくなってきたようですので、あと少し、楽しみに待ちたいと思います。

この記事の冒頭で一度ご紹介していますが、他の切り口からの Drupal 8 入門の記事も書いています。 Drupal 8 に興味のある方はぜひご覧になってみてください。