I actually found a pretty way to do what I looked for.
The method is quite invasive but works fine and may be re-used for different cases.
1. To be as clear as possible, first I rephrase my question in terms of a
general use case:
In the rendering process, how to permit a module to change value of one or more
fields (given field-id, given field-type...) before the formatter (if any) do its own job?
2. The problem to accomplish this:
We can't make the module define a new formatter, because only one may
be defined at the same time for the same field
3. The strategy which led me to the desired result:
- use
hook_field_formatter_info_alter()
to run through existing formatters and "graft" my module inside of those where I wish to intervene
(see detail under 4 below)
- use
hook_field_formatter_prepare_view()
to:
(a) execute the required changes in field values
(the job my module is intended to: here it may be done or not, upon all fields of a given type or precisely identified fiels and so on, depending on any detailed needs)
(b) again run through formatters list and, when involved, fire their own hook_field_formatter_prepare_view()
if it exists
(see detail under 5 below)
- do the same job as in (b) above, successively for each of the other possibly involved hooks of any formatter:
hook_field_formatter_view()
hook_field_formatter_setting_form()
hook_field_formatter_setting_summary()
4. Detail about how to graft my module in the process:
Whith hook_field_formatter_info_alter(&$info)
we face the following $info structure:
$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(
# ...
),
# ...
);
We can easily run through the formatters list and look at "field types" index to select which ones are concerned by our needs.
Then for each involved one, we can:
- substitute our own module name to formatter module name in "module" index
- add a new sub-index in "settings" index (say "our module graft") to register the original formatter module name
So our hook_field_formatter_info_alter()
will be something like:
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';
}
}
}
}
Once flushing class registry, now all involved fields have their formatting phase redirected to our own module.
NOTE: installing a new formatter now requires flushing class registry again, in order our module to take it in hand also.
5. Detail about how to make original formatters to work after us:
As stated above, now it is our own module which is notified when a field has to been formatted, rather than the originally affected formatter.
So we must react in our hook_field_formatter_prepare_view()
, which should look like:
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
);
}
}
}
Finally we also must give a chance to other formatters hooks to execute.
For each one, it should look like (replace HOOK and ARGS by the right data for each hook):
function mymodule_field_formatter_HOOK(ARGS) {
$hook=$display['settings']['mymodule graft'].'_field_formatter_HOOK';
if(function_exists($hook)) {
return $hook(ARGS);
}
}
Hope this helps...