Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 128 |
FieldUiTable | |
0.00% |
0 / 1 |
|
0.00% |
0 / 4 |
1260 | |
0.00% |
0 / 128 |
getInfo | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 6 |
|||
tablePreRender | |
0.00% |
0 / 1 |
210 | |
0.00% |
0 / 55 |
|||
preRenderRegionRows | |
0.00% |
0 / 1 |
272 | |
0.00% |
0 / 57 |
|||
reduceOrder | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 10 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\field_ui\Element\FieldUiTable. | |
*/ | |
namespace Drupal\field_ui\Element; | |
use Drupal\Component\Utility\Html; | |
use Drupal\Core\Render\Element; | |
use Drupal\Core\Render\Element\Table; | |
/** | |
* Provides a field_ui table element. | |
* | |
* @RenderElement("field_ui_table") | |
*/ | |
class FieldUiTable extends Table { | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getInfo() { | |
$info = parent::getInfo(); | |
$info['#regions'] = ['' => []]; | |
$info['#theme'] = 'field_ui_table'; | |
// Prepend FieldUiTable's prerender callbacks. | |
array_unshift($info['#pre_render'], [$this, 'tablePreRender'], [$this, 'preRenderRegionRows']); | |
return $info; | |
} | |
/** | |
* Performs pre-render tasks on field_ui_table elements. | |
* | |
* @param array $elements | |
* A structured array containing two sub-levels of elements. Properties | |
* used: | |
* - #tabledrag: The value is a list of $options arrays that are passed to | |
* drupal_attach_tabledrag(). The HTML ID of the table is added to each | |
* $options array. | |
* | |
* @return array | |
* The $element with prepared variables ready for field-ui-table.html.twig. | |
* | |
* @see drupal_render() | |
* @see \Drupal\Core\Render\Element\Table::preRenderTable() | |
*/ | |
public static function tablePreRender($elements) { | |
$js_settings = array(); | |
// For each region, build the tree structure from the weight and parenting | |
// data contained in the flat form structure, to determine row order and | |
// indentation. | |
$regions = $elements['#regions']; | |
$tree = ['' => ['name' => '', 'children' => []]]; | |
$trees = array_fill_keys(array_keys($regions), $tree); | |
$parents = []; | |
$children = Element::children($elements); | |
$list = array_combine($children, $children); | |
// Iterate on rows until we can build a known tree path for all of them. | |
while ($list) { | |
foreach ($list as $name) { | |
$row = &$elements[$name]; | |
$parent = $row['parent_wrapper']['parent']['#value']; | |
// Proceed if parent is known. | |
if (empty($parent) || isset($parents[$parent])) { | |
// Grab parent, and remove the row from the next iteration. | |
$parents[$name] = $parent ? array_merge($parents[$parent], [$parent]) : []; | |
unset($list[$name]); | |
// Determine the region for the row. | |
$region_name = call_user_func($row['#region_callback'], $row); | |
// Add the element in the tree. | |
$target = &$trees[$region_name]['']; | |
foreach ($parents[$name] as $key) { | |
$target = &$target['children'][$key]; | |
} | |
$target['children'][$name] = ['name' => $name, 'weight' => $row['weight']['#value']]; | |
// Add tabledrag indentation to the first row cell. | |
if ($depth = count($parents[$name])) { | |
$children = Element::children($row); | |
$cell = current($children); | |
$indentation = [ | |
'#theme' => 'indentation', | |
'#size' => $depth, | |
'#suffix' => isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : '', | |
]; | |
$row[$cell]['#prefix'] = \Drupal::service('renderer')->render($indentation); | |
} | |
// Add row id and associate JS settings. | |
$id = Html::getClass($name); | |
$row['#attributes']['id'] = $id; | |
if (isset($row['#js_settings'])) { | |
$row['#js_settings'] += [ | |
'rowHandler' => $row['#row_type'], | |
'name' => $name, | |
'region' => $region_name, | |
]; | |
$js_settings[$id] = $row['#js_settings']; | |
} | |
} | |
} | |
} | |
// Determine rendering order from the tree structure. | |
foreach ($regions as $region_name => $region) { | |
$elements['#regions'][$region_name]['rows_order'] = array_reduce($trees[$region_name], [static::class, 'reduceOrder']); | |
} | |
$elements['#attached']['drupalSettings']['fieldUIRowsData'] = $js_settings; | |
// If the custom #tabledrag is set and there is a HTML ID, add the table's | |
// HTML ID to the options and attach the behavior. | |
// @see \Drupal\Core\Render\Element\Table::preRenderTable() | |
if (!empty($elements['#tabledrag']) && isset($elements['#attributes']['id'])) { | |
foreach ($elements['#tabledrag'] as $options) { | |
$options['table_id'] = $elements['#attributes']['id']; | |
drupal_attach_tabledrag($elements, $options); | |
} | |
} | |
return $elements; | |
} | |
/** | |
* Performs pre-render to move #regions to rows. | |
* | |
* @param array $elements | |
* A structured array containing two sub-levels of elements. Properties | |
* used: | |
* - #tabledrag: The value is a list of $options arrays that are passed to | |
* drupal_attach_tabledrag(). The HTML ID of the table is added to each | |
* $options array. | |
* | |
* @return array | |
* The $element with prepared variables ready for field-ui-table.html.twig. | |
*/ | |
public static function preRenderRegionRows($elements) { | |
// Determine the colspan to use for region rows, by checking the number of | |
// columns in the headers. | |
$columns_count = 0; | |
foreach ($elements['#header'] as $header) { | |
$columns_count += (is_array($header) && isset($header['colspan']) ? $header['colspan'] : 1); | |
} | |
$rows = []; | |
foreach (Element::children($elements) as $key) { | |
$rows[$key] = $elements[$key]; | |
unset($elements[$key]); | |
} | |
// Render rows, region by region. | |
foreach ($elements['#regions'] as $region_name => $region) { | |
$region_name_class = Html::getClass($region_name); | |
// Add region rows. | |
if (isset($region['title']) && empty($region['invisible'])) { | |
$elements['#rows'][] = [ | |
'class' => [ | |
'region-title', | |
'region-' . $region_name_class . '-title' | |
], | |
'no_striping' => TRUE, | |
'data' => [ | |
['data' => $region['title'], 'colspan' => $columns_count], | |
], | |
]; | |
} | |
if (isset($region['message'])) { | |
$class = (empty($region['rows_order']) ? 'region-empty' : 'region-populated'); | |
$elements['#rows'][] = [ | |
'class' => [ | |
'region-message', | |
'region-' . $region_name_class . '-message', $class, | |
], | |
'no_striping' => TRUE, | |
'data' => [ | |
['data' => $region['message'], 'colspan' => $columns_count], | |
], | |
]; | |
} | |
// Add form rows, in the order determined at pre-render time. | |
foreach ($region['rows_order'] as $name) { | |
$element = $rows[$name]; | |
$row = ['data' => []]; | |
if (isset($element['#attributes'])) { | |
$row += $element['#attributes']; | |
} | |
// Render children as table cells. | |
foreach (Element::children($element) as $cell_key) { | |
$child = $element[$cell_key]; | |
// Do not render a cell for children of #type 'value'. | |
if (!(isset($child['#type']) && $child['#type'] == 'value')) { | |
$cell = ['data' => $child]; | |
if (isset($child['#cell_attributes'])) { | |
$cell += $child['#cell_attributes']; | |
} | |
$row['data'][] = $cell; | |
} | |
} | |
$elements['#rows'][] = $row; | |
} | |
} | |
return $elements; | |
} | |
/** | |
* Determines the rendering order of an array representing a tree. | |
* | |
* Callback for array_reduce() within ::tablePreRender(). | |
* | |
* @param mixed $array | |
* Holds the return value of the previous iteration; in the case of the | |
* first iteration it instead holds the value of the initial array. | |
* @param mixed $a | |
* Holds the value of the current iteration. | |
* | |
* @return array | |
* Array where rendering order has been determined. | |
*/ | |
public static function reduceOrder($array, $a) { | |
$array = !$array ? [] : $array; | |
if ($a['name']) { | |
$array[] = $a['name']; | |
} | |
if (!empty($a['children'])) { | |
uasort($a['children'], ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']); | |
$array = array_merge($array, array_reduce($a['children'], [static::class, 'reduceOrder'])); | |
} | |
return $array; | |
} | |
} |