logo

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

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

演示 EBT 模块 下载 EBT 模块

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

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

演示 EPT 模块 滚动

滚动

Form API Drupal 7 —— 在 Drupal 中创建表单

14/10/2025, by Ivan

在前几节课程中,我们已经学习了钩子 hook_block_info()hook_block_view()hook_menu()hook_permission(),因此我们现在已经能够通过编程方式创建任意数量的页面和区块。而在本节中,我们将学习 Drupal 7 的 Form API,用于创建表单。我们将创建一个用于模块管理的表单,并尽量使用我们已经掌握的钩子来巩固知识。

首先,我们输出 3 个区块,这对你来说应该已经不难了:

function sitemade_block_info(){
    $blocks = array(); // 初始化一个空数组
    $blocks[1]['info'] = '用户列表'; // 后台显示的区块标题
    $blocks[2]['info'] = '节点列表';
    $blocks[3]['info'] = '术语列表';
    return $blocks; // 返回区块列表
}
 
function sitemade_block_view($delta = ''){
    $block = array(); // 初始化区块数组
    switch ($delta){
        case 1: 
          $block['subject'] = '用户'; // 区块标题
          $block['content'] = ''; 
          $query = db_select('users', 'u') // 查询用户表
            ->fields('u', array('uid', 'name'))
            ->orderBy('u.uid', 'DESC') // 按 UID 倒序
            ->range(0, 5) // 仅取前 5 个用户
            ->execute();
          $users = $query->fetchAll(PDO::FETCH_ASSOC); 
          foreach($users as $user){
            $block['content'] .='';
          }
        break;
        case 2:
          $block['subject'] = '节点';
          $block['content'] = '';
          $query = db_select('node', 'n')
            ->fields('n', array('nid', 'title'))
            ->orderBy('n.nid', 'DESC')
            ->range(0, 10)
            ->execute();
          $nodes = $query->fetchAll(PDO::FETCH_ASSOC);
          foreach($nodes as $node){
            $block['content'] .='';
        }            
        break;
        case 3:
          $block['subject'] = '术语';
          $block['content'] = '';
          $query = db_select('taxonomy_term_data', 't')
            ->fields('t', array('tid', 'name'))
            ->orderBy('t.tid', 'DESC')
            ->range(0, 10)
            ->execute();
          $terms = $query->fetchAll(PDO::FETCH_ASSOC);
          foreach($terms as $term){
            $block['content'] .='';
          }            
        break;
    }
    return $block;
}

清除缓存以便让新建区块出现在后台。然后将这些区块输出到左侧边栏:

Drupal blocks

接下来我们创建一个后台页面,用于管理这三个区块:

function sitemade_permission(){
    return array(
      'admin content blocks' => array(
        'title' => t('管理内容区块'),
        'description' => t('节点、用户、术语'),
      ),
    );
}
function sitemade_menu(){
    $items = array();    
    $items['admin/config/content/content_blocks'] = array(
      'title' => '用户、节点、术语区块',
      'description' => '区块管理',
      'page callback' => '_sitemade_content_blocks',
      'access arguments' => array('admin content blocks'),
    );
    return $items;
}
function _sitemade_content_blocks(){
    $content = 'Lorem ipsum!';
    return $content;
}

创建的页面应该在 admin/config/content/content_blocks 地址出现:

Drupal admin page

接着,我们为管理员添加查看该页面的权限(这就是我们实现 hook_permission() 的原因)。

Drupal permission

接下来我们修改代码,创建一个管理表单。我们将在表单中配置区块标题以及显示的实体数量(节点、用户、术语)。

首先修改 hook_menu 的 'page callback' 属性:

function sitemade_menu(){
    $items = array();
    $items['admin/config/content/content_blocks'] = array(
      'title' => '用户、节点、术语区块',
      'description' => '区块管理',
      'page callback' => 'drupal_get_form',
      'page arguments' => array('_sitemade_content_blocks'),
      'access arguments' => array('admin content blocks'),
    );
    return $items;
}

drupal_get_form($form_id)

drupal_get_form() 是 drupal_build_form() 的封装,用于在不需要 $form_state 的情况下调用。

参数说明

  • $form_id:表单的唯一标识符。
  • 任何其他参数都会传递给对应的表单构建函数。

返回值

返回表单数组。

因此我们需要重写函数 _sitemade_content_blocks() 以返回表单数组:

function _sitemade_content_blocks(){
    $form = array();
    $form['nodes-title'] = array(
      '#type' => 'textfield',
      '#title' => t('节点区块标题'),
      '#default_value' => variable_get('node_block', ''),
      '#size' => 60,
      '#maxlength' => 64,
      '#description' => t('节点区块的标题'),
    );
    $form['node-selected'] = array(
      '#type' => 'select',
      '#title' => t('节点数量'),
      '#options' => array(1 => t('1'), 2 => t('2'), 3 => t('3'), 4 => t('4'), 5 => t('5'), 6 => t('6'), 7 => t('7'), 8 => t('8'), 9 => t('9'), 10 => t('10')),
      '#default_value' => variable_get('node_block_range', 5),
      '#description' => t('显示在区块中的节点数量'),
    );    
    ...
    $form['actions'] = array('#type' => 'actions');
    $form['actions']['submit'] = array(
      '#type' => 'submit',
      '#value' => t('保存'),
    );
    return $form;
}

最终我们得到一个如下的后台表单:

Drupal admin form

表单已经渲染,现在我们添加提交处理函数。使用钩子 hook_formID_validate()hook_formID_submit() 分别用于验证和保存数据。

表单生成与提交

Drupal 使用 drupal_get_form() 来生成和处理表单,也可以使用 drupal_form_submit() 在没有用户交互的情况下提交表单。

下面是一个示例:

$form = drupal_get_form('my_module_example_form');
...
function my_module_example_form($form, &$form_state) {
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('提交'),
  );
  return $form;
}
function my_module_example_form_submit($form, &$form_state) {
  // 表单处理逻辑
}

现在我们为表单添加提交处理逻辑,使其保存表单数据到 Drupal 变量:

function sitemade_content_blocks_submit($form, &$form_values){
    variable_set('node_block', $form_values['values']['nodes-title']);
    variable_set('user_block', $form_values['values']['users-title']);
    variable_set('term_block', $form_values['values']['terms-title']);
    variable_set('node_block_range', $form_values['values']['node-selected']);
    variable_set('user_block_range', $form_values['values']['user-selected']);
    variable_set('term_block_range', $form_values['values']['term-selected']);
}

表单现在可以正常保存数据。提交后值会保存在 Drupal 变量中(使用 variable_set),默认值则通过 variable_get 读取。

Drupal form submit

最后修改 hook_block_view(),使其从变量中读取标题和显示数量:

function sitemade_block_view($delta = ''){
    $block = array();
    switch ($delta){
        case 1:
          $block['subject'] = variable_get('user_block', '用户');
          $block['content'] = '';
          $query = db_select('users', 'u')
            ->fields('u', array('uid', 'name'))
            ->orderBy('u.uid', 'DESC')
            ->range(0, variable_get('user_block_range', 5))
            ->execute();
          $users = $query->fetchAll(PDO::FETCH_ASSOC);
          foreach($users as $user){
            $block['content'] .='';
          }
        break;
        case 2:
          $block['subject'] = variable_get('node_block', '节点');
          $block['content'] = ''; 
          $query = db_select('node', 'n') 
            ->fields('n', array('nid', 'title')) 
            ->orderBy('n.nid', 'DESC') 
            ->range(0, variable_get('node_block_range', 5)) 
            ->execute(); 
          $nodes = $query->fetchAll(PDO::FETCH_ASSOC); 
          foreach($nodes as $node){
            $block['content'] .=''; 
        }           
        break;
        case 3:
          $block['subject'] = variable_get('term_block', '术语');
          $block['content'] = ''; 
          $query = db_select('taxonomy_term_data', 't') 
            ->fields('t', array('tid', 'name')) 
            ->orderBy('t.tid', 'DESC') 
            ->range(0, variable_get('term_block_range', 10))
            ->execute(); 
          $terms = $query->fetchAll(PDO::FETCH_ASSOC); 
          foreach($terms as $term){ 
            $block['content'] .=''; 
          }         
        break;
    }
    return $block;
}

现在返回首页查看效果。别忘了最后一步 —— 清除缓存!

Drupal block