QueryFacade::deleteItem()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 9
nc 2
nop 2
dl 0
loc 16
rs 9.9666
c 1
b 0
f 0
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
     * 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 insertItem(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
        if (!$this->driver->insert($table, $values)) {
194
            return [
195
                'error' => $this->driver->error(),
196
            ];
197
        }
198
199
        $lastId = $this->driver->lastAutoIncrementId();
200
        return [
201
            'message' => $this->utils->trans->lang('Item%s has been inserted.',
202
                $lastId ? " $lastId" : ''),
203
        ];
204
    }
205
206
    /**
207
     * @param string $table
208
     * @param array $options
209
     *
210
     * @return int
211
     */
212
    private function getQueryLimit(string $table, array $options): int
213
    {
214
        // From edit.inc.php
215
        $indexes = $this->driver->indexes($table);
216
        $uniqueIds = $this->utils->uniqueIds($options['where'], $indexes);
217
        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

217
        return count(/** @scrutinizer ignore-type */ $uniqueIds ?? []) === 0 ? 1 : 0; // Limit to 1 if no unique ids are found.
Loading history...
218
    }
219
220
    /**
221
     * Update one or more items in a table
222
     *
223
     * @param string $table         The table name
224
     * @param array  $options       The query options
225
     * @param array  $values        The updated values
226
     *
227
     * @return array
228
     */
229
    public function updateItem(string $table, array $options, array $values): array
230
    {
231
        $this->action = 'save';
232
        $this->operation = 'update';
233
234
        [$fields, $where] = $this->getFields($table, $options);
235
        $values = $this->reader()->getInputValues($fields, $values);
236
        $limit = $this->getQueryLimit($table, $options);
237
238
        if (!$this->driver->update($table, $values, "\nWHERE $where", $limit)) {
239
            return [
240
                'error' => $this->driver->error(),
241
            ];
242
        }
243
244
        // Get the modified data
245
        // Todo: check if the values in the where clause are changed.
246
        $statement = $this->driver->select($table, array_keys($values), [$where]);
247
        $result = !$statement ? null : $statement->fetchAssoc();
248
        if (!$result) {
249
            return [
250
                'warning' => $this->utils->trans->lang('Unable to read the updated row.'),
251
            ];
252
        }
253
254
        return [
255
            'cols' => $this->writer()->getUpdatedRow($result, $fields, $options),
256
            'message' => $this->utils->trans->lang('Item has been updated.'),
257
        ];
258
    }
259
260
    /**
261
     * Delete one or more items in a table
262
     *
263
     * @param string $table         The table name
264
     * @param array  $options       The query options
265
     *
266
     * @return array
267
     */
268
    public function deleteItem(string $table, array $options): array
269
    {
270
        $this->action = 'save';
271
        $this->operation = 'update';
272
273
        [, $where] = $this->getFields($table, $options);
274
        $limit = $this->getQueryLimit($table, $options);
275
276
        if (!$this->driver->delete($table, "\nWHERE $where", $limit)) {
277
            return [
278
                'error' => $this->driver->error(),
279
            ];
280
        }
281
282
        return [
283
            'message' => $this->utils->trans->lang('Item has been deleted.'),
284
        ];
285
    }
286
}
287