1<?php
2/**
3 * DokuWiki Plugin structprogress
4 * Most Code is taken from decimal Type: https://github.com/cosmocode/dokuwiki-plugin-struct/blob/5c37a46b990a9bc0e314c8faa228db6012387b5f/types/Decimal.php
5 *
6 * @author: saggi <saggi@gmx.de>
7 */
8
9namespace dokuwiki\plugin\structnumber\types;
10
11use dokuwiki\plugin\struct\meta\QueryBuilder;
12use dokuwiki\plugin\struct\meta\QueryBuilderWhere;
13use dokuwiki\plugin\struct\meta\ValidationException;
14use dokuwiki\plugin\struct\types\AbstractMultiBaseType;
15
16/**
17 * Class Number
18 *
19 * A field accepting decimal numbers
20 *
21 */
22class Number extends AbstractMultiBaseType
23{
24    protected $config = array(
25        'min' => '',
26        'max' => '',
27        'format' => "%01.2f",
28        'prefix' => '',
29        'postfix' => ''
30    );
31
32    /**
33     * Output the stored data
34     *
35     * @param string|int $value the value stored in the database
36     * @param \Doku_Renderer $R the renderer currently used to render the data
37     * @param string $mode The mode the output is rendered in (eg. XHTML)
38     * @return bool true if $mode could be satisfied
39     */
40    public function renderValue($value, \Doku_Renderer $R, $mode)
41    {
42        $value = $this->checkFormat($value, $this->config['format']);
43
44        $R->cdata($this->config['prefix'] . $value . $this->config['postfix']);
45        return true;
46    }
47
48    /**
49     * @param int|string $rawvalue
50     * @return int|string
51     * @throws ValidationException
52     */
53    public function validate($rawvalue)
54    {
55        $rawvalue = parent::validate($rawvalue);
56        $rawvalue = str_replace(',', '.', $rawvalue); // we accept both
57
58        if ((string)$rawvalue != (string)floatval($rawvalue)) {
59            throw new ValidationException('Decimal needed');
60        }
61
62        if ($this->config['min'] !== '' && floatval($rawvalue) < floatval($this->config['min'])) {
63            throw new ValidationException('Decimal min', floatval($this->config['min']));
64        }
65
66        if ($this->config['max'] !== '' && floatval($rawvalue) > floatval($this->config['max'])) {
67            throw new ValidationException('Decimal max', floatval($this->config['max']));
68        }
69
70        return $rawvalue;
71    }
72
73    /**
74     * Check the format config
75     *
76     * @param $number
77     * @param $format
78     * @return string
79     */
80    protected function checkFormat($number, $format)
81    {
82        // heck if its a valid sprintf format
83        if (preg_match("/^%(?:['+-:\.]?\D?\d*\.?\d*)?[bdeEfFu]$/",$format)===0) {
84            $format = '%01.2f';
85        }
86        return sprintf($format, $number);
87    }
88
89    /**
90     * Decimals need to be casted to the proper type for sorting
91     *
92     * @param QueryBuilder $QB
93     * @param string $tablealias
94     * @param string $colname
95     * @param string $order
96     */
97    public function sort(QueryBuilder $QB, $tablealias, $colname, $order)
98    {
99        $QB->addOrderBy("CAST($tablealias.$colname AS DECIMAL) $order");
100    }
101
102    /**
103     * Decimals need to be casted to proper type for comparison
104     *
105     * @param QueryBuilderWhere $add
106     * @param string $tablealias
107     * @param string $colname
108     * @param string $comp
109     * @param string|\string[] $value
110     * @param string $op
111     */
112    public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op)
113    {
114        $add = $add->where($op); // open a subgroup
115        $add->where('AND', "$tablealias.$colname != ''"); // make sure the field isn't empty
116        $op = 'AND';
117
118        /** @var QueryBuilderWhere $add Where additionional queries are added to */
119        if (is_array($value)) {
120            $add = $add->where($op); // sub where group
121            $op = 'OR';
122        }
123
124        foreach ((array)$value as $item) {
125            $pl = $add->getQB()->addValue($item);
126            $add->where($op, "CAST($tablealias.$colname AS DECIMAL) $comp CAST($pl AS DECIMAL)");
127        }
128    }
129
130    /**
131     * Only exact matches for numbers
132     *
133     * @return string
134     */
135    public function getDefaultComparator()
136    {
137        return '=';
138    }
139}
140