概览:创建自定义字段
Этот教程最初发布在 Web Wash。不过 Berdir 问我能否在这里发布,所以这就是它。
Drupal 7 的一个模块允许在字段中存储代码示例/片段。它带有一个名为「片段字段」的自定义字段,并显示三个表单元素:描述、源代码和语法高亮模式(选择编程语言)。
但现在是时候将模块升级到 Drupal 8 了。
在本教程中,我将向你展示我是如何在 Drupal 8 中创建一个「基础」自定义字段的。我不会深入讲解 PSR – 4、注解 或 插件,否则教程会过于庞大。
相反,我会添加指向其他网站的链接,这些网站会更深入解释这些概念。
另外,如果你正在寻找 Drupal 8 Field API 的详细文档,请查看以下系列文章:
在 Drupal 8 中,字段不像 Drupal 7 那样通过 hook 实现,而是通过新的 Drupal 8 插件 API 创建。这意味着我们不再实现 hooks,而是为小部件、格式化器和字段元素定义类。Drupal 7 中的大多数 hooks(如 hook_field_schema、hook_field_is_empty 等),现在都变成了类中的方法。
步骤 1:实现字段元素
第一步,我们需要定义一个名为 SnippetsItem 的字段元素类,它扩展自 FieldItemBase。
1. 在 Drupal 8 中,类是通过 PSR-4 加载的。
因此,要定义 SnippetsItem 类,我们需要创建 SnippetsItem.php 文件,并将其放在「module」/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,以及三个 use 语句:Drupal\Core\Field\FieldItemBase、Drupal\Core\Field\FieldStorageDefinitionInterface 和 Drupal\Core\TypedData\DataDefinition。
2. 接下来我们需要定义字段的实际细节,比如字段 ID、标签、默认小部件、格式化器等。这相当于在 Drupal 7 中实现 hook_field_info。
在 Drupal 8 中,许多(如果不是全部)信息类 hooks 都被 注解 取代了。
/**
* 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;
}
该方法用于定义字段值中存在的数据类型。“片段字段”只有三个值:描述、代码和语言。因此我在方法中将这些值定义为字符串。
想要了解更多,请查看 drupal.org 上的 Entity API 如何实现 Typed Data API 文档。
点击这里查看完整文件。注意:需要更新为 PSR-4 规范,更多详情请见 https://www.drupal.org/node/2128865
步骤 2:实现字段小部件
现在我们已经定义了字段元素,接下来创建字段小部件。我们需要创建一个名为 SnippetsDefaultWidget 的类,它扩展自 WidgetBase。
1. 创建文件 SnippetsDefaultWidget.php,并将其添加到「module」/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,并添加以下三个 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。在本模块中是 snippets_code,因为我们在 @FieldType 注解中定义了 id = "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:实现字段格式化器
最后一部分是字段格式化器,我们通过定义一个名为 SnippetsDefaultFormatter 的类来实现,它扩展自 FormatterBase。
1. 创建文件 SnippetsDefaultFormatter.php,并将其添加到「module」/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. 接着我们通过注解定义格式化器。这相当于 Drupal 7 中的 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 而不是 hooks 创建的。一旦你理解了这一点,创建字段的概念和 Drupal 7 非常相似。Drupal 8 中的许多方法都对应于 Drupal 7 的 hooks。
如果你想测试 代码片段,请下载 8.x-dev 版本并试用。