View source
<?php
namespace Drupal\yamlform\Element;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\FormElement;
use Drupal\Core\Form\FormStateInterface;
use Drupal\yamlform\Utility\YamlFormElementHelper;
class YamlFormMultiple extends FormElement {
public function getInfo() {
$class = get_class($this);
return [
'#input' => TRUE,
'#label' => t('item'),
'#labels' => t('items'),
'#header' => NULL,
'#element' => [
'#type' => 'textfield',
'#title' => t('Item value'),
'#title_display' => 'invisible',
'#placeholder' => t('Enter value'),
],
'#cardinality' => FALSE,
'#empty_items' => 1,
'#add_more' => 1,
'#process' => [
[
$class,
'processYamlFormMultiple',
],
],
'#theme_wrappers' => [
'form_element',
],
];
}
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input === FALSE) {
return isset($element['#default_value']) ? $element['#default_value'] : [];
}
elseif (is_array($input) && isset($input['items'])) {
return $input['items'];
}
else {
return NULL;
}
}
public static function processYamlFormMultiple(&$element, FormStateInterface $form_state, &$complete_form) {
$element['#tree'] = TRUE;
$element['#element_validate'] = [
[
get_called_class(),
'validateYamlFormMultiple',
],
];
YamlFormElementHelper::fixStatesWrapper($element);
if ($element['#cardinality']) {
$number_of_items = $element['#cardinality'];
}
else {
$number_of_items_storage_key = self::getStorageKey($element, 'number_of_items');
if ($form_state
->get($number_of_items_storage_key) === NULL) {
if (empty($element['#default_value']) || !is_array($element['#default_value'])) {
$number_of_default_values = 0;
$number_of_empty_items = (int) $element['#empty_items'];
}
else {
$number_of_default_values = count($element['#default_value']);
$number_of_empty_items = 1;
}
$form_state
->set($number_of_items_storage_key, $number_of_default_values + $number_of_empty_items);
}
$number_of_items = $form_state
->get($number_of_items_storage_key);
}
$table_id = implode('_', $element['#parents']) . '_table';
$ajax_settings = [
'callback' => [
get_called_class(),
'ajaxCallback',
],
'wrapper' => $table_id,
];
$element['#child_keys'] = Element::children($element['#element']);
$header = self::buildElementHeader($element);
$row_index = 0;
$weight = 0;
$rows = [];
if (!$form_state
->isProcessingInput() && isset($element['#default_value']) && is_array($element['#default_value'])) {
foreach ($element['#default_value'] as $default_value) {
$rows[$row_index] = self::buildElementRow($table_id, $row_index, $element, $default_value, $weight++, $ajax_settings);
$row_index++;
}
}
while ($row_index < $number_of_items) {
$rows[$row_index] = self::buildElementRow($table_id, $row_index, $element, NULL, $weight++, $ajax_settings);
$row_index++;
}
$element['items'] = [
'#prefix' => '<div id="' . $table_id . '" class="yamlform-multiple-table">',
'#suffix' => '</div>',
'#type' => 'table',
'#header' => $header,
'#tabledrag' => [
[
'action' => 'order',
'relationship' => 'sibling',
'group' => 'yamlform-multiple-sort-weight',
],
],
] + $rows;
if (empty($element['#cardinality'])) {
$element['add'] = [
'#prefix' => '<div class="container-inline">',
'#suffix' => '</div>',
];
$element['add']['submit'] = [
'#type' => 'submit',
'#value' => t('Add'),
'#limit_validation_errors' => [],
'#submit' => [
[
get_called_class(),
'addItemsSubmit',
],
],
'#ajax' => $ajax_settings,
'#name' => $table_id . '_add',
];
$element['add']['more_items'] = [
'#type' => 'number',
'#min' => 1,
'#max' => 100,
'#default_value' => $element['#add_more'],
'#field_suffix' => t('more @labels', [
'@labels' => $element['#labels'],
]),
];
}
$element['#attached']['library'][] = 'yamlform/yamlform.element.multiple';
return $element;
}
public static function buildElementHeader(array $element) {
if (empty($element['#header'])) {
return [
[
'data' => '',
'colspan' => 4,
],
];
}
if (is_array($element['#header'])) {
return $element['#header'];
}
$header = [];
$header['_handle_'] = '';
if ($element['#child_keys']) {
foreach ($element['#child_keys'] as $child_key) {
$header[$child_key] = !empty($element['#element'][$child_key]['#title']) ? $element['#element'][$child_key]['#title'] : '';
}
}
else {
$header['item'] = isset($element['#element']['#title']) ? $element['#element']['#title'] : '';
}
$header['weight'] = t('Weight');
if (empty($element['#cardinality'])) {
$header['_operations_'] = '';
}
return $header;
}
public static function buildElementRow($table_id, $row_index, array $element, $default_value, $weight, array $ajax_settings) {
if ($element['#child_keys']) {
foreach ($element['#child_keys'] as $child_key) {
if (isset($default_value[$child_key])) {
$element['#element'][$child_key]['#default_value'] = $default_value[$child_key];
}
}
}
else {
$element['#element']['#default_value'] = $default_value;
}
$row = [];
$row['_handle_'] = [];
if ($element['#child_keys'] && !empty($element['#header'])) {
foreach ($element['#child_keys'] as $child_key) {
$row[$child_key] = $element['#element'][$child_key];
}
}
else {
$row['_item_'] = $element['#element'];
}
$row['weight'] = [
'#type' => 'weight',
'#delta' => 1000,
'#title' => t('Item weight'),
'#title_display' => 'invisible',
'#attributes' => [
'class' => [
'yamlform-multiple-sort-weight',
],
],
'#default_value' => $weight,
];
if (empty($element['#cardinality'])) {
$row['_operations_'] = [];
$row['_operations_']['add'] = [
'#type' => 'image_button',
'#src' => drupal_get_path('module', 'yamlform') . '/images/icons/plus.svg',
'#limit_validation_errors' => [],
'#submit' => [
[
get_called_class(),
'addItemSubmit',
],
],
'#ajax' => $ajax_settings,
'#row_index' => $row_index,
'#name' => $table_id . '_add_' . $row_index,
];
$row['_operations_']['remove'] = [
'#type' => 'image_button',
'#src' => drupal_get_path('module', 'yamlform') . '/images/icons/ex.svg',
'#limit_validation_errors' => [],
'#submit' => [
[
get_called_class(),
'removeItemSubmit',
],
],
'#ajax' => $ajax_settings,
'#row_index' => $row_index,
'#name' => $table_id . '_remove_' . $row_index,
];
}
$row['#weight'] = $weight;
$row['#attributes']['class'][] = 'draggable';
return $row;
}
public static function addItemsSubmit(array &$form, FormStateInterface $form_state) {
$button = $form_state
->getTriggeringElement();
$element =& NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -2));
$number_of_items_storage_key = self::getStorageKey($element, 'number_of_items');
$number_of_items = $form_state
->get($number_of_items_storage_key);
$more_items = (int) $element['add']['more_items']['#value'];
$form_state
->set($number_of_items_storage_key, $number_of_items + $more_items);
$element['items']['#value'] = array_values($element['items']['#value']);
$form_state
->setValueForElement($element['items'], $element['items']['#value']);
NestedArray::setValue($form_state
->getUserInput(), $element['items']['#parents'], $element['items']['#value']);
$form_state
->setRebuild();
}
public static function addItemSubmit(array &$form, FormStateInterface $form_state) {
$button = $form_state
->getTriggeringElement();
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -4));
$values = [];
foreach ($element['items']['#value'] as $row_index => $value) {
$values[] = $value;
if ($row_index == $button['#row_index']) {
$values[] = [
'item' => '',
'text' => '',
];
}
}
$number_of_items_storage_key = self::getStorageKey($element, 'number_of_items');
$number_of_items = $form_state
->get($number_of_items_storage_key);
$form_state
->set($number_of_items_storage_key, $number_of_items + 1);
$form_state
->setValueForElement($element['items'], $values);
NestedArray::setValue($form_state
->getUserInput(), $element['items']['#parents'], $values);
$form_state
->setRebuild();
}
public static function removeItemSubmit(array &$form, FormStateInterface $form_state) {
$button = $form_state
->getTriggeringElement();
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -4));
$values = $element['items']['#value'];
unset($values[$button['#row_index']]);
$values = array_values($values);
$number_of_items_storage_key = self::getStorageKey($element, 'number_of_items');
$number_of_items = $form_state
->get($number_of_items_storage_key);
if ($number_of_items != 1) {
$form_state
->set($number_of_items_storage_key, $number_of_items - 1);
}
$form_state
->setValueForElement($element['items'], $values);
NestedArray::setValue($form_state
->getUserInput(), $element['items']['#parents'], $values);
$form_state
->setRebuild();
}
public static function ajaxCallback(array &$form, FormStateInterface $form_state) {
$button = $form_state
->getTriggeringElement();
$parent_length = isset($button['#row_index']) ? -4 : -2;
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, $parent_length));
return $element['items'];
}
public static function validateYamlFormMultiple(&$element, FormStateInterface $form_state, &$complete_form) {
$values = NestedArray::getValue($form_state
->getValues(), $element['#parents']);
$items = self::convertValuesToItems($element, $values['items']);
if (!empty($element['#required']) && empty($items)) {
if (isset($element['#required_error'])) {
$form_state
->setError($element, $element['#required_error']);
}
elseif (isset($element['#title'])) {
$form_state
->setError($element, t('@name field is required.', [
'@name' => $element['#title'],
]));
}
else {
$form_state
->setError($element);
}
return;
}
$form_state
->setValueForElement($element, $items);
}
public static function getStorageKey(array $element, $name) {
return 'yamlform_multiple__' . $element['#name'] . '__' . $name;
}
public static function convertValuesToItems(array &$element, array $values = []) {
uasort($values, [
'Drupal\\Component\\Utility\\SortArray',
'sortByWeightElement',
]);
$items = [];
foreach ($values as $value) {
if (isset($value['_item_'])) {
if (!self::isEmpty($value['_item_'])) {
$items[] = $value['_item_'];
}
}
else {
unset($value['weight'], $value['_operations_']);
if (!self::isEmpty($value)) {
$items[] = $value;
}
}
}
return $items;
}
public static function isEmpty($value = NULL) {
if (is_null($value)) {
return TRUE;
}
elseif (is_string($value)) {
return $value === '' ? TRUE : FALSE;
}
elseif (is_array($value)) {
return !array_filter($value, function ($item) {
return !self::isEmpty($item);
});
}
else {
return FALSE;
}
}
}