SelectResult::getColumnValue()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
nc 1
nop 3
dl 0
loc 6
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace Lagdo\DbAdmin\Db\Page\Dql;
4
5
use Lagdo\DbAdmin\Db\Page\AppPage;
6
use Lagdo\DbAdmin\Driver\DriverInterface;
7
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity;
8
use Lagdo\DbAdmin\Driver\Utils\Utils;
9
10
use function array_map;
11
use function current;
12
use function in_array;
13
use function is_string;
14
use function key;
15
use function md5;
16
use function next;
17
use function preg_match;
18
use function strlen;
19
use function strpos;
20
use function trim;
21
22
/**
23
 * Prepare the results of a select query for the frontend.
24
 */
25
class SelectResult
26
{
27
    /**
28
     * The constructor
29
     *
30
     * @param AppPage $page
31
     * @param DriverInterface $driver
32
     * @param Utils $utils
33
     */
34
    public function __construct(private AppPage $page,
35
        private DriverInterface $driver, private Utils $utils)
36
    {}
37
38
    /**
39
     * @param SelectEntity $selectEntity
40
     * @param string $key
41
     * @param int $position
42
     *
43
     * @return array
44
     */
45
    private function getResultHeaderItem(SelectEntity $selectEntity, string $key, int $position): array
46
    {
47
        $valueKey = key($selectEntity->select);
48
        $value = $selectEntity->queryOptions["columns"][$valueKey] ?? [];
49
50
        $fun = $value["fun"] ?? '';
51
        $fieldKey = !$selectEntity->select ? $key :
52
            ($value["col"] ?? current($selectEntity->select));
53
        $field = $selectEntity->fields[$fieldKey];
54
        $name = !$field ? ($fun ? "*" : $key) : $this->page->fieldName($field, $position);
55
56
        return [$fun, $name, $field];
57
    }
58
59
    /**
60
     * @param SelectEntity $selectEntity
61
     * @param string $key
62
     * @param int $position
63
     *
64
     * @return array
65
     */
66
    private function getResultHeader(SelectEntity $selectEntity, string $key, int $position): array
67
    {
68
        if (isset($selectEntity->unselected[$key])) {
69
            return [];
70
        }
71
72
        [$fun, $name, $field] = $this->getResultHeaderItem($selectEntity, $key, $position);
73
        $header = ['field' => $field, 'name' => $name];
74
        if ($name != "") {
75
            $selectEntity->names[$key] = $name;
76
            // $href = remove_from_uri('(order|desc)[^=]*|page') . '&order%5B0%5D=' . urlencode($key);
77
            // $desc = "&desc%5B0%5D=1";
78
            $header['column'] = $this->driver->escapeId($key);
79
            // $header['key'] = $this->utils->html($this->driver->bracketEscape($key));
80
            //! columns looking like functions
81
            $header['title'] = $this->page->applySqlFunction($fun, $name);
82
        }
83
        // $functions[$key] = $fun;
84
        next($selectEntity->select);
85
86
        return $header;
87
    }
88
89
    /**
90
     * Get the result headers from the first result row
91
     *
92
     * @param SelectEntity $selectEntity
93
     * @param array $queryFields
94
     *
95
     * @return void
96
     */
97
    public function setResultHeaders(SelectEntity $selectEntity, array $queryFields): void
98
    {
99
        // Results headers
100
        $selectEntity->headers = [];
101
        $selectEntity->names = [];
102
        // $selectEntity->functions = [];
103
        reset($selectEntity->select);
104
105
        $position = 1;
106
        foreach ($queryFields as $key) {
107
            $header = $this->getResultHeader($selectEntity, $key, $position);
108
            if ($header['name'] ?? '' !== '') {
109
                $position++;
110
            }
111
            $selectEntity->headers[] = $header;
112
        }
113
    }
114
115
    /**
116
     * @param array $rows
117
     * @param array $queryOptions
118
     *
119
     * @return array
120
     */
121
    /*private function getValuesLengths(array $rows, array $queryOptions): array
122
    {
123
        $lengths = [];
124
        if($queryOptions["modify"])
125
        {
126
            foreach($rows as $row)
127
            {
128
                foreach($row as $key => $value)
129
                {
130
                    $lengths[$key] = \max($lengths[$key], \min(40, strlen(\utf8_decode($value))));
131
                }
132
            }
133
        }
134
        return $lengths;
135
    }*/
136
137
    /**
138
     * @param SelectEntity $selectEntity
139
     * @param array $row
140
     *
141
     * @return array
142
     */
143
    private function getUniqueIds(SelectEntity $selectEntity, array $row): array
144
    {
145
        $uniqueIds = $this->utils->uniqueIds($row, $selectEntity->indexes);
146
        if (empty($uniqueIds)) {
147
            $pattern = '~^(COUNT\((\*|(DISTINCT )?`(?:[^`]|``)+`)\)' .
148
                '|(AVG|GROUP_CONCAT|MAX|MIN|SUM)\(`(?:[^`]|``)+`\))$~';
149
            foreach ($row as $key => $value) {
150
                if (!preg_match($pattern, $key)) {
151
                    //! columns looking like functions
152
                    $uniqueIds[$key] = $value;
153
                }
154
            }
155
        }
156
        return $uniqueIds;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $uniqueIds could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
157
    }
158
159
    /**
160
     * @param string $type
161
     * @param mixed $value
162
     *
163
     * @return bool
164
     */
165
    private function shouldEncodeRowId(string $type, $value): bool
166
    {
167
        return in_array($this->driver->jush(), ['sql', 'pgsql']) &&
168
            is_string($value) && strlen($value) > 64 &&
169
            preg_match('~char|text|enum|set~', $type);
170
    }
171
172
    /**
173
     * @param string $key
174
     * @param string $collation
175
     *
176
     * @return string
177
     */
178
    private function getRowIdMd5Key(string $key, string $collation): string
179
    {
180
        return $this->driver->jush() !== 'sql' ||
181
            preg_match("~^utf8~", $collation) ? $key :
182
                "CONVERT($key USING " . $this->driver->charset() . ")";
183
    }
184
185
    /**
186
     * @param SelectEntity $selectEntity
187
     * @param string $key
188
     * @param mixed $value
189
     *
190
     * @return array
191
     */
192
    private function getRowIdValue(SelectEntity $selectEntity, string $key, $value): array
193
    {
194
        $key = trim($key);
195
        $type = '';
196
        $collation = '';
197
        if (isset($selectEntity->fields[$key])) {
198
            $type = $selectEntity->fields[$key]->type;
199
            $collation = $selectEntity->fields[$key]->collation;
200
        }
201
        if ($this->shouldEncodeRowId($type, $value)) {
202
            if (!strpos($key, '(')) {
203
                //! columns looking like functions
204
                $key = $this->driver->escapeId($key);
205
            }
206
            $key = "MD5(" . $this->getRowIdMd5Key($key, $collation) . ")";
207
            $value = md5($value);
208
        }
209
        return [$this->driver->bracketEscape($key), $value];
210
    }
211
212
    /**
213
     * @param SelectEntity $selectEntity
214
     * @param array $row
215
     *
216
     * @return array
217
     */
218
    public function getRowIds(SelectEntity $selectEntity, array $row): array
219
    {
220
        $uniqueIds = $this->getUniqueIds($selectEntity, $row);
221
        // Unique identifier to edit returned data.
222
        // $unique_idf = "";
223
        $rowIds = ['where' => [], 'null' => []];
224
        foreach ($uniqueIds as $key => $value) {
225
            [$key, $value] = $this->getRowIdValue($selectEntity, $key, $value);
226
227
            // $unique_idf .= "&" . ($value !== null ? \urlencode("where[" .
228
            // $key . "]") . "=" .
229
            // \urlencode($value) : \urlencode("null[]") . "=" . \urlencode($key));
230
            if ($value === null) {
231
                $rowIds['null'][] = $key;
232
                continue;
233
            }
234
            $rowIds['where'][$key] = $value;
235
        }
236
        return $rowIds;
237
    }
238
239
    /**
240
     * @param SelectEntity $selectEntity
241
     * @param string $key
242
     * @param mixed $value
243
     *
244
     * @return array
245
     */
246
    private function getColumnValue(SelectEntity $selectEntity, string $key, $value): array
247
    {
248
        $field = $selectEntity->fields[$key] ?? new TableFieldEntity();
249
        $textLength = $selectEntity->textLength;
250
        $value = $this->driver->value($value, $field);
251
        return $this->page->getFieldValue($field, $textLength, $value);
252
    }
253
254
    /**
255
     * @param SelectEntity $selectEntity
256
     * @param array $row
257
     *
258
     * @return array
259
     */
260
    private function getRowValues(SelectEntity $selectEntity, array $row): array
261
    {
262
        $cols = [];
263
        foreach ($row as $key => $value) {
264
            if (isset($selectEntity->names[$key])) {
265
                $cols[] = $this->getColumnValue($selectEntity, $key, $value);
266
            }
267
        }
268
        return $cols;
269
    }
270
271
    /**
272
     * @param SelectEntity $selectEntity
273
     *
274
     * @return array
275
     */
276
    public function getRows(SelectEntity $selectEntity): array
277
    {
278
        return array_map(fn($row) => [
279
            // The unique identifiers to edit the result rows.
280
            'ids' => $this->getRowIds($selectEntity, $row),
281
            'cols' => $this->getRowValues($selectEntity, $row),
282
        ], $selectEntity->rows);
283
    }
284
}
285