Drupal-7, как вызвать hook_field_[formatter_]prepare_view() без перезаписи существующего форматера
Из моего модуля я ищу способ изменить значение текстовых полей во время процесса рендеринга, но БЕЗ создания нового форматера и ДО работающего в данный момент форматера работает.
Другими словами, я хочу, чтобы мои изменения всегда вносились в любое текстовое поле, как общий подготовительный шаг, независимо от того, какой форматтер будет работать впоследствии.
Чтобы это работало:
Я впервые подумал об использовании
hook_field_formatter_prepare_view()
,Чтобы вызвать его, я хотел использовать
hook_field_formatter_info_alter()
добавить имя моего модуля для каждого задействованного средства форматирования, найденного здесь. Но похоже, что индекс "модуль" принимает только уникальное имя модуля, а не массив.
Кстати, я весьма удивлен этим недостатком: мне кажется, имеет смысл разрешить последовательность форматировщиков, как разрешено использовать последовательность фильтров!- Тогда я подумал об использовании
hook_field_prepare_view()
, который, казалось, был лучшим кандидатом, так как документ сказал, что он работает до того, как собственные средства форматирования hook_field_formatter_prepare_view(). Но это тоже не работает: этот хук вызывается только для поля, созданного задействованным модулем (эта проблема обсуждалась здесь).
Любая идея? Заранее спасибо.
1 ответ
Я действительно нашел красивый способ сделать то, что искал.
Этот метод достаточно инвазивен, но отлично работает и может быть использован для разных случаев.
1. Чтобы быть как можно более ясным, сначала я перефразирую свой вопрос в терминах общего случая использования:
В процессе рендеринга, как разрешить модулю изменять значение одного или нескольких полей (заданный идентификатор поля, заданный тип поля...) до того, как средство форматирования (если оно есть) выполнит свою работу?
2. Задача для достижения этой цели:
Мы не можем заставить модуль определять новый форматер, потому что для одного и того же поля одновременно может быть определен только один
3. Стратегия, которая привела меня к желаемому результату:
- использование
hook_field_formatter_info_alter()
пробежать через существующие средства форматирования и "привить" мой модуль внутри тех, где я хочу вмешаться
(см. подробности под 4 ниже) - использование
hook_field_formatter_prepare_view()
чтобы:
(а) выполнить необходимые изменения в значениях поля
(задание, для которого предназначен мой модуль: здесь это может быть выполнено или нет, по всем полям данного типа или точно определенным областям и т. д., в зависимости от любых подробных потребностей)
(б) снова пробежаться по списку форматировщиков и, если они есть, запустить ихhook_field_formatter_prepare_view()
если он существует
(см. подробности под 5 ниже) - выполните ту же работу, что и в пункте (b) выше, последовательно для каждого из других возможных подключений хуков любого форматера:
hook_field_formatter_view()
hook_field_formatter_setting_form()
hook_field_formatter_setting_summary()
4. Подробно о том, как привить мой модуль в процессе:
Whith hook_field_formatter_info_alter(&$info)
мы сталкиваемся со следующей структурой $info:
$info = array(
['formatter machine name'] = array(
['label'] => 'Human readable formatter description',
['field types'] => array(
[0] => 'a_field_type,
[1] => 'another_field_type',
# ...
),
['settings'] => array(
['option A'] => 'option A value',
['option B'] => 'option B value',
# ...
),
['module'] => 'formatter_module_name',
),
['formatter machine name'] = array(
# ...
),
# ...
);
Мы можем легко просмотреть список форматировщиков и посмотреть на индекс "типов полей", чтобы выбрать, какие из них соответствуют нашим потребностям.
Тогда для каждого вовлеченного мы можем:
- подставьте наше собственное имя модуля в имя модуля форматирования в индексе "module"
- добавьте новый субиндекс в индекс "settings" (скажем "наш модуль привит"), чтобы зарегистрировать оригинальное имя модуля форматера
Так что наши hook_field_formatter_info_alter()
будет что-то вроде:
function mymodule_field_formatter_info_alter(&$info) {
if($info) {
foreach($info as $name=>$formatter) {
if(
!@$formatter['settings']['mymodule graft'] # or already grafted
and
array_intersect($formatter['field types'],
array('text','text_long','text_with_summary')) # here it is for text fields only
) {
# substitute mymodule to original module:
$info[$name]['settings']['mymodule graft']=$formatter['module'];
$info[$name]['module']='mymodule';
}
}
}
}
После очистки реестра классов, теперь все задействованные поля имеют свою фазу форматирования, перенаправленную в наш собственный модуль.
ПРИМЕЧАНИЕ: установка нового форматера теперь требует повторной очистки реестра классов, чтобы наш модуль также взял его в свои руки.
5. Подробно о том, как заставить оригинальные форматеры работать после нас:
Как указывалось выше, теперь это наш собственный модуль, который уведомляется о необходимости форматирования поля, а не изначально затронутый форматер.
Поэтому мы должны реагировать на наши hook_field_formatter_prepare_view()
, который должен выглядеть так:
function mymodule_field_formatter_prepare_view(
$entity_type,$entities,$field,$instances,$langcode,&$items,$displays
) {
# here we do our own job with field values:
if($items) {
foreach($items as $nid=>$node_data) {
# ...
}
}
# then we give original formatter a chance to execute its own hook:
foreach($displays as $display) {
$hook=
$display['settings']['mymodule graft'].'_field_formatter_prepare_view';
if(function_exists($hook)) {
$hook(
$entity_type,$entities,$field,$instances,$langcode,$items,$displays
);
}
}
}
Наконец, мы также должны дать шанс другим хакерам форматирования выполнить.
Для каждого из них он должен выглядеть следующим образом (замените HOOK и ARGS правильными данными для каждого крючка):
function mymodule_field_formatter_HOOK(ARGS) {
$hook=$display['settings']['mymodule graft'].'_field_formatter_HOOK';
if(function_exists($hook)) {
return $hook(ARGS);
}
}
Надеюсь это поможет...