datafield-1.x-dev/src/Plugin/DataField/FieldWidget/NumberFormatWidget.php
src/Plugin/DataField/FieldWidget/NumberFormatWidget.php
<?php
namespace Drupal\datafield\Plugin\DataField\FieldWidget;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Field\Attribute\FieldWidget;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'number format' widget.
*/
#[FieldWidget(
id: 'number_format',
label: new TranslatableMarkup('Number format'),
field_types: ['integer', 'numeric', 'float'],
)]
class NumberFormatWidget extends NumberWidget implements ContainerFactoryPluginInterface {
use StringTranslationTrait;
/**
* Constructs a EntityReferenceAutocompleteWidget object.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param mixed $field_definition
* The definition of the field to which the formatter is associated.
* @param \Drupal\Core\Session\AccountInterface $currentUser
* The current user.
*/
public function __construct($plugin_id, $plugin_definition, $field_definition, protected AccountInterface $currentUser) {
unset($plugin_id, $plugin_definition, $field_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration,
$container->get('current_user'),
);
}
/**
* {@inheritdoc}
*/
public function getFormElement(&$element, $item = NULL, $setting = []) {
parent::getFormElement($element, $item, $setting);
// Add validate process width numeric field.
$element['#element_validate'] = [[$this, 'validate']];
$limit = $this->getLimitNumber($element['#field_storage'], $element['#field_storage']['type'], !empty($element['#field_storage']['unsigned']));
$min = $limit ? $limit['min'] : $element['#field_settings']['min'];
if ($element['#field_settings']['min'] && $limit['min']) {
$min = max($element['#field_settings']['min'], $limit['min']);
}
if ($min !== NULL) {
$element['#attributes']['min'] = $min;
}
$max = $limit ? $limit['max'] : $element['#field_settings']['max'];
if ($element['#field_settings']['max'] && $limit['min']) {
$max = min($element['#field_settings']['max'], $limit['max']);
}
if ($max !== NULL) {
$element['#attributes']['max'] = $max;
}
if (array_key_exists('scale', $element['#field_storage'])) {
$element['#attributes']['scale'] = $element['#field_storage']['scale'];
}
$element['#attributes']['field_type'] = $element['#field_storage']['type'];
$element['#type'] = 'textfield';
$element['#attributes']['class'][] = 'text-format-number';
$element['#attached']['library'][] = 'datafield/auto_numeric';
$element['#attached']['library'][] = 'datafield/formatnumber';
$element['#attached']['drupalSettings']['auto_numeric']['language'] = $this->getLanguage();
return $element;
}
/**
* {@inheritdoc}
*/
protected function getLanguage(): string {
$languageId = $this->currentUser->getPreferredLangcode();
$language = 'British';
$formatNum = [
'French' => ['fr', 'af', 'ar', 'be', 'bg', 'br', 'cs', 'eo', 'et', 'fi',
'ht', 'hu', 'hy', 'ka', 'kk', 'ky', 'lt', 'lv', 'mg', 'nb', 'nn', 'oc',
'os', 'pl', 'prs', 'ps', 'pt', 'pt-pt', 'ru', 'rw', 'se', 'sk', 'sq',
'sv', 'tyv', 'uk',
],
'Spanish' => ['de', 'ast', 'az', 'bs', 'ca', 'da', 'el', 'es', 'eu', 'fo',
'fy', 'gl', 'hr', 'id', 'is', 'it', 'jv', 'ku', 'mk', 'nl', 'pt-br',
'ro', 'sl', 'sr', 'tr', 'vi',
],
];
foreach ($formatNum as $lang => $languageIso) {
if (in_array($languageId, $languageIso)) {
$language = $lang;
break;
}
}
return $language;
}
/**
* {@inheritdoc}
*/
protected function getLimitNumber($field_settings, $field_type, $unsigned = FALSE) {
$int_limits = [
'signed' => [
'big' => ['min' => PHP_INT_MIN, 'max' => PHP_INT_MAX],
'normal' => ['min' => -2147483648, 'max' => 2147483647],
'medium' => ['min' => -8388608, 'max' => 8388607],
'small' => ['min' => -32768, 'max' => 32767],
'tiny' => ['min' => -128, 'max' => 127],
],
'unsigned' => [
'big' => ['min' => 0, 'max' => 18446744073709551615],
'normal' => ['min' => 0, 'max' => 4294967295],
'medium' => ['min' => 0, 'max' => 16777215],
'small' => ['min' => 0, 'max' => 65535],
'tiny' => ['min' => 0, 'max' => 255],
],
];
if ($field_type === 'int' || $field_type === 'integer') {
return $int_limits[$unsigned ? 'unsigned' : 'signed'][$field_settings['size']] ?? FALSE;
}
if (in_array($field_type, ['float', 'numeric'], TRUE) && isset($field_settings['precision'], $field_settings['scale'])) {
$value_allow = str_repeat('9', $field_settings['precision'] - $field_settings['scale']) . '.' . str_repeat('9', $field_settings['scale']);
return ['min' => "-$value_allow", 'max' => $value_allow];
}
return FALSE;
}
/**
* Validate the numeric field.
*/
public function validate(&$element, FormStateInterface &$form_state, &$complete_form): void {
$limit = $this->getLimitNumber($element['#field_storage'], $element['#field_storage']['type'], !empty($element['#field_storage']['unsigned']));
$value = $element['#value'];
$user_input = $form_state->getUserInput();
$lang = $this->getLanguage();
switch ($lang) {
case 'Spanish':
case 'French':
$separator = '.';
$decimal_character = ',';
$value = str_replace('.', '', $value);
$value = str_replace(',', '.', $value);
break;
}
$value = preg_replace('/(?!^-)[^\d.]/', '', $value);
if ($value !== '' && $limit) {
if ($value < $limit['min'] || $value > $limit['max']) {
$number_format = new \NumberFormatter($lang, \NumberFormatter::DECIMAL);
$number_format->setSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL, $separator ?? ',');
$number_format->setSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL, $decimal_character ?? '.');
if ($value < $limit['min']) {
$arg = [
'@field' => $element['#field_settings']['label'],
'@limit_value' => $number_format->format($limit['min']),
];
}
else {
$arg = [
'@field' => $element['#field_settings']['label'],
'@limit_value' => $number_format->format($limit['max']),
];
}
$form_state->setError($element, $this->t('@field numeric value out of range @limit_value.', $arg));
return;
}
}
$element['#value'] = $value;
$form_state->setValueForElement($element, $value);
if (!empty($element['#parents']) && count($element['#parents']) > 1) {
NestedArray::setValue($user_input, $element['#parents'], $value);
}
else {
$user_input[$element['#field_name']][$element['#delta']][$element['#machine_name']] = $value;
}
$form_state->setUserInput($user_input);
}
}
