Extra Block Types (EBT) - New Layout Builder experience❗

Extra Block Types (EBT) - styled, customizable block types: Slideshows, Tabs, Cards, Accordions and many others. Built-in settings for background, DOM Box, javascript plugins. Experience the future of layout building today.

Demo EBT modules Download EBT modules

❗Extra Paragraph Types (EPT) - New Paragraphs experience

Extra Paragraph Types (EPT) - analogical paragraph based set of modules.

Demo EPT modules Download EPT modules

Scroll

Form API Drupal 7 создание форм на Drupal

02/12/2019, by Ivan

В прошлых уроках мы познакомились с хуками hook_block_info()hook_block_view()hook_menu()hook_permission() и теперь можем создавать программно сколько угодно страниц и блоков. А в этом уроке мы ознакомимся с Form API Drupal 7 для создания форм. Мы будем создавать форму для администрирования функций модуля и постараемся использовать как можно больше уже известных нам хуков, чтобы закрепить умения.

Для начала мы выведем 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: //такая же delta как мы возвращали в hook_block_info()
          $block['subject'] = 'Пользователи'; //выводим заголовок блока
          $block['content'] = ''; //инициализируем строковую переменную
          $query = db_select('users', 'u') //запрос выборки пользователей
            ->fields('u', array('uid', 'name')) //выбор полей
            ->orderBy('u.uid', 'DESC') //сортируем по убыванию
              ->range(0, 5) //выбор пяти пользователей
            ->execute(); //запрос к БД
          $users = $query->fetchAll(PDO::FETCH_ASSOC); //обрабатывает запрос в виде массива
          foreach($users as $user){
            $block['content'] .='<div><a href="' . base_path() . 'user/' . $user['uid'] . '">' . $user['name'] . '</a></div>';
            // base_path() - функция возвращает путь к корню сайта
          }
        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'] .='<div><a href="' . base_path() . 'node/' . $node['nid'] . '">' . $node['title'] . '</a></div>';
        }            
        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'] .='<div><a href="' . base_path() . 'taxonomy/term/' . $term['tid'] . '">' . $term['name'] . '</a></div>';
          }            
        break;
    }
    return $block;
}

Очищаем кеш, чтобы наши новые блоки появились в админке. Выводим эти блоки в левый сайдбар:

Drupal blocks

Теперь сделаем страницу в админке, куда потом поместим форму для администрирования этих трех блоков:

function sitemade_permission(){
    return array(
      'admin content blocks' => array(
        'title' => t('Admin content blocks'),
        'description' => t('Nodes, Users, Terms'),
      ),
    );
}
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():

drupal_get_form($form_id)

Обертка для drupal_build_form() для использования когда $form_state не нужен.

Параметры drupal_get_form()

$form_id уникальный идентификатор для обрабатываем формы. Если функция с этим идентификатором существует, то вызывается контструктор массива форм. Модули нуждаются в похожем $form_id могут реализовываться в hook_forms(), который  составляет список $form_id значений в нужную функцию конструктора.

Любые другие дополнительные аргументы переданные функцией будет вызваны drupal_get_form, подключаясь к уникальной функции конструктора форм. Например форма node_edit (редактирования ноды) требует чтобы объект ноды был передан когда форма вызывается. Это возможно через реализацию хука hook_form_alter() и hook_FORM_ID_alter() как массив $form_state['build_info']['args'].

Возвращаемое значение drupal_get_form()

Массив формы

Итак, теперь нам нужно переписать функцию _sitemade_content_block(), чтобы она возвращала массив формы.

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['users-title'] = array(
      '#type' => 'textfield',
      '#title' => t('Заголовок блока пользователей'),
      '#default_value' => variable_get('user_block', ''),
      '#size' => 60,
      '#maxlength' => 64,
      '#description' => t('Заголовок блока для Пользователей'),
    );    
    $form['user-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('user_block_range', 5),
      '#description' => t('Количество пользователей выводимых в блоке'),
    );        
    $form['terms-title'] = array(
      '#type' => 'textfield',
      '#title' => t('Заголовок блока терминов'),
      '#default_value' => variable_get('user_block', ''),
      '#size' => 60,
      '#maxlength' => 64,
      '#description' => t('Заголовок блока для терминов'),
    );        
    $form['term-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('term_block_range', 5),
      '#description' => t('Количество терминов выводимых в блоке'),
    );        
    $form['actions'] = array('#type' => 'actions'); //добавляем кнопку сохранения
    $form['actions']['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Save'),
    );
    return $form;
}

'#type' - тип элемента.
'#title' - заголовок.

'#description' - описание.

'#options' - варианты выпадающего списка.
'#default_value' — значение по умолчанию, в данном случае мы подгружаем.

В результате форма администрирования должна стать такой:

Drupal admin form

Мы вывели форму, теперь нужно сделать обработку формы. Для обработки мы будем использовать хуки hook_formID_validate(), hook_formID_submit(), соответственно для проверки и обработки формы.

Генерация форм

Функции доступные для обработки и показа HTML-кода форм в Друпале.

Друпал использует эти функции для обеспечения совместимости обработки и показа форм, для упрощения кода и сокращения количества HTML-кода, который должен быть точно сгенерирован модулями. Главная функция среди используемых для этого в Друпале drupal_get_form(), которая используется для генерации HTML-кода формы. Форма может быть построена программно без участия пользователя с использованием функции drupal_form_submit().

drupal_get_form() принимает, обрабатывает и выводит сгенерированный HTML-код формы для модулей автоматически. Ниже пример как использовать drupal_get_form() и остальные функции для генерации форм.

<?php
$form = drupal_get_form('my_module_example_form');
...
function my_module_example_form($form, &$form_state) {
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}
function my_module_example_form_validate($form, &$form_state) {
  // Проверка корректности заполнения.
}
function my_module_example_form_submit($form, &$form_state) {
  // Обработка запроса формы.
}
?>

Или с некоторым числом дополнительных аргументов:

<?php
$extra = "extra";
$form = drupal_get_form('my_module_example_form', $extra);
...
function my_module_example_form($form, &$form_state, $extra) {
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => $extra,
  );
  return $form;
}
?>

Теперь давайте напишем обработку нашей формы, чтобы значения передаваемые с формы сохранялись в переменных друпала. Установим модуль Devel, который позволит использовать функцию dsm(), распечатывающую любые массивы и объекты. После этого добавим в модуль еще один хук:

function sitemade_content_blocks_submit($form, &$form_values){
    dsm($form_values);
}

Это позволит вывести на экран значения передаваемые с формы:

drupal devel

И теперь можно легко вставить значения в переменные:

function sitemade_content_blocks_submit($form, &$form_values){
    //dsm($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']);
}

Форма готова, после отправки данных формы значения сохраняются в переменные с помощью variable_set(), а потом заполняются дефолтные значения полей с помощью variable_get.

Drupal form submit

Осталось еще изменить хук hook_block_view(), чтобы заголовки блоков и количество ссылок на сущности в них тоже считывались с помощью variable_get().

function sitemade_block_view($delta = ''){
    $block = array(); //инициализируем пустой массив блока
    switch ($delta){
        case 1: //такая же delta как мы возвращали в hook_block_info()
          $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'] .='<div><a href="' . base_path() . 'user/' . $user['uid'] . '">' . $user['name'] . '</a></div>'; 
            // base_path() - функция возвращает путь к корню сайта
          }
        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'] .='<div><a href="' . base_path() . 'node/' . $node['nid'] . '">' . $node['title'] . '</a></div>'; 
        }           
        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'] .='<div><a href="' . base_path() . 'taxonomy/term/' . $term['tid'] . '">' . $term['name'] . '</a></div>'; 
          }         
        break;
    }
    return $block;
}

И теперь можно посмотреть результат на главной странице. И не забываем "Уходя - почисти кэш"!

Drupal block