翻译一篇好的技术文章真的很费脑细胞,所以请且看且珍惜!
原文链接:http://www.sitepoint.com/building-drupal-8-module-blocks-forms/
翻译:Drupal猎人
在本系列文章的第一部分,我们讲了如何从最基础开始开发一个drupal8模块,我们已经看到了需要创建哪些文件来让drupal8识别我们的模块,以及
路由如何工作,还有怎么创建菜单来链接程序配置。
在本教程中,我们将学习进一步的开发,我们可以在这个库里找到我们需要的沙盒模块代码示例,里面两个重要的新功能:区块和表单。
为此,我们将创建一个自定义区块来返回一些可配置的文本,在那之后,我们将创建 一个简单的表单来打印输出用户提交到的数据到屏幕上。
不知道怎么下载这个库的同学看这里:
Drupal8 区块
在Drupal8里,有一个很酷的新变化时,以往的Block(区块) API 已经转换成了插件的形式(一个全新的概念),这意味着区块将是可被重复使用的功能块(在引擎下),
你现在可以在后台UI界面创建一个Block区块,并在整个网站的任何地方重用它,你现在不会再局限于只能使用一次这个区块。
让我们先创建一个简单的区块来打印"Hello World"到屏幕上! 一般情况下,我们首先需要在模块根文件夹下的src/Plugin/Block目录下创建一个class类
文件DemoBlock.php,我们这里暂且把这个新的区块命名为"DemoBlock", 所以你最终在这个文件中,看到的代码会是像这样:
<?php namespace Drupal\demo\Plugin\Block; use Drupal\block\BlockBase; use Drupal\Core\Session\AccountInterface; /** * Provides a 'Demo' block. * * @Block( * id = "demo_block", * admin_label = @Translation("Demo block"), * ) */ class DemoBlock extends BlockBase { /** * {@inheritdoc} */ public function build() { return array( '#markup' => $this->t('Hello World!'), ); } /** * {@inheritdoc} */ public function access(AccountInterface $account) { return $account->hasPermission('access content'); } }
像所有其他的class类文件一样,我们要先在开始时先声明类的命名空间。然后,我们使用BlockBase类以便于我们等下可以继承和扩展它,以及也需要使用
AccountInterface类以便于让我们可以获取当前登录的用户。然后接下来的一些代码你肯定没有在Drupal 7见过,这就是:annotations注释。
Annotations注释是一个PHP发现工具,使用这些注释,我们可以让Drupal知道我们想注册一个新的区块(@ Block),我们设定了Id号:demo_block
和admin_label :Demo block(这些注视将会通过注视翻译系统被drupal识别)。
接下来,我们在DemoBlock类里扩展下BlockBase类,我们将实现两个方法(都是最常见的),build()方法是最重的,因为它返回一个可打印的区块信息数组,access()方法
控制查看此区块的访问权限,这里传递给它的参数是AccountInterface类的一个实例,在这里,实际传递的就是当前用户信息参数。
另外一个有趣的事情需要注意的是,我们不再像以前一样使用全局t()函数直接翻译文本了,但是我们会继承使用父类的t()方法,所以你看到的是$this->t().
就是这样,你现在可以清空缓存,然后进入区块的配置页,最酷的是你现在已经成功创建了一个区块,并且你可以把这个区块放在任何网站里你设定的regions区域。
Drupal 8 区块配置
现在我们已经知道了如何创建一个新的区块,让我们进一步学习下如何用API为它添加配置表单,我们将让你可以编辑此区块,给你一个文本输入框,让你可以输入一个名字,然
后前台就会显示"Hello 名字"来替换 "Hello World".
首先,我们需要定义一个文本框,所以在我们的DemoBlock类里,我们可以添加一个新的方法叫做blockForm():
/** * {@inheritdoc} */ public function blockForm($form, &$form_state) { $form = parent::blockForm($form, $form_state); $config = $this->getConfiguration(); $form['demo_block_settings'] = array( '#type' => 'textfield', '#title' => $this->t('Who'), '#description' => $this->t('Who do you want to say hello to?'), '#default_value' => isset($config['demo_block_settings']) ? $config['demo_block_settings'] : '', ); return $form; }
这个表单API应该看起来很熟悉,似乎和以往的drupal 7里的差不多,然而,细看之下,你会发现有一些新的东西在里面,首先,我们从父类里获取$form数组(所以我们是在已存在
的表单基础上新增创建我们的字段),标准的面向对像,然后,我们通过BlockBase类定义的getConfiguration()方法来获取配置信息,并且我们把demo_block_settings的值作为了
#default_value的默认值.
接下来,到了我们要对我们传递的字段值进行提交处理并保存进区块配置里的时候了:
/** * {@inheritdoc} */ public function blockSubmit($form, &$form_state) { $this->setConfigurationValue('demo_block_settings', $form_state['values']['demo_block_settings']); }
这个方法也存在于DemoBlock类里,它被用于保存demo_block_settings的值(请注意保持键名的一致性)
最后我们需要调整我们的build()方法,让它在打印Hello时包含name名字:
/** * {@inheritdoc} */ public function build() { $config = $this->getConfiguration(); if (isset($config['demo_block_settings']) && !empty($config['demo_block_settings'])) { $name = $config['demo_block_settings']; } else { $name = $this->t('to no one'); } return array( '#markup' => $this->t('Hello @name!', array('@name' => $name)), ); }
现在,这应该看起来相当容易,我们获取区块的配置信息,如果我们的字段值存在,我们就把它打印出来,如果不存在,就打印"to no one", 你现在可以清空缓存后,编辑区块,填写
一个值,然后保存,然后你就能在前台看到" Hello + 名字值"了。
Drupal 8 表单
最后,我们将在本教程中,探讨如何创建一个简单的表单,由于空间的限制,我不会覆盖它的配置管理(通过表单提交存储值),相反,我将举例说明一个简单的表单设定,然后让提交
的值简单的打印在屏幕上,以演示它可以正常工作。
在Drupal 8 里,表单定义函数都在一个class类里被组织在一起,所以让我们定义一个简单的DemoForm类,在src/Form/DemoForm.php中:
<?php /** * @file * Contains \Drupal\demo\Form\DemoForm. */ namespace Drupal\demo\Form; use Drupal\Core\Form\FormBase; class DemoForm extends FormBase { /** * {@inheritdoc}. */ public function getFormId() { return 'demo_form'; } /** * {@inheritdoc}. */ public function buildForm(array $form, array &$form_state) { $form['email'] = array( '#type' => 'email', '#title' => $this->t('Your .com email address.') ); $form['show'] = array( '#type' => 'submit', '#value' => $this->t('Submit'), ); return $form; } /** * {@inheritdoc} */ public function validateForm(array &$form, array &$form_state) { if (strpos($form_state['values']['email'], '.com') === FALSE ) { $this->setFormError('email', $form_state, $this->t('This is not a .com email address.')); } } /** * {@inheritdoc} */ public function submitForm(array &$form, array &$form_state) { drupal_set_message($this->t('Your email address is @email', array('@email' => $form_state['values']['email']))); } }
除了OOP思想的改变,一切应该看起来像以前的drupal 7 一样熟悉。Form API 仍然基本没有什么变化(除了一些新的表单元素的加入和相关class类的封装),所以会发生什么?
首先,我们使用了命名空间的类和核心的FormBase类,所以接下来我们就可以在我们自己的DemoForm类里扩展它,然后我们实现了4个方法,其中3个应该是很熟悉的。 getFormid()方法
是新增的并且是必须要有的,这个方法用于简单的返回form的机器名称,buildForm()方法也是必须要有的,它用于构建一个表单,怎么用?其实就和你之前在drupal 7中的构建方法差
不多,validateForm()方法是可选的,它像drupal 7里一样,用于验证表单提交数据,最后,submitForm()方法用于处理提交的表单数据,一切是不是很合乎逻辑组织。
所以,我们想用这个表单实现什么?我们有一个email邮件字段(Drupal 8的新表单元素),我们希望用户填写它,默认情况下,Drupal会检查是否输入了一个正确的邮箱格式的值,但
在我们的验证函数里我们会确保它是一个合法的带.com后缀的邮箱地址,如果不是,我们会在字段上设置了一个错误提示,最后提交处理,然后打印一个提示消息在网页上。
为了使用这个表单,我们需要做最后一件事,就是提供为这个表单提供一个路由,所以请编辑demo.routing.yml文件,并增加以下代码:
demo.form: path: '/demo/form' defaults: _form: '\Drupal\demo\Form\DemoForm' _title: 'Demo Form' requirements: _permission: 'access content'
这应该看起来很熟悉,因为我们之前有篇文章介绍过怎么路由一个简单页面,唯一最大的区别是, 在 defaults下面,我们用_form替代了之前的_content,并为_form指定了一个表单类,
这个类就是刚刚我们创建的。
清空缓存后就可以输入地址导航到demo/form,然后就可以看到这个表单并测试它。
如果你熟悉以前drupal 7中的drupal_get_form,也知道怎样加载一个表单的方式,因此,你就可以使用drupal 8中的全局Drupal类,比如formBuilder()方法来获取一个表单,像这样
$form = \Drupal::formBuilder()->getForm('Drupal\demo\Form\DemoForm');然后你就可以返回$form 并渲染输出了。
结论
在这篇文章中,我们继续探索了Drupal 8模块开发的两个新的话题:区块和表单。我们已经看到了如何创建我们自己的区块(然后你就可以在区块管理界面创建新区块)。我们
还学会了如何添加和存储一个自定义的配置值(这个值你可以在稍后使用它)。在表单的话题中,我们看到了一个简单的FormBase类的实现,我们用来把用户提交的数据打印到屏幕上。
在接下来的教程中,我们将快速学习下配置表单。我们将利用Drupal 8配置系统来存储用户提交的值。此外,我们将看看service服务是怎么在在Drupal 8里工作的。到时见。
nice