1<?php 2namespace dokuwiki\plugin\struct\types; 3 4use dokuwiki\plugin\struct\meta\QueryBuilder; 5use dokuwiki\plugin\struct\meta\QueryBuilderWhere; 6use dokuwiki\plugin\struct\meta\ValidationException; 7 8/** 9 * Class Decimal 10 * 11 * A field accepting decimal numbers 12 * 13 * @package dokuwiki\plugin\struct\types 14 */ 15class Decimal extends AbstractMultiBaseType { 16 17 protected $config = array( 18 'min' => '', 19 'max' => '', 20 'roundto' => '-1', 21 'decpoint' => '.', 22 'thousands' => "\xE2\x80\xAF", // narrow no-break space 23 'trimzeros' => true, 24 'prefix' => '', 25 'postfix' => '' 26 ); 27 28 /** 29 * Output the stored data 30 * 31 * @param string|int $value the value stored in the database 32 * @param \Doku_Renderer $R the renderer currently used to render the data 33 * @param string $mode The mode the output is rendered in (eg. XHTML) 34 * @return bool true if $mode could be satisfied 35 */ 36 public function renderValue($value, \Doku_Renderer $R, $mode) { 37 if($this->config['roundto'] == -1) { 38 $value = $this->formatWithoutRounding( 39 $value, 40 $this->config['decpoint'], 41 $this->config['thousands'] 42 ); 43 } else { 44 $value = floatval($value); 45 $value = number_format( 46 $value, 47 $this->config['roundto'], 48 $this->config['decpoint'], 49 $this->config['thousands'] 50 ); 51 } 52 if($this->config['trimzeros'] && (strpos($value, $this->config['decpoint']) !== false)) { 53 $value = rtrim($value, '0'); 54 $value = rtrim($value, $this->config['decpoint']); 55 } 56 57 $R->cdata($this->config['prefix'] . $value . $this->config['postfix']); 58 return true; 59 } 60 61 /** 62 * @param int|string $rawvalue 63 * @return int|string 64 * @throws ValidationException 65 */ 66 public function validate($rawvalue) { 67 $rawvalue = parent::validate($rawvalue); 68 $rawvalue = str_replace(',', '.', $rawvalue); // we accept both 69 70 if((string) $rawvalue != (string) floatval($rawvalue)) { 71 throw new ValidationException('Decimal needed'); 72 } 73 74 if($this->config['min'] !== '' && floatval($rawvalue) <= floatval($this->config['min'])) { 75 throw new ValidationException('Decimal min', floatval($this->config['min'])); 76 } 77 78 if($this->config['max'] !== '' && floatval($rawvalue) >= floatval($this->config['max'])) { 79 throw new ValidationException('Decimal max', floatval($this->config['max'])); 80 } 81 82 return $rawvalue; 83 } 84 85 /** 86 * Works like number_format but keeps the decimals as is 87 * 88 * @link http://php.net/manual/en/function.number-format.php#91047 89 * @author info at daniel-marschall dot de 90 * @param float $number 91 * @param string $dec_point 92 * @param string $thousands_sep 93 * @return string 94 */ 95 function formatWithoutRounding($number, $dec_point, $thousands_sep) { 96 $was_neg = $number < 0; // Because +0 == -0 97 98 $tmp = explode('.', $number); 99 $out = number_format(abs(floatval($tmp[0])), 0, $dec_point, $thousands_sep); 100 if(isset($tmp[1])) $out .= $dec_point . $tmp[1]; 101 102 if($was_neg) $out = "-$out"; 103 104 return $out; 105 } 106 107 /** 108 * Decimals need to be casted to the proper type for sorting 109 * 110 * @param QueryBuilder $QB 111 * @param string $tablealias 112 * @param string $colname 113 * @param string $order 114 */ 115 public function sort(QueryBuilder $QB, $tablealias, $colname, $order) { 116 $QB->addOrderBy("CAST($tablealias.$colname AS DECIMAL) $order"); 117 } 118 119 /** 120 * Decimals need to be casted to proper type for comparison 121 * 122 * @param QueryBuilderWhere $add 123 * @param string $tablealias 124 * @param string $colname 125 * @param string $comp 126 * @param string|\string[] $value 127 * @param string $op 128 */ 129 public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) { 130 /** @var QueryBuilderWhere $add Where additionional queries are added to*/ 131 if(is_array($value)) { 132 $add = $add->where($op); // sub where group 133 $op = 'OR'; 134 } 135 foreach((array) $value as $item) { 136 $pl = $add->getQB()->addValue($item); 137 $add->where($op, "CAST($tablealias.$colname AS DECIMAL) $comp CAST($pl AS DECIMAL)"); 138 } 139 } 140 141 142 143} 144