Field API

Опубликовано 2012.04.14 в разделе Drupal

Данное API позволит работать с полями. Оно позволяет создавать новые типы полей. В большинстве случаев встроенных типов полей вполне хватает для пользовательских нужд, но при необходимости можно легко добавить свои.

Работу с Field API будем осуществлять в новом модуле. Назовём его test_fields. Используем ещё одну возможность модулей – зависимость одного модуля от другого. Например, для работы одного модуля необходим другой модуль. Сделаем наш второй модуль зависимым от первого. Для этого добавим в info файл строку с dependencies.

  1. name = Модуль для работы с полями.
  2. description = В этом модуле я буду использовать Field API.
  3. package = test_modules
  4. core=7.x
  5. version = "7.x-1.x-dev"
  6. project = "test_fields"
  7. datestamp = "1332419400"
  8. dependencies[] = test_module

Проверяем модуль в списке модулей.

Как видите – модуль для работы с полями зависит от тестового модуля. Оба модуля находятся в одном пакете и сгруппированы.

Давайте создадим поле, которое позволит выбрать пол автора ноды. То есть при создании ноды будет переключатель М/Ж.

Создавать модуль вы уже умеете. Добавим к модулю ещё один файл – install. Этот файл запустится только при первой установке модуля. Его имя также должно совпадать с именем модуля и иметь расширение install. Добавим в него следующую функцию.

Имя файла test_fields.install

  1. function test_fields_field_schema($field) {
  2. return array(
  3. 'columns' => array(
  4. 'textf' => array(
  5. 'type' => 'int',
  6. ),
  7. ),
  8. );
  9. }

Данный код вызовет hook_field_schema, который зарезервирует место в БД для поля с именем textf. К этому имени мы будем обращаться в следующих функциях. Это поле будет типа integer.

Новое поле должно отображаться в конфигурировании полей ноды.

В добавлении нового поля должно появиться наше новое поле. Вначале определим тип поля. Поместим в файл test_fuields.module следующую функцию.

  1. function test_fields_field_info() {
  2. return array(
  3. 'textf' => array(
  4. 'label' => t('Выбор пола'),
  5. 'description' => t('Описание поля'),
  6. 'default_widget' => 'textf_widget',
  7. 'default_formatter' => 'textf_formatter',
  8. ),
  9. );
  10. }

Теперь можно будет добавить к материалу поле типа «Выбор пола».  Значение элемента default_widget , равное textf_widget говорит о том, что тип виджета будет называться textf. С ним мы будем работать чуть позже. Этот виджет будет отвечать за то, какого типа будет поле в форме добавления ноды.

Теперь нужно дать описание полю, чтобы пользователь понимал поле какого типа он добавляет к материалу – поле ввода, текстовую область, выпадающий список и т.д. Можно вписать сюда всё что угодно.

  1. function test_fields_field_widget_info() {
  2. return array(
  3. 'textf' => array(
  4. 'label' => t('Переключатель'),
  5. 'field types' => array('textf'),
  6. ),
  7. );
  8. }

Назовём наше поле переключателем и в поле field types укажем наше имя - textf, с которого мы начинали написание модуля.

Теперь доступно новое поле для ноды.

Также для поля нужен формат вывода.

Для этого используем hook_field_formatter_info. Пусть Drupal будет выводить М/Ж или  Мужчина/Женщина. Напишем следующую функцию.

  1. function test_fields_field_formatter_info() {
  2. return array(
  3. 'textf_formatter' => array(
  4. 'label' => t('Кратко'),
  5. 'field types' => array('textf'),
  6. ),
  7. 'textf_formatter_2' => array(
  8. 'label' => t('Полно'),
  9. 'field types' => array('textf'),
  10. ),
  11. );
  12. }

textf_formatter – это наш дефолтный формат ввода, который мы указали в функции test_fields_field_info.

Теперь в настройках отображения полей мы можем выбрать, как будет выводиться значение поля.

Далее определимся – как будет выглядеть элемент формы на странице добавления ноды. Пусть это будет два радиобутона. Для этого используем hook_field_widget_form и вспомним о создании форм в Drupal. Напишем следующую функцию.

  1. function test_fields_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  2. switch ($instance['widget']['type']) {
  3. case 'textf':
  4. $element['textf'] = array(
  5. '#type' => 'radios',
  6. '#options' => array(1 => t('М'), 2 => t('Ж')),
  7. '#title' => $element['#title'],
  8. '#description' => $element['#description'],
  9. '#default_value' => isset($items[0]['textf']) ? $items[0]['textf'] : 1,
  10. '#required' => $element['#required'],
  11. '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
  12. '#delta' => $delta,
  13. );
  14. break;
  15. }
  16. return $element;
  17. }

Далее создадим два формата вывода – полный и краткий, о которых мы говорили ранее. Используем hook_field_formatter_view.

  1. function test_fields_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  2. $element = array();
  3. switch ($display['type']) {
  4. case 'textf_formatter':
  5. foreach ($items as $delta => $item) {
  6. if ($item['textf']) {
  7. $val = $item['textf'];
  8. if($val==1){
  9. $element[$delta]['#markup'] = 'М';
  10. }elseif($val==2){
  11. $element[$delta]['#markup'] = 'Ж';
  12. }
  13. }
  14. }
  15. break;
  16. case 'textf_formatter_2':
  17. foreach ($items as $delta => $item) {
  18. if ($item['textf']) {
  19. $val = $item['textf'];
  20. if($val==1){
  21. $element[$delta]['#markup'] = 'Мужчина';
  22. }elseif($val==2){
  23. $element[$delta]['#markup'] = 'Женщина';
  24. }
  25. }
  26. }
  27. break;
  28. }
  29. return $element;
  30. }

Теперь можно проверить, как это всё работает, открыв ноду.

И завершающий шаг – это вариант, когда поле не заполнено. Для этого нужно использовать hook_field_is_empty, который вернёт true.

  1. function test_fields_field_is_empty($item, $field) {
  2. if (empty($item['textf'])) {
  3. return true;
  4. }
  5. }

Этого вполне достаточно для создания собственных полей. Вы можете поэкспериментировать, создавая более сложные поля. Чтение документации позволит вам легко расширить/модифицировать данный код.


13 Комментариев

Оставить комментарий

  1. Гость 2015/10/22

    Спасибо, все проделал все получилось. по поводу первого коммента. Я после добавления function test_fields_field_schema($field) { ждал тоже что у меня поле появится, в общем потупил, скриншотик бы переместить пониже под описание поля инфо что ли

    Ответить

    1. Админ 2015/10/23

      То есть у вас этот хук не сработал? Он добавлен в install и вы точно первый раз включили модуль?
      Ради эксперимента можете попробовать выключить модуль, потом во вкладке удалить - удалить модуль и заново включить - тогда этот хук должен сработать.

      Ответить

      1. Гость 2015/10/23

        Все сработало. Просто после этого добавления, не увидите сразу поле в редактировании типа материала - а у вас по тексту кажется что это так. Поле появится лишь после _field_info() насколько я понял

        Ответить

        1. Админ 2015/10/23

          А, ну да. Чтобы сразу появилось - это нужно программно создавать тип материала с набором полей.

          Ответить

  2. Гость 2014/01/23

    подскажите пожалуйста, а почему у меня на этапе создания файла инсталл и включения модуля не создается автоматически поле custom_fields как на 3 скриншоте? и можно ли реализовать модуль так что если ты его включаешь просто добавлялось автоматически поле и на странице создание ноды оно уже было включено, без возможности выбирать виджет и тип вывода(полно, кратко)? подскажите как это реализовать?

    Ответить

    1. Админ 2014/01/24

      Перед включением модуля все хуки были написаны? Может где-то есть незначительная ошибка?
      По поводу автоматического добавления - впринципе можно...
      Можно попробовать зацепиться за hook_enable, который сработает при включении модуля, и в нём уже кодом добавить поле к ноде.

      Ответить

  3. Гость 2013/08/03

    Большое спасибо за отличный материал.
    Разбираюсь потихоньку с API
    У меня вопрос,извиняюсь,если глупый.
    Я создаю виджет. Допустим:

    в hook_field_widget_form

    1. // ...
    2. case 'my_widget':
    3. $options = list_allowed_values($field);
    4. $default_value = isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL;
    5.  
    6. $element['my_widget'] = array(
    7. '#type' => 'select',
    8. '#options' => $options,
    9. '#title' => $element['#title'],
    10. '#description' => $element['#description'],
    11. '#default_value' => $default_value,
    12. '#required' => $element['#required'],
    13. '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
    14. '#delta' => $delta,
    15. );
    16.  
    17. return $element;
    18. break;
    19. // ...

    Так вот, если в list_field_is_empty() посмотреть dsm($item)

    то у всех нормальных полей-списков - это массив, например:
    (Array, 1 element)
    value => Значение
    а у этого поля в $item просто одно значние,строка.

    Само поле с виджетом нормально отображается, значение сохраняется, но некоторые модули, напр. conditional_fields не работают с данным полем именно по этой причине.

    Никак не дойдет, как это исправить. Подскажите пожалуйста, если будет время.

    Ответить

    1. Админ 2013/08/04

      Про особенности поведения модуля condition_fields увы не смогу подсказать. Как вариант - подебажить этот модуль, посмотреть с какими данными он работает, какого вида массивы ему нужны и постараться повторить требуемую структуру.

      Ответить

      1. Гость 2013/08/04

        Спасибо за ответ!
        А я правильно понимаю, что эта структура формируется в hook_field_widget_form? Не получается, что надо сделать. Тот массив, который попадает в $items мне нужен.
        condition_fields недоработанный, бета. Но у меня и прочие проблемы проявляются

        Ответить

        1. Админ 2013/08/05

          Я бы сказал, что в нём фактически в чистом виде Form API, отвечающее только за вывод формы. Сами кишки будут в других хуках.
          Дебажьте. Поставьте XDebug + PhpStorm - очень удобно.

          Ответить

  4. Гость 2012/10/15

    а как здесь сделать, что бы если я выбираю пол мужской, у меня отображалась картинка определенная?
    Подскажите как правильно прописать в таком случае?

    Ответить

    1. Админ 2012/10/16

      Можно ориентироваться на значение поля в шаблоне ноды (node.tpl.php).
      Например:

      1. if($node->textf == 'М') {
      2. print '<img src="men.png" />';
      3. }else{
      4. print '<img src="woman.png" />';
      5. }

      Ответить

  5. Гость 2012/10/12

    Спасибо, выручил. А то у Томлинсона как то сумбурно написано про поля.

    Ответить

Ваш комментарий успешно создан

Ваш комментарий

  • Для выделения кода оберните его в тэги <code> и </code>
  • Отметьте письмо