logo

额外区块类型 (EBT) - 全新的布局构建器体验❗

额外区块类型 (EBT) - 样式化、可定制的区块类型:幻灯片、标签页、卡片、手风琴等更多类型。内置背景、DOM Box、JavaScript 插件的设置。立即体验布局构建的未来。

演示 EBT 模块 下载 EBT 模块

❗额外段落类型 (EPT) - 全新的 Paragraphs 体验

额外段落类型 (EPT) - 类似的基于 Paragraph 的模块集合。

演示 EPT 模块 滚动

滚动
03/10/2025, by Ivan

概览

在 Drupal 8 中,区块实际上由两个独立的 API 结构组成,以便创建与之前版本 Drupal 所支持的类似的用户界面。这两个 API 分别是 Block Plugin API(可复用的独立 API)和 Block Entity API(Drupal 8 特有的区块放置与可见性控制机制)。

使用 Block Plugin API 创建区块

要在模块代码中定义区块,需要学习和理解 插件 API,特别是 基于注解的插件发现机制,这是 Drupal 8 用来找到定义区块的代码的方法。

创建一个由模块定义的自定义区块包含以下步骤:

让你的区块对 Drupal 和用户可见

Drupal 使用 PSR-4 标准来发现类。假设模块名为 fax,那么自定义区块的代码应该放在 fax/src/Plugin/Block/ 目录下。目录中的每个文件必须与其包含的类同名。例如,如果我们要定义 FaxBlock 类,它应位于 fax/src/Plugin/Block/FaxBlock.php 文件中,并包含如下内容:

namespace Drupal\fax\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 * 提供一个 'Fax' 区块.
 *
 * @Block(
 *   id = "fax_block",
 *   admin_label = @Translation("Fax block"),
 * )
 */
class FaxBlock extends BlockBase {
  // 在这里重写 BlockPluginInterface 方法.
}

注解中的 id 属性定义了区块的唯一机器可读 ID,而 admin_label 则定义了区块在后台界面中显示的可读名称。可用的注解属性可以在 \Drupal\Core\Block\Annotation\Block 中找到。

最常用的两个方法需要重写:

  • BlockPluginInterface::build() —— 返回一个渲染数组,定义区块要显示的内容。
  • BlockBase::access() —— 控制区块的可见性,需返回一个 AccessResult 对象。

为区块添加自定义配置参数

你可以通过重写 BlockPluginInterface::blockForm()BlockPluginInterface::blockSubmit() 方法,并结合使用 BlockBase::setConfigurationValue()BlockBase::getConfiguration() 来为区块的配置表单添加自定义参数。

例如,在 blockForm() 方法中添加一个文本字段,并在 blockSubmit() 中保存用户输入:

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Access\AccessResult;

/**
 * 提供一个 'Fax' 区块.
 *
 * @Block(
 *   id = "fax_block",
 *   admin_label = @Translation("Fax block"),
 * )
 */
class FaxBlock extends BlockBase implements BlockPluginInterface {

  public function build() {
    $config = $this->getConfiguration();
    $fax_number = isset($config['fax_number']) ? $config['fax_number'] : '';
    return [
      '#markup' => $this->t('The fax number is @number!', ['@number' => $fax_number]),
    ];
  }

  public function blockForm($form, FormStateInterface $form_state) {
    $form = parent::blockForm($form, $form_state);
    $config = $this->getConfiguration();

    $form['fax_number'] = [
      '#type' => 'textfield',
      '#title' => t('Fax number'),
      '#default_value' => isset($config['fax_number']) ? $config['fax_number'] : '',
    ];
    return $form;
  }

  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->setConfigurationValue('fax_number', $form_state->getValue('fax_number'));
  }

  public function blockValidate($form, FormStateInterface $form_state) {
    $fax_number = $form_state->getValue('fax_number');
    if (!is_numeric($fax_number)) {
      $form_state->setErrorByName('fax_number', t('Needs to be an integer'));
    }
  }
}

你可以在 build() 方法中通过 getConfiguration() 获取配置数据并展示给用户。access() 方法中也可以添加更复杂的逻辑来决定区块是否显示。

访问条件示例方法:

public function access(AccountInterface $account, $return_as_object = FALSE) {
  return \Drupal\Core\Access\AccessResult::allowedIf($account->isAuthenticated());
}

使用缓存标签的示例:

public function getCacheTags() {
  return \Drupal\Core\Cache\Cache::mergeTags(parent::getCacheTags(), ['node_list']);
}

设置区块的缓存最大时长:

public function getCacheMaxAge() {
  // 禁用该区块缓存
  return 0;
}