improvements-2.x-dev/src/Plugin/Field/FieldFormatter/ImageWithMobileAlternativeFormatter.php
src/Plugin/Field/FieldFormatter/ImageWithMobileAlternativeFormatter.php
<?php
namespace Drupal\improvements\Plugin\Field\FieldFormatter;
use Drupal\breakpoint\BreakpointInterface;
use Drupal\breakpoint\BreakpointManagerInterface;
use Drupal\Core\Entity\EntityFieldManager;
use Drupal\Core\Field\Attribute\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\field\Entity\FieldConfig;
use Drupal\file\FileInterface;
use Drupal\image\Entity\ImageStyle;
use Drupal\image\ImageStyleInterface;
use Drupal\image\Plugin\Field\FieldType\ImageItem;
#[FieldFormatter(
id: 'image_with_mobile_alternative',
label: new TranslatableMarkup('Image with mobile alternative (separate fields)'),
field_types: ['image'],
)]
class ImageWithMobileAlternativeFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings(): array {
return [
'image_style' => '',
'breakpoint_group' => '',
'mobile_breakpoint' => '',
'mobile_field' => '',
'mobile_image_style' => '',
] + parent::defaultSettings();
}
/**
* {@inheritDoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state): array {
$image_styles_options = image_style_options(FALSE);
$breakpoint_manager = \Drupal::service('breakpoint.manager'); /** @var BreakpointManagerInterface $breakpoint_manager */
$breakpoint_group = $this->getSetting('breakpoint_group');
// Image style
$elements['image_style'] = [
'#type' => 'select',
'#title' => t('Default image style'),
'#options' => $image_styles_options,
'#empty_option' => t('None (original image)'),
'#default_value' => $this->getSetting('image_style'),
];
// Breakpoint group
$elements['breakpoint_group'] = [
'#type' => 'select',
'#title' => t('Breakpoint group'),
'#options' => $breakpoint_manager->getGroups(),
'#default_value' => $breakpoint_group,
];
// @TODO Add update form on ajax
if ($breakpoint_group) {
// Mobile breakpoint
$elements['mobile_breakpoint'] = [
'#type' => 'select',
'#title' => t('Mobile breakpoint'),
'#options' => $this->getMobileBreakpointOptions($breakpoint_group),
'#default_value' => $this->getSetting('mobile_breakpoint'),
];
// Mobile field
$elements['mobile_field'] = [
'#type' => 'select',
'#title' => t('Mobile field'),
'#options' => $this->getMobileFieldOptions(),
'#default_value' => $this->getSetting('mobile_field'),
];
// Mobile image style
$elements['mobile_image_style'] = [
'#type' => 'select',
'#title' => t('Mobile image style'),
'#options' => $image_styles_options,
'#empty_option' => t('None (original image)'),
'#default_value' => $this->getSetting('mobile_image_style'),
];
}
return $elements;
}
/**
* {@inheritDoc}
*/
public function settingsSummary(): array {
$summary = [];
if ($image_style = $this->getSetting('image_style')) {
$summary[] = t('Default image style') . ': ' . $this->getSetting('image_style');
}
if ($breakpoint_group = $this->getSetting('breakpoint_group')) {
$summary[] = t('Breakpoint group') . ': ' . $breakpoint_group;
}
if ($mobile_breakpoint = $this->getSetting('mobile_breakpoint')) {
$summary[] = t('Mobile breakpoint') . ': ' . $mobile_breakpoint;
}
if ($mobile_field = $this->getSetting('mobile_field')) {
$summary[] = t('Mobile field') . ': ' . $mobile_field;
}
if ($mobile_image_style = $this->getSetting('mobile_image_style')) {
$summary[] = t('Mobile image style') . ': ' . $mobile_image_style;
}
return $summary;
}
/**
* {@inheritDoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode): array {
$elements = [];
$formatter_settigns = $this->getSettings();
$host_entity = $items->getEntity();
$breakpoint_manager = \Drupal::service('breakpoint.manager'); /** @var BreakpointManagerInterface $breakpoint_manager */
$mobile_breakpoint = $breakpoint_manager->getBreakpointsByGroup($formatter_settigns['breakpoint_group'])[$formatter_settigns['mobile_breakpoint']];
/** @var ImageStyleInterface $default_image_style */
$default_image_style = $formatter_settigns['image_style'] ? ImageStyle::load($formatter_settigns['image_style']) : NULL;
/** @var ImageItem[] $items */
foreach ($items as $delta => $item) {
$default_image_entity = $item->entity; /** @var FileInterface $default_image_entity */
$mobile_image_entity = $host_entity->get($formatter_settigns['mobile_field'])->get($delta)->entity; /** @var FileInterface $mobile_image_entity */
$default_image_url = $this->getUrlForImageWithStyle($default_image_entity->getFileUri(), $formatter_settigns['image_style']);
$default_image_dimensions = ['width' => $item->width, 'height' => $item->height];
if ($default_image_style) {
$default_image_style->transformDimensions($default_image_dimensions, $default_image_entity->getFileUri());
}
$elements[$delta] = [
'#theme' => 'picture',
// Default image
'#img' => [
'src' => $default_image_url,
'alt' => '',
] + $default_image_dimensions,
];
if ($mobile_image_entity) {
$mobile_image_url = $this->getUrlForImageWithStyle($mobile_image_entity->getFileUri(), $formatter_settigns['mobile_image_style']);
// Mobile image
$elements[$delta]['#source'] = [
'mobile' => [
'srcset' => $mobile_image_url,
'type' => $mobile_image_entity->getMimeType(),
'media' => $mobile_breakpoint->getMediaQuery(),
],
];
}
}
return $elements;
}
/**
* Return options for "mobile breakpoint" setting.
*/
protected function getMobileBreakpointOptions(string $breakpoint_group): array {
$breakpoint_manager = \Drupal::service('breakpoint.manager'); /** @var BreakpointManagerInterface $breakpoint_manager */
$breakpoints = $breakpoint_manager->getBreakpointsByGroup($breakpoint_group);
return array_map(function ($breakpoint) {
/** @var BreakpointInterface $breakpoint */
return $breakpoint->getLabel();
}, $breakpoints);
}
/**
* Return options for "mobile field" setting.
*/
protected function getMobileFieldOptions(): array {
$this_field = $this->fieldDefinition; /** @var FieldConfig $this_field */
$this_field_name = $this_field->getName();
$entity_field_manager = \Drupal::service('entity_field.manager'); /** @var EntityFieldManager $entity_field_manager */
$bundle_fields = $entity_field_manager->getFieldDefinitions($this_field->getTargetEntityTypeId(), $this_field->getTargetBundle());
$options = [];
foreach ($bundle_fields as $field_name => $field) {
if ($field->getType() == 'image' && $field->getName() != $this_field_name) {
$options[$field_name] = $field->getLabel();
}
}
return $options;
}
/**
* Return url for image with image style.
*/
protected function getUrlForImageWithStyle(string $path, string $style_name): string {
$file_url_generator = \Drupal::service('file_url_generator');
if ($style_name && ($image_style = ImageStyle::load($style_name))) {
$url = $image_style->buildUrl($path);
}
else {
$url = $file_url_generator->generateAbsoluteString($path);
}
return $file_url_generator->transformRelative($url);
}
}
