164 lines
4.7 KiB
Text
164 lines
4.7 KiB
Text
|
<?php namespace ProcessWire;
|
||
|
|
||
|
/**
|
||
|
* Inputfield for floating point numbers
|
||
|
*
|
||
|
* ProcessWire 3.x, Copyright 2020 by Ryan Cramer
|
||
|
* https://processwire.com
|
||
|
*
|
||
|
* @property int $precision Decimals precision
|
||
|
* @property int $digits Total digits, for when used in decimal mode (default=0)
|
||
|
* @property string $inputType Input type to use, one of "text" or "number"
|
||
|
* @property int|float $min
|
||
|
* @property int|float $max
|
||
|
* @property int|float|string $step
|
||
|
* @property int $size
|
||
|
* @property string $placeholder
|
||
|
* @property int|float $initValue Initial/default value (when used as independent Inputfield)
|
||
|
* @property int|float|string $defaultValue Initial/default value (when used with FieldtypeInteger)
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
class InputfieldFloat extends InputfieldInteger {
|
||
|
|
||
|
public static function getModuleInfo() {
|
||
|
return array(
|
||
|
'title' => __('Float', __FILE__), // Module Title
|
||
|
'summary' => __('Floating point number with precision', __FILE__), // Module Summary
|
||
|
'version' => 104,
|
||
|
'permanent' => true,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Construct
|
||
|
*
|
||
|
*/
|
||
|
public function __construct() {
|
||
|
$this->set('precision', 2);
|
||
|
$this->set('digits', 0);
|
||
|
parent::__construct();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Module init
|
||
|
*
|
||
|
*/
|
||
|
public function init() {
|
||
|
parent::init();
|
||
|
$this->attr('step', 'any'); // HTML5 attr required to support decimals with 'number' types
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get configured precision setting, or if given a value, precision of the value
|
||
|
*
|
||
|
* @param float|string|null $value
|
||
|
* @return int|string Returns integer of precision or blank string if none defined
|
||
|
*
|
||
|
*/
|
||
|
protected function getPrecision($value = null) {
|
||
|
if($value !== null) return FieldtypeFloat::getPrecision($value);
|
||
|
$precision = $this->precision;
|
||
|
return $precision === null || $precision === '' ? '' : (int) $precision;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sanitize value
|
||
|
*
|
||
|
* @param float|string $value
|
||
|
* @return float|string
|
||
|
*
|
||
|
*/
|
||
|
protected function sanitizeValue($value) {
|
||
|
if(!strlen("$value")) return '';
|
||
|
if($this->digits > 0) {
|
||
|
return is_numeric("$value") ? (string) $value : '';
|
||
|
} else if(!is_float($value) && !is_int($value)) {
|
||
|
$value = $this->wire()->sanitizer->float($value, array('blankValue' => ''));
|
||
|
if(!strlen("$value")) return '';
|
||
|
}
|
||
|
$precision = $this->precision;
|
||
|
if($precision === null || $precision === '') {
|
||
|
$precision = FieldtypeFloat::getPrecision($value);
|
||
|
}
|
||
|
return round((float) $value, $precision);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Typecast value to float, override from InputfieldInteger
|
||
|
*
|
||
|
* @param string|int|float $value
|
||
|
* @return int
|
||
|
*
|
||
|
*/
|
||
|
protected function typeValue($value) {
|
||
|
return (float) $value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Override method from Inputfield to convert locale specific decimals for input[type=number]
|
||
|
*
|
||
|
* @param array $attributes
|
||
|
* @return string
|
||
|
*
|
||
|
*/
|
||
|
public function getAttributesString(array $attributes = null) {
|
||
|
if($attributes && $attributes['type'] === 'number') {
|
||
|
$value = isset($attributes['value']) ? $attributes['value'] : null;
|
||
|
if(is_float($value) || is_string($value)) {
|
||
|
if(strlen("$value") && !ctype_digit(str_replace('.', '', ltrim($value, '-')))) {
|
||
|
// float value is using a non "." as decimal point, needs conversion because
|
||
|
// the HTML5 number input type requires "." as the decimal
|
||
|
$attributes['value'] = $this->localeConvertValue($value);
|
||
|
}
|
||
|
}
|
||
|
$precision = (int) $this->precision;
|
||
|
if($precision > 0 && (empty($attributes['step']) || $attributes['step'] === 'any')) {
|
||
|
$attributes['step'] = '.' . ($precision > 1 ? str_repeat('0', $precision - 1) : '') . '1';
|
||
|
}
|
||
|
}
|
||
|
return parent::getAttributesString($attributes);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Convert floats with non "." decimal points to use "." decimal point according to locale
|
||
|
*
|
||
|
* @param float|string $value
|
||
|
* @return string|float Returns string representation of float when value was converted
|
||
|
*
|
||
|
*/
|
||
|
protected function localeConvertValue($value) {
|
||
|
if(!strlen("$value")) return $value;
|
||
|
if(ctype_digit(str_replace('.', '', ltrim($value, '-')))) return $value;
|
||
|
$locale = localeconv();
|
||
|
$decimal = $locale['decimal_point'];
|
||
|
if($decimal === '.' || strpos($value, $decimal) === false) return $value;
|
||
|
$parts = explode($decimal, $value, 2);
|
||
|
$value = implode('.', $parts);
|
||
|
return $value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Inputfield config
|
||
|
*
|
||
|
* @return InputfieldWrapper
|
||
|
*
|
||
|
*/
|
||
|
public function getConfigInputfields() {
|
||
|
$inputfields = parent::getConfigInputfields();
|
||
|
if($this->hasFieldtype === false) {
|
||
|
/** @var InputfieldInteger $f */
|
||
|
$f = $this->wire()->modules->get('InputfieldInteger');
|
||
|
$f->attr('name', 'precision');
|
||
|
$f->label = $this->_('Number of decimal digits to round to');
|
||
|
$f->attr('value', $this->precision);
|
||
|
$f->attr('size', 8);
|
||
|
$inputfields->add($f);
|
||
|
} else {
|
||
|
// precision is configured with FieldtypeFloat
|
||
|
}
|
||
|
return $inputfields;
|
||
|
}
|
||
|
|
||
|
}
|