Узнайте, как создавать динамические списки опций в Drupal, чтобы избежать ручного ввода данных. Изучите настройку allowed_values_function, которая позволяет получать актуальные данные для #options в различных частях сайта.
Иногда случается такая ситуация, что вы хотите создать поле типа список (селект, радио кнопки, чекбоксы), но содержимое в нем должно быть так или иначе динамическим.
Например, вы делаете параграф, и хотите добавить возможность вставки формы в него, для этого вам нужно сделать селект инпут со всеми доступными формами модуля Contact, но вы не хотите вбивать все возможные формы руками, порой их бывает много, и любое добавление\удаление формы повлечет за собой необходимость проконтролировать это везде где прописано руками. В общем геморой и ненадежно. А связи (entityreference) устанавливают связь на сущности, а не их типы\бандлы. Можно конечно найти готовый модуль под это дело, но стоит ли перегружать проект из-за такой мелочи контрибом? Да и это лишь один пример, может значения нужно тянуть вообще с внешнего источника, тут уже точно кастомчик под задачу.
Вы может подумаете, да чо там, альтернуть форму где эти поля, и
подсунать #options
нужный список — это тоже неверный подход в таком случае.
Да, это поможет, до поры до времени, ведь сам Drupal эти значения знать не
будет, и это очень быстро приведет к ошибке и проблемам, а если всплывет позже,
ещё хуже, так как придется всеравно переделывать и, возможно, даже придется
вспоминать и догадываться почему так происходит. Т.е. эти данные что вы
подставите, будут только в форме, но не дальше неё. Очень ненадежное решение.
Для этого как в Drupal 7, так и в 8-ой версии есть специальная настройка для
данного типа полей: allowed_values_function
— в ней указывается название
функции которая будет возвращать тот самый массив для #options
, но он будет
доступен повсюду, даже если вызвать его через EMW в
7-ке: $wrapper->field_name->optionsList()
— сделая это через альтер формы, вы
тут ничего не получите, будет пусто.
Поэтому в этом гайде инфомация о том, как установить allowed_values_function
.
Что в 7-ке, что в 8-ке это делается какими-то странными путями, причем далеко не
очевидными. Это создавая поле программно, никаких проблем, вы сразу это
указываете, но что делать если поле создано в админке? Туда подлезть уже нужно
правильно, хука с альтером никакого для этого нет. Но есть не менее надежные и
хорошие решения, которые, в случае чего, легко откатить и воспроизвести повторно
на другом проекте.
Далее по тексту подразумевается что модуль в котором пишется код имеет название dummy, учитывайте это при копипасте в свои модули..
Drupal 8
В Drupal 8 это делается несколько проще чем в в 7-ке, и из кода вам придется написать только функцию которая возвращает значения для селекта.
Первым делом пишется функция. Я, для примера, привожу код, который возвращает
список доступных форм от модуля Contact. В качестве ключа используется машинное
имя контактной формы (из-за их особенности вызова), а значением является просто
название формы. Данная функция должна принимать 3
аргумента: FieldStorageConfig $definition
, ContentEntityInterface $entity = NULL
и $cacheable
. В нашем случае они не нужны вообще. Но если потребуется, то
можете пользоваться.
/**
* Custom option list for field node.field_example_dynamic_select
*/
function dummy_node_field_example_dynamic_select_options_list(FieldStorageConfig $definition, ContentEntityInterface $entity = NULL, $cacheable) {
$options = [];
$bundle_info = \Drupal::service('entity_type.bundle.info');
$bundles = $bundle_info->getBundleInfo('contact_message');
foreach ($bundles as $machine_name => $info) {
$options[$machine_name] = $info['label'];
}
// Отключаем на выбор персональную контактную форму.
unset($options['personal']);
return $options;
}
Теперь, для нужного поля, нужно указать данную функцию в
параметре allowed_values_function
, просто как строку. Для этого:
- Заходим: Конфигурация - Разработка - Синхронизация конфигурации.
- Переходим на вкладку "Экспортировать".
- Переходим на дополнительную вкладку ниже "Один элемент".
- В "Тип конфигурации" выбираем "Хранилище полей".
- В "Имя конфигурации" выбираем наше поле.
- Вы увидите конфиг файл для данного поля. В нем будет раздел
settings
, а в нем два параметраallowed_values
иallowed_values_function
. Если вы делаете динамически значения, тоallowed_values
должен оставаться{ }
— то есть пустым массивом. Прям тут можете и править значения. Затем в другой ставим название нашей функции и получаем примерно следующее:allowed_values_function: dummy_node_field_example_dynamic_select_options_list
. - Выделяем все что находится в textarea и копируем в буфер обмена.
- Переходим на вкладку "Импортировать".
- Также переходим на второй раздел "Один элемент".
- В "Тип конфигурации", аналогично, выбираем "Хранилище полей".
- В textarea вставляем то что скопировали с изменениями. Если изменения не вноссили на экспорте, вносите тут!
- И жмете кнопку "Импортировать".
Всё готово. Теперь где бы не было это поле, значения там будут тащиться из данной функции.
Drupal 7
Так как в 7-ке нету конфигураций и всё лежит прямо в базе, нам придется использовать соответствующий API + hook для достижения цели.
Для начала, аналогично, создаем функцию, которая будет возвращать данные для селекта. В качестве примера на 7-ке, функция ниже возвращает список все Entityform форм, где ключ — машинное имя формы, а меткой является название формы.
/**
* Custom option list for field field_example_dynamic_select.
*
* @see dummy.install
*/
function dummy_node_field_example_dynamic_select_options_list() {
$entity_info = entity_get_info('entityform');
$allowed_values = array();
foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
$allowed_values[$bundle_name] = $bundle_info['label'];
}
return $allowed_values;
}
Так как я выше написал, в 7-ке нет конфигураций, нам нужно изменить конфигурацию
поля. Делается это исключительно кодом. Код, конечно, можно дернуть в
каком-нибудь /devel/php
— но это будет крайне опасно на будущее, так как тут
хотябы будет след от этих действий. Ведь в 7-ке с этим не так прозрачно как в
8-ке, там в конфиге можно посмотреть что и как, а тут это заморчоеннее, надо
либо в базу лезть либо код по памяти писать. И вообще это не очень хорошо так
поля менять.
Мы сделем "правильным" путем, и объявим hook_update_N()
. Если у вас данный хук
не объявлен, то он будет иметь аналогичные цифры, а если объявлен, то вы,
вероятнее всего, знаете как их менять.
Собственно нам нужен .install
файл где пишутся данные хуки. Если нету, то
создаем и объявляем наш хук.
<?php
/**
* @file
* Main hook for installation and update.
*/
/**
* Implements hook_update_N().
*/
function dummy_update_7101(&$sandbox) {
// Загружаем информацию о нужном поле.
$field_info = field_info_field('field_example_dynamic_select');
// Убираем дефолтные значения.
unset($field_info['settings']['allowed_values']);
// Устанавливаем функцию которая возвращает значения.
$field_info['settings']['allowed_values_function'] = 'dummy_node_field_example_dynamic_select_options_list';
// Сохраняем изменения поля.
field_update_field($field_info);
}
После этого нужно зайти на страницу /update.php
и запустить обновление. Данные
настройки применятся и вы увидите результат.
В случае чего, вы можете смело удалять поле, но если решите откатить обратно —
не поленитесь, напишите dummy_update_7102
(или какой там у вас будет номер по
счету) и проделайте аналогичное просто сделайте unset
для allowed_values_function
. А доступные значения можете уже задать через UI.
Комментарии
Здравствуйте. Я и так стараюсь писать настолько разжеванно, насколько это реально. Подобного рода статьи по большей части заметки уже для тех кто понимает зачем это. Просто такого рода потребности всплывают крайне редко, а если всплыли, с очень большой вероятностью вы понимаете что хотите и вам нужно лишь как это сделать. Даже текущий материал разжеван по полной программе с реальными примерами. Я не представляю что тут ещё можно добавить, честно.
Конечно, мне с моим опытом кажется это супер-просто, но я пытаюсь как могу разжевать доходчиво, ведь я понимаю что это, чаще всего, читают новички. Напишите, что, например, в данном материале вам, как новичку было сложно? Так как я не понимаю что тут ещё раскрыть требуется. Если есть вопросы или уточнения, их можно всегда написать в комментариях, я помогу чем смогу.
У меня есть желание попутно позаписывать видеогайды для материалов, чтобы было куда нагляднее, но что-то пока не могу найти в себе сил взять и собраться, не уверен что там доходчевее будет.
Цитата: "Подобного рода статьи по большей части заметки уже для тех кто понимает зачем это." Согласен, люди с опытом читают статью как легкую сказку) Я новичок, часто захожу на Ваш блог. Но бывают такие статьи в которых я много чего не понимаю(кажется статья о кастомных блоках или формах и о аяксе -- это те, что вспомнил). А так Вам огромное спасибо за труды. На рунете по 8 друпалу, это самый наполненный блог. Было бы очень круто если бы начали снимать видео, особенно о создании кастомных модулей. Буду премного благодарен)
Что неясно лучше уточнить. Некоторые моменты, как данный материал, нет смысла расписывать глубже. Только в каком-то видео в виде задачи в вакууме. Так как там если лезть с примерами, крышу вообще взорвет. Там и кастомное создание и прогрузка сущностей, тот же AJAX. В общем по цепочке столько всплывет, что ещё больше запутает, хотя это лишь одни пример и реальное применение может быть просто выбор в админке и затем вывод на странице, всё. Тут уже кому для чего надо и конкретного примера где это используется нет. Я использую для выбора формы и вставки их в виде параграфа по середине страницы материала. Если есть какие-то приземленные примеры я их конечно же пишу.
Вот сейчас пишу статью про Language Negotiation в 8-ке, там всё очень просто, простые примеры. Но можно в такие дебри залезть, вплодь до Inbound и Outbound путей, которые там также удачно пашут, но во-первых, это надо отдельный материал по Inbound и Outbound, ибо они сами по себе достаточно очень специфичные и в контексте другого примера не зная их, понять будет просто нереально. Во-вторых, это будет не такой частый случай, и кому такое реально потребуется, куда больше пригодиться гайд о самих inbound outbound, чем об их использовании внутри какого-то плагина. Я пишу по кусочкам, например в новых материалах я уже бегло пробегаю по формам, сервисам и прочим мелочам, опускаю что такое плагины и т.д., так как всё это уже отдельно разжевано и стараюсь давать ссылки. Конечно, если есть возможность привести парочку примеров, я стараюсь это сделать, но порой это может только усугубить всю статью и спугнуть.
Проблема в том что все так или иначе взаимосвязанно, начиная делать пример, который затрагивает другую часть ядра, нужно разжевать почему так, или пример не имеет никакого смысла, если его не поймет 90% скопировавших. Да он пашет, да круто, а как оно пашет то? Почему оно так пашет? И поэтому выкручиваюсь как могу. У меня целый список из 20-30 тем о чем нужно рассказать. Сегодня, если успею, допишу новый материал, потом, по возможности, начну разжевывать кэширование и как его контролировать программно. Это, в свою очередь, откроет новый взгляд на старые топики типа Form API, hook_theme() и другие, так как там это всё юзается, но разжевывать там это глупо, иначе статья превратиться в одну книгу. Даже если почитатете технически книги с кодом, там все перелинковано, т.е. такие книги не читаются страница за страницей, они читаются прыжками от темы к теме.
Здравствуйте, полезный блог. Но могли бы вы писать статьи с большим уклонном на новичков?