FormElement.php 9.2 KB

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