One quick way of achieving this is to preprocess the field before it reaches Twig (in MYTHEME.theme) like this:
/**
* Implements hook_preprocess_HOOK().
*/
function MYTHEME_preprocess_field(&$variables, $hook) {
if ($variables['field_name'] == 'field_avatar') {
if (!empty($variables['items'])) {
foreach ($variables['items'] as $delta => &$item) {
$item['content']['#item_attributes']['class'][] = 'foo';
}
}
}
}
Core's template_preprocess_image_formatter() then takes care of putting #item_attributes
and the class into the <img>
tag for you.
The normal way of offering alternative template names based on context is
implementing hook_theme_suggestions_HOOK()
or hook_theme_suggestions_HOOK_alter(). In this case the latter as MYMODULE_theme_suggestions_image_alter()
.
But the problem here is that there's not much context about the field which will eventually wrap this image at this point. template_preprocess_image_formatter()
only carries through the attributes that will end up on the image element itself.
You could be a bit sneaky and use the fact that we have already put the class (or some other identifier) into #item_attributes
when preprocessing the field and use that to generate a new template name for the image once it gets to be rendered:
/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function MYTHEME_theme_suggestions_image_alter(&$suggestions, $variables) {
if (!empty($variables['attributes']['class'] && in_array('foo', $variables['attributes']['class'])) {
$suggestions[] = 'image-foo';
}
}
It has the side-effect of adding an extra class (or other attribute) in your tag by default, but since you could now create image-foo.html.twig
you could probably compensate for that in the template by removing it there if absolutely needed.
Now just make sure your "field identifier" is fairly unique (and recognizable) so you can figure out where that template suggestion in your theme debug output comes from in the future.