Total Complexity | 74 |
Total Lines | 478 |
Duplicated Lines | 0 % |
Changes | 3 | ||
Bugs | 0 | Features | 0 |
Complex classes like ModelClass often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ModelClass, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
34 | abstract class ModelClass extends ModelCore |
||
35 | { |
||
36 | /** |
||
37 | * Returns all models that correspond to the selected filters. |
||
38 | * |
||
39 | * @param array $where filters to apply to model records. |
||
40 | * @param array $order fields to use in the sorting. For example ['code' => 'ASC'] |
||
41 | * @param int $offset |
||
42 | * @param int $limit |
||
43 | * |
||
44 | * @return static[] |
||
45 | */ |
||
46 | public static function all(array $where = [], array $order = [], int $offset = 0, int $limit = 50): array |
||
60 | } |
||
61 | |||
62 | /** |
||
63 | * Allows to use this model as source in CodeModel special model. |
||
64 | * |
||
65 | * @param string $fieldCode |
||
66 | * |
||
67 | * @return CodeModel[] |
||
68 | */ |
||
69 | public function codeModelAll(string $fieldCode = ''): array |
||
70 | { |
||
71 | $results = []; |
||
72 | $field = empty($fieldCode) ? static::primaryColumn() : $fieldCode; |
||
73 | |||
74 | $sql = 'SELECT DISTINCT ' . $field . ' AS code, ' . $this->primaryDescriptionColumn() . ' AS description ' |
||
75 | . 'FROM ' . static::tableName() . ' ORDER BY 2 ASC'; |
||
76 | foreach (self::$dataBase->selectLimit($sql, CodeModel::ALL_LIMIT) as $d) { |
||
77 | $results[] = new CodeModel($d); |
||
78 | } |
||
79 | |||
80 | return $results; |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * Allows to use this model as source in CodeModel special model. |
||
85 | * |
||
86 | * @param string $query |
||
87 | * @param string $fieldCode |
||
88 | * @param DataBaseWhere[] $where |
||
89 | * |
||
90 | * @return CodeModel[] |
||
91 | */ |
||
92 | public function codeModelSearch(string $query, string $fieldCode = '', array $where = []): array |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | * Returns the number of records in the model that meet the condition. |
||
102 | * |
||
103 | * @param DataBaseWhere[] $where filters to apply to model records. |
||
104 | * |
||
105 | * @return int |
||
106 | */ |
||
107 | public function count(array $where = []): int |
||
108 | { |
||
109 | $sql = 'SELECT COUNT(1) AS total FROM ' . static::tableName(); |
||
110 | |||
111 | if ($where) { |
||
112 | $data = self::$dataBase->select($sql . DataBaseWhere::getSQLWhere($where)); |
||
113 | return empty($data) ? 0 : (int)$data[0]['total']; |
||
114 | } |
||
115 | |||
116 | $key = 'model-' . $this->modelClassName() . '-count'; |
||
117 | $count = Cache::get($key); |
||
118 | if (is_null($count)) { |
||
119 | $data = self::$dataBase->select($sql); |
||
120 | $count = empty($data) ? 0 : (int)$data[0]['total']; |
||
121 | Cache::set($key, $count); |
||
122 | } |
||
123 | |||
124 | return $count; |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * Remove the model data from the database. |
||
129 | * |
||
130 | * @return bool |
||
131 | */ |
||
132 | public function delete(): bool |
||
133 | { |
||
134 | if (null === $this->primaryColumnValue()) { |
||
135 | return true; |
||
136 | } |
||
137 | |||
138 | if ($this->pipeFalse('deleteBefore') === false) { |
||
139 | return false; |
||
140 | } |
||
141 | |||
142 | $sql = 'DELETE FROM ' . static::tableName() . ' WHERE ' . static::primaryColumn() |
||
143 | . ' = ' . self::$dataBase->var2str($this->primaryColumnValue()) . ';'; |
||
144 | |||
145 | if (false === self::$dataBase->exec($sql)) { |
||
146 | return false; |
||
147 | } |
||
148 | |||
149 | Cache::deleteMulti('model-' . $this->modelClassName() . '-'); |
||
150 | Cache::deleteMulti('join-model-'); |
||
151 | Cache::deleteMulti('table-' . static::tableName() . '-'); |
||
152 | |||
153 | WorkQueue::send( |
||
154 | 'Model.' . $this->modelClassName() . '.Delete', |
||
155 | $this->primaryColumnValue(), |
||
156 | $this->toArray() |
||
157 | ); |
||
158 | |||
159 | return $this->pipeFalse('delete'); |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * Returns true if the model data is stored in the database. |
||
164 | * |
||
165 | * @return bool |
||
166 | */ |
||
167 | public function exists(): bool |
||
168 | { |
||
169 | $sql = 'SELECT 1 FROM ' . static::tableName() . ' WHERE ' . static::primaryColumn() |
||
170 | . ' = ' . self::$dataBase->var2str($this->primaryColumnValue()) . ';'; |
||
171 | |||
172 | return !empty($this->primaryColumnValue()) && self::$dataBase->select($sql); |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * Returns the model whose primary column corresponds to the value $cod |
||
177 | * |
||
178 | * @param string $code |
||
179 | * |
||
180 | * @return static|false |
||
181 | */ |
||
182 | public function get($code) |
||
183 | { |
||
184 | $data = $this->getRecord($code); |
||
185 | return empty($data) ? false : new static($data[0]); |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Fill the class with the registry values |
||
190 | * whose primary column corresponds to the value $cod, or according to the condition |
||
191 | * where indicated, if value is not reported in $cod. |
||
192 | * Initializes the values of the class if there is no record that |
||
193 | * meet the above conditions. |
||
194 | * Returns True if the record exists and False otherwise. |
||
195 | * |
||
196 | * @param string $code |
||
197 | * @param array $where |
||
198 | * @param array $order |
||
199 | * |
||
200 | * @return bool |
||
201 | */ |
||
202 | public function loadFromCode($code, array $where = [], array $order = []): bool |
||
203 | { |
||
204 | $data = $this->getRecord($code, $where, $order); |
||
205 | if (empty($data)) { |
||
206 | $this->clear(); |
||
207 | return false; |
||
208 | } |
||
209 | |||
210 | $this->loadFromData($data[0]); |
||
211 | return true; |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * Returns the following code for the reported field or the primary key of the model. |
||
216 | * |
||
217 | * @param string $field |
||
218 | * @param array $where |
||
219 | * |
||
220 | * @return int |
||
221 | */ |
||
222 | public function newCode(string $field = '', array $where = []) |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * Returns the name of the column that describes the model, such as name, description... |
||
248 | * |
||
249 | * @return string |
||
250 | */ |
||
251 | public function primaryDescriptionColumn(): string |
||
252 | { |
||
253 | $fields = $this->getModelFields(); |
||
254 | return isset($fields['descripcion']) ? 'descripcion' : static::primaryColumn(); |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * Descriptive identifier for humans of the data record |
||
259 | * |
||
260 | * @return string |
||
261 | */ |
||
262 | public function primaryDescription() |
||
263 | { |
||
264 | $field = $this->primaryDescriptionColumn(); |
||
265 | return $this->{$field} ?? (string)$this->primaryColumnValue(); |
||
266 | } |
||
267 | |||
268 | /** |
||
269 | * Stores the model data in the database. |
||
270 | * |
||
271 | * @return bool |
||
272 | */ |
||
273 | public function save(): bool |
||
274 | { |
||
275 | if ($this->pipeFalse('saveBefore') === false) { |
||
276 | return false; |
||
277 | } |
||
278 | |||
279 | if (false === $this->test()) { |
||
280 | return false; |
||
281 | } |
||
282 | |||
283 | $done = $this->exists() ? $this->saveUpdate() : $this->saveInsert(); |
||
284 | if (false === $done) { |
||
285 | return false; |
||
286 | } |
||
287 | |||
288 | WorkQueue::send( |
||
289 | 'Model.' . $this->modelClassName() . '.Save', |
||
290 | $this->primaryColumnValue(), |
||
291 | $this->toArray() |
||
292 | ); |
||
293 | |||
294 | return $this->pipeFalse('save'); |
||
295 | } |
||
296 | |||
297 | /** |
||
298 | * Returns true if there are no errors in the values of the model properties. |
||
299 | * It runs inside the save method. |
||
300 | * |
||
301 | * @return bool |
||
302 | */ |
||
303 | public function test(): bool |
||
304 | { |
||
305 | if ($this->pipeFalse('testBefore') === false) { |
||
306 | return false; |
||
307 | } |
||
308 | |||
309 | // comprobamos que los campos no nulos tengan algún valor asignado |
||
310 | $fields = $this->getModelFields(); |
||
311 | if (empty($fields)) { |
||
312 | return false; |
||
313 | } |
||
314 | $return = true; |
||
315 | foreach ($fields as $key => $value) { |
||
316 | if ($key == static::primaryColumn()) { |
||
317 | $this->{$key} = empty($this->{$key}) ? null : $this->{$key}; |
||
318 | } elseif (null === $value['default'] && $value['is_nullable'] === 'NO' && $this->{$key} === null) { |
||
319 | Tools::log()->warning('field-can-not-be-null', ['%fieldName%' => $key, '%tableName%' => static::tableName()]); |
||
320 | $return = false; |
||
321 | } |
||
322 | } |
||
323 | if (false === $return) { |
||
324 | return false; |
||
325 | } |
||
326 | |||
327 | return $this->pipeFalse('test'); |
||
328 | } |
||
329 | |||
330 | public function totalSum(string $field, array $where = []): float |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * Returns the url where to see / modify the data. |
||
352 | * |
||
353 | * @param string $type |
||
354 | * @param string $list |
||
355 | * |
||
356 | * @return string |
||
357 | */ |
||
358 | public function url(string $type = 'auto', string $list = 'List'): string |
||
359 | { |
||
360 | $value = $this->primaryColumnValue(); |
||
361 | $model = $this->modelClassName(); |
||
362 | |||
363 | $return = $this->pipe('url', $type, $list); |
||
364 | if ($return) { |
||
365 | return $return; |
||
366 | } |
||
367 | |||
368 | switch ($type) { |
||
369 | case 'edit': |
||
370 | return is_null($value) ? 'Edit' . $model : 'Edit' . $model . '?code=' . rawurlencode($value); |
||
371 | |||
372 | case 'list': |
||
373 | return $list . $model; |
||
374 | |||
375 | case 'new': |
||
376 | return 'Edit' . $model; |
||
377 | } |
||
378 | |||
379 | // default |
||
380 | return empty($value) ? $list . $model : 'Edit' . $model . '?code=' . rawurlencode($value); |
||
381 | } |
||
382 | |||
383 | /** |
||
384 | * Insert the model data in the database. |
||
385 | * |
||
386 | * @param array $values |
||
387 | * |
||
388 | * @return bool |
||
389 | */ |
||
390 | protected function saveInsert(array $values = []): bool |
||
431 | } |
||
432 | |||
433 | /** |
||
434 | * Update the model data in the database. |
||
435 | * |
||
436 | * @param array $values |
||
437 | * |
||
438 | * @return bool |
||
439 | */ |
||
440 | protected function saveUpdate(array $values = []): bool |
||
441 | { |
||
442 | if ($this->pipeFalse('saveUpdateBefore') === false) { |
||
443 | return false; |
||
444 | } |
||
445 | |||
446 | $sql = 'UPDATE ' . static::tableName(); |
||
447 | $coma = ' SET'; |
||
448 | |||
449 | foreach ($this->getModelFields() as $field) { |
||
450 | if ($field['name'] !== static::primaryColumn()) { |
||
451 | $fieldName = $field['name']; |
||
452 | $fieldValue = $values[$fieldName] ?? $this->{$fieldName}; |
||
453 | $sql .= $coma . ' ' . self::$dataBase->escapeColumn($fieldName) . ' = ' . self::$dataBase->var2str($fieldValue); |
||
454 | $coma = ', '; |
||
455 | } |
||
456 | } |
||
457 | |||
458 | $sql .= ' WHERE ' . static::primaryColumn() . ' = ' . self::$dataBase->var2str($this->primaryColumnValue()) . ';'; |
||
459 | if (false === self::$dataBase->exec($sql)) { |
||
460 | return false; |
||
461 | } |
||
462 | |||
463 | Cache::deleteMulti('model-' . $this->modelClassName() . '-'); |
||
464 | Cache::deleteMulti('join-model-'); |
||
465 | Cache::deleteMulti('table-' . static::tableName() . '-'); |
||
466 | |||
467 | WorkQueue::send( |
||
468 | 'Model.' . $this->modelClassName() . '.Update', |
||
469 | $this->primaryColumnValue(), |
||
470 | $this->toArray() |
||
471 | ); |
||
472 | |||
473 | return $this->pipeFalse('saveUpdate'); |
||
474 | } |
||
475 | |||
476 | /** |
||
477 | * Convert an array of filters order by in string. |
||
478 | * |
||
479 | * @param array $order |
||
480 | * |
||
481 | * @return string |
||
482 | */ |
||
483 | private static function getOrderBy(array $order): string |
||
493 | } |
||
494 | |||
495 | /** |
||
496 | * Read the record whose primary column corresponds to the value $cod |
||
497 | * or the first that meets the indicated condition. |
||
498 | * |
||
499 | * @param string $code |
||
500 | * @param array $where |
||
501 | * @param array $order |
||
502 | * |
||
503 | * @return array |
||
504 | */ |
||
505 | private function getRecord($code, array $where = [], array $order = []): array |
||
512 | } |
||
513 | } |
||
514 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.