fortnox-8.x-1.x-dev/src/Element/AddMoreAjax.php
src/Element/AddMoreAjax.php
<?php
namespace Drupal\fortnox\Element;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\FormElement;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides the add more functionality dynamically.
*
* When this form element it's created, a fieldset with the fields data
* declared will appear and it will be created add more functionality
* the fieldset can contain multiple fields or only one field.
*
* Properties:
* - #fields: An array of the fields data containing the field name
* and the field label used for creating the fields inside of the fieldset.
* - #fieldset_details: An array containing the machine name of the
* fieldset and the label.
*
*
* Usage Example:
* @code
* $form['example'] = [
* '#type' => 'dynamic_add_more',
* '#fields' => [
* ['field_name => 'example_type', field_label => $this->t('Example type)],
* ['field_name => 'example_id', 'field_label => $this->('Example ID')],
* ],
* '#fieldset_machine_name => 'example_fieldset',
* @endcode
*
* @FormElement("dynamic_add_more")
*/
class AddMoreAjax extends FormElement {
use StringTranslationTrait;
/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return [
'#fields' => [],
'#fieldset_details' => [],
'#process' => [
[$class, 'processAddMoreFieldset'],
],
'#theme_wrappers' => ['form_element'],
];
}
/**
* Manage the 'add more' implementation dynamically.
*/
public static function processAddMoreFieldset(&$element, FormStateInterface $form_state) {
$fieldset_name = $element['#fieldset_machine_name'];
$default_value = isset($element['#default_value']) ? $element['#default_value'] : NULL;
$fields = $element['#fields'];
$add_fieldset = [
'#prefix' => '<div id="' . $fieldset_name . '-fieldset-wrapper">',
'#suffix' => '</div>',
];
// Add the fieldset prefix and suffix for the ajax callback.
$element += $add_fieldset;
if (!empty($default_value)) {
// Count only the integer keys of the default value array.
$int_default_values = count(array_filter(array_keys($default_value), 'is_int'));
}
// Check if we are on edit case and do have already saved values.
if (!empty($int_default_values) && empty($form_state->getUserInput())) {
$number_items = $int_default_values;
}
else {
// Gather the number of values added by fieldset in the form already.
$number_items = $form_state->get('number_items_' . $fieldset_name);
}
// We have to ensure that there is at least one name field.
if ($number_items === NULL) {
$form_state->set('number_items_' . $fieldset_name, 1);
$number_items = 1;
}
// Add fields based on the numbers added so far.
for ($i = 0; $i < $number_items; $i++) {
foreach ($fields as $field) {
$element[$i][$field['field_name']] = [
'#type' => $field['field_type'],
'#title' => $field['field_label'],
'#default_value' => isset($default_value[$i]) ? $default_value[$i][$field['field_name']] : '',
];
if ($field['field_type'] == 'select') {
$element[$i][$field['field_name']]['#options'] = $field['options'];
}
}
}
$element['actions'] = [
'#type' => 'actions',
];
$element['actions']['add_more'] = [
'#type' => 'submit',
'#value' => 'Add one more',
'#submit' => ['Drupal\fortnox\Element\AddMoreAjax::addOne'],
'#name' => $fieldset_name,
'#ajax' => [
'callback' => 'Drupal\fortnox\Element\AddMoreAjax::addmoreCallback',
'wrapper' => $fieldset_name . '-fieldset-wrapper',
'options' => [
'query' => [
'element_parents' => implode('/', $element['#array_parents']),
],
],
],
'#limit_validation_errors' => [],
];
return $element;
}
/**
* Selects and returns the fieldset with the names in it.
*/
public static function addmoreCallback(&$element, FormStateInterface $form_state, Request $request) {
$form_parents = explode('/', $request->query->get('element_parents'));
// Sanitize form parents before using them.
$form_parents = array_filter($form_parents, [Element::class, 'child']);
// Retrieve the element to be rendered.
return NestedArray::getValue($element, $form_parents);
}
/**
* Increments the max counter and causes a rebuild.
*/
public static function addOne(&$element, FormStateInterface $form_state) {
$user_input = $form_state->getUserInput();
$fieldset_name = $form_state->getTriggeringElement()['#name'];
$fieldset_user_input = $user_input[$fieldset_name];
// Get the total number of the user input to set properly the count.
if (!empty(count($fieldset_user_input))) {
$add_button = count($fieldset_user_input) + 1;
$form_state->set('number_items_' . $fieldset_name, $add_button);
}
else {
$number_fieldset = $form_state->get('number_items_' . $fieldset_name);
$add_button = $number_fieldset + 1;
$form_state->set('number_items_' . $fieldset_name, $add_button);
}
$form_state->setRebuild();
}
}
