| 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
Loading history...
|
|||||||
| 38 | $this->utils, $this->action, $this->operation); |
||||||
| 39 | $fieldInput = new DataFieldInput($this->page, $this->driver, |
||||||
|
0 ignored issues
–
show
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
Loading history...
|
|||||||
| 40 | $this->utils, $this->action, $this->operation); |
||||||
| 41 | return new DataRowWriter($this->page, $this->driver, $this->utils, |
||||||
|
0 ignored issues
–
show
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
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
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
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
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
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
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
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 |