9

Am currently trying to implement a node referenced image field on my Drupal 7 site that will change it's 'view mode' depending on programmatical logic just before render. The field is currently being rendered on a number of content types via their display mode settings, each of which are using the field formatter 'Rendered node'.

first attempt

My first idea was to implement the following, considering a hook is a hook is a hook:

function HOOK_field_formatter_view( $entity_type, $entity, $field ... ){
  switch ($display['type']) {
    case 'node_reference_node':
      /* Programatical logic here to modfy field render settings */
    break;
  }
}

Obviously exchanging HOOK for my module's name.

It didn't really matter if the above would fire before or after the original function node_reference_field_formatter_view in node_reference.module because I would either override it's output entirely or, hopefully, modify its values before rendering. The only problem is, the above hook seems to function only on a per module basis — i.e. it's not site-wide, so basically it doesn't fire for my module.

Now obviously I can write my own field formatter to output a rendered node. But it seems a bit of a waste considering there is already one that exists.

other approaches

My other approaches have been to HOOK_preprocess_node and HOOK_preprocess_field but the former doesn't contain any view_mode information, and the latter contains at least 5 different complicated structures that all have references to a view_mode property at different levels — and it feels rather hacky to have to modify each of their values. Even when I've changed certain view_mode properties the resulting image doesn't change.

question

Does anyone know of a clean way to step in before a field formatter renders (of a contrib module) and change it's settings on a per page request basis — i.e. by not having to change the actual content type's permanent view mode settings?

4
  • 2
    I've been looking for a hook_field_formatter_view_alter() or similar for probably over a year now, it just doesn't exist unfortunately. FYI hook_preprocess_node() definitely does have view_mode available to it, it's in $vars['view_mode'], not $vars['node']->view_mode which you might have been tempted to try.
    – Clive
    Jan 24, 2013 at 16:58
  • @Clive thanks for the info, oh and for pointing out the view_mode on hook_preprocess_node, foolish me! I wonder if anything like hook_field_formatter_view_alter() this exists in D8...
    – Pebbl
    Jan 24, 2013 at 17:58
  • I haven't seen anything yet...but then everything's being converted to plugins in D8 (I'm not sure about fields) so it might be that you can just override an existing class to get the job done, that would be ideal. Here's hoping!
    – Clive
    Jan 24, 2013 at 18:01
  • 1
    @Clive, have you found your magic alter hook mentioned here for D7? Oct 15, 2015 at 15:40

4 Answers 4

12

The question mentions that hook_field_formatter_view() is only called on the originating module, but you can take ownership of the field formatter via hook_field_formatter_info_alter().

You should be able to set the module key of the formatter to MYMODULE like:

function MYMODULE_field_formatter_info_alter(&$info) {
  $info['some_field_formatter']['module'] = 'MYMODULE';
}

Then you can implement MYMODULE_field_formatter_view(), optionally feeding through the existing module which handled it to get an element to alter.

function MYMODULE_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  // Your custom logic
  $element = OTHER_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display);
  // Any alterations
}
4
  • Can you elaborate on your answer? Provide some code? Aug 1, 2014 at 15:29
  • Nice solution, I almost never think to take complete possession of another module's business (and relay back after changes)... just tried it out though and I end up with much simpler code. Cheers!
    – Pebbl
    Aug 2, 2014 at 1:03
  • 1
    @ScottJoudry ~ might just be worth noting that if you take over in this way you need to make sure you have methods for all the field_formatter functionality of node_reference i.e. MYMODULE_field_formatter_settings_summary and MYMODULE_field_formatter_settings_form (even if they are just proxy functions back to the original module) otherwise the back-end UI breaks on any display mode panels, when it tried to find these methods in the wrong module.
    – Pebbl
    Sep 16, 2014 at 17:12
  • The approach of Graham C is interesting, but requires too many overrides.
    – milkovsky
    Mar 24, 2015 at 14:56
2

Ok so I realised why my changes to #view_mode in either hook_preprocess_node and hook_preprocess_fields weren't working. (Thanks to Clive for pointing out that I had totally missed the presence of #view_mode in hook_preprocess_node).

My problem stemmed from the fact that #view_mode had already been processed and converted to the correct #image_style property — something which I had neglected to search for.

Even so, changing this value seems too heavily dependent on which hook you modified it in. I finally got some code working though, that actually changes the rendered image:

function HOOK_preprocess_field( &$vars ){
  $element     = &$vars['element'];
  $entity_type = !empty($element['#entity_type']) ? $element['#entity_type'] : 'unknown';
  $bundle      = !empty($element['#bundle'])      ? $element['#bundle']      : 'unknown';
  $view_mode   = !empty($element['#view_mode'])   ? $element['#view_mode']   : 'unknown';
  $field_name  = !empty($element['#field_name'])  ? $element['#field_name']  : 'unknown';
  switch ( "$entity_type:$view_mode:$bundle/$field_name" ) {
    case 'node:full:mlandingpage/field_lead_image':
      if ( !empty($vars['items']) && 
           ($subelement = &$vars['items'][0]) ) {
        if ( !empty($subelement['field_image']) && 
             ($subfield = &$subelement['field_image'][0]) ) {
          /// this needs to be set to the image_style value, not the view_mode value.
          $subfield['#image_style'] = 'grid-normal-4-cols';
        }
      }
    break;
  }
}

The above still doesn't feel very eloquent, but at least it works. I'll take Clive's word on the fact that such an _alter method doesn't exist for field formatters — it's a shame, formatters are an extremely powerful feature of D7, it would be nice to have more augmenting ability.

Anyway, if any future people have any better ideas, answer away :)

0

The easiest approach will be using the Panelizer.

If don't use Panelizer but default Drupal view modes or Display Suite, try hook_field_display_alter() or hook_field_display_ENTITY_TYPE_alter().

You have entity, display context as well as all the formatter settings there. And you can easily change field render settings. You can even change field formatter to a different one.

The approach works perfect for me. The only disadvantage is that you might be confused with different settings in the the "Manage display" UI.

0

https://www.drupal.org/node/2130757 It gives a good example. hook_field_formatter_third_party_settings_form() is useful to alter the form of the existing field formatter.

But this doesn't work with field groups.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.