Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 4 |
CRAP | |
31.58% |
24 / 76 |
Tableselect | |
0.00% |
0 / 1 |
|
0.00% |
0 / 4 |
404.28 | |
31.58% |
24 / 76 |
getInfo | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 7 |
|||
valueCallback | |
0.00% |
0 / 1 |
56 | |
0.00% |
0 / 10 |
|||
preRenderTableselect | |
0.00% |
0 / 1 |
110 | |
0.00% |
0 / 23 |
|||
processTableselect | |
0.00% |
0 / 1 |
25.48 | |
66.67% |
24 / 36 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Core\Render\Element\Tableselect. | |
*/ | |
namespace Drupal\Core\Render\Element; | |
use Drupal\Core\Form\FormStateInterface; | |
use Drupal\Core\Render\Element; | |
use Drupal\Component\Utility\Html as HtmlUtility; | |
use Drupal\Core\StringTranslation\TranslatableMarkup; | |
/** | |
* Provides a form element for a table with radios or checkboxes in left column. | |
* | |
* Properties: | |
* - #header: An array of table header labels. | |
* - #options: An associative array where each key is the value returned when | |
* a user selects the radio button or checkbox, and each value is the row of | |
* table data. | |
* - #empty: The message to display if table does not have any options. | |
* - #multiple: Set to FALSE to render the table with radios instead checkboxes. | |
* - #js_select: Set to FALSE if you don't want the select all checkbox added to | |
* the header. | |
* | |
* Other properties of the \Drupal\Core\Render\Element\Table element are also | |
* available. | |
* | |
* Usage example: | |
* @code | |
* $header = [ | |
* 'first_name' => t('First Name'), | |
* 'last_name' => t('Last Name'), | |
* ]; | |
* | |
* $options = [ | |
* 1 => ['first_name' => 'Indy', 'last_name' => 'Jones'], | |
* 2 => ['first_name' => 'Darth', 'last_name' => 'Vader'], | |
* 3 => ['first_name' => 'Super', 'last_name' => 'Man'], | |
* ]; | |
* | |
* $form['table'] = array( | |
* '#type' => 'tableselect', | |
* '#header' => $header, | |
* '#options' => $options, | |
* '#empty' => t('No users found'), | |
* ); | |
* @endcode | |
* | |
* See https://www.drupal.org/node/945102 for a full explanation. | |
* | |
* @see \Drupal\Core\Render\Element\Table | |
* | |
* @FormElement("tableselect") | |
*/ | |
class Tableselect extends Table { | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getInfo() { | |
$class = get_class($this); | |
return array( | |
'#input' => TRUE, | |
'#js_select' => TRUE, | |
'#multiple' => TRUE, | |
'#responsive' => TRUE, | |
'#sticky' => FALSE, | |
'#pre_render' => array( | |
array($class, 'preRenderTable'), | |
array($class, 'preRenderTableselect'), | |
), | |
'#process' => array( | |
array($class, 'processTableselect'), | |
), | |
'#options' => array(), | |
'#empty' => '', | |
'#theme' => 'table__tableselect', | |
); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public static function valueCallback(&$element, $input, FormStateInterface $form_state) { | |
// If $element['#multiple'] == FALSE, then radio buttons are displayed and | |
// the default value handling is used. | |
if (isset($element['#multiple']) && $element['#multiple']) { | |
// Checkboxes are being displayed with the default value coming from the | |
// keys of the #default_value property. This differs from the checkboxes | |
// element which uses the array values. | |
if ($input === FALSE) { | |
$value = array(); | |
$element += array('#default_value' => array()); | |
foreach ($element['#default_value'] as $key => $flag) { | |
if ($flag) { | |
$value[$key] = $key; | |
} | |
} | |
return $value; | |
} | |
else { | |
return is_array($input) ? array_combine($input, $input) : array(); | |
} | |
} | |
} | |
/** | |
* Prepares a 'tableselect' #type element for rendering. | |
* | |
* Adds a column of radio buttons or checkboxes for each row of a table. | |
* | |
* @param array $element | |
* An associative array containing the properties and children of | |
* the tableselect element. Properties used: #header, #options, #empty, | |
* and #js_select. The #options property is an array of selection options; | |
* each array element of #options is an array of properties. These | |
* properties can include #attributes, which is added to the | |
* table row's HTML attributes; see table.html.twig. An example of per-row | |
* options: | |
* @code | |
* $options = array( | |
* array( | |
* 'title' => 'How to Learn Drupal', | |
* 'content_type' => 'Article', | |
* 'status' => 'published', | |
* '#attributes' => array('class' => array('article-row')), | |
* ), | |
* array( | |
* 'title' => 'Privacy Policy', | |
* 'content_type' => 'Page', | |
* 'status' => 'published', | |
* '#attributes' => array('class' => array('page-row')), | |
* ), | |
* ); | |
* $header = array( | |
* 'title' => t('Title'), | |
* 'content_type' => t('Content type'), | |
* 'status' => t('Status'), | |
* ); | |
* $form['table'] = array( | |
* '#type' => 'tableselect', | |
* '#header' => $header, | |
* '#options' => $options, | |
* '#empty' => t('No content available.'), | |
* ); | |
* @endcode | |
* | |
* @return array | |
* The processed element. | |
*/ | |
public static function preRenderTableselect($element) { | |
$rows = array(); | |
$header = $element['#header']; | |
if (!empty($element['#options'])) { | |
// Generate a table row for each selectable item in #options. | |
foreach (Element::children($element) as $key) { | |
$row = array(); | |
$row['data'] = array(); | |
if (isset($element['#options'][$key]['#attributes'])) { | |
$row += $element['#options'][$key]['#attributes']; | |
} | |
// Render the checkbox / radio element. | |
$row['data'][] = drupal_render($element[$key]); | |
// As table.html.twig only maps header and row columns by order, create | |
// the correct order by iterating over the header fields. | |
foreach ($element['#header'] as $fieldname => $title) { | |
// A row cell can span over multiple headers, which means less row | |
// cells than headers could be present. | |
if (isset($element['#options'][$key][$fieldname])) { | |
// A header can span over multiple cells and in this case the cells | |
// are passed in an array. The order of this array determines the | |
// order in which they are added. | |
if (is_array($element['#options'][$key][$fieldname]) && !isset($element['#options'][$key][$fieldname]['data'])) { | |
foreach ($element['#options'][$key][$fieldname] as $cell) { | |
$row['data'][] = $cell; | |
} | |
} | |
else { | |
$row['data'][] = $element['#options'][$key][$fieldname]; | |
} | |
} | |
} | |
$rows[] = $row; | |
} | |
// Add an empty header or a "Select all" checkbox to provide room for the | |
// checkboxes/radios in the first table column. | |
if ($element['#js_select']) { | |
// Add a "Select all" checkbox. | |
$element['#attached']['library'][] = 'core/drupal.tableselect'; | |
array_unshift($header, array('class' => array('select-all'))); | |
} | |
else { | |
// Add an empty header when radio buttons are displayed or a "Select all" | |
// checkbox is not desired. | |
array_unshift($header, ''); | |
} | |
} | |
$element['#header'] = $header; | |
$element['#rows'] = $rows; | |
return $element; | |
} | |
/** | |
* Creates checkbox or radio elements to populate a tableselect table. | |
* | |
* @param array $element | |
* An associative array containing the properties and children of the | |
* tableselect element. | |
* @param \Drupal\Core\Form\FormStateInterface $form_state | |
* The current state of the form. | |
* @param array $complete_form | |
* The complete form structure. | |
* | |
* @return array | |
* The processed element. | |
*/ | |
public static function processTableselect(&$element, FormStateInterface $form_state, &$complete_form) { | |
if ($element['#multiple']) { | |
$value = is_array($element['#value']) ? $element['#value'] : array(); | |
} | |
else { | |
// Advanced selection behavior makes no sense for radios. | |
$element['#js_select'] = FALSE; | |
} | |
$element['#tree'] = TRUE; | |
if (count($element['#options']) > 0) { | |
if (!isset($element['#default_value']) || $element['#default_value'] === 0) { | |
$element['#default_value'] = array(); | |
} | |
// Create a checkbox or radio for each item in #options in such a way that | |
// the value of the tableselect element behaves as if it had been of type | |
// checkboxes or radios. | |
foreach ($element['#options'] as $key => $choice) { | |
// Do not overwrite manually created children. | |
if (!isset($element[$key])) { | |
if ($element['#multiple']) { | |
$title = ''; | |
if (isset($element['#options'][$key]['title']) && is_array($element['#options'][$key]['title'])) { | |
if (!empty($element['#options'][$key]['title']['data']['#title'])) { | |
$title = new TranslatableMarkup('Update @title', array( | |
'@title' => $element['#options'][$key]['title']['data']['#title'], | |
)); | |
} | |
} | |
$element[$key] = array( | |
'#type' => 'checkbox', | |
'#title' => $title, | |
'#title_display' => 'invisible', | |
'#return_value' => $key, | |
'#default_value' => isset($value[$key]) ? $key : NULL, | |
'#attributes' => $element['#attributes'], | |
); | |
} | |
else { | |
// Generate the parents as the autogenerator does, so we will have a | |
// unique id for each radio button. | |
$parents_for_id = array_merge($element['#parents'], array($key)); | |
$element[$key] = array( | |
'#type' => 'radio', | |
'#title' => '', | |
'#return_value' => $key, | |
'#default_value' => ($element['#default_value'] == $key) ? $key : NULL, | |
'#attributes' => $element['#attributes'], | |
'#parents' => $element['#parents'], | |
'#id' => HtmlUtility::getUniqueId('edit-' . implode('-', $parents_for_id)), | |
'#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL, | |
); | |
} | |
if (isset($element['#options'][$key]['#weight'])) { | |
$element[$key]['#weight'] = $element['#options'][$key]['#weight']; | |
} | |
} | |
} | |
} | |
else { | |
$element['#value'] = array(); | |
} | |
return $element; | |
} | |
} |