annotate core/modules/field_ui/src/Element/FieldUiTable.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\field_ui\Element;
Chris@0 4
Chris@0 5 use Drupal\Component\Utility\Html;
Chris@0 6 use Drupal\Core\Render\Element;
Chris@0 7 use Drupal\Core\Render\Element\Table;
Chris@0 8
Chris@0 9 /**
Chris@0 10 * Provides a field_ui table element.
Chris@0 11 *
Chris@0 12 * @RenderElement("field_ui_table")
Chris@0 13 */
Chris@0 14 class FieldUiTable extends Table {
Chris@0 15
Chris@0 16 /**
Chris@0 17 * {@inheritdoc}
Chris@0 18 */
Chris@0 19 public function getInfo() {
Chris@0 20 $info = parent::getInfo();
Chris@0 21 $info['#regions'] = ['' => []];
Chris@0 22 $info['#theme'] = 'field_ui_table';
Chris@0 23 // Prepend FieldUiTable's prerender callbacks.
Chris@0 24 array_unshift($info['#pre_render'], [$this, 'tablePreRender'], [$this, 'preRenderRegionRows']);
Chris@0 25 return $info;
Chris@0 26 }
Chris@0 27
Chris@0 28 /**
Chris@0 29 * Performs pre-render tasks on field_ui_table elements.
Chris@0 30 *
Chris@0 31 * @param array $elements
Chris@0 32 * A structured array containing two sub-levels of elements. Properties
Chris@0 33 * used:
Chris@0 34 * - #tabledrag: The value is a list of $options arrays that are passed to
Chris@0 35 * drupal_attach_tabledrag(). The HTML ID of the table is added to each
Chris@0 36 * $options array.
Chris@0 37 *
Chris@0 38 * @return array
Chris@0 39 * The $element with prepared variables ready for field-ui-table.html.twig.
Chris@0 40 *
Chris@16 41 * @see \Drupal\Core\Render\RendererInterface::render()
Chris@0 42 * @see \Drupal\Core\Render\Element\Table::preRenderTable()
Chris@0 43 */
Chris@0 44 public static function tablePreRender($elements) {
Chris@0 45 $js_settings = [];
Chris@0 46
Chris@0 47 // For each region, build the tree structure from the weight and parenting
Chris@0 48 // data contained in the flat form structure, to determine row order and
Chris@0 49 // indentation.
Chris@0 50 $regions = $elements['#regions'];
Chris@0 51 $tree = ['' => ['name' => '', 'children' => []]];
Chris@0 52 $trees = array_fill_keys(array_keys($regions), $tree);
Chris@0 53
Chris@0 54 $parents = [];
Chris@0 55 $children = Element::children($elements);
Chris@0 56 $list = array_combine($children, $children);
Chris@0 57
Chris@0 58 // Iterate on rows until we can build a known tree path for all of them.
Chris@0 59 while ($list) {
Chris@0 60 foreach ($list as $name) {
Chris@0 61 $row = &$elements[$name];
Chris@0 62 $parent = $row['parent_wrapper']['parent']['#value'];
Chris@0 63 // Proceed if parent is known.
Chris@0 64 if (empty($parent) || isset($parents[$parent])) {
Chris@0 65 // Grab parent, and remove the row from the next iteration.
Chris@0 66 $parents[$name] = $parent ? array_merge($parents[$parent], [$parent]) : [];
Chris@0 67 unset($list[$name]);
Chris@0 68
Chris@0 69 // Determine the region for the row.
Chris@0 70 $region_name = call_user_func_array($row['#region_callback'], [&$row]);
Chris@0 71
Chris@0 72 // Add the element in the tree.
Chris@0 73 $target = &$trees[$region_name][''];
Chris@0 74 foreach ($parents[$name] as $key) {
Chris@0 75 $target = &$target['children'][$key];
Chris@0 76 }
Chris@0 77 $target['children'][$name] = ['name' => $name, 'weight' => $row['weight']['#value']];
Chris@0 78
Chris@0 79 // Add tabledrag indentation to the first row cell.
Chris@0 80 if ($depth = count($parents[$name])) {
Chris@0 81 $children = Element::children($row);
Chris@0 82 $cell = current($children);
Chris@0 83 $indentation = [
Chris@0 84 '#theme' => 'indentation',
Chris@0 85 '#size' => $depth,
Chris@0 86 '#suffix' => isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : '',
Chris@0 87 ];
Chris@0 88 $row[$cell]['#prefix'] = \Drupal::service('renderer')->render($indentation);
Chris@0 89 }
Chris@0 90
Chris@0 91 // Add row id and associate JS settings.
Chris@0 92 $id = Html::getClass($name);
Chris@0 93 $row['#attributes']['id'] = $id;
Chris@0 94 if (isset($row['#js_settings'])) {
Chris@0 95 $row['#js_settings'] += [
Chris@0 96 'rowHandler' => $row['#row_type'],
Chris@0 97 'name' => $name,
Chris@0 98 'region' => $region_name,
Chris@0 99 ];
Chris@0 100 $js_settings[$id] = $row['#js_settings'];
Chris@0 101 }
Chris@0 102 }
Chris@0 103 }
Chris@0 104 }
Chris@0 105
Chris@0 106 // Determine rendering order from the tree structure.
Chris@0 107 foreach ($regions as $region_name => $region) {
Chris@0 108 $elements['#regions'][$region_name]['rows_order'] = array_reduce($trees[$region_name], [static::class, 'reduceOrder']);
Chris@0 109 }
Chris@0 110
Chris@0 111 $elements['#attached']['drupalSettings']['fieldUIRowsData'] = $js_settings;
Chris@0 112
Chris@0 113 // If the custom #tabledrag is set and there is a HTML ID, add the table's
Chris@0 114 // HTML ID to the options and attach the behavior.
Chris@0 115 // @see \Drupal\Core\Render\Element\Table::preRenderTable()
Chris@0 116 if (!empty($elements['#tabledrag']) && isset($elements['#attributes']['id'])) {
Chris@0 117 foreach ($elements['#tabledrag'] as $options) {
Chris@0 118 $options['table_id'] = $elements['#attributes']['id'];
Chris@0 119 drupal_attach_tabledrag($elements, $options);
Chris@0 120 }
Chris@0 121 }
Chris@0 122
Chris@0 123 return $elements;
Chris@0 124 }
Chris@0 125
Chris@0 126 /**
Chris@0 127 * Performs pre-render to move #regions to rows.
Chris@0 128 *
Chris@0 129 * @param array $elements
Chris@0 130 * A structured array containing two sub-levels of elements. Properties
Chris@0 131 * used:
Chris@0 132 * - #tabledrag: The value is a list of $options arrays that are passed to
Chris@0 133 * drupal_attach_tabledrag(). The HTML ID of the table is added to each
Chris@0 134 * $options array.
Chris@0 135 *
Chris@0 136 * @return array
Chris@0 137 * The $element with prepared variables ready for field-ui-table.html.twig.
Chris@0 138 */
Chris@0 139 public static function preRenderRegionRows($elements) {
Chris@0 140 // Determine the colspan to use for region rows, by checking the number of
Chris@0 141 // columns in the headers.
Chris@0 142 $columns_count = 0;
Chris@0 143 foreach ($elements['#header'] as $header) {
Chris@0 144 $columns_count += (is_array($header) && isset($header['colspan']) ? $header['colspan'] : 1);
Chris@0 145 }
Chris@0 146
Chris@0 147 $rows = [];
Chris@0 148 foreach (Element::children($elements) as $key) {
Chris@0 149 $rows[$key] = $elements[$key];
Chris@0 150 unset($elements[$key]);
Chris@0 151 }
Chris@0 152
Chris@0 153 // Render rows, region by region.
Chris@0 154 foreach ($elements['#regions'] as $region_name => $region) {
Chris@0 155 $region_name_class = Html::getClass($region_name);
Chris@0 156
Chris@0 157 // Add region rows.
Chris@0 158 if (isset($region['title']) && empty($region['invisible'])) {
Chris@0 159 $elements['#rows'][] = [
Chris@0 160 'class' => [
Chris@0 161 'region-title',
Chris@17 162 'region-' . $region_name_class . '-title',
Chris@0 163 ],
Chris@0 164 'no_striping' => TRUE,
Chris@0 165 'data' => [
Chris@0 166 ['data' => $region['title'], 'colspan' => $columns_count],
Chris@0 167 ],
Chris@0 168 ];
Chris@0 169 }
Chris@0 170 if (isset($region['message'])) {
Chris@0 171 $class = (empty($region['rows_order']) ? 'region-empty' : 'region-populated');
Chris@0 172 $elements['#rows'][] = [
Chris@0 173 'class' => [
Chris@0 174 'region-message',
Chris@0 175 'region-' . $region_name_class . '-message', $class,
Chris@0 176 ],
Chris@0 177 'no_striping' => TRUE,
Chris@0 178 'data' => [
Chris@0 179 ['data' => $region['message'], 'colspan' => $columns_count],
Chris@0 180 ],
Chris@0 181 ];
Chris@0 182 }
Chris@0 183
Chris@0 184 // Add form rows, in the order determined at pre-render time.
Chris@0 185 foreach ($region['rows_order'] as $name) {
Chris@0 186 $element = $rows[$name];
Chris@0 187
Chris@0 188 $row = ['data' => []];
Chris@0 189 if (isset($element['#attributes'])) {
Chris@0 190 $row += $element['#attributes'];
Chris@0 191 }
Chris@0 192
Chris@0 193 // Render children as table cells.
Chris@0 194 foreach (Element::children($element) as $cell_key) {
Chris@0 195 $child = $element[$cell_key];
Chris@0 196 // Do not render a cell for children of #type 'value'.
Chris@0 197 if (!(isset($child['#type']) && $child['#type'] == 'value')) {
Chris@0 198 $cell = ['data' => $child];
Chris@0 199 if (isset($child['#cell_attributes'])) {
Chris@0 200 $cell += $child['#cell_attributes'];
Chris@0 201 }
Chris@0 202 $row['data'][] = $cell;
Chris@0 203 }
Chris@0 204 }
Chris@0 205 $elements['#rows'][] = $row;
Chris@0 206 }
Chris@0 207 }
Chris@0 208
Chris@0 209 return $elements;
Chris@0 210 }
Chris@0 211
Chris@0 212 /**
Chris@0 213 * Determines the rendering order of an array representing a tree.
Chris@0 214 *
Chris@0 215 * Callback for array_reduce() within ::tablePreRender().
Chris@0 216 *
Chris@0 217 * @param mixed $array
Chris@0 218 * Holds the return value of the previous iteration; in the case of the
Chris@0 219 * first iteration it instead holds the value of the initial array.
Chris@0 220 * @param mixed $a
Chris@0 221 * Holds the value of the current iteration.
Chris@0 222 *
Chris@0 223 * @return array
Chris@0 224 * Array where rendering order has been determined.
Chris@0 225 */
Chris@0 226 public static function reduceOrder($array, $a) {
Chris@0 227 $array = !$array ? [] : $array;
Chris@0 228 if ($a['name']) {
Chris@0 229 $array[] = $a['name'];
Chris@0 230 }
Chris@0 231 if (!empty($a['children'])) {
Chris@0 232 uasort($a['children'], ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
Chris@0 233 $array = array_merge($array, array_reduce($a['children'], [static::class, 'reduceOrder']));
Chris@0 234 }
Chris@0 235
Chris@0 236 return $array;
Chris@0 237 }
Chris@0 238
Chris@0 239 }