diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceFieldFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceFieldFormatter.php
index 31006f1..0e38d33 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceFieldFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceFieldFormatter.php
@@ -3,13 +3,13 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Entity\EntityDisplayBase;
+use Drupal\Core\Entity\EntityFieldManager;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
-use Drupal\Core\Entity\EntityFieldManager;
use Drupal\Core\Field\FormatterPluginManager;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@@ -65,6 +65,13 @@ class EntityReferenceFieldFormatter extends EntityReferenceFormatterBase impleme
protected $availableFieldOptions;
/**
+ * The list of available formatters, keyed by field type.
+ *
+ * @var array
+ */
+ protected $availableFormatterOptions;
+
+ /**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
@@ -147,7 +154,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
$build = [];
foreach ($entities as $delta => $entity) {
if ($entity->hasField($field_name)) {
- $build[$delta] = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId())->viewField($entity->get($field_name), array_filter($formatter_settings));
+ $build[$delta] = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId())->viewField($entity->get($field_name), $formatter_settings);
}
}
return $build;
@@ -167,7 +174,10 @@ protected function getAvailableFieldOptions() {
$entity_type_id = $this->fieldDefinition->getSetting('target_type');
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
- // We always show the entity label as an option to be selected.
+
+ // If possible, initialize the options array with the target entity's label,
+ // because we always want the label to be a selectable option for this
+ // formatter.
if ($entity_type->hasKey('label')) {
$label_key = $entity_type->getKey('label');
$field_names = [
@@ -181,9 +191,13 @@ protected function getAvailableFieldOptions() {
$target_bundles = empty($this->fieldDefinition->getSetting('handler_settings')['target_bundles']) ? array_keys($this->entityTypeBundleInfo->getBundleInfo($entity_type_id)) : $this->fieldDefinition->getSetting('handler_settings')['target_bundles'];
foreach ($target_bundles as $bundle) {
+ // Fetch all fields present on each target bundle.
$bundle_field_names = [];
/** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
foreach ($this->entityFieldManager->getFieldDefinitions($entity_type_id, $bundle) as $field_machine_name => $field_definition) {
+ // We use ::isDisplayConfigurable() to filter out the base fields that
+ // should not be exposed to the user. The only exception to this is the
+ // entity label, which was added earlier and will always be an option.
if ($field_definition->isDisplayConfigurable('view')) {
$bundle_field_names[$field_machine_name] = $field_definition->getLabel();
}
@@ -202,11 +216,16 @@ protected function getAvailableFieldOptions() {
* The field storage definition.
*
* @return string[]
- * The field formatter labels keys by plugin ID.
+ * The field formatter labels, keyed by plugin ID.
*/
protected function getAvailableFormatterOptions(FieldStorageDefinitionInterface $field_storage_definition) {
+ $field_type = $field_storage_definition->getType();
+ if (isset($this->availableFormatterOptions[$field_type])) {
+ return $this->availableFormatterOptions[$field_type];
+ }
+
$field_definition = BaseFieldDefinition::createFromFieldStorageDefinition($field_storage_definition);
- $formatters = $this->formatterPluginManager->getOptions($field_storage_definition->getType());
+ $formatters = $this->formatterPluginManager->getOptions($field_type);
$options = [];
foreach ($formatters as $formatter_id => $formatter_options) {
$formatter_definition = $this->formatterPluginManager->getDefinition($formatter_id);
@@ -216,6 +235,8 @@ protected function getAvailableFormatterOptions(FieldStorageDefinitionInterface
$options[$formatter_id] = $formatter_definition['label'];
}
}
+
+ $this->availableFormatterOptions[$field_type] = $options;
return $options;
}
@@ -236,7 +257,7 @@ public static function onFormatterTypeChange(array $form, FormStateInterface $fo
/**
* Rebuilds the form on select submit.
*/
- public static function rebuildSubmit(array &$form, FormStateInterface $form_state) {
+ public static function rebuildOnSubmit(array &$form, FormStateInterface $form_state) {
$form_state->setRebuild(TRUE);
}
@@ -269,7 +290,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
'wrapper' => 'field-formatter-ajax',
'method' => 'replace',
],
- '#submit' => [[static::class, 'rebuildSubmit']],
+ '#submit' => [[static::class, 'rebuildOnSubmit']],
'#executes_submit_callback' => TRUE,
];
@@ -299,7 +320,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
'wrapper' => 'field-formatter-settings-ajax',
'method' => 'replace',
],
- '#submit' => [[static::class, 'rebuildSubmit']],
+ '#submit' => [[static::class, 'rebuildOnSubmit']],
'#executes_submit_callback' => TRUE,
];
@@ -319,6 +340,9 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
if ($formatter = $this->formatterPluginManager->getInstance($options)) {
$settings_form = $formatter->settingsForm([], $form_state);
}
+ else {
+ drupal_set_message('Could not instantiate the formatter plugin, please choose a different formatter.', 'error');
+ }
$form['settings'] = $settings_form;
$form['settings']['#prefix'] = '
';
$form['settings']['#suffix'] = '
';
@@ -332,23 +356,13 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
*/
public function settingsSummary() {
$summary = parent::settingsSummary();
- $field_storage_definitions = NULL;
$field_name = $this->getFieldName();
- if ($this->getSetting('field_name')) {
- $summary[] = $this->t('Field "%field_name" displayed.', ['%field_name' => $field_name['label']]);
- }
- else {
- $summary[] = $this->t('The field "%field_name" will be used by default.', ['%field_name' => $field_name['label']]);
- }
-
$formatter_type = $this->getFormatterType($field_name['machine_name']);
- if ($this->getSetting('type')) {
- $summary[] = $this->t('Formatter "%type" used.', ['%type' => $formatter_type['label']]);
- }
- else {
- $summary[] = $this->t('The "%type" formatter will be used by default.', ['%type' => $formatter_type['label']]);
- }
+ $summary[] = $this->t('Displaying the field "%field_name", using the formatter "%type".', [
+ '%field_name' => $field_name['label'],
+ '%type' => $formatter_type['label'],
+ ]);
return $summary;
}
@@ -359,7 +373,8 @@ public function settingsSummary() {
* @param string $key
* The setting name.
* @param \Drupal\Core\Form\FormStateInterface $form_state
- * The form state.
+ * (optional) The form state object. If absent, this method is equivalent to
+ * parent::getSetting().
*
* @return mixed|null
* The value of the setting, or NULL if absent.
diff --git a/core/modules/media/media.module b/core/modules/media/media.module
index eeb2ac1..88b28f3 100644
--- a/core/modules/media/media.module
+++ b/core/modules/media/media.module
@@ -6,12 +6,12 @@
*/
use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\field\FieldConfigInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
+use Drupal\field\FieldConfigInterface;
/**
* Implements hook_help().
@@ -114,3 +114,81 @@ function template_preprocess_media(array &$variables) {
$variables['content'][$key] = $variables['elements'][$key];
}
}
+
+/**
+ * Implements hook_ENTITY_TYPE_presave().
+ */
+function media_field_config_presave(FieldConfigInterface $field) {
+ // Don't change anything during a configuration sync.
+ if ($field->isSyncing()) {
+ return;
+ }
+
+ // If this field formatter has not been configured yet, set the defaults to
+ // something more appropriate than the entity label. In our case, the
+ // source field.
+ if ($field->getSetting('handler') !== 'default:media' || empty($field->getSetting('handler_settings')['target_bundles'])) {
+ return;
+ }
+
+ // If there are multiple target bundles allowed, we can't assume which field
+ // to use.
+ if (count($field->getSetting('handler_settings')['target_bundles']) > 1) {
+ return;
+ }
+
+ $field_name = $field->get('field_name');
+ $media_bundle = array_keys($field->getSetting('handler_settings')['target_bundles'])[0];
+ $display = entity_get_display($field->get('entity_type'), $field->get('bundle'), 'default');
+
+ // @TODO How to detect if we need to define the formatter? At this point the
+ // display is already created and the formatter defaults were populated for
+ // this field type. How to detect then if the defaults were automattically
+ // set, or if the user really chose those settings? For now we assume that if
+ // the field is not used in any content, it is safe to change the display
+ // settings, but it may be too much to assume.
+ $num_entities = \Drupal::entityQuery($field->getTargetEntityTypeId())
+ ->condition('type', $field->getTargetBundle())
+ ->condition($field_name, NULL, 'IS NOT NULL')
+ ->count()
+ ->accessCheck(FALSE)
+ ->execute();
+ if ($num_entities > 0) {
+ return;
+ }
+
+ /** @var \Drupal\media\MediaTypeInterface $media_type */
+ $media_type = \Drupal::entityTypeManager()->getStorage('media_type')->load($media_bundle);
+ $source_field_name = $media_type->getSource()->getConfiguration()['source_field'];
+ // Take decision on which formatter to use based on known source plugins.
+ // @TODO Maybe we should move this decision to the source plugins? This would
+ // allow contrib sources also to provide their preferred default formatters.
+ $source_field_formatter_type = FALSE;
+ $source_field_formatter_settings = [];
+ switch ($media_type->getSource()->getPluginId()) {
+ case 'image':
+ $source_field_formatter_type = 'image';
+ $source_field_formatter_settings = [
+ 'image_style' => '',
+ 'image_link' => '',
+ ];
+ break;
+
+ case 'file':
+ $source_field_formatter_type = 'file_default';
+ break;
+
+ }
+
+ if ($source_field_formatter_type) {
+ $display->setComponent($field_name, [
+ 'type' => 'entity_reference_field',
+ 'settings' => [
+ 'field_name' => $source_field_name,
+ 'type' => $source_field_formatter_type,
+ 'settings' => $source_field_formatter_settings,
+ 'label' => 'hidden',
+ ],
+ ])->save();
+ }
+}
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceFieldFormatterTest.php b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceFieldFormatterTest.php
index 3f1c64d..16058db 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceFieldFormatterTest.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceFieldFormatterTest.php
@@ -96,8 +96,7 @@ public function testEntityReferenceFieldFormatter() {
$page->selectFieldOption('fields[field_related_content][type]', 'entity_reference_field');
$result = $assert_session->waitForElementVisible('css', '.field-plugin-summary-cell .ajax-new-content');
$this->assertNotEmpty($result);
- $assert_session->pageTextContains('The field "Title" will be used by default.');
- $assert_session->pageTextContains('The "Plain text" formatter will be used by default.');
+ $assert_session->pageTextContains('Displaying the field "Title", using the formatter "Plain text".');
// Save without choosing anything and verify that the child title is shown
// by default.
@@ -133,8 +132,7 @@ public function testEntityReferenceFieldFormatter() {
$page->pressButton('Update');
$result = $assert_session->waitForElementVisible('css', '.field-plugin-summary-cell .ajax-new-content');
$this->assertNotEmpty($result);
- $assert_session->pageTextContains('Field "Body" displayed.');
- $assert_session->pageTextContains('Formatter "Default" used.');
+ $assert_session->pageTextContains('Displaying the field "Body", using the formatter "Default".');
$page->pressButton('Save');
$this->drupalGet('/node/' . $this->testParentNode->id());