Problem/Motivation
- Create decimal field, set precision to 10 (minimum in the UI and scale to 4
- Saving new node with value 19999.0000 succeeds (precision is 5+4 = 9).
- Saving new node with value 99999.0000 fails (same precision as above).
or
- Create decimal field, set scale to anything over 6 (need 8 to store bitcoin values for example). I used 16 as precission.
- Saving new node with value 20.12345678 fails validation while 0.1234567 succeeds.
This is because Drupal\Component\Utility\Number::validStep() returns false. Detailed investigation on this function (which is only used once in Drupal), reveals that the first argument ($value) is received as a string, but $step and $offset are floats. PHP seems to slightly mangle the $step value on the first case above from 0.0001 to 0.00010000000000000001438. Passing the step parameter as a string in the case of decimal numbers maintains the correct precision, and allows a correct approximation calculation.
Proposed resolution
Bypass weak PHP floating-point handling by passing the step as a string.
Remaining tasks
Merge. Commit. Decimals FTW!
User interface changes
None.
API changes
A new field was added to the Number FormElement, in order to decide when to use the workaround. This "#number_type" field may be useful in other cases.
Data model changes
None.
Possible workaround if you need big decimals
Disable this validation by setting the render element #step
to 'any'
i.e.
$element['#step'] = 'any';
In the case of fields, you can do
function MYMODULE_form_FORM_WITH_BIG_DECIMAL_FIELD_alter (array &$form, FormStateInterface $form_state) {
$form['field_some']['widget'][0]['value']['#step']='any';
}
And if you want to target all decimal fields:
/**
* Prevents validation of decimal numbers
* @see https://www.drupal.org/node/2230909
*/
function MYMODULE_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
$field_definition = $context['items']->getFieldDefinition();
if ($field_definition->getType() == 'decimal') {
$element['value']['#step'] = 'any';
}
}