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\FieldItemListInterface; use Drupal\Core\Field\FormatterBase; use Drupal\Core\Form\FormStateInterface; 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 = @Translation("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. */ private 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. */ private 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. */ private 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); } }