toolshed-8.x-1.x-dev/modules/toolshed_media/src/Plugin/Field/FieldFormatter/ImageStyleTrait.php
modules/toolshed_media/src/Plugin/Field/FieldFormatter/ImageStyleTrait.php
<?php
namespace Drupal\toolshed_media\Plugin\Field\FieldFormatter;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides the formatter settings, summary, and building for image field.
*
* Common methods for supporting either rendering options for responsive image
* styles or image styles with fields that work images or media.
*/
trait ImageStyleTrait {
/**
* Service which retrieves and maintains Drupal system configurations.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected ConfigFactoryInterface $configFactory;
/**
* Service which manages various entity types and their handlers.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* Get the default field formatter options for the image styler.
*
* This method should be compatible with field formatter defaultSettings()
* method.
*
* @return array
* Default options for an image style formatter using these trait options.
*/
public static function defaultSettings(): array {
return [
'image_style' => '',
'link_to' => NULL,
'decorative' => FALSE,
'image_loading' => 'lazy',
] + parent::defaultSettings();
}
/**
* Get the current options for linking the image to.
*
* @return array<string, string|\Stringable>
* An array of 'link to' options with the labels as the value.
*/
abstract public function getLinkOptions(): array;
/**
* Create a renderable array to support field image rendering.
*
* The renderable uses the field formatter settings, and the display style
* settings to build a base renderable with the styles applied. Calling
* methods are expected to set the "#item" value to provide the image file
* to render.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field items being rendered.
*
* @return array
* A renderable array to ath be used to generate an formatted image item
* element.
*/
protected function getDisplayRenderable(FieldItemListInterface $items): array {
$settings = $this->getSettings();
$style = $settings['image_style'] ?? 'none';
$styleStorage = $this->entityTypeManager->getStorage('image_style');
$display = [];
$display['#theme'] = 'image_formatter';
$display['#item_attributes'] = ($settings['decorative'] ?? FALSE) ? [
'role' => 'presentation',
'alt' => '',
] : [];
// If image loading is an array or null, use "lazy" as the default.
$display['#item_attributes']['loading'] = $settings['image_loading']['attribute'] ?? 'lazy';
// Theme with responsive or image preset styles based on user selection.
if (str_starts_with($style, 'responsive:')) {
[, $style] = explode(':', $style, 2);
try {
/** @var \Drupal\responsive_image\ResponsiveImageStyleInterface $rStyle */
$rStyle = $this->entityTypeManager
->getStorage('responsive_image_style')
->load($style);
if ($rStyle) {
$cacheTags = $rStyle->getCacheTags();
foreach ($styleStorage->loadMultiple($rStyle->getImageStyleIds()) as $imgStyle) {
$cacheTags = Cache::mergeTags($cacheTags, $imgStyle->getCacheTags());
}
$display['#theme'] = 'responsive_image_formatter';
$display['#responsive_image_style_id'] = $style;
$display['#cache']['tags'] = $cacheTags;
}
}
catch (PluginNotFoundException $e) {
// Responsive image module is missing, fallback to an image formatter.
}
}
elseif ('none' !== $style && $imageStyle = $styleStorage->load($style)) {
$display['#image_style'] = $style;
$display['#cache'] = [
'tags' => $imageStyle->getCacheTags(),
];
}
$display['#url'] = @('entity' === $settings['link_to'])
? $items->getEntity()->toUrl()
: NULL;
return $display;
}
/**
* Generates a field formatter summary.
*
* The summary should be compatible with the field formatter
* ::settingsSummary() method.
*
* @return array
* The field formatter settings summary.
*
* @see \Drupal\Core\Field\FormatterInterface::settingsSummary()
*/
public function settingsSummary(): array {
if ($styleId = $this->getSetting('image_style')) {
if (str_starts_with($styleId, 'responsive:')) {
try {
[, $rStyleId] = explode(':', $styleId, 2);
$styler = $this->entityTypeManager
->getStorage('responsive_image_style')
->load($rStyleId);
}
catch (PluginNotFoundException $e) {
// Responsive image style module is missing, skip.
}
}
else {
$styler = $this->entityTypeManager
->getStorage('image_style')
->load($styleId);
}
}
$styleParams = [
'@label' => isset($styler) ? $styler->label() : $this->t('Original'),
];
$summary = [];
$summary[] = $this->getSetting('decorative')
? $this->t('Image style (decorative): @label', $styleParams)
: $this->t('Image style: @label', $styleParams);
$summary[] = $this->t('Image loading: :loading', [
':loading' => $this->getSetting('image_loading')['attribute'] ?? 'lazy',
]);
$linkTo = $this->getSetting('link_to');
$linkOpts = $this->getLinkOptions();
if (!empty($linkTo) && !empty($linkOpts[$linkTo])) {
$summary[] = $this->t('Link to: @target', [
'@target' => $linkOpts[$linkTo],
]);
}
// Fallback is only needed for media types when no thumbnail available.
if ('image' !== $this->fieldDefinition->getType()) {
$summary[] = $this->t('Fallback display: %fallback_display', [
'%fallback_display' => $this->getSetting('fallback_display'),
]);
}
return $summary;
}
/**
* Generates a config form for the image styler formatter settings.
*
* @return array
* Image style settings form for the media styler formatters.
*
* @see \Drupal\Core\Field\FormatterInterface::settingsForm()
*/
public function settingsForm(array $form, FormStateInterface $form_state): array {
$form = parent::settingsForm($form, $form_state);
$styleStorage = $this->entityTypeManager->getStorage('image_style');
$styleOpts = ['none' => $this->t('Original')];
foreach ($styleStorage->loadMultiple() as $styleId => $style) {
$styleOpts[$styleId] = $style->label();
}
// Add responsive image styles if any are available.
try {
$responsiveOpts = [];
$responsiveStorage = $this->entityTypeManager->getStorage('responsive_image_style');
/** @var \Drupal\responsive_image\ResponsiveImageStyleInterface $responsive */
foreach ($responsiveStorage->loadMultiple() as $responsiveId => $responsive) {
if ($responsive->hasImageStyleMappings()) {
$responsiveOpts["responsive:{$responsiveId}"] = $responsive->label();
}
}
}
catch (PluginNotFoundException $e) {
// Responsive image styles is not installed, we can safely skip.
}
// If being used with embed styles, filter the allowed embed styles to
// only the allowed styles. This prevents the usage of image presets
// that don't make sense here or, could be dangerous in an embed situation.
if ($this->viewMode === '_entity_embed') {
$embedStyles = $this->configFactory->get('toolshed.media.embed_styles');
$filterStyles = $embedStyles->get('styles');
$filterResponsive = $embedStyles->get('responsiveStyles');
if (!empty($filterStyles)) {
$styleOpts = $embedStyles->get('filter_method') === 'only_listed'
? array_intersect_key($styleOpts, $filterStyles)
: array_diff_key($styleOpts, $filterStyles);
}
if ($responsiveOpts && $filterResponsive) {
$styleOpts = $embedStyles->get('filter_method') === 'only_listed'
? array_intersect_key($responsiveOpts, $filterResponsive)
: array_diff_key($responsiveOpts, $filterResponsive);
}
}
$form['image_style'] = [
'#type' => 'select',
'#title' => $this->t('Image style'),
// Make responsive options available if any of them are available.
'#options' => $responsiveOpts ? [
(string) $this->t('Image styles') => $styleOpts,
(string) $this->t('Responsive styles') => $responsiveOpts,
] : $styleOpts,
'#default_value' => $this->getSetting('image_style'),
];
$form['decorative'] = [
'#type' => 'checkbox',
'#title' => $this->t('Is image decorative?'),
'#default_value' => $this->getSetting('decorative'),
];
$form['image_loading'] = [
'#type' => 'details',
'#title' => $this->t("Image loading"),
'#tree' => TRUE,
'attribute' => [
'#type' => 'select',
'#options' => [
'lazy' => $this->t('Lazy (load when in viewport)'),
'eager' => $this->t('Eager (load immediately)'),
],
'#default_value' => $this->getSetting('image_loading')['attribute'] ?? 'lazy',
'#description' => $this->t('<p>Select how to render and load images using the loading attribute (<em>loading=lazy</em>). This improves performance by allowing browsers to lazily load images.</p><p><a href=":link">Learn more about the loading attribute for images.</a></p>', [
':link' => 'https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes',
]),
],
];
$form['link_to'] = [
'#type' => 'select',
'#title' => $this->t('Link to:'),
'#options' => [
'' => $this->t('No link'),
] + $this->getLinkOptions(),
'#default_value' => $this->getSetting('link_to'),
];
return $form;
}
}
