diff --git a/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php index 0121dcd6bd5fcdaba6ad9fcb3d9094d91aa0c8bd..1d890c974d3a97230d4bf607343313fdea6c308c 100644 --- a/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php @@ -21,8 +21,8 @@ use Drupal\Core\Url; use Drupal\field_ui\FieldUI; use Drupal\media\Entity\Media; -use Drupal\media_library\MediaLibraryUiBuilder; use Drupal\media_library\MediaLibraryState; +use Drupal\media_library\MediaLibraryUiBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Validator\ConstraintViolationInterface; @@ -321,6 +321,10 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen ], ]; + if ($this->fieldDefinition->isRequired()) { + $element['#element_validate'][] = [static::class, 'validateRequired']; + } + // When the list of allowed types in the field configuration is null, // ::getAllowedMediaTypeIdsSorted() returns all existing media types. When // the list of allowed types is an empty array, we show a message to users @@ -940,4 +944,29 @@ protected static function setFieldState(array $element, FormStateInterface $form static::setWidgetState($element['#field_parents'], $element['#field_name'], $form_state, $field_state); } + /** + * Validates whether the widget is required and contains values. + * + * @param array $element + * The form element. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + * @param array $form + * The form array. + */ + public static function validateRequired(array $element, FormStateInterface $form_state, array $form) { + // If a remove button triggered submit, this validation isn't needed. + if (in_array([static::class, 'removeItem'], $form_state->getSubmitHandlers(), TRUE)) { + return; + } + + $field_state = static::getFieldState($element, $form_state); + // Trigger error if the field is required and no media is present. Although + // the Form API's default validation would also catch this, the validation + // error message is too vague, so a more precise one is provided here. + if (count($field_state['items']) === 0) { + $form_state->setError($element, t('@name field is required.', ['@name' => $element['#title']])); + } + } + } diff --git a/core/modules/media_library/tests/src/FunctionalJavascript/EntityReferenceWidgetTest.php b/core/modules/media_library/tests/src/FunctionalJavascript/EntityReferenceWidgetTest.php index 5bfad98b6bc07ab4e406ff6a195d62c47d36985a..53400620ec168d60ab292b0fee2cb09feb5fd3ef 100644 --- a/core/modules/media_library/tests/src/FunctionalJavascript/EntityReferenceWidgetTest.php +++ b/core/modules/media_library/tests/src/FunctionalJavascript/EntityReferenceWidgetTest.php @@ -2,6 +2,8 @@ namespace Drupal\Tests\media_library\FunctionalJavascript; +use Drupal\field\Entity\FieldConfig; + /** * Tests the Media library entity reference widget. * @@ -448,4 +450,34 @@ public function testWidget() { $assert_session->pageTextNotContains('Snake'); } + /** + * Tests saving a required media library field. + */ + public function testRequiredMediaField() { + $assert_session = $this->assertSession(); + $page = $this->getSession()->getPage(); + + // Make field_unlimited_media required. + $field_config = FieldConfig::loadByName('node', 'basic_page', 'field_unlimited_media'); + $field_config->setRequired(TRUE)->save(); + + $this->drupalGet('node/add/basic_page'); + + $page->fillField('Title', 'My page'); + $page->pressButton('Save'); + + // Check that a clear error message is shown. + $assert_session->pageTextNotContains('This value should not be null.'); + $assert_session->pageTextContains(sprintf('%s field is required.', $field_config->label())); + + // Open the media library, select an item and save the node. + $this->openMediaLibraryForField('field_unlimited_media'); + $this->selectMediaItem(0); + $this->pressInsertSelected('Added one media item.'); + $page->pressButton('Save'); + + // Confirm that the node was created. + $this->assertSession()->pageTextContains('Basic page My page has been created.'); + } + }