Passed
Push — main ( 08974e...644444 )
by Thierry
02:28
created

QueryFacade::getInsertQuery()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
c 0
b 0
f 0
nc 2
nop 3
dl 0
loc 11
rs 10
1
<?php
2
3
namespace Lagdo\DbAdmin\Db\Driver\Facades;
4
5
use Lagdo\DbAdmin\Db\Page\Dml\DataFieldInput;
6
use Lagdo\DbAdmin\Db\Page\Dml\DataFieldValue;
7
use Lagdo\DbAdmin\Db\Page\Dml\DataRowReader;
8
use Lagdo\DbAdmin\Db\Page\Dml\DataRowWriter;
9
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity;
10
11
use function count;
12
13
/**
14
 * Facade to table query functions
15
 */
16
class QueryFacade extends AbstractFacade
17
{
18
    /**
19
     * @var string
20
     * read => edit action for single row insert, update or delete
21
     * save => save action for insert, update or delete
22
     * select => edit action for bulk update
23
     * clone => clone a selected set of data rows
24
     */
25
    private string $action;
26
27
    /**
28
     * @var string
29
     */
30
    private string $operation;
31
32
    /**
33
     * @return DataRowWriter
34
     */
35
    private function writer(): DataRowWriter
36
    {
37
        $fieldValue = new DataFieldValue($this->page, $this->driver,
0 ignored issues
show
Bug introduced by
It seems like $this->driver can also be of type null; however, parameter $driver of Lagdo\DbAdmin\Db\Page\Dm...eldValue::__construct() does only seem to accept Lagdo\DbAdmin\Driver\DriverInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

37
        $fieldValue = new DataFieldValue($this->page, /** @scrutinizer ignore-type */ $this->driver,
Loading history...
38
            $this->utils, $this->action, $this->operation);
39
        $fieldInput = new DataFieldInput($this->page, $this->driver,
0 ignored issues
show
Bug introduced by
It seems like $this->driver can also be of type null; however, parameter $driver of Lagdo\DbAdmin\Db\Page\Dm...eldInput::__construct() does only seem to accept Lagdo\DbAdmin\Driver\DriverInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

39
        $fieldInput = new DataFieldInput($this->page, /** @scrutinizer ignore-type */ $this->driver,
Loading history...
40
            $this->utils, $this->action, $this->operation);
41
        return new DataRowWriter($this->page, $this->driver, $this->utils,
0 ignored issues
show
Bug introduced by
It seems like $this->driver can also be of type null; however, parameter $driver of Lagdo\DbAdmin\Db\Page\Dm...owWriter::__construct() does only seem to accept Lagdo\DbAdmin\Driver\DriverInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

41
        return new DataRowWriter($this->page, /** @scrutinizer ignore-type */ $this->driver, $this->utils,
Loading history...
42
            $this->action, $this->operation, $fieldValue, $fieldInput);
43
    }
44
45
    /**
46
     * @return DataRowReader
47
     */
48
    private function reader(): DataRowReader
49
    {
50
        return new DataRowReader($this->page, $this->driver, $this->utils);
0 ignored issues
show
Bug introduced by
It seems like $this->driver can also be of type null; however, parameter $driver of Lagdo\DbAdmin\Db\Page\Dm...owReader::__construct() does only seem to accept Lagdo\DbAdmin\Driver\DriverInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

50
        return new DataRowReader($this->page, /** @scrutinizer ignore-type */ $this->driver, $this->utils);
Loading history...
51
    }
52
53
    /**
54
     * Get the table fields
55
     *
56
     * @param string $table         The table name
57
     * @param array  $options       The query options
58
     *
59
     * @return array
60
     */
61
    private function getFields(string $table, array $options): array
62
    {
63
        // From edit.inc.php
64
        $fields = $this->driver->fields($table);
0 ignored issues
show
Bug introduced by
The method fields() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

64
        /** @scrutinizer ignore-call */ 
65
        $fields = $this->driver->fields($table);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
65
        // Important: get the where clauses before filtering the fields.
66
        $where = $this->operation === 'insert' ? [] :
67
            $this->driver->where($options, $fields);
68
        // Remove fields without the required privilege, or that cannot be edited.
69
        $fields = array_filter($fields, fn(TableFieldEntity $field) =>
70
            isset($field->privileges[$this->operation]) &&
71
            $this->page->fieldName($field) !== '' && !$field->generated);
72
73
        return [$fields, $where];
74
    }
75
76
    /**
77
     * Get data for insert in a table
78
     *
79
     * @param string $table         The table name
80
     * @param array  $options       The query options
81
     *
82
     * @return array
83
     */
84
    public function getInsertData(string $table, array $options = []): array
85
    {
86
        $this->action = 'read';
87
        $this->operation = 'insert';
88
89
        [$fields,] = $this->getFields($table, $options);
90
        if (empty($fields)) {
91
            return [
92
                'error' => $this->utils->trans->lang('You have no privileges to update this table.'),
93
            ];
94
        }
95
96
        // No data when inserting a new row
97
        return [
98
            'fields' => $this->writer()->getInputValues($fields, $options),
99
        ];
100
    }
101
102
    /**
103
     * @param array<TableFieldEntity> $fields
104
     *
105
     * @return array
106
     */
107
    private function getRowSelectClauses(array $fields): array
108
    {
109
        // if (!$this->driver->support("table")) {
110
        //     return ["*"];
111
        // }
112
113
        // From edit.inc.php
114
        $select = [];
115
        foreach ($fields as $name => $field) {
116
            if (isset($field->privileges["select"])) {
117
                $as = $this->action === 'clone' && $field->autoIncrement ? "''" :
118
                    $this->driver->convertField($field);
119
                $select[] = ($as ? "$as AS " : "") . $this->driver->escapeId($name);
120
            }
121
        }
122
        return $select;
123
    }
124
125
    /**
126
     * Get data for update/delete of a single row.
127
     *
128
     * @param string $table         The table name
129
     * @param array  $options       The query options
130
     *
131
     * @return array
132
     */
133
    public function getUpdateData(string $table, array $options = []): array
134
    {
135
        $this->action = 'read';
136
        $this->operation = 'update';
137
138
        // From edit.inc.php
139
        [$fields, $where] = $this->getFields($table, $options);
140
        if (empty($fields) || !$where) {
141
            return [
142
                'error' => $this->utils->trans->lang('You have no privileges to update this table.'),
143
            ];
144
        }
145
146
        // From edit.inc.php
147
        $select = $this->getRowSelectClauses($fields);
148
        if (count($select) === 0) {
149
            return [
150
                'error' => $this->utils->trans->lang('Unable to find the edited data row.'),
151
            ]; // No data
152
        }
153
154
        $statement = $this->driver->select($table, $select, [$where],
155
            $select, [], $this->action === 'select' ? 2 : 1);
156
        if (!$statement) {
157
            return [
158
                'error' => $this->driver->error(),
159
            ]; // Error
160
        }
161
162
        $rowData = $statement->fetchAssoc();
163
        if($this->action === 'select' && (!$rowData || $statement->fetchAssoc()))
164
        {
165
            // $statement->rowCount() != 1 isn't available in all drivers
166
            return [
167
                'error' => $this->utils->trans->lang('Unable to find the edited data row.'),
168
            ]; // No data
169
        }
170
171
        return [
172
            'fields' => $this->writer()->getInputValues($fields, $rowData),
173
        ];
174
    }
175
176
    /**
177
     * Build the SQL query to insert a new item in a table
178
     *
179
     * @param string $table         The table name
180
     * @param array  $options       The query options
181
     * @param array  $values        The updated values
182
     *
183
     * @return array
184
     */
185
    public function getInsertQuery(string $table, array $options, array $values): array
186
    {
187
        $this->action = 'save';
188
        $this->operation = 'insert';
189
190
        [$fields,] = $this->getFields($table, $options);
191
        $values = $this->reader()->getInputValues($fields, $values);
192
193
        $query = $this->driver->getInsertQuery($table, $values);
194
        return $query !== '' ? ['query' => $query] : [
195
            'error' => $this->utils->trans->lang('Unable to build the SQL code for this insert query.'),
196
        ];
197
    }
198
199
    /**
200
     * Insert a new item in a table
201
     *
202
     * @param string $table         The table name
203
     * @param array  $options       The query options
204
     * @param array  $values        The updated values
205
     *
206
     * @return array
207
     */
208
    public function insertItem(string $table, array $options, array $values): array
209
    {
210
        $this->action = 'save';
211
        $this->operation = 'insert';
212
213
        [$fields,] = $this->getFields($table, $options);
214
        $values = $this->reader()->getInputValues($fields, $values);
215
216
        if (!$this->driver->insert($table, $values)) {
217
            return [
218
                'error' => $this->driver->error(),
219
            ];
220
        }
221
222
        $lastId = $this->driver->lastAutoIncrementId();
223
        return [
224
            'message' => $this->utils->trans->lang('Item%s has been inserted.',
225
                $lastId ? " $lastId" : ''),
226
        ];
227
    }
228
229
    /**
230
     * @param string $table
231
     * @param array $options
232
     *
233
     * @return int
234
     */
235
    private function getQueryLimit(string $table, array $options): int
236
    {
237
        // From edit.inc.php
238
        $indexes = $this->driver->indexes($table);
239
        $uniqueIds = $this->utils->uniqueIds($options['where'], $indexes);
240
        return count($uniqueIds ?? []) === 0 ? 1 : 0; // Limit to 1 if no unique ids are found.
0 ignored issues
show
Bug introduced by
It seems like $uniqueIds ?? array() can also be of type null; however, parameter $value of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

240
        return count(/** @scrutinizer ignore-type */ $uniqueIds ?? []) === 0 ? 1 : 0; // Limit to 1 if no unique ids are found.
Loading history...
241
    }
242
243
    /**
244
     * Build the SQL query to update one or more items in a table
245
     *
246
     * @param string $table         The table name
247
     * @param array  $options       The query options
248
     * @param array  $values        The updated values
249
     *
250
     * @return array
251
     */
252
    public function getUpdateQuery(string $table, array $options, array $values): array
253
    {
254
        $this->action = 'save';
255
        $this->operation = 'update';
256
257
        [$fields, $where] = $this->getFields($table, $options);
258
        $values = $this->reader()->getInputValues($fields, $values);
259
        $limit = $this->getQueryLimit($table, $options);
260
261
        $query = $this->driver->getUpdateQuery($table, $values, "\nWHERE $where", $limit);
262
        return $query !== '' ? ['query' => $query] : [
263
            'error' => $this->utils->trans->lang('Unable to build the SQL code for this insert query.'),
264
        ];
265
    }
266
267
    /**
268
     * Update one or more items in a table
269
     *
270
     * @param string $table         The table name
271
     * @param array  $options       The query options
272
     * @param array  $values        The updated values
273
     *
274
     * @return array
275
     */
276
    public function updateItem(string $table, array $options, array $values): array
277
    {
278
        $this->action = 'save';
279
        $this->operation = 'update';
280
281
        [$fields, $where] = $this->getFields($table, $options);
282
        $values = $this->reader()->getInputValues($fields, $values);
283
        $limit = $this->getQueryLimit($table, $options);
284
285
        if (!$this->driver->update($table, $values, "\nWHERE $where", $limit)) {
286
            return [
287
                'error' => $this->driver->error(),
288
            ];
289
        }
290
291
        // Get the modified data
292
        // Todo: check if the values in the where clause are changed.
293
        $statement = $this->driver->select($table, array_keys($values), [$where]);
294
        $result = !$statement ? null : $statement->fetchAssoc();
295
        if (!$result) {
296
            return [
297
                'warning' => $this->utils->trans->lang('Unable to read the updated row.'),
298
            ];
299
        }
300
301
        return [
302
            'cols' => $this->writer()->getUpdatedRow($result, $fields, $options),
303
            'message' => $this->utils->trans->lang('Item has been updated.'),
304
        ];
305
    }
306
307
    /**
308
     * Build the SQL query to delete one or more items in a table
309
     *
310
     * @param string $table         The table name
311
     * @param array  $options       The query options
312
     *
313
     * @return array
314
     */
315
    public function getDeleteQuery(string $table, array $options): array
316
    {
317
        $this->action = 'save';
318
        $this->operation = 'update';
319
320
        [, $where] = $this->getFields($table, $options);
321
        $limit = $this->getQueryLimit($table, $options);
322
323
        $query = $this->driver->getDeleteQuery($table, "\nWHERE $where", $limit);
324
        return $query !== '' ? ['query' => $query] : [
325
            'error' => $this->utils->trans->lang('Unable to build the SQL code for this insert query.'),
326
        ];
327
    }
328
329
    /**
330
     * Delete one or more items in a table
331
     *
332
     * @param string $table         The table name
333
     * @param array  $options       The query options
334
     *
335
     * @return array
336
     */
337
    public function deleteItem(string $table, array $options): array
338
    {
339
        $this->action = 'save';
340
        $this->operation = 'update';
341
342
        [, $where] = $this->getFields($table, $options);
343
        $limit = $this->getQueryLimit($table, $options);
344
345
        if (!$this->driver->delete($table, "\nWHERE $where", $limit)) {
346
            return [
347
                'error' => $this->driver->error(),
348
            ];
349
        }
350
351
        return [
352
            'message' => $this->utils->trans->lang('Item has been deleted.'),
353
        ];
354
    }
355
}
356