PageRenderTime 23ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/core/lib/Drupal/Core/Render/Element/Tableselect.php

http://github.com/drupal/drupal
PHP | 287 lines | 139 code | 15 blank | 133 comment | 27 complexity | 457bc107764fdd298f47948da5d988d6 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. namespace Drupal\Core\Render\Element;
  3. use Drupal\Core\Form\FormStateInterface;
  4. use Drupal\Core\Render\Element;
  5. use Drupal\Component\Utility\Html as HtmlUtility;
  6. use Drupal\Core\StringTranslation\TranslatableMarkup;
  7. /**
  8. * Provides a form element for a table with radios or checkboxes in left column.
  9. *
  10. * Properties:
  11. * - #header: An array of table header labels.
  12. * - #options: An associative array where each key is the value returned when
  13. * a user selects the radio button or checkbox, and each value is the row of
  14. * table data.
  15. * - #empty: The message to display if table does not have any options.
  16. * - #multiple: Set to FALSE to render the table with radios instead checkboxes.
  17. * - #js_select: Set to FALSE if you don't want the select all checkbox added to
  18. * the header.
  19. *
  20. * Other properties of the \Drupal\Core\Render\Element\Table element are also
  21. * available.
  22. *
  23. * Usage example:
  24. * @code
  25. * $header = [
  26. * 'color' => $this->t('Color'),
  27. * 'shape' => $this->t('Shape'),
  28. * ];
  29. *
  30. * $options = [
  31. * 1 => ['color' => 'Red', 'shape' => 'Triangle'],
  32. * 2 => ['color' => 'Green', 'shape' => 'Square'],
  33. * 3 => ['color' => 'Blue', 'shape' => 'Hexagon'],
  34. * ];
  35. *
  36. * $form['table'] = array(
  37. * '#type' => 'tableselect',
  38. * '#header' => $header,
  39. * '#options' => $options,
  40. * '#empty' => $this->t('No shapes found'),
  41. * );
  42. * @endcode
  43. *
  44. * See https://www.drupal.org/node/945102 for a full explanation.
  45. *
  46. * @see \Drupal\Core\Render\Element\Table
  47. *
  48. * @FormElement("tableselect")
  49. */
  50. class Tableselect extends Table {
  51. /**
  52. * {@inheritdoc}
  53. */
  54. public function getInfo() {
  55. $class = get_class($this);
  56. return [
  57. '#input' => TRUE,
  58. '#js_select' => TRUE,
  59. '#multiple' => TRUE,
  60. '#responsive' => TRUE,
  61. '#sticky' => FALSE,
  62. '#pre_render' => [
  63. [$class, 'preRenderTable'],
  64. [$class, 'preRenderTableselect'],
  65. ],
  66. '#process' => [
  67. [$class, 'processTableselect'],
  68. ],
  69. '#options' => [],
  70. '#empty' => '',
  71. '#theme' => 'table__tableselect',
  72. ];
  73. }
  74. /**
  75. * {@inheritdoc}
  76. */
  77. public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
  78. // If $element['#multiple'] == FALSE, then radio buttons are displayed and
  79. // the default value handling is used.
  80. if (isset($element['#multiple']) && $element['#multiple']) {
  81. // Checkboxes are being displayed with the default value coming from the
  82. // keys of the #default_value property. This differs from the checkboxes
  83. // element which uses the array values.
  84. if ($input === FALSE) {
  85. $value = [];
  86. $element += ['#default_value' => []];
  87. foreach ($element['#default_value'] as $key => $flag) {
  88. if ($flag) {
  89. $value[$key] = $key;
  90. }
  91. }
  92. return $value;
  93. }
  94. else {
  95. return is_array($input) ? array_combine($input, $input) : [];
  96. }
  97. }
  98. }
  99. /**
  100. * Prepares a 'tableselect' #type element for rendering.
  101. *
  102. * Adds a column of radio buttons or checkboxes for each row of a table.
  103. *
  104. * @param array $element
  105. * An associative array containing the properties and children of
  106. * the tableselect element. Properties used: #header, #options, #empty,
  107. * and #js_select. The #options property is an array of selection options;
  108. * each array element of #options is an array of properties. These
  109. * properties can include #attributes, which is added to the
  110. * table row's HTML attributes; see table.html.twig. An example of per-row
  111. * options:
  112. * @code
  113. * $options = array(
  114. * array(
  115. * 'title' => $this->t('How to Learn Drupal'),
  116. * 'content_type' => $this->t('Article'),
  117. * 'status' => 'published',
  118. * '#attributes' => array('class' => array('article-row')),
  119. * ),
  120. * array(
  121. * 'title' => $this->t('Privacy Policy'),
  122. * 'content_type' => $this->t('Page'),
  123. * 'status' => 'published',
  124. * '#attributes' => array('class' => array('page-row')),
  125. * ),
  126. * );
  127. * $header = array(
  128. * 'title' => $this->t('Title'),
  129. * 'content_type' => $this->t('Content type'),
  130. * 'status' => $this->t('Status'),
  131. * );
  132. * $form['table'] = array(
  133. * '#type' => 'tableselect',
  134. * '#header' => $header,
  135. * '#options' => $options,
  136. * '#empty' => $this->t('No content available.'),
  137. * );
  138. * @endcode
  139. *
  140. * @return array
  141. * The processed element.
  142. */
  143. public static function preRenderTableselect($element) {
  144. $rows = [];
  145. $header = $element['#header'];
  146. if (!empty($element['#options'])) {
  147. // Generate a table row for each selectable item in #options.
  148. foreach (Element::children($element) as $key) {
  149. $row = [];
  150. $row['data'] = [];
  151. if (isset($element['#options'][$key]['#attributes'])) {
  152. $row += $element['#options'][$key]['#attributes'];
  153. }
  154. // Render the checkbox / radio element.
  155. $row['data'][] = \Drupal::service('renderer')->render($element[$key]);
  156. // As table.html.twig only maps header and row columns by order, create
  157. // the correct order by iterating over the header fields.
  158. foreach ($element['#header'] as $fieldname => $title) {
  159. // A row cell can span over multiple headers, which means less row
  160. // cells than headers could be present.
  161. if (isset($element['#options'][$key][$fieldname])) {
  162. // A header can span over multiple cells and in this case the cells
  163. // are passed in an array. The order of this array determines the
  164. // order in which they are added.
  165. if (is_array($element['#options'][$key][$fieldname]) && !isset($element['#options'][$key][$fieldname]['data'])) {
  166. foreach ($element['#options'][$key][$fieldname] as $cell) {
  167. $row['data'][] = $cell;
  168. }
  169. }
  170. else {
  171. $row['data'][] = $element['#options'][$key][$fieldname];
  172. }
  173. }
  174. }
  175. $rows[] = $row;
  176. }
  177. // Add an empty header or a "Select all" checkbox to provide room for the
  178. // checkboxes/radios in the first table column.
  179. if ($element['#js_select']) {
  180. // Add a "Select all" checkbox.
  181. $element['#attached']['library'][] = 'core/drupal.tableselect';
  182. array_unshift($header, ['class' => ['select-all']]);
  183. }
  184. else {
  185. // Add an empty header when radio buttons are displayed or a "Select all"
  186. // checkbox is not desired.
  187. array_unshift($header, '');
  188. }
  189. }
  190. $element['#header'] = $header;
  191. $element['#rows'] = $rows;
  192. return $element;
  193. }
  194. /**
  195. * Creates checkbox or radio elements to populate a tableselect table.
  196. *
  197. * @param array $element
  198. * An associative array containing the properties and children of the
  199. * tableselect element.
  200. * @param \Drupal\Core\Form\FormStateInterface $form_state
  201. * The current state of the form.
  202. * @param array $complete_form
  203. * The complete form structure.
  204. *
  205. * @return array
  206. * The processed element.
  207. */
  208. public static function processTableselect(&$element, FormStateInterface $form_state, &$complete_form) {
  209. if ($element['#multiple']) {
  210. $value = is_array($element['#value']) ? $element['#value'] : [];
  211. }
  212. else {
  213. // Advanced selection behavior makes no sense for radios.
  214. $element['#js_select'] = FALSE;
  215. }
  216. $element['#tree'] = TRUE;
  217. if (count($element['#options']) > 0) {
  218. if (!isset($element['#default_value']) || $element['#default_value'] === 0) {
  219. $element['#default_value'] = [];
  220. }
  221. // Create a checkbox or radio for each item in #options in such a way that
  222. // the value of the tableselect element behaves as if it had been of type
  223. // checkboxes or radios.
  224. foreach ($element['#options'] as $key => $choice) {
  225. // Do not overwrite manually created children.
  226. if (!isset($element[$key])) {
  227. if ($element['#multiple']) {
  228. $title = '';
  229. if (isset($element['#options'][$key]['title']) && is_array($element['#options'][$key]['title'])) {
  230. if (!empty($element['#options'][$key]['title']['data']['#title'])) {
  231. $title = new TranslatableMarkup('Update @title', [
  232. '@title' => $element['#options'][$key]['title']['data']['#title'],
  233. ]);
  234. }
  235. }
  236. $element[$key] = [
  237. '#type' => 'checkbox',
  238. '#title' => $title,
  239. '#title_display' => 'invisible',
  240. '#return_value' => $key,
  241. '#default_value' => isset($value[$key]) ? $key : NULL,
  242. '#attributes' => $element['#attributes'],
  243. '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
  244. ];
  245. }
  246. else {
  247. // Generate the parents as the autogenerator does, so we will have a
  248. // unique id for each radio button.
  249. $parents_for_id = array_merge($element['#parents'], [$key]);
  250. $element[$key] = [
  251. '#type' => 'radio',
  252. '#title' => '',
  253. '#return_value' => $key,
  254. '#default_value' => ($element['#default_value'] == $key) ? $key : NULL,
  255. '#attributes' => $element['#attributes'],
  256. '#parents' => $element['#parents'],
  257. '#id' => HtmlUtility::getUniqueId('edit-' . implode('-', $parents_for_id)),
  258. '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
  259. ];
  260. }
  261. if (isset($element['#options'][$key]['#weight'])) {
  262. $element[$key]['#weight'] = $element['#options'][$key]['#weight'];
  263. }
  264. }
  265. }
  266. }
  267. else {
  268. $element['#value'] = [];
  269. }
  270. return $element;
  271. }
  272. }