Commit 8196034a authored by alexpott's avatar alexpott

Issue #2316533 by tim.plunkett: Add getValue/setValue/hasValue and isValueEmpty to FormState.

parent dc11fb9a
......@@ -99,7 +99,7 @@
* get the format string entered by the user:
* @code
* $format_value = \Drupal\Component\Utility\NestedArray::getValue(
* $form_state['values'],
* $form_state->getValues(),
* $form_state['triggering_element']['#array_parents']);
* @endcode
*
......
......@@ -238,7 +238,11 @@ function form_builder($form_id, &$element, FormStateInterface $form_state) {
*/
function form_state_values_clean(FormStateInterface $form_state) {
// Remove internal Form API values.
unset($form_state['values']['form_id'], $form_state['values']['form_token'], $form_state['values']['form_build_id'], $form_state['values']['op']);
$form_state
->unsetValue('form_id')
->unsetValue('form_token')
->unsetValue('form_build_id')
->unsetValue('op');
// Remove button values.
// form_builder() collects all button elements in a form. We remove the button
......@@ -247,9 +251,9 @@ function form_state_values_clean(FormStateInterface $form_state) {
// Remove this button's value from the submitted form values by finding
// the value corresponding to this button.
// We iterate over the #parents of this button and move a reference to
// each parent in $form_state['values']. For example, if #parents is:
// each parent in $form_state->getValues(). For example, if #parents is:
// array('foo', 'bar', 'baz')
// then the corresponding $form_state['values'] part will look like this:
// then the corresponding $form_state->getValues() part will look like this:
// array(
// 'foo' => array(
// 'bar' => array(
......@@ -259,14 +263,14 @@ function form_state_values_clean(FormStateInterface $form_state) {
// )
// We start by (re)moving 'baz' to $last_parent, so we are able unset it
// at the end of the iteration. Initially, $values will contain a
// reference to $form_state['values'], but in the iteration we move the
// reference to $form_state['values']['foo'], and finally to
// $form_state['values']['foo']['bar'], which is the level where we can
// unset 'baz' (that is stored in $last_parent).
// reference to $form_state->getValues(), but in the iteration we move the
// reference to $form_state->getValue('foo'), and finally to
// $form_state->getValue(array('foo', 'bar')), which is the level where we
// can unset 'baz' (that is stored in $last_parent).
$parents = $button['#parents'];
$last_parent = array_pop($parents);
$key_exists = NULL;
$values = &NestedArray::getValue($form_state['values'], $parents, $key_exists);
$values = &NestedArray::getValue($form_state->getValues(), $parents, $key_exists);
if ($key_exists && is_array($values)) {
unset($values[$last_parent]);
}
......@@ -285,7 +289,7 @@ function form_state_values_clean(FormStateInterface $form_state) {
* The current state of the form.
*
* @return
* The data that will appear in the $form_state['values'] collection
* The data that will appear in the $form_state->getValues() collection
* for this element. Return nothing to use the default.
*/
function form_type_image_button_value($form, $input, FormStateInterface $form_state) {
......@@ -414,7 +418,7 @@ function form_type_checkboxes_value($element, $input = FALSE) {
* the element's default value should be returned.
*
* @return array
* The data that will appear in the $form_state['values'] collection
* The data that will appear in the $form_state->getValues() collection
* for this element. Return nothing to use the default.
*/
function form_type_table_value(array $element, $input = FALSE) {
......@@ -2021,8 +2025,8 @@ function form_process_vertical_tabs($element, FormStateInterface $form_state) {
// form is rendered, e.g. on preview pages or when form validation
// fails.
$name = implode('__', $element['#parents']);
if (isset($form_state['values'][$name . '__active_tab'])) {
$element['#default_tab'] = $form_state['values'][$name . '__active_tab'];
if ($form_state->hasValue($name . '__active_tab')){
$element['#default_tab'] = $form_state->getValue($name . '__active_tab');
}
$element[$name . '__active_tab'] = array(
'#type' => 'hidden',
......@@ -2373,7 +2377,7 @@ function form_validate_number(&$element, FormStateInterface $form_state) {
* element's default value should be returned.
*
* @return
* The data that will appear in the $form_state['values'] collection for
* The data that will appear in the $form_state->getValues() collection for
* this element. Return nothing to use the default.
*/
function form_type_range_value($element, $input = FALSE) {
......
......@@ -155,7 +155,7 @@ function install_drupal($settings = array()) {
* 'langcode' that are normally passed in via the URL) and 'forms' (which can
* be used to programmatically submit forms during the installation; the keys
* of each element indicate the name of the installation task that the form
* submission is for, and the values are used as the $form_state['values']
* submission is for, and the values are used as the $form_state->getValues()
* array that is passed on to the form submission via drupal_form_submit()).
*
* @see \Drupal\Core\Form\FormBuilderInterface::submitForm()
......@@ -182,7 +182,7 @@ function install_state_defaults() {
// An array of forms to be programmatically submitted during the
// installation. The keys of each element indicate the name of the
// installation task that the form submission is for, and the values are
// used as the $form_state['values'] array that is passed on to the form
// used as the $form_state->getValues() array that is passed on to the form
// submission via drupal_form_submit().
'forms' => array(),
// This becomes TRUE only at the end of the installation process, after
......@@ -824,10 +824,9 @@ function install_get_form($form_id, array &$install_state) {
else {
// For non-interactive installs, submit the form programmatically with the
// values taken from the installation state.
$form_state['values'] = array();
$install_form_id = $form_builder->getFormId($form_id, $form_state);
if (!empty($install_state['forms'][$install_form_id])) {
$form_state['values'] = $install_state['forms'][$install_form_id];
$form_state->set('values', $install_state['forms'][$install_form_id]);
}
$form_builder->submitForm($form_id, $form_state);
......
......@@ -59,7 +59,7 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['negate'] = $form_state['values']['negate'];
$this->configuration['negate'] = $form_state->getValue('negate');
}
/**
......
......@@ -147,7 +147,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
foreach ($form_state['values'][$this->entitiesKey] as $id => $value) {
foreach ($form_state->getValue($this->entitiesKey) as $id => $value) {
if (isset($this->entities[$id]) && $this->entities[$id]->get($this->weightKey) != $value['weight']) {
// Save entity only when its weight was changed.
$this->entities[$id]->set($this->weightKey, $value['weight']);
......
......@@ -122,7 +122,7 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['label'] = $form_state['values']['label'];
$this->configuration['label'] = $form_state->getValue('label');
}
/**
......
......@@ -113,7 +113,7 @@ protected function copyFormValuesToEntity(EntityInterface $entity, array $form,
// Then extract the values of fields that are not rendered through widgets,
// by simply copying from top-level form values. This leaves the fields
// that are not being edited within this form untouched.
foreach ($form_state['values'] as $name => $values) {
foreach ($form_state->getValues() as $name => $values) {
if ($entity->hasField($name) && !isset($extracted[$name])) {
$entity->set($name, $values);
}
......
......@@ -24,14 +24,15 @@ interface EntityFormDisplayInterface extends EntityDisplayInterface {
* larger form.
*
* By default, submitted field values appear at the top-level of
* $form_state['values']. A different location within $form_state['values']
* can be specified by setting the '#parents' property on the incoming $form
* parameter. Because of name clashes, two instances of the same field cannot
* appear within the same $form element, or within the same '#parents' space.
* $form_state->getValues(). A different location within
* $form_state->getValues() can be specified by setting the '#parents'
* property on the incoming $form parameter. Because of name clashes, two
* instances of the same field cannot appear within the same $form element, or
* within the same '#parents' space.
*
* Sample resulting structure in $form:
* @code
* '#parents' => The location of field values in $form_state['values'],
* '#parents' => The location of field values in $form_state->getValues(),
* '#entity_type' => The name of the entity type,
* '#bundle' => The name of the bundle,
* // One sub-array per field appearing in the entity, keyed by field name.
......@@ -91,9 +92,9 @@ interface EntityFormDisplayInterface extends EntityDisplayInterface {
* The form structure to fill in. This can be a full form structure, or a
* sub-element of a larger form. The #parents property can be set to
* control the location of submitted field values within
* $form_state['values']. If not specified, $form['#parents'] is set to an
* empty array, which results in field values located at the top-level of
* $form_state['values'].
* $form_state->getValues(). If not specified, $form['#parents'] is set to
* an empty array, which results in field values located at the top-level of
* $form_state->getValues().
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
......@@ -144,8 +145,8 @@ public function validateFormValues(ContentEntityInterface $entity, array &$form,
*
* @return array
* An array whose keys and values are the keys of the top-level entries in
* $form_state['values'] that have been processed. The remaining entries, if
* any, do not correspond to widgets and should be extracted manually by
* $form_state->getValues() that have been processed. The remaining entries,
* if any, do not correspond to widgets and should be extracted manually by
* the caller if needed.
*/
public function extractFormValues(ContentEntityInterface $entity, array &$form, FormStateInterface $form_state);
......
......@@ -307,8 +307,8 @@ public function isDefaultFormLangcode(FormStateInterface $form_state) {
*/
protected function updateFormLangcode(FormStateInterface $form_state) {
// Update the form language as it might have changed.
if (isset($form_state['values']['langcode']) && $this->isDefaultFormLangcode($form_state)) {
$form_state['langcode'] = $form_state['values']['langcode'];
if ($form_state->hasValue('langcode') && $this->isDefaultFormLangcode($form_state)) {
$form_state['langcode'] = $form_state->getValue('langcode');
}
}
......@@ -352,7 +352,7 @@ protected function copyFormValuesToEntity(EntityInterface $entity, array $form,
// @todo: This relies on a method that only exists for config and content
// entities, in a different way. Consider moving this logic to a config
// entity specific implementation.
foreach ($form_state['values'] as $key => $value) {
foreach ($form_state->getValues() as $key => $value) {
$entity->set($key, $value);
}
}
......
......@@ -331,10 +331,10 @@ protected function formSingleElement(FieldItemListInterface $items, $delta, arra
public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
$field_name = $this->fieldDefinition->getName();
// Extract the values from $form_state['values'].
// Extract the values from $form_state->getValues().
$path = array_merge($form['#parents'], array($field_name));
$key_exists = NULL;
$values = NestedArray::getValue($form_state['values'], $path, $key_exists);
$values = NestedArray::getValue($form_state->getValues(), $path, $key_exists);
if ($key_exists) {
// Account for drag-and-drop reordering if needed.
......
......@@ -83,9 +83,9 @@ public function settingsSummary();
* - #field_parents: The 'parents' space for the field in the form. Most
* widgets can simply overlook this property. This identifies the
* location where the field values are placed within
* $form_state['values'], and is used to access processing information
* for the field through the getWidgetState() and setWidgetState()
* methods.
* $form_state->getValues(), and is used to access processing
* information for the field through the getWidgetState() and
* setWidgetState() methods.
* - #title: The sanitized element label for the field instance, ready for
* output.
* - #description: The sanitized element description for the field instance,
......
......@@ -46,9 +46,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
}
// Decide on a default backend.
if (isset($form_state['values']['connection_settings']['authorize_filetransfer_default'])) {
$authorize_filetransfer_default = $form_state['values']['connection_settings']['authorize_filetransfer_default'];
}
if ($authorize_filetransfer_default = $form_state->getValue(array('connection_settings', 'authorize_filetransfer_default')));
elseif ($authorize_filetransfer_default = $this->config('system.authorize')->get('filetransfer_default'));
else {
$authorize_filetransfer_default = key($available_backends);
......@@ -111,7 +109,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
$form['connection_settings'][$name] += $this->addConnectionSettings($name);
// Start non-JS code.
if (isset($form_state['values']['connection_settings']['authorize_filetransfer_default']) && $form_state['values']['connection_settings']['authorize_filetransfer_default'] == $name) {
if ($form_state->getValue(array('connection_settings', 'authorize_filetransfer_default')) == $name) {
// Change the submit button to the submit_process one.
$form['submit_process']['#attributes'] = array();
......@@ -146,9 +144,9 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
return;
}
if (isset($form_state['values']['connection_settings'])) {
$backend = $form_state['values']['connection_settings']['authorize_filetransfer_default'];
$filetransfer = $this->getFiletransfer($backend, $form_state['values']['connection_settings'][$backend]);
if ($form_connection_settings = $form_state->getValue('connection_settings')) {
$backend = $form_connection_settings['authorize_filetransfer_default'];
$filetransfer = $this->getFiletransfer($backend, $form_connection_settings[$backend]);
try {
if (!$filetransfer) {
throw new \Exception($this->t('The connection protocol %backend does not exist.', array('%backend' => $backend)));
......@@ -170,11 +168,12 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$form_connection_settings = $form_state->getValue('connection_settings');
switch ($form_state['triggering_element']['#name']) {
case 'process_updates':
// Save the connection settings to the DB.
$filetransfer_backend = $form_state['values']['connection_settings']['authorize_filetransfer_default'];
$filetransfer_backend = $form_connection_settings['authorize_filetransfer_default'];
// If the database is available then try to save our settings. We have
// to make sure it is available since this code could potentially (will
......@@ -182,7 +181,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
// database is set up.
try {
$connection_settings = array();
foreach ($form_state['values']['connection_settings'][$filetransfer_backend] as $key => $value) {
foreach ($form_connection_settings[$filetransfer_backend] as $key => $value) {
// We do *not* want to store passwords in the database, unless the
// backend explicitly says so via the magic #filetransfer_save form
// property. Otherwise, we store everything that's not explicitly
......@@ -202,7 +201,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
// Save the connection settings minus the password.
$this->config('system.authorize')->set('filetransfer_connection_settings_' . $filetransfer_backend, $connection_settings);
$filetransfer = $this->getFiletransfer($filetransfer_backend, $form_state['values']['connection_settings'][$filetransfer_backend]);
$filetransfer = $this->getFiletransfer($filetransfer_backend, $form_connection_settings[$filetransfer_backend]);
// Now run the operation.
$this->runOperation($filetransfer);
......@@ -220,7 +219,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
case 'change_connection_type':
$form_state['rebuild'] = TRUE;
unset($form_state['values']['connection_settings']['authorize_filetransfer_default']);
$form_state->unsetValue(array('connection_settings', 'authorize_filetransfer_default'));
break;
}
}
......
......@@ -237,7 +237,7 @@ public function buildForm($form_id, FormStateInterface &$form_state) {
// Now that we have a constructed form, process it. This is where:
// - Element #process functions get called to further refine $form.
// - User input, if any, gets incorporated in the #value property of the
// corresponding elements and into $form_state['values'].
// corresponding elements and into $form_state->getValues().
// - Validation and submission handlers are called.
// - If this submission is part of a multistep workflow, the form is rebuilt
// to contain the information of the next step.
......@@ -386,7 +386,7 @@ public function submitForm($form_arg, FormStateInterface &$form_state) {
// the form, to be consistent with what self::buildForm() does for
// non-programmatic submissions (form builder functions may expect it to be
// there).
$form_state->set('input', $form_state->get('values'));
$form_state->set('input', $form_state->getValues());
$form_state->set('programmed', TRUE);
......@@ -466,8 +466,8 @@ public function processForm($form_id, &$form, FormStateInterface &$form_state) {
// self::doBuildForm() finishes building the form by calling element
// #process functions and mapping user input, if any, to #value properties,
// and also storing the values in $form_state['values']. We need to retain
// the unprocessed $form in case it needs to be cached.
// and also storing the values in $form_state->getValues(). We need to
// retain the unprocessed $form in case it needs to be cached.
$unprocessed_form = $form;
$form = $this->doBuildForm($form_id, $form, $form_state);
......@@ -869,10 +869,10 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state
// buttons often have their #name property not derived from their
// #parents property, we can't assume that input processing that's
// happened up until here has resulted in
// $form_state['values'][BUTTON_NAME] being set. But it's common for
// $form_state->getValue(BUTTON_NAME) being set. But it's common for
// forms to have several buttons named 'op' and switch on
// $form_state['values']['op'] during submit handler execution.
$form_state->addValue($triggering_element['#name'], $triggering_element['#value']);
// $form_state->getValue('op') during submit handler execution.
$form_state->setValue($triggering_element['#name'], $triggering_element['#value']);
}
}
return $element;
......@@ -1016,7 +1016,7 @@ protected function handleInputElement($form_id, &$element, FormStateInterface &$
}
}
// Set the element's value in $form_state['values'], but only, if its key
// Set the element's value in $form_state->getValues(), but only, if its key
// does not exist yet (a #value_callback may have already populated it).
if (!NestedArray::keyExists($form_state->getValues(), $element['#parents'])) {
$form_state->setValueForElement($element, $element['#value']);
......
......@@ -137,9 +137,9 @@ public function setCache($form_build_id, $form, FormStateInterface $form_state);
* name exists, it is called to build the form array.
* @param $form_state
* The current state of the form. Most important is the
* $form_state['values'] collection, a tree of data used to simulate the
* $form_state->getValues() collection, a tree of data used to simulate the
* incoming \Drupal::request()->request information from a user's form
* submission. If a key is not filled in $form_state['values'], then the
* submission. If a key is not filled in $form_state->getValues(), then the
* default value of the respective element is used. To submit an unchecked
* checkbox or other control that browsers submit by not having a
* \Drupal::request()->request entry, include the key, but set the value to
......@@ -158,7 +158,7 @@ public function setCache($form_build_id, $form, FormStateInterface $form_state);
* @endcode
* would be called via self::submitForm() as follows:
* @code
* $form_state['values'] = $my_form_values;
* $form_state->set('values', $my_form_values);
* $form_state['build_info']['args'] = array(&$object);
* drupal_form_submit('mymodule_form', $form_state);
* @endcode
......@@ -166,11 +166,12 @@ public function setCache($form_build_id, $form, FormStateInterface $form_state);
* @code
* // register a new user
* $form_state = new FormState();
* $form_state['values']['name'] = 'robo-user';
* $form_state['values']['mail'] = 'robouser@example.com';
* $form_state['values']['pass']['pass1'] = 'password';
* $form_state['values']['pass']['pass2'] = 'password';
* $form_state['values']['op'] = t('Create new account');
* $values['name'] = 'robo-user';
* $values['mail'] = 'robouser@example.com';
* $values['pass']['pass1'] = 'password';
* $values['pass']['pass2'] = 'password';
* $values['op'] = t('Create new account');
* $form_state->set('values', $values);
* drupal_form_submit('user_register_form', $form_state);
* @endcode
*/
......@@ -302,7 +303,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state);
* the next submission needs to be processed, a multi-step workflow is
* needed. This is most commonly implemented with a submit handler setting
* persistent data within $form_state based on *validated* values in
* $form_state['values'] and setting $form_state['rebuild']. The form
* $form_state->getValues() and setting $form_state['rebuild']. The form
* building functions must then be implemented to use the $form_state data
* to rebuild the form with the structure appropriate for the new state.
* - Where user input must affect the rendering of the form without affecting
......
......@@ -196,7 +196,7 @@ class FormState implements FormStateInterface, \ArrayAccess {
*
* @var array
*/
protected $values;
protected $values = array();
/**
* The array of values as they were submitted by the user.
......@@ -559,30 +559,63 @@ public function addBuildInfo($property, $value) {
/**
* {@inheritdoc}
*/
public function getValues() {
return $this->values ?: array();
public function &getValues() {
return $this->values;
}
/**
* {@inheritdoc}
*/
public function addValue($property, $value) {
$values = $this->getValues();
$values[$property] = $value;
$this->set('values', $values);
public function &getValue($key, $default = NULL) {
$exists = NULL;
$value = &NestedArray::getValue($this->getValues(), (array) $key, $exists);
if (!$exists) {
$value = $default;
}
return $value;
}
/**
* {@inheritdoc}
*/
public function setValue($key, $value) {
NestedArray::setValue($this->getValues(), (array) $key, $value, TRUE);
return $this;
}
/**
* {@inheritdoc}
*/
public function setValueForElement($element, $value) {
$values = $this->getValues();
NestedArray::setValue($values, $element['#parents'], $value, TRUE);
$this->set('values', $values);
public function unsetValue($key) {
NestedArray::unsetValue($this->getValues(), (array) $key);
return $this;
}
/**
* {@inheritdoc}
*/
public function hasValue($key) {
$exists = NULL;
$value = NestedArray::getValue($this->getValues(), (array) $key, $exists);
return $exists && isset($value);
}
/**
* {@inheritdoc}
*/
public function isValueEmpty($key) {
$exists = NULL;
$value = NestedArray::getValue($this->getValues(), (array) $key, $exists);
return !$exists || empty($value);
}
/**
* {@inheritdoc}
*/
public function setValueForElement($element, $value) {
return $this->setValue($element['#parents'], $value);
}
/**
* {@inheritdoc}
*/
......@@ -670,10 +703,10 @@ public function setErrorByName($name, $message = '') {
// Exploding by '][' reconstructs the element's #parents. If the
// reconstructed #parents begin with the same keys as the specified
// section, then the element's values are within the part of
// $form_state['values'] that the clicked button requires to be valid,
// so errors for this element must be recorded. As the exploded array
// will all be strings, we need to cast every value of the section
// array to string.
// $form_state->getValues() that the clicked button requires to be
// valid, so errors for this element must be recorded. As the exploded
// array will all be strings, we need to cast every value of the
// section array to string.
if (array_slice(explode('][', $name), 0, count($section)) === array_map('strval', $section)) {
$record = TRUE;
break;
......
......@@ -181,12 +181,79 @@ public function addBuildInfo($property, $value);
* @return array
* An associative array of values submitted to the form.
*/
public function getValues();
public function &getValues();
/**
* {@inheritdoc}
* Returns the submitted form value for a specific key.
*
* @param string|array $key
* Values are stored as a multi-dimensional associative array. If $key is a
* string, it will return $values[$key]. If $key is an array, each element
* of the array will be used as a nested key. If $key = array('foo', 'bar')
* it will return $values['foo']['bar'].
* @param mixed $default
* (optional) The default value if the specified key does not exist.
*
* @return mixed
* The value for the given key, or NULL.
*/
public function &getValue($key, $default = NULL);
/**
* Sets the submitted form value for a specific key.
*
* @param string|array $key
* Values are stored as a multi-dimensional associative array. If $key is a
* string, it will use $values[$key] = $value. If $key is an array, each
* element of the array will be used as a nested key. If
* $key = array('foo', 'bar') it will use $values['foo']['bar'] = $value.
* @param mixed $value
* The value to set.
*
* @return $this
*/
public function setValue($key, $value);
/**
* Removes a specific key from the submitted form values.
*
* @param string|array $key
* Values are stored as a multi-dimensional associative array. If $key is a
* string, it will use unset($values[$key]). If $key is an array, each
* element of the array will be used as a nested key. If
* $key = array('foo', 'bar') it will use unset($values['foo']['bar']).
*
* @return $this
*/
public function unsetValue($key);
/**
* Determines if a specific key is present in the submitted form values.
*
* @param string|array $key
* Values are stored as a multi-dimensional associative array. If $key is a
* string, it will return isset($values[$key]). If $key is an array, each
* element of the array will be used as a nested key. If
* $key = array('foo', 'bar') it will return isset($values['foo']['bar']).
*
* @return bool
* TRUE if the $key is set, FALSE otherwise.
*/
public function hasValue($key);
/**