1<?php 2 3namespace Drupal\Core\Render\Element; 4 5use Drupal\Core\Form\FormStateInterface; 6use Drupal\Core\Render\BubbleableMetadata; 7use Drupal\Core\Url; 8 9/** 10 * Provides a base class for form element plugins. 11 * 12 * Form elements are a subset of render elements, representing elements for 13 * HTML forms, which can be referenced in form arrays. See the 14 * @link theme_render Render API topic @endlink for an overview of render 15 * arrays and render elements, and the @link form_api Form API topic @endlink 16 * for an overview of forms and form arrays. 17 * 18 * The elements of form arrays are divided up into properties (whose keys 19 * start with #) and children (whose keys do not start with #). The properties 20 * provide data or settings that are used in rendering and form processing. 21 * Some properties are specific to a particular type of form/render element, 22 * some are available for any render element, and some are available for any 23 * form input element. A list of the properties that are available for all form 24 * elements follows; see \Drupal\Core\Render\Element\RenderElement for some 25 * additional information, as well as a list of properties that are common to 26 * all render elements (including form elements). Properties specific to a 27 * particular element are documented on that element's class. 28 * 29 * Here is a list of properties that are used during the rendering and form 30 * processing of form elements: 31 * - #after_build: (array) Array of callables or function names, which are 32 * called after the element is built. Arguments: $element, $form_state. 33 * - #ajax: (array) Array of elements to specify Ajax behavior. See 34 * the @link ajax Ajax API topic @endlink for more information. 35 * - #array_parents: (string[], read-only) Array of names of all the element's 36 * parents (including itself) in the render array. See also #parents, #tree. 37 * - #default_value: Default value for the element. See also #value. 38 * - #description: (string) Help or description text for the element. In an 39 * ideal user interface, the #title should be enough to describe the element, 40 * so most elements should not have a description; if you do need one, make 41 * sure it is translated. If it is not already wrapped in a safe markup 42 * object, it will be filtered for XSS safety. 43 * - #disabled: (bool) If TRUE, the element is shown but does not accept 44 * user input. 45 * - #element_validate: (array) Array of callables or function names, which 46 * are called to validate the input. Arguments: $element, $form_state, $form. 47 * - #field_prefix: (string) Prefix to display before the HTML input element. 48 * Should be translated, normally. If it is not already wrapped in a safe 49 * markup object, will be filtered for XSS safety. 50 * - #field_suffix: (string) Suffix to display after the HTML input element. 51 * Should be translated, normally. If it is not already wrapped in a safe 52 * markup object, will be filtered for XSS safety. 53 * - #input: (bool, internal) Whether or not the element accepts input. 54 * - #parents: (string[], read-only) Array of names of the element's parents 55 * for purposes of getting values out of $form_state. See also 56 * #array_parents, #tree. 57 * - #process: (array) Array of callables or function names, which are 58 * called during form building. Arguments: $element, $form_state, $form. 59 * - #processed: (bool, internal) Set to TRUE when the element is processed. 60 * - #required: (bool) Whether or not input is required on the element. 61 * - #states: (array) Information about JavaScript states, such as when to 62 * hide or show the element based on input on other elements. 63 * See \Drupal\Core\Form\FormHelper::processStates() for documentation. 64 * - #title: (string) Title of the form element. Should be translated. 65 * - #title_display: (string) Where and how to display the #title. Possible 66 * values: 67 * - before: Label goes before the element (default for most elements). 68 * - after: Label goes after the element (default for radio elements). 69 * - invisible: Label is there but is made invisible using CSS. 70 * - attribute: Make it the title attribute (hover tooltip). 71 * - #tree: (bool) TRUE if the values of this element and its children should 72 * be hierarchical in $form_state; FALSE if the values should be flat. 73 * See also #parents, #array_parents. 74 * - #value_callback: (callable) Callable or function name, which is called 75 * to transform the raw user input to the element's value. Arguments: 76 * $element, $input, $form_state. 77 * 78 * @see \Drupal\Core\Render\Annotation\FormElement 79 * @see \Drupal\Core\Render\Element\FormElementInterface 80 * @see \Drupal\Core\Render\ElementInfoManager 81 * @see plugin_api 82 * 83 * @ingroup theme_render 84 */ 85abstract class FormElement extends RenderElement implements FormElementInterface { 86 87 /** 88 * {@inheritdoc} 89 */ 90 public static function valueCallback(&$element, $input, FormStateInterface $form_state) { 91 return NULL; 92 } 93 94 /** 95 * #process callback for #pattern form element property. 96 * 97 * @param array $element 98 * An associative array containing the properties and children of the 99 * generic input element. 100 * @param \Drupal\Core\Form\FormStateInterface $form_state 101 * The current state of the form. 102 * @param array $complete_form 103 * The complete form structure. 104 * 105 * @return array 106 * The processed element. 107 */ 108 public static function processPattern(&$element, FormStateInterface $form_state, &$complete_form) { 109 if (isset($element['#pattern']) && !isset($element['#attributes']['pattern'])) { 110 $element['#attributes']['pattern'] = $element['#pattern']; 111 $element['#element_validate'][] = [get_called_class(), 'validatePattern']; 112 } 113 114 return $element; 115 } 116 117 /** 118 * #element_validate callback for #pattern form element property. 119 * 120 * @param $element 121 * An associative array containing the properties and children of the 122 * generic form element. 123 * @param $form_state 124 * The current state of the form. 125 * @param array $complete_form 126 * The complete form structure. 127 */ 128 public static function validatePattern(&$element, FormStateInterface $form_state, &$complete_form) { 129 if ($element['#value'] !== '') { 130 // The pattern must match the entire string and should have the same 131 // behavior as the RegExp object in ECMA 262. 132 // - Use bracket-style delimiters to avoid introducing a special delimiter 133 // character like '/' that would have to be escaped. 134 // - Put in brackets so that the pattern can't interfere with what's 135 // prepended and appended. 136 $pattern = '{^(?:' . $element['#pattern'] . ')$}'; 137 138 if (!preg_match($pattern, $element['#value'])) { 139 $form_state->setError($element, t('%name field is not in the right format.', ['%name' => $element['#title']])); 140 } 141 } 142 } 143 144 /** 145 * Adds autocomplete functionality to elements. 146 * 147 * This sets up autocomplete functionality for elements with an 148 * #autocomplete_route_name property, using the #autocomplete_route_parameters 149 * property if present. 150 * 151 * For example, suppose your autocomplete route name is 152 * 'mymodule.autocomplete' and its path is 153 * '/mymodule/autocomplete/{a}/{b}'. In a form array, you would create a text 154 * field with properties: 155 * @code 156 * '#autocomplete_route_name' => 'mymodule.autocomplete', 157 * '#autocomplete_route_parameters' => array('a' => $some_key, 'b' => $some_id), 158 * @endcode 159 * If the user types "keywords" in that field, the full path called would be: 160 * 'mymodule_autocomplete/$some_key/$some_id?q=keywords' 161 * 162 * @param array $element 163 * The form element to process. Properties used: 164 * - #autocomplete_route_name: A route to be used as callback URL by the 165 * autocomplete JavaScript library. 166 * - #autocomplete_route_parameters: The parameters to be used in 167 * conjunction with the route name. 168 * @param \Drupal\Core\Form\FormStateInterface $form_state 169 * The current state of the form. 170 * @param array $complete_form 171 * The complete form structure. 172 * 173 * @return array 174 * The form element. 175 */ 176 public static function processAutocomplete(&$element, FormStateInterface $form_state, &$complete_form) { 177 $url = NULL; 178 $access = FALSE; 179 180 if (!empty($element['#autocomplete_route_name'])) { 181 $parameters = isset($element['#autocomplete_route_parameters']) ? $element['#autocomplete_route_parameters'] : []; 182 $url = Url::fromRoute($element['#autocomplete_route_name'], $parameters)->toString(TRUE); 183 /** @var \Drupal\Core\Access\AccessManagerInterface $access_manager */ 184 $access_manager = \Drupal::service('access_manager'); 185 $access = $access_manager->checkNamedRoute($element['#autocomplete_route_name'], $parameters, \Drupal::currentUser(), TRUE); 186 } 187 188 if ($access) { 189 $metadata = BubbleableMetadata::createFromRenderArray($element); 190 if ($access->isAllowed()) { 191 $element['#attributes']['class'][] = 'form-autocomplete'; 192 $metadata->addAttachments(['library' => ['core/drupal.autocomplete']]); 193 // Provide a data attribute for the JavaScript behavior to bind to. 194 $element['#attributes']['data-autocomplete-path'] = $url->getGeneratedUrl(); 195 $metadata = $metadata->merge($url); 196 } 197 $metadata 198 ->merge(BubbleableMetadata::createFromObject($access)) 199 ->applyTo($element); 200 } 201 202 return $element; 203 } 204 205} 206