class FieldStorageAddForm
Same name in other branches
- 9 core/modules/field_ui/src/Form/FieldStorageAddForm.php \Drupal\field_ui\Form\FieldStorageAddForm
- 8.9.x core/modules/field_ui/src/Form/FieldStorageAddForm.php \Drupal\field_ui\Form\FieldStorageAddForm
- 10 core/modules/field_ui/src/Form/FieldStorageAddForm.php \Drupal\field_ui\Form\FieldStorageAddForm
Provides a form for the "field storage" add subform.
@internal
Hierarchy
- class \Drupal\Core\Form\FormBase implements \Drupal\Core\Form\FormInterface, \Drupal\Core\DependencyInjection\ContainerInjectionInterface uses \Drupal\Core\DependencyInjection\DependencySerializationTrait, \Drupal\Core\Logger\LoggerChannelTrait, \Drupal\Core\Messenger\MessengerTrait, \Drupal\Core\Routing\RedirectDestinationTrait, \Drupal\Core\StringTranslation\StringTranslationTrait
- class \Drupal\field_ui\Form\FieldStorageAddForm extends \Drupal\Core\Form\FormBase uses \Drupal\Core\Ajax\AjaxHelperTrait
Expanded class hierarchy of FieldStorageAddForm
File
-
core/
modules/ field_ui/ src/ Form/ FieldStorageAddForm.php, line 30
Namespace
Drupal\field_ui\FormView source
class FieldStorageAddForm extends FormBase {
use AjaxHelperTrait;
/**
* The name of the entity type.
*
* @var string
*/
protected $entityTypeId;
/**
* The entity bundle.
*
* @var string
*/
protected $bundle;
public function __construct(EntityTypeManagerInterface $entityTypeManager, FieldTypePluginManagerInterface $fieldTypePluginManager, ConfigFactoryInterface $configFactory, EntityFieldManagerInterface $entityFieldManager, PrivateTempStore $tempStore, FieldTypeCategoryManagerInterface $fieldTypeCategoryManager) {
$this->setConfigFactory($configFactory);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('entity_type.manager'), $container->get('plugin.manager.field.field_type'), $container->get('config.factory'), $container->get('entity_field.manager'), $container->get('tempstore.private')
->get('field_ui'), $container->get('plugin.manager.field.field_type_category'));
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'field_ui_field_storage_add_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL, $bundle = NULL, $selected_field_type = NULL, $display_as_group = 'false') {
$display_as_group = str_contains($display_as_group, 'true');
if (!$form_state->get('entity_type_id')) {
$form_state->set('entity_type_id', $entity_type_id);
}
if (!$form_state->get('bundle')) {
$form_state->set('bundle', $bundle);
}
if (!$form_state->get('field_type')) {
$form_state->set('field_type', $selected_field_type);
}
if (!$form_state->get('display_as_group')) {
$form_state->set('display_as_group', $display_as_group);
}
$this->entityTypeId = $form_state->get('entity_type_id');
$this->bundle = $form_state->get('bundle');
$unique_definitions = [];
$grouped_definitions = $this->fieldTypePluginManager
->getGroupedDefinitions($this->fieldTypePluginManager
->getEntityTypeUiDefinitions($this->entityTypeId), 'label', 'id');
if (array_key_exists($selected_field_type, $grouped_definitions)) {
$field_types = $grouped_definitions[$selected_field_type];
foreach ($field_types as $name => $field_type) {
$unique_definitions[$selected_field_type][$name] = [
'unique_identifier' => $name,
] + $field_type;
}
}
$entity_type = $this->entityTypeManager
->getDefinition($this->entityTypeId);
$route_parameters_back = [] + FieldUI::getRouteBundleParameter($entity_type, $this->bundle);
$form['actions'] = [
'#type' => 'actions',
];
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#size' => 30,
'#required' => TRUE,
'#maxlength' => 255,
'#weight' => -20,
];
$field_prefix = $this->configFactory
->get('field_ui.settings')
->get('field_prefix');
$form['field_name'] = [
'#type' => 'machine_name',
'#field_prefix' => $field_prefix,
'#size' => 15,
'#description' => $this->t('A unique machine-readable name containing letters, numbers, and underscores.'),
// Calculate characters depending on the length of the field prefix
// setting. Maximum length is 32.
'#maxlength' => FieldStorageConfig::NAME_MAX_LENGTH - strlen($field_prefix),
'#machine_name' => [
'source' => [
'label',
],
'exists' => [
$this,
'fieldNameExists',
],
],
'#required' => TRUE,
];
$form['field_options_wrapper'] = [
'#prefix' => '<div class="field-options-wrapper">',
'#suffix' => '</div>',
];
// Set the selected field to the form state by checking
// the checked attribute.
if (isset($selected_field_type)) {
if ($display_as_group) {
$form['field_options_wrapper']['label'] = [
'#type' => 'label',
'#title' => $this->t('Choose a field type'),
'#required' => TRUE,
];
$form['field_options_wrapper']['fields'] = [
'#type' => 'container',
'#attributes' => [
'class' => [
'group-field-options',
],
],
];
foreach ($unique_definitions[$selected_field_type] as $option_key => $option) {
$description = !is_array($option['description']) ? $option['description'] : [
'#theme' => 'item_list',
'#items' => $option['description'],
];
$radio_element = [
'#type' => 'radio',
'#theme_wrappers' => [
'form_element__new_storage_type',
],
'#title' => $option['label'],
'#description' => $description,
'#id' => Html::getClass($option['unique_identifier']),
'#weight' => $option['weight'],
'#parents' => [
'field_options_wrapper',
],
'#attributes' => [
'class' => [
'field-option-radio',
],
'data-once' => 'field-click-to-select',
'checked' => $this->getRequest()->request
->get('field_options_wrapper') !== NULL && $this->getRequest()->request
->get('field_options_wrapper') == $option_key,
],
'#wrapper_attributes' => [
'class' => [
'js-click-to-select',
'subfield-option',
],
],
'#variant' => 'field-suboption',
];
$radio_element['#return_value'] = $option['unique_identifier'];
if ((string) $option['unique_identifier'] === 'entity_reference') {
$radio_element['#title'] = 'Other';
$radio_element['#weight'] = 10;
}
$group_field_options[$option['unique_identifier']] = $radio_element;
}
uasort($group_field_options, [
SortArray::class,
'sortByWeightProperty',
]);
$form['field_options_wrapper']['fields'] += $group_field_options;
}
$form['actions']['previous'] = [
'#type' => 'link',
'#title' => $this->t('Change field type'),
'#url' => Url::fromRoute("field_ui.field_storage_config_add_{$entity_type_id}", $route_parameters_back),
'#attributes' => [
'class' => [
'button',
'use-ajax',
],
'data-dialog-type' => 'modal',
'data-dialog-options' => Json::encode([
'width' => '1100',
]),
],
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Continue'),
'#submit' => [
'::submitForm',
],
'#attributes' => [
'class' => [
'button',
'button--primary',
],
'data-dialog-type' => 'modal',
'data-dialog-options' => Json::encode([
'width' => '1100',
]),
],
];
if ($this->isAjax()) {
$form['actions']['submit']['#ajax']['callback'] = '::ajaxSubmit';
}
}
// Place the 'translatable' property as an explicit value so that contrib
// modules can form_alter() the value for newly created fields. By default,
// we create field storage as translatable, so it will be possible to enable
// translation at field level.
$form['translatable'] = [
'#type' => 'value',
'#value' => TRUE,
];
$form['#prefix'] = '<div id="field-storage-subfield">';
$form['#suffix'] = '</div>';
$form['#attached']['library'] = [
'field_ui/drupal.field_ui',
'field_ui/drupal.field_ui.manage_fields',
'core/drupal.dialog.ajax',
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// Missing subtype.
if (!$form_state->getValue('field_options_wrapper') && isset($form['field_options_wrapper']['fields'])) {
$form_state->setErrorByName('field_options_wrapper', $this->t('You need to select a field type.'));
}
// Additional validation to work when JS is disabled.
if (!$form_state->getValue('label')) {
$form_state->setErrorByName('label', $this->t('Label field is required.'));
}
if (!$form_state->getValue('field_name')) {
$form_state->setErrorByName('label', $this->t('Machine-readable name field is required.'));
}
else {
$field_name = $form_state->getValue('field_name');
// Add the field prefix.
$field_name = $this->config('field_ui.settings')
->get('field_prefix') . $field_name;
$form_state->setValueForElement($form['field_name'], $field_name);
// Set the temp store here, so we can actually see the error on the modal.
$field_storage_type = $form_state->getValue('field_options_wrapper') ?? $form_state->get('field_type');
$this->setTempStore($this->entityTypeId, $field_storage_type, $this->bundle, $form_state->getValue('label'), $form_state->getValue('field_name'), $form_state->getValue('translatable'));
if (!empty($this->messenger()
->messagesByType('error'))) {
$form_state->setErrorByName('drupal-modal', $this->t('There was a problem creating field @label: @message', [
'@label' => $form_state->getValue('label'),
'@message' => explode(':', $this->messenger()
->messagesByType('error')[0])[1],
]));
// We need to clear out the messenger so that we just see the message
// on the modal and not on the page when it closes.
$this->messenger()
->deleteAll();
}
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$form_state->setRedirectUrl($this->getRedirectUrl($form_state->getValue('field_name')));
}
/**
* Gets the redirect URL.
*
* @param string $field_name
* The field name.
*
* @return \Drupal\Core\Url
* The URL to redirect to.
*
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
private function getRedirectUrl(string $field_name) : Url {
$route_parameters = [
'field_name' => $field_name,
'entity_type' => $this->entityTypeId,
] + FieldUI::getRouteBundleParameter($this->entityTypeManager
->getDefinition($this->entityTypeId), $this->bundle);
return Url::fromRoute("field_ui.field_add_{$this->entityTypeId}", $route_parameters);
}
/**
* Submit form #ajax callback.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* An AJAX response that display validation error messages or represents a
* successful submission.
*
* @see \Drupal\Core\Ajax\AjaxFormHelperTrait
*/
public function ajaxSubmit(array &$form, FormStateInterface $form_state) : AjaxResponse {
if ($form_state->hasAnyErrors()) {
$form['status_messages'] = [
'#type' => 'status_messages',
'#weight' => -1000,
];
$form['#sorted'] = FALSE;
$response = new AjaxResponse();
$response->addCommand(new ReplaceCommand('#field-storage-subfield', $form));
}
else {
$response = $this->successfulAjaxSubmit($form, $form_state);
}
return $response;
}
/**
* Respond to a successful AJAX submission.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* An AJAX response.
*/
protected function successfulAjaxSubmit(array $form, FormStateInterface $form_state) : AjaxResponse {
$response = new AjaxResponse();
$response->addCommand(new OpenModalDialogWithUrl($this->getRedirectUrl($form_state->getValue('field_name'))
->toString(), []));
return $response;
}
/**
* Get default options from preconfigured options for a new field.
*
* @param string $field_name
* The machine name of the field.
* @param string $preset_key
* A key in the preconfigured options array for the field.
*
* @return array
* An array of settings with keys 'field_storage_config', 'field_config',
* 'entity_form_display', and 'entity_view_display'.
*
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*
* @see \Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface::getPreconfiguredOptions()
*/
protected function getNewFieldDefaults(string $field_name, string $preset_key) : array {
$field_type_definition = $this->fieldTypePluginManager
->getDefinition($field_name);
$options = $this->fieldTypePluginManager
->getPreconfiguredOptions($field_type_definition['id']);
$field_options = $options[$preset_key] ?? [];
$default_options = [];
// Merge in preconfigured field storage options.
if (isset($field_options['field_storage_config'])) {
foreach ([
'cardinality',
'settings',
] as $key) {
if (isset($field_options['field_storage_config'][$key])) {
$default_options['field_storage_config'][$key] = $field_options['field_storage_config'][$key];
}
}
}
// Merge in preconfigured field options.
if (isset($field_options['field_config'])) {
foreach ([
'required',
'settings',
] as $key) {
if (isset($field_options['field_config'][$key])) {
$default_options['field_config'][$key] = $field_options['field_config'][$key];
}
}
}
// Preconfigured options only apply to the default display modes.
foreach ([
'entity_form_display',
'entity_view_display',
] as $key) {
if (isset($field_options[$key])) {
$default_options[$key] = [
'default' => array_intersect_key($field_options[$key], [
'type' => '',
'settings' => [],
]),
];
}
else {
$default_options[$key] = [
'default' => [],
];
}
}
return $default_options;
}
/**
* Store field information in temp store in order to build the edit form.
*
* @param string $entity_type
* The name of the entity type.
* @param string $field_storage_type
* The machine name of the field storage.
* @param string $bundle
* The entity bundle.
* @param string $field_label
* The label of the field.
* @param string $field_machine_name
* The machine name of the field.
* @param bool $translatable
* TRUE if the field is translatable.
*/
public function setTempStore(string $entity_type, string $field_storage_type, string $bundle, string $field_label, string $field_machine_name, bool $translatable) : void {
$field_values = [
'entity_type' => $entity_type,
'bundle' => $bundle,
];
$default_options = [];
// Check if we're dealing with a preconfigured field.
if (strpos($field_storage_type, 'field_ui:') === 0) {
[
,
$field_type,
$preset_key,
] = explode(':', $field_storage_type, 3);
$default_options = $this->getNewFieldDefaults($field_type, $preset_key);
}
else {
$field_type = $field_storage_type;
}
$field_values += [
$default_options['field_config'] ?? [],
'field_name' => $field_machine_name,
'label' => $field_label,
// Field translatability should be explicitly enabled by the users.
'translatable' => FALSE,
];
$field_storage_values = [
$default_options['field_storage_config'] ?? [],
'field_name' => $field_machine_name,
'type' => $field_type,
'entity_type' => $entity_type,
'translatable' => $translatable,
];
try {
$field_storage_entity = $this->entityTypeManager
->getStorage('field_storage_config')
->create($field_storage_values);
} catch (\Exception $e) {
$this->messenger()
->addError($this->t('There was a problem creating field :@message', [
'@message' => $e->getMessage(),
]));
return;
}
// Save field and field storage values in tempstore.
$this->tempStore
->set($entity_type . ':' . $field_machine_name, [
'field_storage' => $field_storage_entity,
'field_config_values' => $field_values,
'default_options' => $default_options,
]);
}
/**
* Checks if a field machine name is taken.
*
* @param string $value
* The machine name, not prefixed.
* @param array $element
* An array containing the structure of the 'field_name' element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return bool
* Whether or not the field machine name is taken.
*/
public function fieldNameExists($value, $element, FormStateInterface $form_state) {
// Add the field prefix.
$field_name = $this->configFactory
->get('field_ui.settings')
->get('field_prefix') . $value;
$field_storage_definitions = $this->entityFieldManager
->getFieldStorageDefinitions($this->entityTypeId);
return isset($field_storage_definitions[$field_name]);
}
/**
* Submit handler for displaying fields after a group is selected.
*/
public static function rebuildWithOptions($form, FormStateInterface &$form_state) {
$form_state->setRebuild();
}
/**
* Submit handler for resetting the form.
*/
public static function startOver($form, FormStateInterface &$form_state) {
$form_state->unsetValue('new_storage_type');
$form_state->setRebuild();
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title | Overrides |
---|---|---|---|---|---|
AjaxHelperTrait::getRequestWrapperFormat | protected | function | Gets the wrapper format of the current request. | ||
AjaxHelperTrait::isAjax | protected | function | Determines if the current request is via AJAX. | ||
DependencySerializationTrait::$_entityStorages | protected | property | |||
DependencySerializationTrait::$_serviceIds | protected | property | |||
DependencySerializationTrait::__sleep | public | function | 1 | ||
DependencySerializationTrait::__wakeup | public | function | 2 | ||
FieldStorageAddForm::$bundle | protected | property | The entity bundle. | ||
FieldStorageAddForm::$entityTypeId | protected | property | The name of the entity type. | ||
FieldStorageAddForm::ajaxSubmit | public | function | Submit form #ajax callback. | ||
FieldStorageAddForm::buildForm | public | function | Form constructor. | Overrides FormInterface::buildForm | |
FieldStorageAddForm::create | public static | function | Instantiates a new instance of this class. | Overrides FormBase::create | |
FieldStorageAddForm::fieldNameExists | public | function | Checks if a field machine name is taken. | ||
FieldStorageAddForm::getFormId | public | function | Returns a unique string identifying the form. | Overrides FormInterface::getFormId | |
FieldStorageAddForm::getNewFieldDefaults | protected | function | Get default options from preconfigured options for a new field. | ||
FieldStorageAddForm::getRedirectUrl | private | function | Gets the redirect URL. | ||
FieldStorageAddForm::rebuildWithOptions | public static | function | Submit handler for displaying fields after a group is selected. | ||
FieldStorageAddForm::setTempStore | public | function | Store field information in temp store in order to build the edit form. | ||
FieldStorageAddForm::startOver | public static | function | Submit handler for resetting the form. | ||
FieldStorageAddForm::submitForm | public | function | Form submission handler. | Overrides FormInterface::submitForm | |
FieldStorageAddForm::successfulAjaxSubmit | protected | function | Respond to a successful AJAX submission. | ||
FieldStorageAddForm::validateForm | public | function | Form validation handler. | Overrides FormBase::validateForm | |
FieldStorageAddForm::__construct | public | function | |||
FormBase::$configFactory | protected | property | The config factory. | 2 | |
FormBase::$requestStack | protected | property | The request stack. | 1 | |
FormBase::$routeMatch | protected | property | The route match. | ||
FormBase::config | protected | function | Retrieves a configuration object. | ||
FormBase::configFactory | protected | function | Gets the config factory for this form. | 2 | |
FormBase::container | private | function | Returns the service container. | ||
FormBase::currentUser | protected | function | Gets the current user. | 2 | |
FormBase::getRequest | protected | function | Gets the request object. | ||
FormBase::getRouteMatch | protected | function | Gets the route match. | ||
FormBase::logger | protected | function | Gets the logger for a specific channel. | ||
FormBase::redirect | protected | function | Returns a redirect response object for the specified route. | ||
FormBase::resetConfigFactory | public | function | Resets the configuration factory. | ||
FormBase::setConfigFactory | public | function | Sets the config factory for this form. | ||
FormBase::setRequestStack | public | function | Sets the request stack object to use. | ||
LoggerChannelTrait::$loggerFactory | protected | property | The logger channel factory service. | ||
LoggerChannelTrait::getLogger | protected | function | Gets the logger for a specific channel. | ||
LoggerChannelTrait::setLoggerFactory | public | function | Injects the logger channel factory. | ||
MessengerTrait::$messenger | protected | property | The messenger. | 16 | |
MessengerTrait::messenger | public | function | Gets the messenger. | 16 | |
MessengerTrait::setMessenger | public | function | Sets the messenger. | ||
RedirectDestinationTrait::$redirectDestination | protected | property | The redirect destination service. | 2 | |
RedirectDestinationTrait::getDestinationArray | protected | function | Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url. | ||
RedirectDestinationTrait::getRedirectDestination | protected | function | Returns the redirect destination service. | ||
RedirectDestinationTrait::setRedirectDestination | public | function | Sets the redirect destination service. | ||
StringTranslationTrait::$stringTranslation | protected | property | The string translation service. | 3 | |
StringTranslationTrait::formatPlural | protected | function | Formats a string containing a count of items. | ||
StringTranslationTrait::getNumberOfPlurals | protected | function | Returns the number of plurals supported by a given language. | ||
StringTranslationTrait::getStringTranslation | protected | function | Gets the string translation service. | ||
StringTranslationTrait::setStringTranslation | public | function | Sets the string translation service to use. | 2 | |
StringTranslationTrait::t | protected | function | Translates a string to the current language or to a given language. | 1 |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.