
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;
}
Вопрос: Как сделать это поле множественным?
Данный параметр уже можно настроить через интерфейс администратора в настройках поля.
Если вам понравилась данная статья или остались вопросы, то воспользуйтесь формой комментариев ниже.
