DRUPAL8模块开发 - DRUPAL8 Form API

翻译者:长风Drupal开发团队(成都长风云信息技术有限公司)

总览

Drupal8表单类实现了\Drupal\Cyr\Frase\FormInterface,表单的基本工作流由接口的buildForm, validateForm和submitForm方法定义。当一个表单被请求时,它被定义为一个可渲染数组,通常被称为表单API数组或简单的$Form数组。$form数组通过渲染过程转换为HTML并显示给最终用户。当用户提交表单请求到表单显示的同一URL时,Drupal会注意到请求中的传入HTTP POST数据,而这次不是构建表单并将其显示为HTML,而是构建表单,然后继续调用可应用的验证和提交处理程序。

将表单定义为结构化数组而不是直接HTML有很多优点,包括:

1、所有表单的HTML输出一致。

2、由一个模块提供的表单可以很容易地被另一个模块修改,而不需要复杂的搜索和替换逻辑。

3、诸如文件上传和投票控件之类的复杂表单元素可以封装在包括显示和处理逻辑的可重用捆绑包中。

Drupal中常用的几种类型。每个都有一个基类,您可以在自己的自定义模块中扩展。

首先,确定需要构建的表单类型:

一种通用形式。扩展窗体库。

使管理员能够更新模块设置的配置表单。扩展配置窗体库。

删除内容或配置的表单,提供确认步骤。扩展确认窗体库。

FrimeBASE实现Frand接口,同时配置FraseBasic和RealMrFraseBaseExchange FraseBase,因此扩展这些类的任何表单都必须实现一些需要的方法。

所需方法

FormBase实现Frand接口,因此需要在其层次结构中具有FormBase的任何形式来实现以下几种方法:

  • getFormId()
  • buildForm()
  • validateForm()
  • submitForm()

getFormId()

public function getFormId()

这需要返回一个字符串,它是表单的唯一ID。命名空间基于您的模块名的表单ID。

例子:

public function getFormId() {

return 'mymodule_settings';

}

buildForm()

public function buildForm(array $form, FormStateInterface $form_state)

这返回一个表单API数组,它定义了表单所组成的每个元素。

例子:

public function buildForm(array $form, FormStateInterface $form_state) {

$form['phone_number'] = [

'#type' => 'tel',

'#title' => 'Example phone',

];

$form['submit'] = [

'#type' => 'submit',

'#value' => $this->t('Submit'),

];

 

return $form;

}

验证表单

在用户填写表单并单击提交按钮之后,通常希望对正在收集的数据执行某种类型的验证。要使用Drupal的表单API来实现这一点,我们只需在ExampleForm类中从\Drupal \Corr\Form\FormInterface实现validateForm 方法。

表单中的用户提交值包含在$FuffStand状态对象$FuffyStale-> GETValk(“FieldIyID”)中,其中“FieldId ID”是在FormExample::buildForm()中向表单元素添加表单元素时使用的键。我们可以在这个值上执行自定义验证。如果需要获取所有提交的值,可以使用$form_state->getValues()。

表单验证方法可以使用任何必要的PHP处理来验证字段包含期望值,并且在它是无效值的情况下引发错误。在这种情况下,因为我们扩展了\Drupal \ Cyr\Frase\FraseBASE类,所以我们可以使用\Drupal\Cyr\\Frase\FormStateInterface::setErrorByName()在特定的表单元素上注册一个错误,并提供一个相关的消息来解释错误。

当提交表单时,Drupal遍历窗体的所有验证处理程序,默认验证处理程序和开发人员添加的任何验证处理程序。如果存在任何错误,则重新生成表单的HTML,显示错误消息,并突出显示有错误的字段。这允许用户纠正任何错误并重新提交表单。如果不存在错误,则执行表单的提交处理程序。

下面是一个简单的validateForm()方法的例子:

/**

* {@inheritdoc}

*/public function validateForm(array &$form, FormStateInterface $form_state) {

if (strlen($form_state->getValue('phone_number')) < 3) {

$form_state->setErrorByName('phone_number', $this->t('The phone number is too short. Please enter a full phone number.'));

}}

如果在窗体验证期间没有注册错误,则Drupal继续处理该表单。在这一点上,假设$form_state->getValues()中的值是有效的,并且可以随时处理和使用我们的模块需要使用数据的任何方式。

 

提交表单/处理表单数据

 

最后,我们准备利用我们收集的数据,并将其保存到数据库或发送电子邮件或任何其他操作。要使用Drupal的表单API来实现这一点,我们需要在我们的ExpReForm类中从\Drupal \ Corn\Form \ FormInterface实现子表单方法。

 

就像上面的验证方法一样,当表单提交时,从用户收集的值在$form_state->getValues() 中,在这一点上,我们可以假设它们已经被验证并准备好让我们使用。访问Drupal8表单中的'phone_number’字段的值可以通过访问Drupal 表单的$form_state->getValue('phone_number')来完成。

下面是一个简单的Drupal8中提交表单方法的例子,该方法使用drupal_set_message()显示页面上的“phone_number”字段的值:

/**

* {@inheritdoc}

*/public function submitForm(array &$form, FormStateInterface $form_state) {

drupal_set_message($this->t('Your phone number is @number', ['@number' => $form_state->getValue('phone_number')]));}

这确实是一个简单的处理表单提交数据的例子,对于更加复杂的例子,可以查看这里:https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Form%21FormBase.php/class/uses/FormBase/8

 

下面似乎一个更加复杂的表单类

<?php

 

namespace Drupal\example\Form;

 

use Drupal\Core\Form\FormBase;use Drupal\Core\Form\FormStateInterface;

 

/**

* Implements an example form.

*/class ExampleForm extends FormBase {

 

/**

* {@inheritdoc}

*/

public function getFormId() {

return 'example_form';

}

 

/**

* {@inheritdoc}

*/

public function buildForm(array $form, FormStateInterface $form_state) {

$form['phone_number'] = [

'#type' => 'tel',

'#title' => $this->t('Your phone number'),

];

$form['actions']['#type'] = 'actions';

$form['actions']['submit'] = [

'#type' => 'submit',

'#value' => $this->t('Save'),

'#button_type' => 'primary',

];

return $form;

}

 

/**

* {@inheritdoc}

*/

public function validateForm(array &$form, FormStateInterface $form_state) {

if (strlen($form_state->getValue('phone_number')) < 3) {

$form_state->setErrorByName('phone_number', $this->t('The phone number is too short. Please enter a full phone number.'));

}

}

 

/**

* {@inheritdoc}

*/

public function submitForm(array &$form, FormStateInterface $form_state) {

drupal_set_message($this->t('Your phone number is @number', ['@number' => $form_state->getValue('phone_number')]));

}

 

}

窗体的ID由窗体类上的getFormId()方法返回。创建表单的方法是调用buildForm()方法,并且有用于验证和提交的专用方法。

在请求中集成表单

路由系统允许将表单类提供为路由处理程序,在这种情况下,路由系统负责实例化该类并调用适当的方法。若要将此窗体集成到Drupal站点的URI结构中,请使用如下路径:

example.form:

path: '/example-form'

defaults:

_title: 'Example form'

_form: '\Drupal\example\Form\ExampleForm'

requirements:

_permission: 'access content'

 

“_form”键告诉路由系统,所提供的类名是要实例化并作为表单处理的表单类。

请注意,表单类和路由条目是使表单工作所需的唯一两个片段,没有其他包装代码可写入。

在路由之外检索此表单

虽然Drupal 7的drupal_get_form()在Drupal 8中消失了,但有一个表单建器服务可以用来检索和处理表单。与drupal7 的drupal_get_form() 相似功能 的Drupal 8方法如下:

$form = \Drupal::formBuilder()->getForm('Drupal\example\Form\ExampleForm');

 

传递给getForm()方法的参数是定义表单的类的名称,是\Drupal\Core\Form\FormInterface的实现。如果需要向表单传递任何其他参数,则在类名称之后传递它们。

例子:

$extra = '612-123-4567';$form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm', $extra);...public function buildForm(array $form, FormStateInterface $form_state, $extra = NULL)

$form['phone_number'] = [

'#type' => 'tel',

'#title' => $this->t('Your phone number'),

'#value' => $extra,

];

return $form;}

在某些特殊情况下,您可能需要在FormBuilder调用类的buildForm()方法之前处理$Form对象,在这种情况下,您可以执行

$form_object = new \Drupal\mymodule\Form\ExampleForm($something_special); $form_builder->getForm($form_object);

修改表单

修改表单Drupal8 Form API的达到类似 Drupal 7的功能。使用hook_form_alter() 和/或hook_form_FORM_ID_alter()。

/**

* Implements hook_form_FORM_ID_alter().

*/

function example2_form_example_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {

  $form['phone_number']['#description'] = t('Start with + and your country code.');

}