diff --git a/src/MediaBundleForm.php b/src/MediaBundleForm.php index e8d0b37..014a538 100644 --- a/src/MediaBundleForm.php +++ b/src/MediaBundleForm.php @@ -316,7 +316,6 @@ class MediaBundleForm extends EntityForm { $plugin_configuration = !empty($this->configurableInstances[$plugin]['plugin_config']) ? $this->configurableInstances[$plugin]['plugin_config'] : []; $instance = $this->mediaTypeManager->createInstance($plugin, $plugin_configuration); $instance->submitConfigurationForm($form, $form_state); - } /** diff --git a/src/MediaTypeDefaultFieldTrait.php b/src/MediaTypeDefaultFieldTrait.php index 83d40be..70053ff 100644 --- a/src/MediaTypeDefaultFieldTrait.php +++ b/src/MediaTypeDefaultFieldTrait.php @@ -11,13 +11,17 @@ use Drupal\field\Entity\FieldStorageConfig; /** * To exhibit by derivatives of Drupal\media_entity\MediaTypeBase. - * - * Implements most of the behaviour required for a single source field that may + * + * Implements most of the behavior required for a single source field that may * be populated with a default field upon creation of a bundle. + * + * You will need to add a source_field key to your configuration schema. */ trait MediaTypeDefaultFieldTrait { use StringTranslationTrait; + protected $defaultSourceFieldName; + /** * Produce an array of allowed field types for the source field. * @@ -28,11 +32,6 @@ trait MediaTypeDefaultFieldTrait { abstract public function allowedSourceFieldTypes(); /** - * Return the default source field's name. - */ - abstract public function defaultSourceFieldName(); - - /** * Return the default source field's type. */ abstract public function defaultSourceFieldType(); @@ -54,12 +53,12 @@ trait MediaTypeDefaultFieldTrait { /** * Produce a form fragment to configure the source field. - * + * * To be called from a media type's buildConfigurationForm() method. - * + * * @param array $form * The $form array as passed to the buildConfigurationForm(). - * + * * @param \Drupal\Core\Form\FormStateInterface $form_state * @return array */ @@ -76,24 +75,28 @@ trait MediaTypeDefaultFieldTrait { } } - // Select the source field. Only show when the bundle is not new, so there - // will potentially be fields to select. + // Add a checkbox to allow the field being created automatically on save. + // Only include it when the bundle is new, so we can use its presence to + // know we can safely override the source field configuration. + if ($bundle->isNew()) { + $form['create_source_field'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Create a source field automatically when saving this form.'), + '#description' => $this->t('If checked, a default field will be created and used as a source field. If you uncheck the field, you will need to create a field and revisit this form later to select it.'), + '#default_value' => TRUE, + ]; + } + + // Select the source field. Only show it when the bundle is not new, so + // there will potentially be fields to select. $form['source_field'] = array( '#type' => 'select', '#title' => $this->t('Field with source information'), - '#default_value' => empty($this->configuration['source_field']) ? $this->defaultSourceFieldName() : $this->configuration['source_field'], + '#default_value' => $this->configuration['source_field'], '#options' => $options, '#access' => !$bundle->isNew(), ); - // Add a checkbox to allow the field being created automatically on save. - $form['create_source_field'] = [ - '#type' => 'checkbox', - '#title' => $this->t('Create a source field automatically when saving this form.'), - '#description' => $this->t('If checked, a default field will be created and used as a source field. If you uncheck the field, you will need to create a field and revisit this form later to select it.'), - '#default_value' => TRUE, - '#access' => $bundle->isNew(), - ]; return $form; } @@ -102,7 +105,7 @@ trait MediaTypeDefaultFieldTrait { * {@inheritdoc} */ public function reactOnBundleCreated($bundle_name, $entity_type_id) { - if (!empty($this->configuration['create_source_field'])) { + if (isset($this->defaultSourceFieldName)) { $this->createDefaultSourceField($bundle_name); } } @@ -112,22 +115,23 @@ trait MediaTypeDefaultFieldTrait { */ public function createDefaultSourceField($bundle_name) { - // @todo Sanity-check: defaultSourceFieldType is actually allowed. - - // Create / load the field storage. - if (!$storage = FieldStorageConfig::loadByName('media', $this->defaultSourceFieldName())) { - $storage = FieldStorageConfig::create([ - 'field_name' => $this->defaultSourceFieldName(), - 'entity_type' => 'media', - 'type' => $this->defaultSourceFieldType(), - ]); - $storage->save(); + if (!in_array($this->defaultSourceFieldType(), $this->allowedSourceFieldTypes())) { + throw new \LogicException('Default source field type is not within the allowed field types.'); } + $field_name = $this->defaultSourceFieldName; + + $storage = FieldStorageConfig::create([ + 'field_name' => $field_name, + 'entity_type' => 'media', + 'type' => $this->defaultSourceFieldType(), + ]); + $storage->save(); + // Create the field instance. FieldConfig::create([ 'entity_type' => 'media', - 'field_name' => $this->defaultSourceFieldName(), + 'field_name' => $field_name, 'label' => $this->defaultSourceFieldLabel(), 'required' => TRUE, 'bundle' => $bundle_name, @@ -141,7 +145,7 @@ trait MediaTypeDefaultFieldTrait { 'mode' => 'default', 'status' => TRUE, ]); - $form_display->setComponent($this->defaultSourceFieldName(), [ + $form_display->setComponent($field_name, [ 'type' => $this->defaultSourceFieldWidget(), ])->save(); @@ -153,9 +157,54 @@ trait MediaTypeDefaultFieldTrait { 'mode' => 'default', 'status' => TRUE, ]); - $display->setComponent($this->defaultSourceFieldName(), [ + $display->setComponent($field_name, [ 'type' => $this->defaultSourceFieldFormatter(), ])->save(); } + /** + * Determine a free field name to use as the default field. + * + * @return string + * An appropriate field name that was determined to be available. + */ + protected function determineDefaultSourceFieldName() { + // Determine a unique field name. + if (isset($this->defaultSourceFieldName)) { + return $this->defaultSourceFieldName; + } + + $base_field_name = 'field_media_' . $this->getPluginId(); + $field_name_candidate = $base_field_name; + $postfix = ''; + while (FieldStorageConfig::loadByName('media', $field_name_candidate)) { + if (empty($postfix)) { + $postfix = 1; + } + else { + $postfix++; + } + + $field_name_candidate = $base_field_name . '_' . $postfix; + } + + return $this->defaultSourceFieldName = $field_name_candidate; + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + // Take out the create_source_field key when present so it doesn't end up + // in the configuration. Record it was set so we can later decide to + // actually create the field. Determine the field name here so it correctly + // is entered into the configuration. + if (isset($configuration['create_source_field'])) { + unset($configuration['create_source_field']); + $configuration['source_field'] = $this->determineDefaultSourceFieldName(); + } + + parent::setConfiguration($configuration); + } + } diff --git a/src/MediaTypeInterface.php b/src/MediaTypeInterface.php index 4cda123..632dcec 100644 --- a/src/MediaTypeInterface.php +++ b/src/MediaTypeInterface.php @@ -95,15 +95,4 @@ interface MediaTypeInterface extends PluginInspectionInterface, ConfigurablePlug */ public function reactOnBundleCreated($bundle_name, $entity_type_id); - /** - * Create a default source field. - * - * Plugins defining media bundles are strongly encouraged to provide a default - * source field to all fields used as source, when a new bundle is created. - * - * @param string $bundle_name - * The machine-name of the bundle recently created. - */ - public function createDefaultSourceField($bundle_name); - } diff --git a/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php b/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php index 18571f1..5ba6800 100644 --- a/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php +++ b/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php @@ -7,6 +7,7 @@ use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\Core\Form\FormStateInterface; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; +use Drupal\media_entity\MediaTypeDefaultFieldTrait; use Drupal\media_entity\Plugin\MediaEntity\Type\Generic; /** @@ -19,21 +20,42 @@ use Drupal\media_entity\Plugin\MediaEntity\Type\Generic; * ) */ class TestType extends Generic { + use MediaTypeDefaultFieldTrait; /** - * The name of the default source field on the media entity. + * {@inheritdoc} + */ + public function allowedSourceFieldTypes() { + return ['string']; + } + + /** + * {@inheritdoc} + */ + public function defaultSourceFieldLabel() { + return $this->t('Test source'); + } + + /** + * {@inheritdoc} */ - const MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME = 'field_media_test_type'; + public function defaultSourceFieldType() { + return 'string'; + } /** - * The id of the widget to be used when creating the default source field. + * {@inheritdoc} */ - const MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_WIDGET = 'string_textfield'; + public function defaultSourceFieldWidget() { + return 'string_textfield'; + } /** - * The id of the formatter to be used when creating the default source field. + * {@inheritdoc} */ - const MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_FORMATTER = 'string'; + public function defaultSourceFieldFormatter() { + return 'string'; + } /** * {@inheritdoc} @@ -65,96 +87,11 @@ class TestType extends Generic { '#default_value' => empty($this->configuration['test_config_value']) ? NULL : $this->configuration['test_config_value'], ]; - $options = []; - $allowed_field_types = ['string']; - /** @var \Drupal\media_entity\MediaBundleInterface $bundle */ - $bundle = $form_state->getFormObject()->getEntity(); - foreach ($this->entityFieldManager->getFieldDefinitions('media', $bundle->id()) as $field_name => $field) { - if (in_array($field->getType(), $allowed_field_types) && !$field->getFieldStorageDefinition()->isBaseField()) { - $options[$field_name] = $field->getLabel(); - } - } + $form = $this->defaultFieldConfigurationForm($form, $form_state); - $form['source_field'] = array( - '#type' => 'select', - '#title' => $this->t('Field with source information'), - '#description' => $this->t('Field on media entity that stores the source information. You can create a bundle without selecting a value for this dropdown initially. This dropdown can be populated after adding fields to the bundle.'), - '#default_value' => empty($this->configuration['source_field']) ? self::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME : $this->configuration['source_field'], - '#options' => $options, - ); - - // Add a checkbox to allow the field being created automatically on save. - if (empty($this->configuration['source_field'])) { - $form['create_source_field'] = [ - '#type' => 'checkbox', - '#title' => $this->t('Create a source field automatically when saving this form.'), - '#description' => $this->t('If checked, a default field will be created and used as a source field. You can change this setting later.'), - '#default_value' => $this->configuration['create_source_field'], - '#access' => $bundle->isNew(), - ]; - } + $form['source_field']['#description'] = $this->t('Field on media entity that stores the source information.'); return $form; } - /** - * {@inheritdoc} - */ - public function reactOnBundleCreated($bundle_name, $entity_type_id) { - if (!empty($this->configuration['create_source_field'])) { - $this->createDefaultSourceField($bundle_name); - } - } - - /** - * {@inheritdoc} - */ - public function createDefaultSourceField($bundle_name) { - - // Create / load the field storage. - if (!$storage = FieldStorageConfig ::loadByName('media', static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME)) { - $storage = FieldStorageConfig::create([ - 'field_name' => static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME, - 'entity_type' => 'media', - 'type' => 'string', - ]); - $storage->save(); - } - - // Create the field instance. - FieldConfig::create([ - 'entity_type' => 'media', - 'field_name' => static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME, - 'label' => $this->t('Tweet URL'), - 'required' => TRUE, - 'bundle' => $bundle_name, - ])->save(); - - // Make the field visible on the form display. - /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ - $form_display = EntityFormDisplay::create([ - 'targetEntityType' => 'media', - 'bundle' => $bundle_name, - 'mode' => 'default', - 'status' => TRUE, - ]); - $form_display->setComponent(static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME, [ - 'type' => static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_WIDGET, - ])->save(); - - // Make the field visible on the media entity itself. - /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */ - $display = EntityViewDisplay::create([ - 'targetEntityType' => 'media', - 'bundle' => $bundle_name, - 'mode' => 'default', - 'status' => TRUE, - ]); - $display->setComponent(static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME, [ - 'type' => static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_FORMATTER, - ])->save(); - - // @TODO Evaluate if it's necessary to save the 'source_field' config value. - } - }