Blame | Last modification | View Log | Download
<?php namespace ProcessWire;/*** ProcessWire Decimal Fieldtype** Field that stores a decimal number.** For documentation about the fields used in this class, please see:* /wire/core/Fieldtype.php** ProcessWire 3.x, Copyright 2020 by Ryan Cramer* https://processwire.com**/class FieldtypeDecimal extends Fieldtype {public static function getModuleInfo() {return array('title' => __('Decimal', __FILE__),'summary' => __('Field that stores a decimal number', __FILE__),'version' => 1,);}/*** Get blank value** @param Page $page* @param Field $field* @return string**/public function getBlankValue(Page $page, Field $field) {return '';}/*** Is given value considered empty to this Fieldtype?** @param Field $field* @param mixed $value* @return bool**/public function isEmptyValue(Field $field, $value) {// when zeroNotEmpty option is set, we don't count a literal "0" is being a blank valueif($field->get('zeroNotEmpty') && strlen("$value") && !strlen(trim($value, "0.,"))) return false;return empty($value);}/*** Sanitize value** @param Page $page* @param Field $field* @param float|int|string $value* @return string**/public function sanitizeValue(Page $page, Field $field, $value) {list(/*$digits*/, $precision) = $this->getDigitsAndPrecision($field);if(!strlen("$value")) return '';$value = $this->wire()->sanitizer->float($value, array('precision' => null,'blankValue' => '','getString' => true,));if(!strlen($value)) return '';if(strpos($value, '.') !== false) {list($a, $b) = explode('.', $value, 2);if(!strlen($a)) $a = '0';while(strlen($b) < $precision) $b .= '0';$value = "$a.$b";if(strlen($b) > $precision) {$value = (string) round((float) $value, $precision);}}return $value;}/*** Get Inputfield for this Fieldtype** @param Page $page* @param Field $field* @return Inputfield**/public function getInputfield(Page $page, Field $field) {/** @var InputfieldFloat $inputfield */$inputfield = $this->wire()->modules->get('InputfieldFloat');$inputfield->set('precision', $field->get('precision'));return $inputfield;}/*** Sleep value for DB storage** @param Page $page* @param Field $field* @param string|float|int* @return string**/public function ___sleepValue(Page $page, Field $field, $value) {$value = (string) $value;if(!strlen($value)) return $value;if(!ctype_digit(str_replace('.', '', $value))) $value = $this->sanitizeValue($page, $field, $value);return $value;}/*** Get compatible Fieldtypes** @param Field $field* @return null|Fieldtypes**/public function ___getCompatibleFieldtypes(Field $field) {$fieldtypes = parent::___getCompatibleFieldtypes($field);foreach($fieldtypes as $type) {if($type instanceof FieldtypeDecimal) continue;if($type instanceof FieldtypeInteger || $type instanceof FieldtypeFloat) continue;$fieldtypes->remove($type);}return $fieldtypes;}/*** Get DB schema for this Fieldtype** @param Field $field* @return array**/public function getDatabaseSchema(Field $field) {$schema = parent::getDatabaseSchema($field);list($digits, $precision) = $this->getDigitsAndPrecision($field);$schema['data'] = "DECIMAL($digits,$precision)";return $schema;}/*** Called when Field using this Fieldtype has been saved** Check and update database schema as necessary** @param Field $field**/public function ___savedField(Field $field) {$database = $this->wire()->database;$info = $database->columnExists($field->getTable(), 'data', true);if(!$info) return;$table = $field->getTable();$schema = $this->getDatabaseSchema($field);$colType = str_replace(' ', '', strtoupper($info['Type']));$defType = str_replace(' ', '', strtoupper($schema['data']));if($colType !== $defType) {$database->exec("ALTER TABLE `$table` MODIFY `data` $defType");$this->message(sprintf($this->_('Updated field ā%1$sā schema: %2$s => %3$s'), $field->name, $colType, $defType));}}/*** Get digits and precision** @param Field $field* @return array of [ digits, precision ]**/protected function getDigitsAndPrecision(Field $field) {$digits = $field->get('digits');if($digits === null || $digits < 1) $digits = 10;$digits = (int) $digits;$precision = $field->get('precision');if($precision === null || $precision < 0) $precision = 2;$precision = (int) $precision;if($digits < $precision) $digits = $precision;if($digits > 65) $digits = 65;return array($digits, $precision);}/*** Get field configuration** @param Field $field* @return InputfieldWrapper**/public function ___getConfigInputfields(Field $field) {$inputfields = parent::___getConfigInputfields($field);list($digits, $precision) = $this->getDigitsAndPrecision($field);$example = $this->_('For example:') . ' ';/** @var InputfieldInteger $f */$f = $this->wire()->modules->get('InputfieldInteger');$f->attr('name', 'digits');$f->label = $this->_('Total number of supported digits');$f->description = $this->_('Includes digits both before and after the decimal point.');$f->val($digits);$f->inputType = 'number';$f->columnWidth = 50;$f->min = 1;$f->max = 65;$f->detail = $example . $this->_('999.99 is 5 digits total so would require a value of 5 or higher.');$inputfields->add($f);/** @var InputfieldInteger $f */$f = $this->wire()->modules->get('InputfieldInteger');$f->attr('name', 'precision');$f->label = $this->_('Number of digits present after decimal');$f->description = $this->_('This value will be less than (or in some cases equal to) the total number of digits.');$f->detail = $example . $this->_('999.99 and 100.00 have 2 digits after the decimal');$f->val((int) $precision);$f->inputType = 'number';$f->columnWidth = 50;$f->min = 0;$f->max = 65;$inputfields->add($f);/** @var FieldtypeInteger $ft */$ft = $this->wire()->fieldtypes->FieldtypeInteger;// use the same 'zeroNotEmpty' setting as FieldtypeInteger$f = $ft->___getConfigInputfields($field)->getChildByName('zeroNotEmpty');if($f) $inputfields->add($f);return $inputfields;}}