Drupal по праву считают очень гибкой CMS, а иногда и вовсе называют CMF. Обилие модулей и возможностей ядра позволяет решать огромный спектр прикладных задач при создании сайта. Сейчас я бы хотел рассмотреть один часто встречающийся случай.
Нам нужно запрограммировать поле и связать его с определенной сущностью.
Сперва рассмотрим простой вариант, когда требуется создать поле уже существующего типа, с существующими вариантами отображения: text
, textarea
, select
и т.д.
Для этого потребуется создать файл testmodule.install
и реализовать хук-функцию hook_install()
.
//testmodule.install /** * Implements hook_install(). */ function testmodule_install() { // Our field named ''field_testmodule_field'' $field_name = 'field_testmodule_field'; // Check out existing information of this field if (!field_info_field($field_name)) { field_info_cache_clear(); $field = array( 'field_name' => $field_name, 'type' => 'text', // Already existed field type 'label' => t('TestModule custom field'), ); field_create_field($field); } // Now, when we have created our field, we must create at least one instance of it in any entity $instance['field_name'] = $field_name; $instance['entity_type'] = 'node'; $instance['bundle'] = 'article'; field_create_instance($instance); }
В первой части функции мы создали само поле как абстракцию, а во второй создали его экземпляр в node
типа bundle
. После установки модуля поле появится в указанном типе сущности. Будут созданы две таблицы field_data_field_testmodule_field
и field_revision_field_testmodule_field
, которые будут содержать набор служебных полей, как и в других аналогичных таблицах.
Вопрос: Можно ли привязывать новое поле не только к нодам?
Да, можно, например к
field_collection
или к любому другому типу сущностей. Для этого нужно заменить две строчки при создании экземпляра поля:$instance['entity_type'] = 'field_collection_item'; $instance['bundle'] = 'field_collection_name';
Если же, требуемое поле должно обладать новым типом и способом отображения, то в вышеприведенный код нужно внести ряд дополнений: дописать созданную функцию и создать hook_field_schema()
для того, чтобы описать свойства самой сущности поля такие как внутренние поля, тип, отображение и вид формы внесения данных.
//testmodule.install /** * Implements hook_install(). */ function testmodule_install() { // Our field named ''field_testmodule_field'' $field_name = 'field_testmodule_field'; // Check out existing information of this field if (!field_info_field($field_name)) { field_info_cache_clear(); $field = array( 'field_name' => $field_name, 'type' => 'testmodule_field_value', 'label' => t('TestModule custom field'), 'cardinality' => '1', // number of widget to be applued in form by default 'widget' => array( 'type' => 'field_testmodule_field_widget', // hook_widget 'settings' => array( 'multiple_value_widget' => 'table', ) ), 'formater' => array( 'type' => 'field_testmodule_field_formatter', // hook_formatter ), ); field_create_field($field); } // Now, when we have created our field, we must create at least one instance of it in any entity $instance = field_info_field($field_name); $instance['entity_type'] = 'node'; $instance['bundle'] = 'article'; field_create_instance($instance); } /** * Implements hook_field_schema(). */ function testmodule_field_schema($field) { if($field['type'] == 'testmodule_field_value'){ $columns = array( 'field_1' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, ), 'field_2' => array( 'type' => 'varchar', 'length' => '256', 'not null' => FALSE, 'default' => '', ), ); return array('columns' => $columns); } }
В testmodule_field_schema
мы определили набор вложенных полей, которые будут добавлены в базу данных. Пример существующего поля с похожей структурой — image
с полями fid
, description
и format
. Теперь мы должны описать как будет выглядеть форма внесения данных в поле и отображение информации, хранящейся в поле. Для этого мы создаем файл testmodule.module
и продолжаем работать в нем.
Для корректного описания поля нам нужно определить ряд хук-функций:
hook_field_info()
hook_field_validate()
hook_field_is_empty()
hook_field_widget_info()
hook_field_widget_form()
hook_field_formatter_info()
hook_field_formatter_view()
Вопрос: Есть ли среди этих хуков необязательные?
Да,
hook_field_validate()
Данные хук функции позволят нам определить алгоритмы проверки поля, внешний вид и вид формы.
//testmodule.module /** * Implements hook_field_info(). */ function testmodule_field_info() { return array( 'testmodule_field_value' => array( 'label' => t('Test field'), 'property_type' => 'testmodule_field_value', 'property_callbacks' => array('testmodule_field_value_property_info_callback'), 'description' => t('PID relation select'), 'default_widget' => 'field_testmodule_field_widget', 'default_formatter' => ''field_testmodule_field_formatter', ), ); } function testmodule_field_value_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) { $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']]; $property['getter callback'] = 'entity_metadata_field_verbatim_get'; $property['setter callback'] = 'entity_metadata_field_verbatim_set'; unset($property['query callback']); $schema = field_info_field('field_testmodule_field'); foreach ($schema['columns'] as $key => $val) { $property['property info'][$key] = array( 'label' => $key, 'type' => 'text', 'setter callback' => 'entity_property_verbatim_set', ); } } /** * Implements hook_field_validate(). */ function testmodule_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { } /** * Implements hook_field_is_empty(). */ function testmodule_field_is_empty($item, $field) { if (empty($item['field_1'])) { return TRUE; } if (empty($item['field_2'])) { return TRUE; } } /** * Implements hook_field_widget_info(). */ function testmodule_field_widget_info() { return array( 'field_testmodule_field_widget' => array( 'label' => t('Test field widget'), 'field types' => array('field_testmodule_field'), ), ); } /** * Implements hook_field_formatter_info(). */ function nova_statistics_field_formatter_info() { return array( 'field_testmodule_field_formatter' => array( 'label' => t('Test field formatter'), 'field types' => array('field_testmodule_field'), ), ); } /** * Implements hook_field_widget_form(). */ function testmodule_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { $list = array (' - ', 'first_property', 'second_property'); $element = array( '#type' => 'container', '#attributes' => array('class' => array('field-item inline-fields')), ); //var_dump($instance['widget']['type']);die(); switch ($instance['widget']['type']) { case 'field_testmodule_field_widget': $element['field_1'] = array( '#type' => 'select', '#title' => 'Field 1:', '#description' => isset($element['#description']) ? $element['#description'] : '', '#default_value' => isset($items[$delta]['field_1']) ? $items[$delta]['field_1'] : '', '#required' => isset($element['#required']) ? $element['#required'] : '', '#options' => $list, '#weight' => isset($element['#weight']) ? $element['#weight'] : 0, '#delta' => $delta, ); $elemen['field_1']['#attributes']['class'][] = 'inline-field'; $element['field_2'] = array( '#type' => 'textfield', '#title' => 'Field 2:', '#size' => 12, '#description' => isset($element['#description']) ? $element['#description'] : '', '#default_value' => isset($items[$delta]['field_2']) ? $items[$delta]['field_2'] : '', '#required' => isset($element['#required']) ? $element['#required'] : '', '#weight' => isset($element['#weight']) ? $element['#weight'] : 0, '#delta' => $delta, ); $element['field_2']['#attributes']['class'][] = 'inline-field'; break; } return $element; } /** * Implements hook_field_formatter_view(). */ function testmodule_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { $element = array(); switch ($display['type']) { case 'field_testmodule_field_formatter': foreach ($items as $delta => $item) { switch ($item) { case 'field_1': $formattedText = $item['field_1']; $element[$delta]['#markup'] = $formattedText; break; case 'field_2': $formattedText = $item['field_2']; $element[$delta]['#markup'] = $formattedText; break; } } break; } return $element; }
Вопрос: Как сделать это поле множественным?
Данный параметр уже можно настроить через интерфейс администратора в настройках поля.
Если вам понравилась данная статья или остались вопросы, то воспользуйтесь формой комментариев ниже.